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 RegionSelectionAfterSplit rsas = Config->get_region_selection_after_split();
282 /* There are three classes of regions that we might want selected after
283 splitting selected regions:
284 - regions selected before the split operation, and unaffected by it
285 - newly-created regions before the split
286 - newly-created regions after the split
289 if (rsas & Existing) {
290 // region selections that existed before the split.
291 selection->add ( pre_selected_regions );
294 for (RegionSelection::iterator ri = latest_regionviews.begin(); ri != latest_regionviews.end(); ri++) {
295 if ((*ri)->region()->position() < where) {
296 // new regions created before the split
297 if (rsas & NewlyCreatedLeft) {
298 selection->add (*ri);
301 // new regions created after the split
302 if (rsas & NewlyCreatedRight) {
303 selection->add (*ri);
308 if( working_on_selection ) {
309 selection->add (latest_regionviews); //these are the new regions created after the split
313 commit_reversible_command ();
316 /** Move one extreme of the current range selection. If more than one range is selected,
317 * the start of the earliest range or the end of the latest range is moved.
319 * @param move_end true to move the end of the current range selection, false to move
321 * @param next true to move the extreme to the next region boundary, false to move to
325 Editor::move_range_selection_start_or_end_to_region_boundary (bool move_end, bool next)
327 if (selection->time.start() == selection->time.end_frame()) {
331 framepos_t start = selection->time.start ();
332 framepos_t end = selection->time.end_frame ();
334 /* the position of the thing we may move */
335 framepos_t pos = move_end ? end : start;
336 int dir = next ? 1 : -1;
338 /* so we don't find the current region again */
339 if (dir > 0 || pos > 0) {
343 framepos_t const target = get_region_boundary (pos, dir, true, false);
358 begin_reversible_selection_op (_("alter selection"));
359 selection->set_preserving_all_ranges (start, end);
360 commit_reversible_selection_op ();
364 Editor::nudge_forward_release (GdkEventButton* ev)
366 if (ev->state & Keyboard::PrimaryModifier) {
367 nudge_forward (false, true);
369 nudge_forward (false, false);
375 Editor::nudge_backward_release (GdkEventButton* ev)
377 if (ev->state & Keyboard::PrimaryModifier) {
378 nudge_backward (false, true);
380 nudge_backward (false, false);
387 Editor::nudge_forward (bool next, bool force_playhead)
390 framepos_t next_distance;
396 RegionSelection rs = get_regions_from_selection_and_entered ();
398 if (!force_playhead && !rs.empty()) {
400 begin_reversible_command (_("nudge regions forward"));
402 for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
403 boost::shared_ptr<Region> r ((*i)->region());
405 distance = get_nudge_distance (r->position(), next_distance);
408 distance = next_distance;
412 r->set_position (r->position() + distance);
413 _session->add_command (new StatefulDiffCommand (r));
416 commit_reversible_command ();
419 } else if (!force_playhead && !selection->markers.empty()) {
422 bool in_command = false;
423 const int32_t divisions = get_grid_music_divisions (0);
425 for (MarkerSelection::iterator i = selection->markers.begin(); i != selection->markers.end(); ++i) {
427 Location* loc = find_location_from_marker ((*i), is_start);
431 XMLNode& before (loc->get_state());
434 distance = get_nudge_distance (loc->start(), next_distance);
436 distance = next_distance;
438 if (max_framepos - distance > loc->start() + loc->length()) {
439 loc->set_start (loc->start() + distance, false, true, divisions);
441 loc->set_start (max_framepos - loc->length(), false, true, divisions);
444 distance = get_nudge_distance (loc->end(), next_distance);
446 distance = next_distance;
448 if (max_framepos - distance > loc->end()) {
449 loc->set_end (loc->end() + distance, false, true, divisions);
451 loc->set_end (max_framepos, false, true, divisions);
453 if (loc->is_session_range()) {
454 _session->set_end_is_free (false);
458 begin_reversible_command (_("nudge location forward"));
461 XMLNode& after (loc->get_state());
462 _session->add_command (new MementoCommand<Location>(*loc, &before, &after));
467 commit_reversible_command ();
470 distance = get_nudge_distance (playhead_cursor->current_frame (), next_distance);
471 _session->request_locate (playhead_cursor->current_frame () + distance);
476 Editor::nudge_backward (bool next, bool force_playhead)
479 framepos_t next_distance;
485 RegionSelection rs = get_regions_from_selection_and_entered ();
487 if (!force_playhead && !rs.empty()) {
489 begin_reversible_command (_("nudge regions backward"));
491 for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
492 boost::shared_ptr<Region> r ((*i)->region());
494 distance = get_nudge_distance (r->position(), next_distance);
497 distance = next_distance;
502 if (r->position() > distance) {
503 r->set_position (r->position() - distance);
507 _session->add_command (new StatefulDiffCommand (r));
510 commit_reversible_command ();
512 } else if (!force_playhead && !selection->markers.empty()) {
515 bool in_command = false;
517 for (MarkerSelection::iterator i = selection->markers.begin(); i != selection->markers.end(); ++i) {
519 Location* loc = find_location_from_marker ((*i), is_start);
523 XMLNode& before (loc->get_state());
526 distance = get_nudge_distance (loc->start(), next_distance);
528 distance = next_distance;
530 if (distance < loc->start()) {
531 loc->set_start (loc->start() - distance, false, true, get_grid_music_divisions(0));
533 loc->set_start (0, false, true, get_grid_music_divisions(0));
536 distance = get_nudge_distance (loc->end(), next_distance);
539 distance = next_distance;
542 if (distance < loc->end() - loc->length()) {
543 loc->set_end (loc->end() - distance, false, true, get_grid_music_divisions(0));
545 loc->set_end (loc->length(), false, true, get_grid_music_divisions(0));
547 if (loc->is_session_range()) {
548 _session->set_end_is_free (false);
552 begin_reversible_command (_("nudge location forward"));
555 XMLNode& after (loc->get_state());
556 _session->add_command (new MementoCommand<Location>(*loc, &before, &after));
560 commit_reversible_command ();
565 distance = get_nudge_distance (playhead_cursor->current_frame (), next_distance);
567 if (playhead_cursor->current_frame () > distance) {
568 _session->request_locate (playhead_cursor->current_frame () - distance);
570 _session->goto_start();
576 Editor::nudge_forward_capture_offset ()
578 RegionSelection rs = get_regions_from_selection_and_entered ();
580 if (!_session || rs.empty()) {
584 begin_reversible_command (_("nudge forward"));
586 framepos_t const distance = _session->worst_output_latency();
588 for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
589 boost::shared_ptr<Region> r ((*i)->region());
592 r->set_position (r->position() + distance);
593 _session->add_command(new StatefulDiffCommand (r));
596 commit_reversible_command ();
600 Editor::nudge_backward_capture_offset ()
602 RegionSelection rs = get_regions_from_selection_and_entered ();
604 if (!_session || rs.empty()) {
608 begin_reversible_command (_("nudge backward"));
610 framepos_t const distance = _session->worst_output_latency();
612 for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
613 boost::shared_ptr<Region> r ((*i)->region());
617 if (r->position() > distance) {
618 r->set_position (r->position() - distance);
622 _session->add_command(new StatefulDiffCommand (r));
625 commit_reversible_command ();
628 struct RegionSelectionPositionSorter {
629 bool operator() (RegionView* a, RegionView* b) {
630 return a->region()->position() < b->region()->position();
635 Editor::sequence_regions ()
638 framepos_t r_end_prev;
646 RegionSelection rs = get_regions_from_selection_and_entered ();
647 rs.sort(RegionSelectionPositionSorter());
651 bool in_command = false;
653 for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
654 boost::shared_ptr<Region> r ((*i)->region());
662 if(r->position_locked())
669 r->set_position(r_end_prev);
673 begin_reversible_command (_("sequence regions"));
676 _session->add_command (new StatefulDiffCommand (r));
678 r_end=r->position() + r->length();
684 commit_reversible_command ();
693 Editor::move_to_start ()
695 _session->goto_start ();
699 Editor::move_to_end ()
702 _session->request_locate (_session->current_end_frame());
706 Editor::build_region_boundary_cache ()
709 vector<RegionPoint> interesting_points;
710 boost::shared_ptr<Region> r;
711 TrackViewList tracks;
714 region_boundary_cache.clear ();
720 switch (_snap_type) {
721 case SnapToRegionStart:
722 interesting_points.push_back (Start);
724 case SnapToRegionEnd:
725 interesting_points.push_back (End);
727 case SnapToRegionSync:
728 interesting_points.push_back (SyncPoint);
730 case SnapToRegionBoundary:
731 interesting_points.push_back (Start);
732 interesting_points.push_back (End);
735 fatal << string_compose (_("build_region_boundary_cache called with snap_type = %1"), _snap_type) << endmsg;
736 abort(); /*NOTREACHED*/
740 TimeAxisView *ontrack = 0;
743 if (!selection->tracks.empty()) {
744 tlist = selection->tracks.filter_to_unique_playlists ();
746 tlist = track_views.filter_to_unique_playlists ();
749 while (pos < _session->current_end_frame() && !at_end) {
752 framepos_t lpos = max_framepos;
754 for (vector<RegionPoint>::iterator p = interesting_points.begin(); p != interesting_points.end(); ++p) {
756 if ((r = find_next_region (pos, *p, 1, tlist, &ontrack)) == 0) {
757 if (*p == interesting_points.back()) {
760 /* move to next point type */
766 rpos = r->first_frame();
770 rpos = r->last_frame();
774 rpos = r->sync_position ();
782 RouteTimeAxisView *rtav;
784 if (ontrack != 0 && (rtav = dynamic_cast<RouteTimeAxisView*>(ontrack)) != 0 ) {
785 if (rtav->track() != 0) {
786 speed = rtav->track()->speed();
790 rpos = track_frame_to_session_frame (rpos, speed);
796 /* prevent duplicates, but we don't use set<> because we want to be able
800 vector<framepos_t>::iterator ri;
802 for (ri = region_boundary_cache.begin(); ri != region_boundary_cache.end(); ++ri) {
808 if (ri == region_boundary_cache.end()) {
809 region_boundary_cache.push_back (rpos);
816 /* finally sort to be sure that the order is correct */
818 sort (region_boundary_cache.begin(), region_boundary_cache.end());
821 boost::shared_ptr<Region>
822 Editor::find_next_region (framepos_t frame, RegionPoint point, int32_t dir, TrackViewList& tracks, TimeAxisView **ontrack)
824 TrackViewList::iterator i;
825 framepos_t closest = max_framepos;
826 boost::shared_ptr<Region> ret;
830 framepos_t track_frame;
831 RouteTimeAxisView *rtav;
833 for (i = tracks.begin(); i != tracks.end(); ++i) {
836 boost::shared_ptr<Region> r;
839 if ( (rtav = dynamic_cast<RouteTimeAxisView*>(*i)) != 0 ) {
840 if (rtav->track()!=0)
841 track_speed = rtav->track()->speed();
844 track_frame = session_frame_to_track_frame(frame, track_speed);
846 if ((r = (*i)->find_next_region (track_frame, point, dir)) == 0) {
852 rpos = r->first_frame ();
856 rpos = r->last_frame ();
860 rpos = r->sync_position ();
864 // rpos is a "track frame", converting it to "_session frame"
865 rpos = track_frame_to_session_frame(rpos, track_speed);
868 distance = rpos - frame;
870 distance = frame - rpos;
873 if (distance < closest) {
885 Editor::find_next_region_boundary (framepos_t pos, int32_t dir, const TrackViewList& tracks)
887 framecnt_t distance = max_framepos;
888 framepos_t current_nearest = -1;
890 for (TrackViewList::const_iterator i = tracks.begin(); i != tracks.end(); ++i) {
891 framepos_t contender;
894 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (*i);
900 if ((contender = rtv->find_next_region_boundary (pos, dir)) < 0) {
904 d = ::llabs (pos - contender);
907 current_nearest = contender;
912 return current_nearest;
916 Editor::get_region_boundary (framepos_t pos, int32_t dir, bool with_selection, bool only_onscreen)
921 if (with_selection && Config->get_region_boundaries_from_selected_tracks()) {
923 if (!selection->tracks.empty()) {
925 target = find_next_region_boundary (pos, dir, selection->tracks);
929 if (only_onscreen || Config->get_region_boundaries_from_onscreen_tracks()) {
930 get_onscreen_tracks (tvl);
931 target = find_next_region_boundary (pos, dir, tvl);
933 target = find_next_region_boundary (pos, dir, track_views);
939 if (only_onscreen || Config->get_region_boundaries_from_onscreen_tracks()) {
940 get_onscreen_tracks (tvl);
941 target = find_next_region_boundary (pos, dir, tvl);
943 target = find_next_region_boundary (pos, dir, track_views);
951 Editor::cursor_to_region_boundary (bool with_selection, int32_t dir)
953 framepos_t pos = playhead_cursor->current_frame ();
960 // so we don't find the current region again..
961 if (dir > 0 || pos > 0) {
965 if ((target = get_region_boundary (pos, dir, with_selection, false)) < 0) {
969 _session->request_locate (target);
973 Editor::cursor_to_next_region_boundary (bool with_selection)
975 cursor_to_region_boundary (with_selection, 1);
979 Editor::cursor_to_previous_region_boundary (bool with_selection)
981 cursor_to_region_boundary (with_selection, -1);
985 Editor::cursor_to_region_point (EditorCursor* cursor, RegionPoint point, int32_t dir)
987 boost::shared_ptr<Region> r;
988 framepos_t pos = cursor->current_frame ();
994 TimeAxisView *ontrack = 0;
996 // so we don't find the current region again..
1000 if (!selection->tracks.empty()) {
1002 r = find_next_region (pos, point, dir, selection->tracks, &ontrack);
1004 } else if (clicked_axisview) {
1007 t.push_back (clicked_axisview);
1009 r = find_next_region (pos, point, dir, t, &ontrack);
1013 r = find_next_region (pos, point, dir, track_views, &ontrack);
1022 pos = r->first_frame ();
1026 pos = r->last_frame ();
1030 pos = r->sync_position ();
1035 RouteTimeAxisView *rtav;
1037 if ( ontrack != 0 && (rtav = dynamic_cast<RouteTimeAxisView*>(ontrack)) != 0 ) {
1038 if (rtav->track() != 0) {
1039 speed = rtav->track()->speed();
1043 pos = track_frame_to_session_frame(pos, speed);
1045 if (cursor == playhead_cursor) {
1046 _session->request_locate (pos);
1048 cursor->set_position (pos);
1053 Editor::cursor_to_next_region_point (EditorCursor* cursor, RegionPoint point)
1055 cursor_to_region_point (cursor, point, 1);
1059 Editor::cursor_to_previous_region_point (EditorCursor* cursor, RegionPoint point)
1061 cursor_to_region_point (cursor, point, -1);
1065 Editor::cursor_to_selection_start (EditorCursor *cursor)
1069 switch (mouse_mode) {
1071 if (!selection->regions.empty()) {
1072 pos = selection->regions.start();
1077 if (!selection->time.empty()) {
1078 pos = selection->time.start ();
1086 if (cursor == playhead_cursor) {
1087 _session->request_locate (pos);
1089 cursor->set_position (pos);
1094 Editor::cursor_to_selection_end (EditorCursor *cursor)
1098 switch (mouse_mode) {
1100 if (!selection->regions.empty()) {
1101 pos = selection->regions.end_frame();
1106 if (!selection->time.empty()) {
1107 pos = selection->time.end_frame ();
1115 if (cursor == playhead_cursor) {
1116 _session->request_locate (pos);
1118 cursor->set_position (pos);
1123 Editor::selected_marker_to_region_boundary (bool with_selection, int32_t dir)
1133 if (selection->markers.empty()) {
1137 if (!mouse_frame (mouse, ignored)) {
1141 add_location_mark (mouse);
1144 if ((loc = find_location_from_marker (selection->markers.front(), ignored)) == 0) {
1148 framepos_t pos = loc->start();
1150 // so we don't find the current region again..
1151 if (dir > 0 || pos > 0) {
1155 if ((target = get_region_boundary (pos, dir, with_selection, false)) < 0) {
1159 loc->move_to (target, 0);
1163 Editor::selected_marker_to_next_region_boundary (bool with_selection)
1165 selected_marker_to_region_boundary (with_selection, 1);
1169 Editor::selected_marker_to_previous_region_boundary (bool with_selection)
1171 selected_marker_to_region_boundary (with_selection, -1);
1175 Editor::selected_marker_to_region_point (RegionPoint point, int32_t dir)
1177 boost::shared_ptr<Region> r;
1182 if (!_session || selection->markers.empty()) {
1186 if ((loc = find_location_from_marker (selection->markers.front(), ignored)) == 0) {
1190 TimeAxisView *ontrack = 0;
1194 // so we don't find the current region again..
1198 if (!selection->tracks.empty()) {
1200 r = find_next_region (pos, point, dir, selection->tracks, &ontrack);
1204 r = find_next_region (pos, point, dir, track_views, &ontrack);
1213 pos = r->first_frame ();
1217 pos = r->last_frame ();
1221 pos = r->adjust_to_sync (r->first_frame());
1226 RouteTimeAxisView *rtav;
1228 if (ontrack != 0 && (rtav = dynamic_cast<RouteTimeAxisView*>(ontrack)) != 0) {
1229 if (rtav->track() != 0) {
1230 speed = rtav->track()->speed();
1234 pos = track_frame_to_session_frame(pos, speed);
1236 loc->move_to (pos, 0);
1240 Editor::selected_marker_to_next_region_point (RegionPoint point)
1242 selected_marker_to_region_point (point, 1);
1246 Editor::selected_marker_to_previous_region_point (RegionPoint point)
1248 selected_marker_to_region_point (point, -1);
1252 Editor::selected_marker_to_selection_start ()
1258 if (!_session || selection->markers.empty()) {
1262 if ((loc = find_location_from_marker (selection->markers.front(), ignored)) == 0) {
1266 switch (mouse_mode) {
1268 if (!selection->regions.empty()) {
1269 pos = selection->regions.start();
1274 if (!selection->time.empty()) {
1275 pos = selection->time.start ();
1283 loc->move_to (pos, 0);
1287 Editor::selected_marker_to_selection_end ()
1293 if (!_session || selection->markers.empty()) {
1297 if ((loc = find_location_from_marker (selection->markers.front(), ignored)) == 0) {
1301 switch (mouse_mode) {
1303 if (!selection->regions.empty()) {
1304 pos = selection->regions.end_frame();
1309 if (!selection->time.empty()) {
1310 pos = selection->time.end_frame ();
1318 loc->move_to (pos, 0);
1322 Editor::scroll_playhead (bool forward)
1324 framepos_t pos = playhead_cursor->current_frame ();
1325 framecnt_t delta = (framecnt_t) floor (current_page_samples() / 0.8);
1328 if (pos == max_framepos) {
1332 if (pos < max_framepos - delta) {
1351 _session->request_locate (pos);
1355 Editor::cursor_align (bool playhead_to_edit)
1361 if (playhead_to_edit) {
1363 if (selection->markers.empty()) {
1367 _session->request_locate (selection->markers.front()->position(), _session->transport_rolling());
1370 const int32_t divisions = get_grid_music_divisions (0);
1371 /* move selected markers to playhead */
1373 for (MarkerSelection::iterator i = selection->markers.begin(); i != selection->markers.end(); ++i) {
1376 Location* loc = find_location_from_marker (*i, ignored);
1378 if (loc->is_mark()) {
1379 loc->set_start (playhead_cursor->current_frame (), false, true, divisions);
1381 loc->set (playhead_cursor->current_frame (),
1382 playhead_cursor->current_frame () + loc->length(), true, divisions);
1389 Editor::scroll_backward (float pages)
1391 framepos_t const one_page = (framepos_t) rint (_visible_canvas_width * samples_per_pixel);
1392 framepos_t const cnt = (framepos_t) floor (pages * one_page);
1395 if (leftmost_frame < cnt) {
1398 frame = leftmost_frame - cnt;
1401 reset_x_origin (frame);
1405 Editor::scroll_forward (float pages)
1407 framepos_t const one_page = (framepos_t) rint (_visible_canvas_width * samples_per_pixel);
1408 framepos_t const cnt = (framepos_t) floor (pages * one_page);
1411 if (max_framepos - cnt < leftmost_frame) {
1412 frame = max_framepos - cnt;
1414 frame = leftmost_frame + cnt;
1417 reset_x_origin (frame);
1421 Editor::scroll_tracks_down ()
1423 double vert_value = vertical_adjustment.get_value() + vertical_adjustment.get_page_size();
1424 if (vert_value > vertical_adjustment.get_upper() - _visible_canvas_height) {
1425 vert_value = vertical_adjustment.get_upper() - _visible_canvas_height;
1428 vertical_adjustment.set_value (vert_value);
1432 Editor::scroll_tracks_up ()
1434 vertical_adjustment.set_value (vertical_adjustment.get_value() - vertical_adjustment.get_page_size());
1438 Editor::scroll_tracks_down_line ()
1440 double vert_value = vertical_adjustment.get_value() + 60;
1442 if (vert_value > vertical_adjustment.get_upper() - _visible_canvas_height) {
1443 vert_value = vertical_adjustment.get_upper() - _visible_canvas_height;
1446 vertical_adjustment.set_value (vert_value);
1450 Editor::scroll_tracks_up_line ()
1452 reset_y_origin (vertical_adjustment.get_value() - 60);
1456 Editor::scroll_down_one_track (bool skip_child_views)
1458 TrackViewList::reverse_iterator next = track_views.rend();
1459 const double top_of_trackviews = vertical_adjustment.get_value();
1461 for (TrackViewList::reverse_iterator t = track_views.rbegin(); t != track_views.rend(); ++t) {
1462 if ((*t)->hidden()) {
1466 /* If this is the upper-most visible trackview, we want to display
1467 * the one above it (next)
1469 * Note that covers_y_position() is recursive and includes child views
1471 std::pair<TimeAxisView*,double> res = (*t)->covers_y_position (top_of_trackviews);
1474 if (skip_child_views) {
1477 /* automation lane (one level, non-recursive)
1479 * - if no automation lane exists -> move to next tack
1480 * - if the first (here: bottom-most) matches -> move to next tack
1481 * - if no y-axis match is found -> the current track is at the top
1482 * -> move to last (here: top-most) automation lane
1484 TimeAxisView::Children kids = (*t)->get_child_list();
1485 TimeAxisView::Children::reverse_iterator nkid = kids.rend();
1487 for (TimeAxisView::Children::reverse_iterator ci = kids.rbegin(); ci != kids.rend(); ++ci) {
1488 if ((*ci)->hidden()) {
1492 std::pair<TimeAxisView*,double> dev;
1493 dev = (*ci)->covers_y_position (top_of_trackviews);
1495 /* some automation lane is currently at the top */
1496 if (ci == kids.rbegin()) {
1497 /* first (bottom-most) autmation lane is at the top.
1498 * -> move to next track
1507 if (nkid != kids.rend()) {
1508 ensure_time_axis_view_is_visible (**nkid, true);
1516 /* move to the track below the first one that covers the */
1518 if (next != track_views.rend()) {
1519 ensure_time_axis_view_is_visible (**next, true);
1527 Editor::scroll_up_one_track (bool skip_child_views)
1529 TrackViewList::iterator prev = track_views.end();
1530 double top_of_trackviews = vertical_adjustment.get_value ();
1532 for (TrackViewList::iterator t = track_views.begin(); t != track_views.end(); ++t) {
1534 if ((*t)->hidden()) {
1538 /* find the trackview at the top of the trackview group
1540 * Note that covers_y_position() is recursive and includes child views
1542 std::pair<TimeAxisView*,double> res = (*t)->covers_y_position (top_of_trackviews);
1545 if (skip_child_views) {
1548 /* automation lane (one level, non-recursive)
1550 * - if no automation lane exists -> move to prev tack
1551 * - if no y-axis match is found -> the current track is at the top -> move to prev track
1552 * (actually last automation lane of previous track, see below)
1553 * - if first (top-most) lane is at the top -> move to this track
1554 * - else move up one lane
1556 TimeAxisView::Children kids = (*t)->get_child_list();
1557 TimeAxisView::Children::iterator pkid = kids.end();
1559 for (TimeAxisView::Children::iterator ci = kids.begin(); ci != kids.end(); ++ci) {
1560 if ((*ci)->hidden()) {
1564 std::pair<TimeAxisView*,double> dev;
1565 dev = (*ci)->covers_y_position (top_of_trackviews);
1567 /* some automation lane is currently at the top */
1568 if (ci == kids.begin()) {
1569 /* first (top-most) autmation lane is at the top.
1570 * jump directly to this track's top
1572 ensure_time_axis_view_is_visible (**t, true);
1575 else if (pkid != kids.end()) {
1576 /* some other automation lane is at the top.
1577 * move up to prev automation lane.
1579 ensure_time_axis_view_is_visible (**pkid, true);
1582 assert(0); // not reached
1593 if (prev != track_views.end()) {
1594 // move to bottom-most automation-lane of the previous track
1595 TimeAxisView::Children kids = (*prev)->get_child_list();
1596 TimeAxisView::Children::reverse_iterator pkid = kids.rend();
1597 if (!skip_child_views) {
1598 // find the last visible lane
1599 for (TimeAxisView::Children::reverse_iterator ci = kids.rbegin(); ci != kids.rend(); ++ci) {
1600 if (!(*ci)->hidden()) {
1606 if (pkid != kids.rend()) {
1607 ensure_time_axis_view_is_visible (**pkid, true);
1609 ensure_time_axis_view_is_visible (**prev, true);
1618 Editor::scroll_left_step ()
1620 framepos_t xdelta = (current_page_samples() / 8);
1622 if (leftmost_frame > xdelta) {
1623 reset_x_origin (leftmost_frame - xdelta);
1631 Editor::scroll_right_step ()
1633 framepos_t xdelta = (current_page_samples() / 8);
1635 if (max_framepos - xdelta > leftmost_frame) {
1636 reset_x_origin (leftmost_frame + xdelta);
1638 reset_x_origin (max_framepos - current_page_samples());
1643 Editor::scroll_left_half_page ()
1645 framepos_t xdelta = (current_page_samples() / 2);
1646 if (leftmost_frame > xdelta) {
1647 reset_x_origin (leftmost_frame - xdelta);
1654 Editor::scroll_right_half_page ()
1656 framepos_t xdelta = (current_page_samples() / 2);
1657 if (max_framepos - xdelta > leftmost_frame) {
1658 reset_x_origin (leftmost_frame + xdelta);
1660 reset_x_origin (max_framepos - current_page_samples());
1667 Editor::tav_zoom_step (bool coarser)
1669 DisplaySuspender ds;
1673 if (selection->tracks.empty()) {
1676 ts = &selection->tracks;
1679 for (TrackViewList::iterator i = ts->begin(); i != ts->end(); ++i) {
1680 TimeAxisView *tv = (static_cast<TimeAxisView*>(*i));
1681 tv->step_height (coarser);
1686 Editor::tav_zoom_smooth (bool coarser, bool force_all)
1688 DisplaySuspender ds;
1692 if (selection->tracks.empty() || force_all) {
1695 ts = &selection->tracks;
1698 for (TrackViewList::iterator i = ts->begin(); i != ts->end(); ++i) {
1699 TimeAxisView *tv = (static_cast<TimeAxisView*>(*i));
1700 uint32_t h = tv->current_height ();
1705 if (h >= TimeAxisView::preset_height (HeightSmall)) {
1710 tv->set_height (h + 5);
1716 Editor::temporal_zoom_step_mouse_focus_scale (bool zoom_out, double scale)
1718 Editing::ZoomFocus temp_focus = zoom_focus;
1719 zoom_focus = Editing::ZoomFocusMouse;
1720 temporal_zoom_step_scale (zoom_out, scale);
1721 zoom_focus = temp_focus;
1725 Editor::temporal_zoom_step_mouse_focus (bool zoom_out)
1727 temporal_zoom_step_mouse_focus_scale (zoom_out, 2.0);
1731 Editor::temporal_zoom_step (bool zoom_out)
1733 temporal_zoom_step_scale (zoom_out, 2.0);
1737 Editor::temporal_zoom_step_scale (bool zoom_out, double scale)
1739 ENSURE_GUI_THREAD (*this, &Editor::temporal_zoom_step, zoom_out, scale)
1741 framecnt_t nspp = samples_per_pixel;
1745 if (nspp == samples_per_pixel) {
1750 if (nspp == samples_per_pixel) {
1755 temporal_zoom (nspp);
1759 Editor::temporal_zoom (framecnt_t fpp)
1765 framepos_t current_page = current_page_samples();
1766 framepos_t current_leftmost = leftmost_frame;
1767 framepos_t current_rightmost;
1768 framepos_t current_center;
1769 framepos_t new_page_size;
1770 framepos_t half_page_size;
1771 framepos_t leftmost_after_zoom = 0;
1773 bool in_track_canvas;
1774 bool use_mouse_frame = true;
1778 if (fpp == samples_per_pixel) {
1782 // Imposing an arbitrary limit to zoom out as too much zoom out produces
1783 // segfaults for lack of memory. If somebody decides this is not high enough I
1784 // believe it can be raisen to higher values but some limit must be in place.
1786 // This constant represents 1 day @ 48kHz on a 1600 pixel wide display
1787 // all of which is used for the editor track displays. The whole day
1788 // would be 4147200000 samples, so 2592000 samples per pixel.
1790 nfpp = min (fpp, (framecnt_t) 2592000);
1791 nfpp = max ((framecnt_t) 1, nfpp);
1793 new_page_size = (framepos_t) floor (_visible_canvas_width * nfpp);
1794 half_page_size = new_page_size / 2;
1796 switch (zoom_focus) {
1798 leftmost_after_zoom = current_leftmost;
1801 case ZoomFocusRight:
1802 current_rightmost = leftmost_frame + current_page;
1803 if (current_rightmost < new_page_size) {
1804 leftmost_after_zoom = 0;
1806 leftmost_after_zoom = current_rightmost - new_page_size;
1810 case ZoomFocusCenter:
1811 current_center = current_leftmost + (current_page/2);
1812 if (current_center < half_page_size) {
1813 leftmost_after_zoom = 0;
1815 leftmost_after_zoom = current_center - half_page_size;
1819 case ZoomFocusPlayhead:
1820 /* centre playhead */
1821 l = playhead_cursor->current_frame () - (new_page_size * 0.5);
1824 leftmost_after_zoom = 0;
1825 } else if (l > max_framepos) {
1826 leftmost_after_zoom = max_framepos - new_page_size;
1828 leftmost_after_zoom = (framepos_t) l;
1832 case ZoomFocusMouse:
1833 /* try to keep the mouse over the same point in the display */
1835 if (_drags->active()) {
1836 where = _drags->current_pointer_frame ();
1837 } else if (!mouse_frame (where, in_track_canvas)) {
1838 use_mouse_frame = false;
1841 if (use_mouse_frame) {
1842 l = - ((new_page_size * ((where - current_leftmost)/(double)current_page)) - where);
1845 leftmost_after_zoom = 0;
1846 } else if (l > max_framepos) {
1847 leftmost_after_zoom = max_framepos - new_page_size;
1849 leftmost_after_zoom = (framepos_t) l;
1852 /* use playhead instead */
1853 where = playhead_cursor->current_frame ();
1855 if (where < half_page_size) {
1856 leftmost_after_zoom = 0;
1858 leftmost_after_zoom = where - half_page_size;
1864 /* try to keep the edit point in the same place */
1865 where = get_preferred_edit_position ();
1869 double l = - ((new_page_size * ((where - current_leftmost)/(double)current_page)) - where);
1872 leftmost_after_zoom = 0;
1873 } else if (l > max_framepos) {
1874 leftmost_after_zoom = max_framepos - new_page_size;
1876 leftmost_after_zoom = (framepos_t) l;
1880 /* edit point not defined */
1887 // leftmost_after_zoom = min (leftmost_after_zoom, _session->current_end_frame());
1889 reposition_and_zoom (leftmost_after_zoom, nfpp);
1893 Editor::calc_extra_zoom_edges(framepos_t &start, framepos_t &end)
1895 /* this func helps make sure we leave a little space
1896 at each end of the editor so that the zoom doesn't fit the region
1897 precisely to the screen.
1900 GdkScreen* screen = gdk_screen_get_default ();
1901 const gint pixwidth = gdk_screen_get_width (screen);
1902 const gint mmwidth = gdk_screen_get_width_mm (screen);
1903 const double pix_per_mm = (double) pixwidth/ (double) mmwidth;
1904 const double one_centimeter_in_pixels = pix_per_mm * 10.0;
1906 const framepos_t range = end - start;
1907 const framecnt_t new_fpp = (framecnt_t) ceil ((double) range / (double) _visible_canvas_width);
1908 const framepos_t extra_samples = (framepos_t) floor (one_centimeter_in_pixels * new_fpp);
1910 if (start > extra_samples) {
1911 start -= extra_samples;
1916 if (max_framepos - extra_samples > end) {
1917 end += extra_samples;
1924 Editor::get_selection_extents (framepos_t &start, framepos_t &end) const
1926 start = max_framepos;
1930 //ToDo: if notes are selected, set extents to that selection
1932 //ToDo: if control points are selected, set extents to that selection
1934 if ( !selection->regions.empty() ) {
1935 RegionSelection rs = get_regions_from_selection_and_entered ();
1937 for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
1939 if ((*i)->region()->position() < start) {
1940 start = (*i)->region()->position();
1943 if ((*i)->region()->last_frame() + 1 > end) {
1944 end = (*i)->region()->last_frame() + 1;
1948 } else if (!selection->time.empty()) {
1949 start = selection->time.start();
1950 end = selection->time.end_frame();
1952 ret = false; //no selection found
1955 if ((start == 0 && end == 0) || end < start) {
1964 Editor::temporal_zoom_selection (Editing::ZoomAxis axes)
1966 if (!selection) return;
1968 //ToDo: if notes are selected, zoom to that
1970 //ToDo: if control points are selected, zoom to that
1972 if (axes == Horizontal || axes == Both) {
1974 framepos_t start, end;
1975 if (get_selection_extents (start, end)) {
1976 calc_extra_zoom_edges (start, end);
1977 temporal_zoom_by_frame (start, end);
1981 if (axes == Vertical || axes == Both) {
1987 Editor::temporal_zoom_session ()
1989 ENSURE_GUI_THREAD (*this, &Editor::temporal_zoom_session)
1992 framecnt_t start = _session->current_start_frame();
1993 framecnt_t end = _session->current_end_frame();
1995 if (_session->actively_recording () ) {
1996 framepos_t cur = playhead_cursor->current_frame ();
1998 /* recording beyond the end marker; zoom out
1999 * by 5 seconds more so that if 'follow
2000 * playhead' is active we don't immediately
2003 end = cur + _session->frame_rate() * 5;
2007 if ((start == 0 && end == 0) || end < start) {
2011 calc_extra_zoom_edges(start, end);
2013 temporal_zoom_by_frame (start, end);
2018 Editor::temporal_zoom_by_frame (framepos_t start, framepos_t end)
2020 if (!_session) return;
2022 if ((start == 0 && end == 0) || end < start) {
2026 framepos_t range = end - start;
2028 const framecnt_t new_fpp = (framecnt_t) ceil ((double) range / (double) _visible_canvas_width);
2030 framepos_t new_page = range;
2031 framepos_t middle = (framepos_t) floor ((double) start + ((double) range / 2.0f));
2032 framepos_t new_leftmost = (framepos_t) floor ((double) middle - ((double) new_page / 2.0f));
2034 if (new_leftmost > middle) {
2038 if (new_leftmost < 0) {
2042 reposition_and_zoom (new_leftmost, new_fpp);
2046 Editor::temporal_zoom_to_frame (bool coarser, framepos_t frame)
2052 framecnt_t range_before = frame - leftmost_frame;
2056 if (samples_per_pixel <= 1) {
2059 new_spp = samples_per_pixel + (samples_per_pixel/2);
2061 range_before += range_before/2;
2063 if (samples_per_pixel >= 1) {
2064 new_spp = samples_per_pixel - (samples_per_pixel/2);
2066 /* could bail out here since we cannot zoom any finer,
2067 but leave that to the equality test below
2069 new_spp = samples_per_pixel;
2072 range_before -= range_before/2;
2075 if (new_spp == samples_per_pixel) {
2079 /* zoom focus is automatically taken as @param frame when this
2083 framepos_t new_leftmost = frame - (framepos_t)range_before;
2085 if (new_leftmost > frame) {
2089 if (new_leftmost < 0) {
2093 reposition_and_zoom (new_leftmost, new_spp);
2098 Editor::choose_new_marker_name(string &name) {
2100 if (!UIConfiguration::instance().get_name_new_markers()) {
2101 /* don't prompt user for a new name */
2105 ArdourPrompter dialog (true);
2107 dialog.set_prompt (_("New Name:"));
2109 dialog.set_title (_("New Location Marker"));
2111 dialog.set_name ("MarkNameWindow");
2112 dialog.set_size_request (250, -1);
2113 dialog.set_position (Gtk::WIN_POS_MOUSE);
2115 dialog.add_button (Stock::OK, RESPONSE_ACCEPT);
2116 dialog.set_initial_text (name);
2120 switch (dialog.run ()) {
2121 case RESPONSE_ACCEPT:
2127 dialog.get_result(name);
2134 Editor::add_location_from_selection ()
2138 if (selection->time.empty()) {
2142 if (_session == 0 || clicked_axisview == 0) {
2146 framepos_t start = selection->time[clicked_selection].start;
2147 framepos_t end = selection->time[clicked_selection].end;
2149 _session->locations()->next_available_name(rangename,"selection");
2150 Location *location = new Location (*_session, start, end, rangename, Location::IsRangeMarker, get_grid_music_divisions(0));
2152 begin_reversible_command (_("add marker"));
2154 XMLNode &before = _session->locations()->get_state();
2155 _session->locations()->add (location, true);
2156 XMLNode &after = _session->locations()->get_state();
2157 _session->add_command(new MementoCommand<Locations>(*(_session->locations()), &before, &after));
2159 commit_reversible_command ();
2163 Editor::add_location_mark (framepos_t where)
2167 select_new_marker = true;
2169 _session->locations()->next_available_name(markername,"mark");
2170 if (!choose_new_marker_name(markername)) {
2173 Location *location = new Location (*_session, where, where, markername, Location::IsMark, get_grid_music_divisions (0));
2174 begin_reversible_command (_("add marker"));
2176 XMLNode &before = _session->locations()->get_state();
2177 _session->locations()->add (location, true);
2178 XMLNode &after = _session->locations()->get_state();
2179 _session->add_command(new MementoCommand<Locations>(*(_session->locations()), &before, &after));
2181 commit_reversible_command ();
2185 Editor::set_session_start_from_playhead ()
2191 if ((loc = _session->locations()->session_range_location()) == 0) { //should never happen
2192 _session->set_session_extents ( _session->audible_frame(), _session->audible_frame() );
2194 XMLNode &before = loc->get_state();
2196 _session->set_session_extents ( _session->audible_frame(), loc->end() );
2198 XMLNode &after = loc->get_state();
2200 begin_reversible_command (_("Set session start"));
2202 _session->add_command (new MementoCommand<Location>(*loc, &before, &after));
2204 commit_reversible_command ();
2209 Editor::set_session_end_from_playhead ()
2215 if ((loc = _session->locations()->session_range_location()) == 0) { //should never happen
2216 _session->set_session_extents ( _session->audible_frame(), _session->audible_frame() );
2218 XMLNode &before = loc->get_state();
2220 _session->set_session_extents ( loc->start(), _session->audible_frame() );
2222 XMLNode &after = loc->get_state();
2224 begin_reversible_command (_("Set session start"));
2226 _session->add_command (new MementoCommand<Location>(*loc, &before, &after));
2228 commit_reversible_command ();
2231 _session->set_end_is_free (false);
2236 Editor::toggle_location_at_playhead_cursor ()
2238 if (!do_remove_location_at_playhead_cursor())
2240 add_location_from_playhead_cursor();
2245 Editor::add_location_from_playhead_cursor ()
2247 add_location_mark (_session->audible_frame());
2251 Editor::do_remove_location_at_playhead_cursor ()
2253 bool removed = false;
2256 XMLNode &before = _session->locations()->get_state();
2258 //find location(s) at this time
2259 Locations::LocationList locs;
2260 _session->locations()->find_all_between (_session->audible_frame(), _session->audible_frame()+1, locs, Location::Flags(0));
2261 for (Locations::LocationList::iterator i = locs.begin(); i != locs.end(); ++i) {
2262 if ((*i)->is_mark()) {
2263 _session->locations()->remove (*i);
2270 begin_reversible_command (_("remove marker"));
2271 XMLNode &after = _session->locations()->get_state();
2272 _session->add_command(new MementoCommand<Locations>(*(_session->locations()), &before, &after));
2273 commit_reversible_command ();
2280 Editor::remove_location_at_playhead_cursor ()
2282 do_remove_location_at_playhead_cursor ();
2285 /** Add a range marker around each selected region */
2287 Editor::add_locations_from_region ()
2289 RegionSelection rs = get_regions_from_selection_and_entered ();
2294 bool commit = false;
2296 XMLNode &before = _session->locations()->get_state();
2298 for (RegionSelection::iterator i = rs.begin (); i != rs.end (); ++i) {
2300 boost::shared_ptr<Region> region = (*i)->region ();
2302 Location *location = new Location (*_session, region->position(), region->last_frame(), region->name(), Location::IsRangeMarker, 0);
2304 _session->locations()->add (location, true);
2309 begin_reversible_command (selection->regions.size () > 1 ? _("add markers") : _("add marker"));
2310 XMLNode &after = _session->locations()->get_state();
2311 _session->add_command (new MementoCommand<Locations>(*(_session->locations()), &before, &after));
2312 commit_reversible_command ();
2316 /** Add a single range marker around all selected regions */
2318 Editor::add_location_from_region ()
2320 RegionSelection rs = get_regions_from_selection_and_entered ();
2326 XMLNode &before = _session->locations()->get_state();
2330 if (rs.size() > 1) {
2331 _session->locations()->next_available_name(markername, "regions");
2333 RegionView* rv = *(rs.begin());
2334 boost::shared_ptr<Region> region = rv->region();
2335 markername = region->name();
2338 if (!choose_new_marker_name(markername)) {
2342 // single range spanning all selected
2343 Location *location = new Location (*_session, selection->regions.start(), selection->regions.end_frame(), markername, Location::IsRangeMarker, 0);
2344 _session->locations()->add (location, true);
2346 begin_reversible_command (_("add marker"));
2347 XMLNode &after = _session->locations()->get_state();
2348 _session->add_command (new MementoCommand<Locations>(*(_session->locations()), &before, &after));
2349 commit_reversible_command ();
2355 Editor::jump_forward_to_mark ()
2361 framepos_t pos = _session->locations()->first_mark_after (playhead_cursor->current_frame());
2367 _session->request_locate (pos, _session->transport_rolling());
2371 Editor::jump_backward_to_mark ()
2377 framepos_t pos = _session->locations()->first_mark_before (playhead_cursor->current_frame());
2379 //handle the case where we are rolling, and we're less than one-half second past the mark, we want to go to the prior mark...
2380 if ( _session->transport_rolling() ) {
2381 if ( (playhead_cursor->current_frame() - pos) < _session->frame_rate()/2 ) {
2382 framepos_t prior = _session->locations()->first_mark_before ( pos );
2391 _session->request_locate (pos, _session->transport_rolling());
2397 framepos_t const pos = _session->audible_frame ();
2400 _session->locations()->next_available_name (markername, "mark");
2402 if (!choose_new_marker_name (markername)) {
2406 _session->locations()->add (new Location (*_session, pos, 0, markername, Location::IsMark, 0), true);
2410 Editor::clear_markers ()
2413 begin_reversible_command (_("clear markers"));
2415 XMLNode &before = _session->locations()->get_state();
2416 _session->locations()->clear_markers ();
2417 XMLNode &after = _session->locations()->get_state();
2418 _session->add_command(new MementoCommand<Locations>(*(_session->locations()), &before, &after));
2420 commit_reversible_command ();
2425 Editor::clear_ranges ()
2428 begin_reversible_command (_("clear ranges"));
2430 XMLNode &before = _session->locations()->get_state();
2432 _session->locations()->clear_ranges ();
2434 XMLNode &after = _session->locations()->get_state();
2435 _session->add_command(new MementoCommand<Locations>(*(_session->locations()), &before, &after));
2437 commit_reversible_command ();
2442 Editor::clear_locations ()
2444 begin_reversible_command (_("clear locations"));
2446 XMLNode &before = _session->locations()->get_state();
2447 _session->locations()->clear ();
2448 XMLNode &after = _session->locations()->get_state();
2449 _session->add_command(new MementoCommand<Locations>(*(_session->locations()), &before, &after));
2451 commit_reversible_command ();
2455 Editor::unhide_markers ()
2457 for (LocationMarkerMap::iterator i = location_markers.begin(); i != location_markers.end(); ++i) {
2458 Location *l = (*i).first;
2459 if (l->is_hidden() && l->is_mark()) {
2460 l->set_hidden(false, this);
2466 Editor::unhide_ranges ()
2468 for (LocationMarkerMap::iterator i = location_markers.begin(); i != location_markers.end(); ++i) {
2469 Location *l = (*i).first;
2470 if (l->is_hidden() && l->is_range_marker()) {
2471 l->set_hidden(false, this);
2476 /* INSERT/REPLACE */
2479 Editor::insert_region_list_selection (float times)
2481 RouteTimeAxisView *tv = 0;
2482 boost::shared_ptr<Playlist> playlist;
2484 if (clicked_routeview != 0) {
2485 tv = clicked_routeview;
2486 } else if (!selection->tracks.empty()) {
2487 if ((tv = dynamic_cast<RouteTimeAxisView*>(selection->tracks.front())) == 0) {
2490 } else if (entered_track != 0) {
2491 if ((tv = dynamic_cast<RouteTimeAxisView*>(entered_track)) == 0) {
2498 if ((playlist = tv->playlist()) == 0) {
2502 boost::shared_ptr<Region> region = _regions->get_single_selection ();
2507 begin_reversible_command (_("insert region"));
2508 playlist->clear_changes ();
2509 playlist->add_region ((RegionFactory::create (region, true)), get_preferred_edit_position(), times);
2510 if (Config->get_edit_mode() == Ripple)
2511 playlist->ripple (get_preferred_edit_position(), region->length() * times, boost::shared_ptr<Region>());
2513 _session->add_command(new StatefulDiffCommand (playlist));
2514 commit_reversible_command ();
2517 /* BUILT-IN EFFECTS */
2520 Editor::reverse_selection ()
2525 /* GAIN ENVELOPE EDITING */
2528 Editor::edit_envelope ()
2535 Editor::transition_to_rolling (bool fwd)
2541 if (_session->config.get_external_sync()) {
2542 switch (Config->get_sync_source()) {
2546 /* transport controlled by the master */
2551 if (_session->is_auditioning()) {
2552 _session->cancel_audition ();
2556 _session->request_transport_speed (fwd ? 1.0f : -1.0f);
2560 Editor::play_from_start ()
2562 _session->request_locate (_session->current_start_frame(), true);
2566 Editor::play_from_edit_point ()
2568 _session->request_locate (get_preferred_edit_position(), true);
2572 Editor::play_from_edit_point_and_return ()
2574 framepos_t start_frame;
2575 framepos_t return_frame;
2577 start_frame = get_preferred_edit_position ( EDIT_IGNORE_PHEAD );
2579 if (_session->transport_rolling()) {
2580 _session->request_locate (start_frame, false);
2584 /* don't reset the return frame if its already set */
2586 if ((return_frame = _session->requested_return_frame()) < 0) {
2587 return_frame = _session->audible_frame();
2590 if (start_frame >= 0) {
2591 _session->request_roll_at_and_return (start_frame, return_frame);
2596 Editor::play_selection ()
2598 framepos_t start, end;
2599 if (!get_selection_extents ( start, end))
2602 AudioRange ar (start, end, 0);
2603 list<AudioRange> lar;
2606 _session->request_play_range (&lar, true);
2611 Editor::maybe_locate_with_edit_preroll (framepos_t location)
2613 if ( _session->transport_rolling() || !UIConfiguration::instance().get_follow_edits() || _session->config.get_external_sync() )
2616 location -= _session->preroll_samples (location);
2618 //don't try to locate before the beginning of time
2623 //if follow_playhead is on, keep the playhead on the screen
2624 if ( _follow_playhead )
2625 if ( location < leftmost_frame )
2626 location = leftmost_frame;
2628 _session->request_locate( location );
2632 Editor::play_with_preroll ()
2634 framepos_t start, end;
2635 if ( UIConfiguration::instance().get_follow_edits() && get_selection_extents ( start, end) ) {
2636 const framepos_t preroll = _session->preroll_samples (start);
2638 framepos_t ret = start;
2640 if (start > preroll) {
2641 start = start - preroll;
2644 end = end + preroll; //"post-roll"
2646 AudioRange ar (start, end, 0);
2647 list<AudioRange> lar;
2650 _session->request_play_range (&lar, true);
2651 _session->set_requested_return_frame (ret); //force auto-return to return to range start, without the preroll
2653 framepos_t ph = playhead_cursor->current_frame ();
2654 const framepos_t preroll = _session->preroll_samples (ph);
2657 start = ph - preroll;
2661 _session->request_locate (start, true);
2662 _session->set_requested_return_frame (ph); //force auto-return to return to playhead location, without the preroll
2667 Editor::rec_with_preroll ()
2669 framepos_t ph = playhead_cursor->current_frame ();
2670 framepos_t preroll = _session->preroll_samples (ph);
2671 framepos_t start = std::max ((framepos_t)0, ph - preroll);
2673 _session->request_preroll_record (ph);
2674 _session->maybe_enable_record ();
2675 _session->request_locate (start, true);
2676 _session->set_requested_return_frame (ph);
2681 Editor::play_location (Location& location)
2683 if (location.start() <= location.end()) {
2687 _session->request_bounded_roll (location.start(), location.end());
2691 Editor::loop_location (Location& location)
2693 if (location.start() <= location.end()) {
2699 if ((tll = transport_loop_location()) != 0) {
2700 tll->set (location.start(), location.end());
2702 // enable looping, reposition and start rolling
2703 _session->request_locate (tll->start(), true);
2704 _session->request_play_loop (true);
2709 Editor::do_layer_operation (LayerOperation op)
2711 if (selection->regions.empty ()) {
2715 bool const multiple = selection->regions.size() > 1;
2719 begin_reversible_command (_("raise regions"));
2721 begin_reversible_command (_("raise region"));
2727 begin_reversible_command (_("raise regions to top"));
2729 begin_reversible_command (_("raise region to top"));
2735 begin_reversible_command (_("lower regions"));
2737 begin_reversible_command (_("lower region"));
2743 begin_reversible_command (_("lower regions to bottom"));
2745 begin_reversible_command (_("lower region"));
2750 set<boost::shared_ptr<Playlist> > playlists = selection->regions.playlists ();
2751 for (set<boost::shared_ptr<Playlist> >::iterator i = playlists.begin(); i != playlists.end(); ++i) {
2752 (*i)->clear_owned_changes ();
2755 for (RegionSelection::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
2756 boost::shared_ptr<Region> r = (*i)->region ();
2768 r->lower_to_bottom ();
2772 for (set<boost::shared_ptr<Playlist> >::iterator i = playlists.begin(); i != playlists.end(); ++i) {
2773 vector<Command*> cmds;
2775 _session->add_commands (cmds);
2778 commit_reversible_command ();
2782 Editor::raise_region ()
2784 do_layer_operation (Raise);
2788 Editor::raise_region_to_top ()
2790 do_layer_operation (RaiseToTop);
2794 Editor::lower_region ()
2796 do_layer_operation (Lower);
2800 Editor::lower_region_to_bottom ()
2802 do_layer_operation (LowerToBottom);
2805 /** Show the region editor for the selected regions */
2807 Editor::show_region_properties ()
2809 selection->foreach_regionview (&RegionView::show_region_editor);
2812 /** Show the midi list editor for the selected MIDI regions */
2814 Editor::show_midi_list_editor ()
2816 selection->foreach_midi_regionview (&MidiRegionView::show_list_editor);
2820 Editor::rename_region ()
2822 RegionSelection rs = get_regions_from_selection_and_entered ();
2828 ArdourDialog d (_("Rename Region"), true, false);
2830 Label label (_("New name:"));
2833 hbox.set_spacing (6);
2834 hbox.pack_start (label, false, false);
2835 hbox.pack_start (entry, true, true);
2837 d.get_vbox()->set_border_width (12);
2838 d.get_vbox()->pack_start (hbox, false, false);
2840 d.add_button(Gtk::Stock::CANCEL, Gtk::RESPONSE_CANCEL);
2841 d.add_button(Gtk::Stock::OK, Gtk::RESPONSE_OK);
2843 d.set_size_request (300, -1);
2845 entry.set_text (rs.front()->region()->name());
2846 entry.select_region (0, -1);
2848 entry.signal_activate().connect (sigc::bind (sigc::mem_fun (d, &Dialog::response), RESPONSE_OK));
2854 int const ret = d.run();
2858 if (ret != RESPONSE_OK) {
2862 std::string str = entry.get_text();
2863 strip_whitespace_edges (str);
2865 rs.front()->region()->set_name (str);
2866 _regions->redisplay ();
2870 /** Start an audition of the first selected region */
2872 Editor::play_edit_range ()
2874 framepos_t start, end;
2876 if (get_edit_op_range (start, end)) {
2877 _session->request_bounded_roll (start, end);
2882 Editor::play_selected_region ()
2884 framepos_t start = max_framepos;
2887 RegionSelection rs = get_regions_from_selection_and_entered ();
2893 for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
2894 if ((*i)->region()->position() < start) {
2895 start = (*i)->region()->position();
2897 if ((*i)->region()->last_frame() + 1 > end) {
2898 end = (*i)->region()->last_frame() + 1;
2902 _session->request_bounded_roll (start, end);
2906 Editor::audition_playlist_region_standalone (boost::shared_ptr<Region> region)
2908 _session->audition_region (region);
2912 Editor::region_from_selection ()
2914 if (clicked_axisview == 0) {
2918 if (selection->time.empty()) {
2922 framepos_t start = selection->time[clicked_selection].start;
2923 framepos_t end = selection->time[clicked_selection].end;
2925 TrackViewList tracks = get_tracks_for_range_action ();
2927 framepos_t selection_cnt = end - start + 1;
2929 for (TrackSelection::iterator i = tracks.begin(); i != tracks.end(); ++i) {
2930 boost::shared_ptr<Region> current;
2931 boost::shared_ptr<Playlist> pl;
2932 framepos_t internal_start;
2935 if ((pl = (*i)->playlist()) == 0) {
2939 if ((current = pl->top_region_at (start)) == 0) {
2943 internal_start = start - current->position();
2944 RegionFactory::region_name (new_name, current->name(), true);
2948 plist.add (ARDOUR::Properties::start, current->start() + internal_start);
2949 plist.add (ARDOUR::Properties::length, selection_cnt);
2950 plist.add (ARDOUR::Properties::name, new_name);
2951 plist.add (ARDOUR::Properties::layer, 0);
2953 boost::shared_ptr<Region> region (RegionFactory::create (current, plist));
2958 Editor::create_region_from_selection (vector<boost::shared_ptr<Region> >& new_regions)
2960 if (selection->time.empty() || selection->tracks.empty()) {
2964 framepos_t start, end;
2965 if (clicked_selection) {
2966 start = selection->time[clicked_selection].start;
2967 end = selection->time[clicked_selection].end;
2969 start = selection->time.start();
2970 end = selection->time.end_frame();
2973 TrackViewList ts = selection->tracks.filter_to_unique_playlists ();
2974 sort_track_selection (ts);
2976 for (TrackSelection::iterator i = ts.begin(); i != ts.end(); ++i) {
2977 boost::shared_ptr<Region> current;
2978 boost::shared_ptr<Playlist> playlist;
2979 framepos_t internal_start;
2982 if ((playlist = (*i)->playlist()) == 0) {
2986 if ((current = playlist->top_region_at(start)) == 0) {
2990 internal_start = start - current->position();
2991 RegionFactory::region_name (new_name, current->name(), true);
2995 plist.add (ARDOUR::Properties::start, current->start() + internal_start);
2996 plist.add (ARDOUR::Properties::length, end - start + 1);
2997 plist.add (ARDOUR::Properties::name, new_name);
2999 new_regions.push_back (RegionFactory::create (current, plist));
3004 Editor::split_multichannel_region ()
3006 RegionSelection rs = get_regions_from_selection_and_entered ();
3012 vector< boost::shared_ptr<Region> > v;
3014 for (list<RegionView*>::iterator x = rs.begin(); x != rs.end(); ++x) {
3015 (*x)->region()->separate_by_channel (*_session, v);
3020 Editor::new_region_from_selection ()
3022 region_from_selection ();
3023 cancel_selection ();
3027 add_if_covered (RegionView* rv, const AudioRange* ar, RegionSelection* rs)
3029 switch (rv->region()->coverage (ar->start, ar->end - 1)) {
3030 // n.b. -1 because AudioRange::end is one past the end, but coverage expects inclusive ranges
3031 case Evoral::OverlapNone:
3039 * - selected tracks, or if there are none...
3040 * - tracks containing selected regions, or if there are none...
3045 Editor::get_tracks_for_range_action () const
3049 if (selection->tracks.empty()) {
3051 /* use tracks with selected regions */
3053 RegionSelection rs = selection->regions;
3055 for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
3056 TimeAxisView* tv = &(*i)->get_time_axis_view();
3058 if (!t.contains (tv)) {
3064 /* no regions and no tracks: use all tracks */
3070 t = selection->tracks;
3073 return t.filter_to_unique_playlists();
3077 Editor::separate_regions_between (const TimeSelection& ts)
3079 bool in_command = false;
3080 boost::shared_ptr<Playlist> playlist;
3081 RegionSelection new_selection;
3083 TrackViewList tmptracks = get_tracks_for_range_action ();
3084 sort_track_selection (tmptracks);
3086 for (TrackSelection::iterator i = tmptracks.begin(); i != tmptracks.end(); ++i) {
3088 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> ((*i));
3094 if (!rtv->is_track()) {
3098 /* no edits to destructive tracks */
3100 if (rtv->track()->destructive()) {
3104 if ((playlist = rtv->playlist()) != 0) {
3106 playlist->clear_changes ();
3108 /* XXX need to consider musical time selections here at some point */
3110 double speed = rtv->track()->speed();
3112 for (list<AudioRange>::const_iterator t = ts.begin(); t != ts.end(); ++t) {
3114 sigc::connection c = rtv->view()->RegionViewAdded.connect (
3115 sigc::mem_fun(*this, &Editor::collect_new_region_view));
3117 latest_regionviews.clear ();
3119 playlist->partition ((framepos_t)((*t).start * speed),
3120 (framepos_t)((*t).end * speed), false);
3124 if (!latest_regionviews.empty()) {
3126 rtv->view()->foreach_regionview (sigc::bind (
3127 sigc::ptr_fun (add_if_covered),
3128 &(*t), &new_selection));
3131 begin_reversible_command (_("separate"));
3135 /* pick up changes to existing regions */
3137 vector<Command*> cmds;
3138 playlist->rdiff (cmds);
3139 _session->add_commands (cmds);
3141 /* pick up changes to the playlist itself (adds/removes)
3144 _session->add_command(new StatefulDiffCommand (playlist));
3151 // selection->set (new_selection);
3153 commit_reversible_command ();
3157 struct PlaylistState {
3158 boost::shared_ptr<Playlist> playlist;
3162 /** Take tracks from get_tracks_for_range_action and cut any regions
3163 * on those tracks so that the tracks are empty over the time
3167 Editor::separate_region_from_selection ()
3169 /* preferentially use *all* ranges in the time selection if we're in range mode
3170 to allow discontiguous operation, since get_edit_op_range() currently
3171 returns a single range.
3174 if (!selection->time.empty()) {
3176 separate_regions_between (selection->time);
3183 if (get_edit_op_range (start, end)) {
3185 AudioRange ar (start, end, 1);
3189 separate_regions_between (ts);
3195 Editor::separate_region_from_punch ()
3197 Location* loc = _session->locations()->auto_punch_location();
3199 separate_regions_using_location (*loc);
3204 Editor::separate_region_from_loop ()
3206 Location* loc = _session->locations()->auto_loop_location();
3208 separate_regions_using_location (*loc);
3213 Editor::separate_regions_using_location (Location& loc)
3215 if (loc.is_mark()) {
3219 AudioRange ar (loc.start(), loc.end(), 1);
3224 separate_regions_between (ts);
3227 /** Separate regions under the selected region */
3229 Editor::separate_under_selected_regions ()
3231 vector<PlaylistState> playlists;
3235 rs = get_regions_from_selection_and_entered();
3237 if (!_session || rs.empty()) {
3241 begin_reversible_command (_("separate region under"));
3243 list<boost::shared_ptr<Region> > regions_to_remove;
3245 for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
3246 // we can't just remove the region(s) in this loop because
3247 // this removes them from the RegionSelection, and they thus
3248 // disappear from underneath the iterator, and the ++i above
3249 // SEGVs in a puzzling fashion.
3251 // so, first iterate over the regions to be removed from rs and
3252 // add them to the regions_to_remove list, and then
3253 // iterate over the list to actually remove them.
3255 regions_to_remove.push_back ((*i)->region());
3258 for (list<boost::shared_ptr<Region> >::iterator rl = regions_to_remove.begin(); rl != regions_to_remove.end(); ++rl) {
3260 boost::shared_ptr<Playlist> playlist = (*rl)->playlist();
3263 // is this check necessary?
3267 vector<PlaylistState>::iterator i;
3269 //only take state if this is a new playlist.
3270 for (i = playlists.begin(); i != playlists.end(); ++i) {
3271 if ((*i).playlist == playlist) {
3276 if (i == playlists.end()) {
3278 PlaylistState before;
3279 before.playlist = playlist;
3280 before.before = &playlist->get_state();
3282 playlist->freeze ();
3283 playlists.push_back(before);
3286 //Partition on the region bounds
3287 playlist->partition ((*rl)->first_frame() - 1, (*rl)->last_frame() + 1, true);
3289 //Re-add region that was just removed due to the partition operation
3290 playlist->add_region( (*rl), (*rl)->first_frame() );
3293 vector<PlaylistState>::iterator pl;
3295 for (pl = playlists.begin(); pl != playlists.end(); ++pl) {
3296 (*pl).playlist->thaw ();
3297 _session->add_command(new MementoCommand<Playlist>(*(*pl).playlist, (*pl).before, &(*pl).playlist->get_state()));
3300 commit_reversible_command ();
3304 Editor::crop_region_to_selection ()
3306 if (!selection->time.empty()) {
3308 crop_region_to (selection->time.start(), selection->time.end_frame());
3315 if (get_edit_op_range (start, end)) {
3316 crop_region_to (start, end);
3323 Editor::crop_region_to (framepos_t start, framepos_t end)
3325 vector<boost::shared_ptr<Playlist> > playlists;
3326 boost::shared_ptr<Playlist> playlist;
3329 if (selection->tracks.empty()) {
3330 ts = track_views.filter_to_unique_playlists();
3332 ts = selection->tracks.filter_to_unique_playlists ();
3335 sort_track_selection (ts);
3337 for (TrackSelection::iterator i = ts.begin(); i != ts.end(); ++i) {
3339 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> ((*i));
3345 boost::shared_ptr<Track> t = rtv->track();
3347 if (t != 0 && ! t->destructive()) {
3349 if ((playlist = rtv->playlist()) != 0) {
3350 playlists.push_back (playlist);
3355 if (playlists.empty()) {
3360 framepos_t new_start;
3362 framecnt_t new_length;
3363 bool in_command = false;
3365 for (vector<boost::shared_ptr<Playlist> >::iterator i = playlists.begin(); i != playlists.end(); ++i) {
3367 /* Only the top regions at start and end have to be cropped */
3368 boost::shared_ptr<Region> region_at_start = (*i)->top_region_at(start);
3369 boost::shared_ptr<Region> region_at_end = (*i)->top_region_at(end);
3371 vector<boost::shared_ptr<Region> > regions;
3373 if (region_at_start != 0) {
3374 regions.push_back (region_at_start);
3376 if (region_at_end != 0) {
3377 regions.push_back (region_at_end);
3380 /* now adjust lengths */
3381 for (vector<boost::shared_ptr<Region> >::iterator i = regions.begin(); i != regions.end(); ++i) {
3383 pos = (*i)->position();
3384 new_start = max (start, pos);
3385 if (max_framepos - pos > (*i)->length()) {
3386 new_end = pos + (*i)->length() - 1;
3388 new_end = max_framepos;
3390 new_end = min (end, new_end);
3391 new_length = new_end - new_start + 1;
3394 begin_reversible_command (_("trim to selection"));
3397 (*i)->clear_changes ();
3398 (*i)->trim_to (new_start, new_length);
3399 _session->add_command (new StatefulDiffCommand (*i));
3404 commit_reversible_command ();
3409 Editor::region_fill_track ()
3411 boost::shared_ptr<Playlist> playlist;
3412 RegionSelection regions = get_regions_from_selection_and_entered ();
3413 RegionSelection foo;
3415 framepos_t const end = _session->current_end_frame ();
3417 if (regions.empty () || regions.end_frame () + 1 >= end) {
3421 framepos_t const start_frame = regions.start ();
3422 framepos_t const end_frame = regions.end_frame ();
3423 framecnt_t const gap = end_frame - start_frame + 1;
3425 begin_reversible_command (Operations::region_fill);
3427 selection->clear_regions ();
3429 for (RegionSelection::iterator i = regions.begin(); i != regions.end(); ++i) {
3431 boost::shared_ptr<Region> r ((*i)->region());
3433 TimeAxisView& tv = (*i)->get_time_axis_view();
3434 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (&tv);
3435 latest_regionviews.clear ();
3436 sigc::connection c = rtv->view()->RegionViewAdded.connect (sigc::mem_fun(*this, &Editor::collect_new_region_view));
3438 framepos_t const position = end_frame + (r->first_frame() - start_frame + 1);
3439 playlist = (*i)->region()->playlist();
3440 playlist->clear_changes ();
3441 playlist->duplicate_until (r, position, gap, end);
3442 _session->add_command(new StatefulDiffCommand (playlist));
3446 foo.insert (foo.end(), latest_regionviews.begin(), latest_regionviews.end());
3450 selection->set (foo);
3453 commit_reversible_command ();
3457 Editor::set_region_sync_position ()
3459 set_sync_point (get_preferred_edit_position (), get_regions_from_selection_and_edit_point ());
3463 Editor::set_sync_point (framepos_t where, const RegionSelection& rs)
3465 bool in_command = false;
3467 for (RegionSelection::const_iterator r = rs.begin(); r != rs.end(); ++r) {
3469 if (!(*r)->region()->covers (where)) {
3473 boost::shared_ptr<Region> region ((*r)->region());
3476 begin_reversible_command (_("set sync point"));
3480 region->clear_changes ();
3481 region->set_sync_position (where);
3482 _session->add_command(new StatefulDiffCommand (region));
3486 commit_reversible_command ();
3490 /** Remove the sync positions of the selection */
3492 Editor::remove_region_sync ()
3494 RegionSelection rs = get_regions_from_selection_and_entered ();
3500 begin_reversible_command (_("remove region sync"));
3502 for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
3504 (*i)->region()->clear_changes ();
3505 (*i)->region()->clear_sync_position ();
3506 _session->add_command(new StatefulDiffCommand ((*i)->region()));
3509 commit_reversible_command ();
3513 Editor::naturalize_region ()
3515 RegionSelection rs = get_regions_from_selection_and_entered ();
3521 if (rs.size() > 1) {
3522 begin_reversible_command (_("move regions to original position"));
3524 begin_reversible_command (_("move region to original position"));
3527 for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
3528 (*i)->region()->clear_changes ();
3529 (*i)->region()->move_to_natural_position ();
3530 _session->add_command (new StatefulDiffCommand ((*i)->region()));
3533 commit_reversible_command ();
3537 Editor::align_regions (RegionPoint what)
3539 RegionSelection const rs = get_regions_from_selection_and_edit_point ();
3545 begin_reversible_command (_("align selection"));
3547 framepos_t const position = get_preferred_edit_position ();
3549 for (RegionSelection::const_iterator i = rs.begin(); i != rs.end(); ++i) {
3550 align_region_internal ((*i)->region(), what, position);
3553 commit_reversible_command ();
3556 struct RegionSortByTime {
3557 bool operator() (const RegionView* a, const RegionView* b) {
3558 return a->region()->position() < b->region()->position();
3563 Editor::align_regions_relative (RegionPoint point)
3565 RegionSelection const rs = get_regions_from_selection_and_edit_point ();
3571 framepos_t const position = get_preferred_edit_position ();
3573 framepos_t distance = 0;
3577 list<RegionView*> sorted;
3578 rs.by_position (sorted);
3580 boost::shared_ptr<Region> r ((*sorted.begin())->region());
3585 if (position > r->position()) {
3586 distance = position - r->position();
3588 distance = r->position() - position;
3594 if (position > r->last_frame()) {
3595 distance = position - r->last_frame();
3596 pos = r->position() + distance;
3598 distance = r->last_frame() - position;
3599 pos = r->position() - distance;
3605 pos = r->adjust_to_sync (position);
3606 if (pos > r->position()) {
3607 distance = pos - r->position();
3609 distance = r->position() - pos;
3615 if (pos == r->position()) {
3619 begin_reversible_command (_("align selection (relative)"));
3621 /* move first one specially */
3623 r->clear_changes ();
3624 r->set_position (pos);
3625 _session->add_command(new StatefulDiffCommand (r));
3627 /* move rest by the same amount */
3631 for (list<RegionView*>::iterator i = sorted.begin(); i != sorted.end(); ++i) {
3633 boost::shared_ptr<Region> region ((*i)->region());
3635 region->clear_changes ();
3638 region->set_position (region->position() + distance);
3640 region->set_position (region->position() - distance);
3643 _session->add_command(new StatefulDiffCommand (region));
3647 commit_reversible_command ();
3651 Editor::align_region (boost::shared_ptr<Region> region, RegionPoint point, framepos_t position)
3653 begin_reversible_command (_("align region"));
3654 align_region_internal (region, point, position);
3655 commit_reversible_command ();
3659 Editor::align_region_internal (boost::shared_ptr<Region> region, RegionPoint point, framepos_t position)
3661 region->clear_changes ();
3665 region->set_position (region->adjust_to_sync (position));
3669 if (position > region->length()) {
3670 region->set_position (position - region->length());
3675 region->set_position (position);
3679 _session->add_command(new StatefulDiffCommand (region));
3683 Editor::trim_region_front ()
3689 Editor::trim_region_back ()
3691 trim_region (false);
3695 Editor::trim_region (bool front)
3697 framepos_t where = get_preferred_edit_position();
3698 RegionSelection rs = get_regions_from_selection_and_edit_point ();
3704 begin_reversible_command (front ? _("trim front") : _("trim back"));
3706 for (list<RegionView*>::const_iterator i = rs.by_layer().begin(); i != rs.by_layer().end(); ++i) {
3707 if (!(*i)->region()->locked()) {
3709 (*i)->region()->clear_changes ();
3712 (*i)->region()->trim_front (where);
3714 (*i)->region()->trim_end (where);
3717 _session->add_command (new StatefulDiffCommand ((*i)->region()));
3721 commit_reversible_command ();
3724 /** Trim the end of the selected regions to the position of the edit cursor */
3726 Editor::trim_region_to_loop ()
3728 Location* loc = _session->locations()->auto_loop_location();
3732 trim_region_to_location (*loc, _("trim to loop"));
3736 Editor::trim_region_to_punch ()
3738 Location* loc = _session->locations()->auto_punch_location();
3742 trim_region_to_location (*loc, _("trim to punch"));
3746 Editor::trim_region_to_location (const Location& loc, const char* str)
3748 RegionSelection rs = get_regions_from_selection_and_entered ();
3749 bool in_command = false;
3751 for (RegionSelection::iterator x = rs.begin(); x != rs.end(); ++x) {
3752 RegionView* rv = (*x);
3754 /* require region to span proposed trim */
3755 switch (rv->region()->coverage (loc.start(), loc.end())) {
3756 case Evoral::OverlapInternal:
3762 RouteTimeAxisView* tav = dynamic_cast<RouteTimeAxisView*> (&rv->get_time_axis_view());
3771 if (tav->track() != 0) {
3772 speed = tav->track()->speed();
3775 start = session_frame_to_track_frame (loc.start(), speed);
3776 end = session_frame_to_track_frame (loc.end(), speed);
3778 rv->region()->clear_changes ();
3779 rv->region()->trim_to (start, (end - start));
3782 begin_reversible_command (str);
3785 _session->add_command(new StatefulDiffCommand (rv->region()));
3789 commit_reversible_command ();
3794 Editor::trim_region_to_previous_region_end ()
3796 return trim_to_region(false);
3800 Editor::trim_region_to_next_region_start ()
3802 return trim_to_region(true);
3806 Editor::trim_to_region(bool forward)
3808 RegionSelection rs = get_regions_from_selection_and_entered ();
3809 bool in_command = false;
3811 boost::shared_ptr<Region> next_region;
3813 for (RegionSelection::iterator x = rs.begin(); x != rs.end(); ++x) {
3815 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (*x);
3821 AudioTimeAxisView* atav = dynamic_cast<AudioTimeAxisView*> (&arv->get_time_axis_view());
3829 if (atav->track() != 0) {
3830 speed = atav->track()->speed();
3834 boost::shared_ptr<Region> region = arv->region();
3835 boost::shared_ptr<Playlist> playlist (region->playlist());
3837 region->clear_changes ();
3841 next_region = playlist->find_next_region (region->first_frame(), Start, 1);
3847 region->trim_end((framepos_t) ( (next_region->first_frame() - 1) * speed));
3848 arv->region_changed (PropertyChange (ARDOUR::Properties::length));
3852 next_region = playlist->find_next_region (region->first_frame(), Start, 0);
3858 region->trim_front((framepos_t) ((next_region->last_frame() + 1) * speed));
3860 arv->region_changed (ARDOUR::bounds_change);
3864 begin_reversible_command (_("trim to region"));
3867 _session->add_command(new StatefulDiffCommand (region));
3871 commit_reversible_command ();
3876 Editor::unfreeze_route ()
3878 if (clicked_routeview == 0 || !clicked_routeview->is_track()) {
3882 clicked_routeview->track()->unfreeze ();
3886 Editor::_freeze_thread (void* arg)
3888 return static_cast<Editor*>(arg)->freeze_thread ();
3892 Editor::freeze_thread ()
3894 /* create event pool because we may need to talk to the session */
3895 SessionEvent::create_per_thread_pool ("freeze events", 64);
3896 /* create per-thread buffers for process() tree to use */
3897 clicked_routeview->audio_track()->freeze_me (*current_interthread_info);
3898 current_interthread_info->done = true;
3903 Editor::freeze_route ()
3909 /* stop transport before we start. this is important */
3911 _session->request_transport_speed (0.0);
3913 /* wait for just a little while, because the above call is asynchronous */
3915 Glib::usleep (250000);
3917 if (clicked_routeview == 0 || !clicked_routeview->is_audio_track()) {
3921 if (!clicked_routeview->track()->bounceable (clicked_routeview->track()->main_outs(), true)) {
3923 _("This track/bus cannot be frozen because the signal adds or loses channels before reaching the outputs.\n"
3924 "This is typically caused by plugins that generate stereo output from mono input or vice versa.")
3926 d.set_title (_("Cannot freeze"));
3931 if (clicked_routeview->track()->has_external_redirects()) {
3932 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"
3933 "Freezing will only process the signal as far as the first send/insert/return."),
3934 clicked_routeview->track()->name()), true, MESSAGE_INFO, BUTTONS_NONE, true);
3936 d.add_button (_("Freeze anyway"), Gtk::RESPONSE_OK);
3937 d.add_button (_("Don't freeze"), Gtk::RESPONSE_CANCEL);
3938 d.set_title (_("Freeze Limits"));
3940 int response = d.run ();
3943 case Gtk::RESPONSE_CANCEL:
3950 InterThreadInfo itt;
3951 current_interthread_info = &itt;
3953 InterthreadProgressWindow ipw (current_interthread_info, _("Freeze"), _("Cancel Freeze"));
3955 pthread_create_and_store (X_("freezer"), &itt.thread, _freeze_thread, this);
3957 CursorContext::Handle cursor_ctx = CursorContext::create(*this, _cursors->wait);
3959 while (!itt.done && !itt.cancel) {
3960 gtk_main_iteration ();
3963 pthread_join (itt.thread, 0);
3964 current_interthread_info = 0;
3968 Editor::bounce_range_selection (bool replace, bool enable_processing)
3970 if (selection->time.empty()) {
3974 TrackSelection views = selection->tracks;
3976 for (TrackViewList::iterator i = views.begin(); i != views.end(); ++i) {
3978 if (enable_processing) {
3980 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (*i);
3982 if (rtv && rtv->track() && replace && enable_processing && !rtv->track()->bounceable (rtv->track()->main_outs(), false)) {
3984 _("You can't perform this operation because the processing of the signal "
3985 "will cause one or more of the tracks to end up with a region with more channels than this track has inputs.\n\n"
3986 "You can do this without processing, which is a different operation.")
3988 d.set_title (_("Cannot bounce"));
3995 framepos_t start = selection->time[clicked_selection].start;
3996 framepos_t end = selection->time[clicked_selection].end;
3997 framepos_t cnt = end - start + 1;
3998 bool in_command = false;
4000 for (TrackViewList::iterator i = views.begin(); i != views.end(); ++i) {
4002 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (*i);
4008 boost::shared_ptr<Playlist> playlist;
4010 if ((playlist = rtv->playlist()) == 0) {
4014 InterThreadInfo itt;
4016 playlist->clear_changes ();
4017 playlist->clear_owned_changes ();
4019 boost::shared_ptr<Region> r;
4021 if (enable_processing) {
4022 r = rtv->track()->bounce_range (start, start+cnt, itt, rtv->track()->main_outs(), false);
4024 r = rtv->track()->bounce_range (start, start+cnt, itt, boost::shared_ptr<Processor>(), false);
4032 list<AudioRange> ranges;
4033 ranges.push_back (AudioRange (start, start+cnt, 0));
4034 playlist->cut (ranges); // discard result
4035 playlist->add_region (r, start);
4039 begin_reversible_command (_("bounce range"));
4042 vector<Command*> cmds;
4043 playlist->rdiff (cmds);
4044 _session->add_commands (cmds);
4046 _session->add_command (new StatefulDiffCommand (playlist));
4050 commit_reversible_command ();
4054 /** Delete selected regions, automation points or a time range */
4058 //special case: if the user is pointing in the editor/mixer strip, they may be trying to delete a plugin.
4059 //we need this because the editor-mixer strip is in the editor window, so it doesn't get the bindings from the mix window
4060 bool deleted = false;
4061 if ( current_mixer_strip && current_mixer_strip == MixerStrip::entered_mixer_strip() )
4062 deleted = current_mixer_strip->delete_processors ();
4068 /** Cut selected regions, automation points or a time range */
4075 /** Copy selected regions, automation points or a time range */
4083 /** @return true if a Cut, Copy or Clear is possible */
4085 Editor::can_cut_copy () const
4087 if (!selection->time.empty() || !selection->regions.empty() || !selection->points.empty())
4094 /** Cut, copy or clear selected regions, automation points or a time range.
4095 * @param op Operation (Delete, Cut, Copy or Clear)
4098 Editor::cut_copy (CutCopyOp op)
4100 /* only cancel selection if cut/copy is successful.*/
4106 opname = _("delete");
4115 opname = _("clear");
4119 /* if we're deleting something, and the mouse is still pressed,
4120 the thing we started a drag for will be gone when we release
4121 the mouse button(s). avoid this. see part 2 at the end of
4125 if (op == Delete || op == Cut || op == Clear) {
4126 if (_drags->active ()) {
4131 if ( op != Delete ) //"Delete" doesn't change copy/paste buf
4132 cut_buffer->clear ();
4134 if (entered_marker) {
4136 /* cut/delete op while pointing at a marker */
4139 Location* loc = find_location_from_marker (entered_marker, ignored);
4141 if (_session && loc) {
4142 entered_marker = NULL;
4143 Glib::signal_idle().connect (sigc::bind (sigc::mem_fun(*this, &Editor::really_remove_marker), loc));
4150 switch (mouse_mode) {
4153 begin_reversible_command (opname + ' ' + X_("MIDI"));
4155 commit_reversible_command ();
4161 bool did_edit = false;
4163 if (!selection->regions.empty() || !selection->points.empty()) {
4164 begin_reversible_command (opname + ' ' + _("objects"));
4167 if (!selection->regions.empty()) {
4168 cut_copy_regions (op, selection->regions);
4170 if (op == Cut || op == Delete) {
4171 selection->clear_regions ();
4175 if (!selection->points.empty()) {
4176 cut_copy_points (op);
4178 if (op == Cut || op == Delete) {
4179 selection->clear_points ();
4182 } else if (selection->time.empty()) {
4183 framepos_t start, end;
4184 /* no time selection, see if we can get an edit range
4187 if (get_edit_op_range (start, end)) {
4188 selection->set (start, end);
4190 } else if (!selection->time.empty()) {
4191 begin_reversible_command (opname + ' ' + _("range"));
4194 cut_copy_ranges (op);
4196 if (op == Cut || op == Delete) {
4197 selection->clear_time ();
4202 /* reset repeated paste state */
4205 commit_reversible_command ();
4208 if (op == Delete || op == Cut || op == Clear) {
4214 struct AutomationRecord {
4215 AutomationRecord () : state (0) , line(NULL) {}
4216 AutomationRecord (XMLNode* s, const AutomationLine* l) : state (s) , line (l) {}
4218 XMLNode* state; ///< state before any operation
4219 const AutomationLine* line; ///< line this came from
4220 boost::shared_ptr<Evoral::ControlList> copy; ///< copied events for the cut buffer
4222 struct PointsSelectionPositionSorter {
4223 bool operator() (ControlPoint* a, ControlPoint* b) {
4224 return (*(a->model()))->when < (*(b->model()))->when;
4227 /** Cut, copy or clear selected automation points.
4228 * @param op Operation (Cut, Copy or Clear)
4231 Editor::cut_copy_points (Editing::CutCopyOp op, Evoral::Beats earliest, bool midi)
4233 if (selection->points.empty ()) {
4237 /* XXX: not ideal, as there may be more than one track involved in the point selection */
4238 _last_cut_copy_source_track = &selection->points.front()->line().trackview;
4240 /* Keep a record of the AutomationLists that we end up using in this operation */
4241 typedef std::map<boost::shared_ptr<AutomationList>, AutomationRecord> Lists;
4244 /* user could select points in any order */
4245 selection->points.sort(PointsSelectionPositionSorter ());
4247 /* Go through all selected points, making an AutomationRecord for each distinct AutomationList */
4248 for (PointSelection::iterator sel_point = selection->points.begin(); sel_point != selection->points.end(); ++sel_point) {
4249 const AutomationLine& line = (*sel_point)->line();
4250 const boost::shared_ptr<AutomationList> al = line.the_list();
4251 if (lists.find (al) == lists.end ()) {
4252 /* We haven't seen this list yet, so make a record for it. This includes
4253 taking a copy of its current state, in case this is needed for undo later.
4255 lists[al] = AutomationRecord (&al->get_state (), &line);
4259 if (op == Cut || op == Copy) {
4260 /* This operation will involve putting things in the cut buffer, so create an empty
4261 ControlList for each of our source lists to put the cut buffer data in.
4263 for (Lists::iterator i = lists.begin(); i != lists.end(); ++i) {
4264 i->second.copy = i->first->create (i->first->parameter (), i->first->descriptor());
4267 /* Add all selected points to the relevant copy ControlLists */
4268 framepos_t start = std::numeric_limits<framepos_t>::max();
4269 for (PointSelection::iterator sel_point = selection->points.begin(); sel_point != selection->points.end(); ++sel_point) {
4270 boost::shared_ptr<AutomationList> al = (*sel_point)->line().the_list();
4271 AutomationList::const_iterator ctrl_evt = (*sel_point)->model ();
4273 lists[al].copy->fast_simple_add ((*ctrl_evt)->when, (*ctrl_evt)->value);
4275 /* Update earliest MIDI start time in beats */
4276 earliest = std::min(earliest, Evoral::Beats((*ctrl_evt)->when));
4278 /* Update earliest session start time in frames */
4279 start = std::min(start, (*sel_point)->line().session_position(ctrl_evt));
4283 /* Snap start time backwards, so copy/paste is snap aligned. */
4285 if (earliest == Evoral::Beats::max()) {
4286 earliest = Evoral::Beats(); // Weird... don't offset
4288 earliest.round_down_to_beat();
4290 if (start == std::numeric_limits<double>::max()) {
4291 start = 0; // Weird... don't offset
4293 snap_to(start, RoundDownMaybe);
4296 const double line_offset = midi ? earliest.to_double() : start;
4297 for (Lists::iterator i = lists.begin(); i != lists.end(); ++i) {
4298 /* Correct this copy list so that it is relative to the earliest
4299 start time, so relative ordering between points is preserved
4300 when copying from several lists and the paste starts at the
4301 earliest copied piece of data. */
4302 boost::shared_ptr<Evoral::ControlList> &al_cpy = i->second.copy;
4303 for (AutomationList::iterator ctrl_evt = al_cpy->begin(); ctrl_evt != al_cpy->end(); ++ctrl_evt) {
4304 (*ctrl_evt)->when -= line_offset;
4307 /* And add it to the cut buffer */
4308 cut_buffer->add (al_cpy);
4312 if (op == Delete || op == Cut) {
4313 /* This operation needs to remove things from the main AutomationList, so do that now */
4315 for (Lists::iterator i = lists.begin(); i != lists.end(); ++i) {
4316 i->first->freeze ();
4319 /* Remove each selected point from its AutomationList */
4320 for (PointSelection::iterator sel_point = selection->points.begin(); sel_point != selection->points.end(); ++sel_point) {
4321 AutomationLine& line = (*sel_point)->line ();
4322 boost::shared_ptr<AutomationList> al = line.the_list();
4326 if (dynamic_cast<AudioRegionGainLine*> (&line)) {
4327 /* removing of first and last gain point in region gain lines is prohibited*/
4328 if (line.is_last_point (*(*sel_point)) || line.is_first_point (*(*sel_point))) {
4334 al->erase ((*sel_point)->model ());
4338 /* Thaw the lists and add undo records for them */
4339 for (Lists::iterator i = lists.begin(); i != lists.end(); ++i) {
4340 boost::shared_ptr<AutomationList> al = i->first;
4342 _session->add_command (new MementoCommand<AutomationList> (*al.get(), i->second.state, &(al->get_state ())));
4347 /** Cut, copy or clear selected automation points.
4348 * @param op Operation (Cut, Copy or Clear)
4351 Editor::cut_copy_midi (CutCopyOp op)
4353 Evoral::Beats earliest = Evoral::Beats::max();
4354 for (MidiRegionSelection::iterator i = selection->midi_regions.begin(); i != selection->midi_regions.end(); ++i) {
4355 MidiRegionView* mrv = dynamic_cast<MidiRegionView*>(*i);
4357 if (!mrv->selection().empty()) {
4358 earliest = std::min(earliest, (*mrv->selection().begin())->note()->time());
4360 mrv->cut_copy_clear (op);
4362 /* XXX: not ideal, as there may be more than one track involved in the selection */
4363 _last_cut_copy_source_track = &mrv->get_time_axis_view();
4367 if (!selection->points.empty()) {
4368 cut_copy_points (op, earliest, true);
4369 if (op == Cut || op == Delete) {
4370 selection->clear_points ();
4375 struct lt_playlist {
4376 bool operator () (const PlaylistState& a, const PlaylistState& b) {
4377 return a.playlist < b.playlist;
4381 struct PlaylistMapping {
4383 boost::shared_ptr<Playlist> pl;
4385 PlaylistMapping (TimeAxisView* tvp) : tv (tvp) {}
4388 /** Remove `clicked_regionview' */
4390 Editor::remove_clicked_region ()
4392 if (clicked_routeview == 0 || clicked_regionview == 0) {
4396 begin_reversible_command (_("remove region"));
4398 boost::shared_ptr<Playlist> playlist = clicked_routeview->playlist();
4400 playlist->clear_changes ();
4401 playlist->clear_owned_changes ();
4402 playlist->remove_region (clicked_regionview->region());
4403 if (Config->get_edit_mode() == Ripple)
4404 playlist->ripple (clicked_regionview->region()->position(), -clicked_regionview->region()->length(), boost::shared_ptr<Region>());
4406 /* We might have removed regions, which alters other regions' layering_index,
4407 so we need to do a recursive diff here.
4409 vector<Command*> cmds;
4410 playlist->rdiff (cmds);
4411 _session->add_commands (cmds);
4413 _session->add_command(new StatefulDiffCommand (playlist));
4414 commit_reversible_command ();
4418 /** Remove the selected regions */
4420 Editor::remove_selected_regions ()
4422 RegionSelection rs = get_regions_from_selection_and_entered ();
4424 if (!_session || rs.empty()) {
4428 list<boost::shared_ptr<Region> > regions_to_remove;
4430 for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
4431 // we can't just remove the region(s) in this loop because
4432 // this removes them from the RegionSelection, and they thus
4433 // disappear from underneath the iterator, and the ++i above
4434 // SEGVs in a puzzling fashion.
4436 // so, first iterate over the regions to be removed from rs and
4437 // add them to the regions_to_remove list, and then
4438 // iterate over the list to actually remove them.
4440 regions_to_remove.push_back ((*i)->region());
4443 vector<boost::shared_ptr<Playlist> > playlists;
4445 for (list<boost::shared_ptr<Region> >::iterator rl = regions_to_remove.begin(); rl != regions_to_remove.end(); ++rl) {
4447 boost::shared_ptr<Playlist> playlist = (*rl)->playlist();
4450 // is this check necessary?
4454 /* get_regions_from_selection_and_entered() guarantees that
4455 the playlists involved are unique, so there is no need
4459 playlists.push_back (playlist);
4461 playlist->clear_changes ();
4462 playlist->clear_owned_changes ();
4463 playlist->freeze ();
4464 playlist->remove_region (*rl);
4465 if (Config->get_edit_mode() == Ripple)
4466 playlist->ripple ((*rl)->position(), -(*rl)->length(), boost::shared_ptr<Region>());
4470 vector<boost::shared_ptr<Playlist> >::iterator pl;
4471 bool in_command = false;
4473 for (pl = playlists.begin(); pl != playlists.end(); ++pl) {
4476 /* We might have removed regions, which alters other regions' layering_index,
4477 so we need to do a recursive diff here.
4481 begin_reversible_command (_("remove region"));
4484 vector<Command*> cmds;
4485 (*pl)->rdiff (cmds);
4486 _session->add_commands (cmds);
4488 _session->add_command(new StatefulDiffCommand (*pl));
4492 commit_reversible_command ();
4496 /** Cut, copy or clear selected regions.
4497 * @param op Operation (Cut, Copy or Clear)
4500 Editor::cut_copy_regions (CutCopyOp op, RegionSelection& rs)
4502 /* we can't use a std::map here because the ordering is important, and we can't trivially sort
4503 a map when we want ordered access to both elements. i think.
4506 vector<PlaylistMapping> pmap;
4508 framepos_t first_position = max_framepos;
4510 typedef set<boost::shared_ptr<Playlist> > FreezeList;
4511 FreezeList freezelist;
4513 /* get ordering correct before we cut/copy */
4515 rs.sort_by_position_and_track ();
4517 for (RegionSelection::iterator x = rs.begin(); x != rs.end(); ++x) {
4519 first_position = min ((framepos_t) (*x)->region()->position(), first_position);
4521 if (op == Cut || op == Clear || op == Delete) {
4522 boost::shared_ptr<Playlist> pl = (*x)->region()->playlist();
4525 FreezeList::iterator fl;
4527 // only take state if this is a new playlist.
4528 for (fl = freezelist.begin(); fl != freezelist.end(); ++fl) {
4534 if (fl == freezelist.end()) {
4535 pl->clear_changes();
4536 pl->clear_owned_changes ();
4538 freezelist.insert (pl);
4543 TimeAxisView* tv = &(*x)->get_time_axis_view();
4544 vector<PlaylistMapping>::iterator z;
4546 for (z = pmap.begin(); z != pmap.end(); ++z) {
4547 if ((*z).tv == tv) {
4552 if (z == pmap.end()) {
4553 pmap.push_back (PlaylistMapping (tv));
4557 for (RegionSelection::iterator x = rs.begin(); x != rs.end(); ) {
4559 boost::shared_ptr<Playlist> pl = (*x)->region()->playlist();
4562 /* region not yet associated with a playlist (e.g. unfinished
4569 TimeAxisView& tv = (*x)->get_time_axis_view();
4570 boost::shared_ptr<Playlist> npl;
4571 RegionSelection::iterator tmp;
4578 vector<PlaylistMapping>::iterator z;
4580 for (z = pmap.begin(); z != pmap.end(); ++z) {
4581 if ((*z).tv == &tv) {
4586 assert (z != pmap.end());
4589 npl = PlaylistFactory::create (pl->data_type(), *_session, "cutlist", true);
4597 boost::shared_ptr<Region> r = (*x)->region();
4598 boost::shared_ptr<Region> _xx;
4604 pl->remove_region (r);
4605 if (Config->get_edit_mode() == Ripple)
4606 pl->ripple (r->position(), -r->length(), boost::shared_ptr<Region>());
4610 _xx = RegionFactory::create (r);
4611 npl->add_region (_xx, r->position() - first_position);
4612 pl->remove_region (r);
4613 if (Config->get_edit_mode() == Ripple)
4614 pl->ripple (r->position(), -r->length(), boost::shared_ptr<Region>());
4618 /* copy region before adding, so we're not putting same object into two different playlists */
4619 npl->add_region (RegionFactory::create (r), r->position() - first_position);
4623 pl->remove_region (r);
4624 if (Config->get_edit_mode() == Ripple)
4625 pl->ripple (r->position(), -r->length(), boost::shared_ptr<Region>());
4634 list<boost::shared_ptr<Playlist> > foo;
4636 /* the pmap is in the same order as the tracks in which selected regions occurred */
4638 for (vector<PlaylistMapping>::iterator i = pmap.begin(); i != pmap.end(); ++i) {
4641 foo.push_back ((*i).pl);
4646 cut_buffer->set (foo);
4650 _last_cut_copy_source_track = 0;
4652 _last_cut_copy_source_track = pmap.front().tv;
4656 for (FreezeList::iterator pl = freezelist.begin(); pl != freezelist.end(); ++pl) {
4659 /* We might have removed regions, which alters other regions' layering_index,
4660 so we need to do a recursive diff here.
4662 vector<Command*> cmds;
4663 (*pl)->rdiff (cmds);
4664 _session->add_commands (cmds);
4666 _session->add_command (new StatefulDiffCommand (*pl));
4671 Editor::cut_copy_ranges (CutCopyOp op)
4673 TrackViewList ts = selection->tracks.filter_to_unique_playlists ();
4675 /* Sort the track selection now, so that it if is used, the playlists
4676 selected by the calls below to cut_copy_clear are in the order that
4677 their tracks appear in the editor. This makes things like paste
4678 of ranges work properly.
4681 sort_track_selection (ts);
4684 if (!entered_track) {
4687 ts.push_back (entered_track);
4690 for (TrackViewList::iterator i = ts.begin(); i != ts.end(); ++i) {
4691 (*i)->cut_copy_clear (*selection, op);
4696 Editor::paste (float times, bool from_context)
4698 DEBUG_TRACE (DEBUG::CutNPaste, "paste to preferred edit pos\n");
4700 paste_internal (get_preferred_edit_position (EDIT_IGNORE_NONE, from_context), times, get_grid_music_divisions (0));
4704 Editor::mouse_paste ()
4709 if (!mouse_frame (where, ignored)) {
4714 paste_internal (where, 1, get_grid_music_divisions (0));
4718 Editor::paste_internal (framepos_t position, float times, const int32_t sub_num)
4720 DEBUG_TRACE (DEBUG::CutNPaste, string_compose ("apparent paste position is %1\n", position));
4722 if (cut_buffer->empty(internal_editing())) {
4726 if (position == max_framepos) {
4727 position = get_preferred_edit_position();
4728 DEBUG_TRACE (DEBUG::CutNPaste, string_compose ("preferred edit position is %1\n", position));
4731 if (position == last_paste_pos) {
4732 /* repeated paste in the same position */
4735 /* paste in new location, reset repeated paste state */
4737 last_paste_pos = position;
4740 /* get everything in the correct order */
4743 if (!selection->tracks.empty()) {
4744 /* If there is a track selection, paste into exactly those tracks and
4745 only those tracks. This allows the user to be explicit and override
4746 the below "do the reasonable thing" logic. */
4747 ts = selection->tracks.filter_to_unique_playlists ();
4748 sort_track_selection (ts);
4750 /* Figure out which track to base the paste at. */
4751 TimeAxisView* base_track = NULL;
4752 if (_edit_point == Editing::EditAtMouse && entered_track) {
4753 /* With the mouse edit point, paste onto the track under the mouse. */
4754 base_track = entered_track;
4755 } else if (_edit_point == Editing::EditAtMouse && entered_regionview) {
4756 /* With the mouse edit point, paste onto the track of the region under the mouse. */
4757 base_track = &entered_regionview->get_time_axis_view();
4758 } else if (_last_cut_copy_source_track) {
4759 /* Paste to the track that the cut/copy came from (see mantis #333). */
4760 base_track = _last_cut_copy_source_track;
4762 /* This is "impossible" since we've copied... well, do nothing. */
4766 /* Walk up to parent if necessary, so base track is a route. */
4767 while (base_track->get_parent()) {
4768 base_track = base_track->get_parent();
4771 /* Add base track and all tracks below it. The paste logic will select
4772 the appropriate object types from the cut buffer in relative order. */
4773 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
4774 if ((*i)->order() >= base_track->order()) {
4779 /* Sort tracks so the nth track of type T will pick the nth object of type T. */
4780 sort_track_selection (ts);
4782 /* Add automation children of each track in order, for pasting several lines. */
4783 for (TrackViewList::iterator i = ts.begin(); i != ts.end();) {
4784 /* Add any automation children for pasting several lines */
4785 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*>(*i++);
4790 typedef RouteTimeAxisView::AutomationTracks ATracks;
4791 const ATracks& atracks = rtv->automation_tracks();
4792 for (ATracks::const_iterator a = atracks.begin(); a != atracks.end(); ++a) {
4793 i = ts.insert(i, a->second.get());
4798 /* We now have a list of trackviews starting at base_track, including
4799 automation children, in the order shown in the editor, e.g. R1,
4800 R1.A1, R1.A2, R2, R2.A1, ... */
4803 begin_reversible_command (Operations::paste);
4805 if (ts.size() == 1 && cut_buffer->lines.size() == 1 &&
4806 dynamic_cast<AutomationTimeAxisView*>(ts.front())) {
4807 /* Only one line copied, and one automation track selected. Do a
4808 "greedy" paste from one automation type to another. */
4810 PasteContext ctx(paste_count, times, ItemCounts(), true);
4811 ts.front()->paste (position, *cut_buffer, ctx, sub_num);
4815 /* Paste into tracks */
4817 PasteContext ctx(paste_count, times, ItemCounts(), false);
4818 for (TrackViewList::iterator i = ts.begin(); i != ts.end(); ++i) {
4819 (*i)->paste (position, *cut_buffer, ctx, sub_num);
4823 commit_reversible_command ();
4827 Editor::duplicate_regions (float times)
4829 RegionSelection rs (get_regions_from_selection_and_entered());
4830 duplicate_some_regions (rs, times);
4834 Editor::duplicate_some_regions (RegionSelection& regions, float times)
4836 if (regions.empty ()) {
4840 boost::shared_ptr<Playlist> playlist;
4841 RegionSelection sel = regions; // clear (below) may clear the argument list if its the current region selection
4842 RegionSelection foo;
4844 framepos_t const start_frame = regions.start ();
4845 framepos_t const end_frame = regions.end_frame ();
4846 framecnt_t const gap = end_frame - start_frame + 1;
4848 begin_reversible_command (Operations::duplicate_region);
4850 selection->clear_regions ();
4852 for (RegionSelection::iterator i = sel.begin(); i != sel.end(); ++i) {
4854 boost::shared_ptr<Region> r ((*i)->region());
4856 TimeAxisView& tv = (*i)->get_time_axis_view();
4857 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (&tv);
4858 latest_regionviews.clear ();
4859 sigc::connection c = rtv->view()->RegionViewAdded.connect (sigc::mem_fun(*this, &Editor::collect_new_region_view));
4861 framepos_t const position = end_frame + (r->first_frame() - start_frame + 1);
4862 playlist = (*i)->region()->playlist();
4863 playlist->clear_changes ();
4864 playlist->duplicate (r, position, gap, times);
4865 _session->add_command(new StatefulDiffCommand (playlist));
4869 foo.insert (foo.end(), latest_regionviews.begin(), latest_regionviews.end());
4873 selection->set (foo);
4876 commit_reversible_command ();
4880 Editor::duplicate_selection (float times)
4882 if (selection->time.empty() || selection->tracks.empty()) {
4886 boost::shared_ptr<Playlist> playlist;
4888 TrackViewList ts = selection->tracks.filter_to_unique_playlists ();
4890 bool in_command = false;
4892 for (TrackViewList::iterator i = ts.begin(); i != ts.end(); ++i) {
4893 if ((playlist = (*i)->playlist()) == 0) {
4896 playlist->clear_changes ();
4898 if (clicked_selection) {
4899 playlist->duplicate_range (selection->time[clicked_selection], times);
4901 playlist->duplicate_ranges (selection->time, times);
4905 begin_reversible_command (_("duplicate range selection"));
4908 _session->add_command (new StatefulDiffCommand (playlist));
4913 if (times == 1.0f) {
4914 // now "move" range selection to after the current range selection
4915 framecnt_t distance = 0;
4917 if (clicked_selection) {
4919 selection->time[clicked_selection].end - selection->time[clicked_selection].start;
4921 distance = selection->time.end_frame () - selection->time.start ();
4924 selection->move_time (distance);
4926 commit_reversible_command ();
4930 /** Reset all selected points to the relevant default value */
4932 Editor::reset_point_selection ()
4934 for (PointSelection::iterator i = selection->points.begin(); i != selection->points.end(); ++i) {
4935 ARDOUR::AutomationList::iterator j = (*i)->model ();
4936 (*j)->value = (*i)->line().the_list()->default_value ();
4941 Editor::center_playhead ()
4943 float const page = _visible_canvas_width * samples_per_pixel;
4944 center_screen_internal (playhead_cursor->current_frame (), page);
4948 Editor::center_edit_point ()
4950 float const page = _visible_canvas_width * samples_per_pixel;
4951 center_screen_internal (get_preferred_edit_position(), page);
4954 /** Caller must begin and commit a reversible command */
4956 Editor::clear_playlist (boost::shared_ptr<Playlist> playlist)
4958 playlist->clear_changes ();
4960 _session->add_command (new StatefulDiffCommand (playlist));
4964 Editor::nudge_track (bool use_edit, bool forwards)
4966 boost::shared_ptr<Playlist> playlist;
4967 framepos_t distance;
4968 framepos_t next_distance;
4972 start = get_preferred_edit_position();
4977 if ((distance = get_nudge_distance (start, next_distance)) == 0) {
4981 if (selection->tracks.empty()) {
4985 TrackViewList ts = selection->tracks.filter_to_unique_playlists ();
4986 bool in_command = false;
4988 for (TrackViewList::iterator i = ts.begin(); i != ts.end(); ++i) {
4990 if ((playlist = (*i)->playlist()) == 0) {
4994 playlist->clear_changes ();
4995 playlist->clear_owned_changes ();
4997 playlist->nudge_after (start, distance, forwards);
5000 begin_reversible_command (_("nudge track"));
5003 vector<Command*> cmds;
5005 playlist->rdiff (cmds);
5006 _session->add_commands (cmds);
5008 _session->add_command (new StatefulDiffCommand (playlist));
5012 commit_reversible_command ();
5017 Editor::remove_last_capture ()
5019 vector<string> choices;
5026 if (Config->get_verify_remove_last_capture()) {
5027 prompt = _("Do you really want to destroy the last capture?"
5028 "\n(This is destructive and cannot be undone)");
5030 choices.push_back (_("No, do nothing."));
5031 choices.push_back (_("Yes, destroy it."));
5033 Gtkmm2ext::Choice prompter (_("Destroy last capture"), prompt, choices);
5035 if (prompter.run () == 1) {
5036 _session->remove_last_capture ();
5037 _regions->redisplay ();
5041 _session->remove_last_capture();
5042 _regions->redisplay ();
5047 Editor::normalize_region ()
5053 RegionSelection rs = get_regions_from_selection_and_entered ();
5059 NormalizeDialog dialog (rs.size() > 1);
5061 if (dialog.run () != RESPONSE_ACCEPT) {
5065 CursorContext::Handle cursor_ctx = CursorContext::create(*this, _cursors->wait);
5068 /* XXX: should really only count audio regions here */
5069 int const regions = rs.size ();
5071 /* Make a list of the selected audio regions' maximum amplitudes, and also
5072 obtain the maximum amplitude of them all.
5074 list<double> max_amps;
5075 list<double> rms_vals;
5078 bool use_rms = dialog.constrain_rms ();
5080 for (RegionSelection::const_iterator i = rs.begin(); i != rs.end(); ++i) {
5081 AudioRegionView const * arv = dynamic_cast<AudioRegionView const *> (*i);
5085 dialog.descend (1.0 / regions);
5086 double const a = arv->audio_region()->maximum_amplitude (&dialog);
5088 double r = arv->audio_region()->rms (&dialog);
5089 max_rms = max (max_rms, r);
5090 rms_vals.push_back (r);
5094 /* the user cancelled the operation */
5098 max_amps.push_back (a);
5099 max_amp = max (max_amp, a);
5103 list<double>::const_iterator a = max_amps.begin ();
5104 list<double>::const_iterator l = rms_vals.begin ();
5105 bool in_command = false;
5107 for (RegionSelection::iterator r = rs.begin(); r != rs.end(); ++r) {
5108 AudioRegionView* const arv = dynamic_cast<AudioRegionView*> (*r);
5113 arv->region()->clear_changes ();
5115 double amp = dialog.normalize_individually() ? *a : max_amp;
5116 double target = dialog.target_peak (); // dB
5119 double const amp_rms = dialog.normalize_individually() ? *l : max_rms;
5120 const double t_rms = dialog.target_rms ();
5121 const gain_t c_peak = dB_to_coefficient (target);
5122 const gain_t c_rms = dB_to_coefficient (t_rms);
5123 if ((amp_rms / c_rms) > (amp / c_peak)) {
5129 arv->audio_region()->normalize (amp, target);
5132 begin_reversible_command (_("normalize"));
5135 _session->add_command (new StatefulDiffCommand (arv->region()));
5142 commit_reversible_command ();
5148 Editor::reset_region_scale_amplitude ()
5154 RegionSelection rs = get_regions_from_selection_and_entered ();
5160 bool in_command = false;
5162 for (RegionSelection::iterator r = rs.begin(); r != rs.end(); ++r) {
5163 AudioRegionView* const arv = dynamic_cast<AudioRegionView*>(*r);
5166 arv->region()->clear_changes ();
5167 arv->audio_region()->set_scale_amplitude (1.0f);
5170 begin_reversible_command ("reset gain");
5173 _session->add_command (new StatefulDiffCommand (arv->region()));
5177 commit_reversible_command ();
5182 Editor::adjust_region_gain (bool up)
5184 RegionSelection rs = get_regions_from_selection_and_entered ();
5186 if (!_session || rs.empty()) {
5190 bool in_command = false;
5192 for (RegionSelection::iterator r = rs.begin(); r != rs.end(); ++r) {
5193 AudioRegionView* const arv = dynamic_cast<AudioRegionView*>(*r);
5198 arv->region()->clear_changes ();
5200 double dB = accurate_coefficient_to_dB (arv->audio_region()->scale_amplitude ());
5208 arv->audio_region()->set_scale_amplitude (dB_to_coefficient (dB));
5211 begin_reversible_command ("adjust region gain");
5214 _session->add_command (new StatefulDiffCommand (arv->region()));
5218 commit_reversible_command ();
5224 Editor::reverse_region ()
5230 Reverse rev (*_session);
5231 apply_filter (rev, _("reverse regions"));
5235 Editor::strip_region_silence ()
5241 RegionSelection rs = get_regions_from_selection_and_entered ();
5247 std::list<RegionView*> audio_only;
5249 for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
5250 AudioRegionView* const arv = dynamic_cast<AudioRegionView*> (*i);
5252 audio_only.push_back (arv);
5256 assert (!audio_only.empty());
5258 StripSilenceDialog d (_session, audio_only);
5259 int const r = d.run ();
5263 if (r == Gtk::RESPONSE_OK) {
5264 ARDOUR::AudioIntervalMap silences;
5265 d.silences (silences);
5266 StripSilence s (*_session, silences, d.fade_length());
5268 apply_filter (s, _("strip silence"), &d);
5273 Editor::apply_midi_note_edit_op_to_region (MidiOperator& op, MidiRegionView& mrv)
5275 Evoral::Sequence<Evoral::Beats>::Notes selected;
5276 mrv.selection_as_notelist (selected, true);
5278 vector<Evoral::Sequence<Evoral::Beats>::Notes> v;
5279 v.push_back (selected);
5281 Evoral::Beats pos_beats = Evoral::Beats (mrv.midi_region()->beat()) - mrv.midi_region()->start_beats();
5283 return op (mrv.midi_region()->model(), pos_beats, v);
5287 Editor::apply_midi_note_edit_op (MidiOperator& op, const RegionSelection& rs)
5293 bool in_command = false;
5295 for (RegionSelection::const_iterator r = rs.begin(); r != rs.end(); ) {
5296 RegionSelection::const_iterator tmp = r;
5299 MidiRegionView* const mrv = dynamic_cast<MidiRegionView*> (*r);
5302 Command* cmd = apply_midi_note_edit_op_to_region (op, *mrv);
5305 begin_reversible_command (op.name ());
5309 _session->add_command (cmd);
5317 commit_reversible_command ();
5322 Editor::fork_region ()
5324 RegionSelection rs = get_regions_from_selection_and_entered ();
5330 CursorContext::Handle cursor_ctx = CursorContext::create(*this, _cursors->wait);
5331 bool in_command = false;
5335 for (RegionSelection::iterator r = rs.begin(); r != rs.end(); ) {
5336 RegionSelection::iterator tmp = r;
5339 MidiRegionView* const mrv = dynamic_cast<MidiRegionView*>(*r);
5343 boost::shared_ptr<Playlist> playlist = mrv->region()->playlist();
5344 boost::shared_ptr<MidiSource> new_source = _session->create_midi_source_by_stealing_name (mrv->midi_view()->track());
5345 boost::shared_ptr<MidiRegion> newregion = mrv->midi_region()->clone (new_source);
5348 begin_reversible_command (_("Fork Region(s)"));
5351 playlist->clear_changes ();
5352 playlist->replace_region (mrv->region(), newregion, mrv->region()->position());
5353 _session->add_command(new StatefulDiffCommand (playlist));
5355 error << string_compose (_("Could not unlink %1"), mrv->region()->name()) << endmsg;
5363 commit_reversible_command ();
5368 Editor::quantize_region ()
5371 quantize_regions(get_regions_from_selection_and_entered ());
5376 Editor::quantize_regions (const RegionSelection& rs)
5378 if (rs.n_midi_regions() == 0) {
5382 if (!quantize_dialog) {
5383 quantize_dialog = new QuantizeDialog (*this);
5386 if (quantize_dialog->is_mapped()) {
5387 /* in progress already */
5391 quantize_dialog->present ();
5392 const int r = quantize_dialog->run ();
5393 quantize_dialog->hide ();
5395 if (r == Gtk::RESPONSE_OK) {
5396 Quantize quant (quantize_dialog->snap_start(),
5397 quantize_dialog->snap_end(),
5398 quantize_dialog->start_grid_size(),
5399 quantize_dialog->end_grid_size(),
5400 quantize_dialog->strength(),
5401 quantize_dialog->swing(),
5402 quantize_dialog->threshold());
5404 apply_midi_note_edit_op (quant, rs);
5409 Editor::legatize_region (bool shrink_only)
5412 legatize_regions(get_regions_from_selection_and_entered (), shrink_only);
5417 Editor::legatize_regions (const RegionSelection& rs, bool shrink_only)
5419 if (rs.n_midi_regions() == 0) {
5423 Legatize legatize(shrink_only);
5424 apply_midi_note_edit_op (legatize, rs);
5428 Editor::transform_region ()
5431 transform_regions(get_regions_from_selection_and_entered ());
5436 Editor::transform_regions (const RegionSelection& rs)
5438 if (rs.n_midi_regions() == 0) {
5445 const int r = td.run();
5448 if (r == Gtk::RESPONSE_OK) {
5449 Transform transform(td.get());
5450 apply_midi_note_edit_op(transform, rs);
5455 Editor::transpose_region ()
5458 transpose_regions(get_regions_from_selection_and_entered ());
5463 Editor::transpose_regions (const RegionSelection& rs)
5465 if (rs.n_midi_regions() == 0) {
5470 int const r = d.run ();
5472 if (r == RESPONSE_ACCEPT) {
5473 Transpose transpose(d.semitones ());
5474 apply_midi_note_edit_op (transpose, rs);
5479 Editor::insert_patch_change (bool from_context)
5481 RegionSelection rs = get_regions_from_selection_and_entered ();
5487 const framepos_t p = get_preferred_edit_position (EDIT_IGNORE_NONE, from_context);
5489 /* XXX: bit of a hack; use the MIDNAM from the first selected region;
5490 there may be more than one, but the PatchChangeDialog can only offer
5491 one set of patch menus.
5493 MidiRegionView* first = dynamic_cast<MidiRegionView*> (rs.front ());
5495 Evoral::PatchChange<Evoral::Beats> empty (Evoral::Beats(), 0, 0, 0);
5496 PatchChangeDialog d (0, _session, empty, first->instrument_info(), Gtk::Stock::ADD);
5498 if (d.run() == RESPONSE_CANCEL) {
5502 for (RegionSelection::iterator i = rs.begin (); i != rs.end(); ++i) {
5503 MidiRegionView* const mrv = dynamic_cast<MidiRegionView*> (*i);
5505 if (p >= mrv->region()->first_frame() && p <= mrv->region()->last_frame()) {
5506 mrv->add_patch_change (p - mrv->region()->position(), d.patch ());
5513 Editor::apply_filter (Filter& filter, string command, ProgressReporter* progress)
5515 RegionSelection rs = get_regions_from_selection_and_entered ();
5521 CursorContext::Handle cursor_ctx = CursorContext::create(*this, _cursors->wait);
5522 bool in_command = false;
5527 int const N = rs.size ();
5529 for (RegionSelection::iterator r = rs.begin(); r != rs.end(); ) {
5530 RegionSelection::iterator tmp = r;
5533 AudioRegionView* const arv = dynamic_cast<AudioRegionView*>(*r);
5535 boost::shared_ptr<Playlist> playlist = arv->region()->playlist();
5538 progress->descend (1.0 / N);
5541 if (arv->audio_region()->apply (filter, progress) == 0) {
5543 playlist->clear_changes ();
5544 playlist->clear_owned_changes ();
5547 begin_reversible_command (command);
5551 if (filter.results.empty ()) {
5553 /* no regions returned; remove the old one */
5554 playlist->remove_region (arv->region ());
5558 std::vector<boost::shared_ptr<Region> >::iterator res = filter.results.begin ();
5560 /* first region replaces the old one */
5561 playlist->replace_region (arv->region(), *res, (*res)->position());
5565 while (res != filter.results.end()) {
5566 playlist->add_region (*res, (*res)->position());
5572 /* We might have removed regions, which alters other regions' layering_index,
5573 so we need to do a recursive diff here.
5575 vector<Command*> cmds;
5576 playlist->rdiff (cmds);
5577 _session->add_commands (cmds);
5579 _session->add_command(new StatefulDiffCommand (playlist));
5583 progress->ascend ();
5592 commit_reversible_command ();
5597 Editor::external_edit_region ()
5603 Editor::reset_region_gain_envelopes ()
5605 RegionSelection rs = get_regions_from_selection_and_entered ();
5607 if (!_session || rs.empty()) {
5611 bool in_command = false;
5613 for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
5614 AudioRegionView* const arv = dynamic_cast<AudioRegionView*>(*i);
5616 boost::shared_ptr<AutomationList> alist (arv->audio_region()->envelope());
5617 XMLNode& before (alist->get_state());
5619 arv->audio_region()->set_default_envelope ();
5622 begin_reversible_command (_("reset region gain"));
5625 _session->add_command (new MementoCommand<AutomationList>(*arv->audio_region()->envelope().get(), &before, &alist->get_state()));
5630 commit_reversible_command ();
5635 Editor::set_region_gain_visibility (RegionView* rv)
5637 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (rv);
5639 arv->update_envelope_visibility();
5644 Editor::set_gain_envelope_visibility ()
5650 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
5651 AudioTimeAxisView* v = dynamic_cast<AudioTimeAxisView*>(*i);
5653 v->audio_view()->foreach_regionview (sigc::mem_fun (this, &Editor::set_region_gain_visibility));
5659 Editor::toggle_gain_envelope_active ()
5661 if (_ignore_region_action) {
5665 RegionSelection rs = get_regions_from_selection_and_entered ();
5667 if (!_session || rs.empty()) {
5671 bool in_command = false;
5673 for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
5674 AudioRegionView* const arv = dynamic_cast<AudioRegionView*>(*i);
5676 arv->region()->clear_changes ();
5677 arv->audio_region()->set_envelope_active (!arv->audio_region()->envelope_active());
5680 begin_reversible_command (_("region gain envelope active"));
5683 _session->add_command (new StatefulDiffCommand (arv->region()));
5688 commit_reversible_command ();
5693 Editor::toggle_region_lock ()
5695 if (_ignore_region_action) {
5699 RegionSelection rs = get_regions_from_selection_and_entered ();
5701 if (!_session || rs.empty()) {
5705 begin_reversible_command (_("toggle region lock"));
5707 for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
5708 (*i)->region()->clear_changes ();
5709 (*i)->region()->set_locked (!(*i)->region()->locked());
5710 _session->add_command (new StatefulDiffCommand ((*i)->region()));
5713 commit_reversible_command ();
5717 Editor::toggle_region_video_lock ()
5719 if (_ignore_region_action) {
5723 RegionSelection rs = get_regions_from_selection_and_entered ();
5725 if (!_session || rs.empty()) {
5729 begin_reversible_command (_("Toggle Video Lock"));
5731 for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
5732 (*i)->region()->clear_changes ();
5733 (*i)->region()->set_video_locked (!(*i)->region()->video_locked());
5734 _session->add_command (new StatefulDiffCommand ((*i)->region()));
5737 commit_reversible_command ();
5741 Editor::toggle_region_lock_style ()
5743 if (_ignore_region_action) {
5747 RegionSelection rs = get_regions_from_selection_and_entered ();
5749 if (!_session || rs.empty()) {
5753 begin_reversible_command (_("region lock style"));
5755 for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
5756 (*i)->region()->clear_changes ();
5757 PositionLockStyle const ns = (*i)->region()->position_lock_style() == AudioTime ? MusicTime : AudioTime;
5758 (*i)->region()->set_position_lock_style (ns);
5759 _session->add_command (new StatefulDiffCommand ((*i)->region()));
5762 commit_reversible_command ();
5766 Editor::toggle_opaque_region ()
5768 if (_ignore_region_action) {
5772 RegionSelection rs = get_regions_from_selection_and_entered ();
5774 if (!_session || rs.empty()) {
5778 begin_reversible_command (_("change region opacity"));
5780 for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
5781 (*i)->region()->clear_changes ();
5782 (*i)->region()->set_opaque (!(*i)->region()->opaque());
5783 _session->add_command (new StatefulDiffCommand ((*i)->region()));
5786 commit_reversible_command ();
5790 Editor::toggle_record_enable ()
5792 bool new_state = false;
5794 for (TrackSelection::iterator i = selection->tracks.begin(); i != selection->tracks.end(); ++i) {
5795 RouteTimeAxisView *rtav = dynamic_cast<RouteTimeAxisView *>(*i);
5798 if (!rtav->is_track())
5802 new_state = !rtav->track()->rec_enable_control()->get_value();
5806 rtav->track()->rec_enable_control()->set_value (new_state, Controllable::UseGroup);
5811 Editor::toggle_solo ()
5813 bool new_state = false;
5815 boost::shared_ptr<ControlList> cl (new ControlList);
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()->soloed ();
5829 cl->push_back (rtav->route()->solo_control());
5832 _session->set_controls (cl, new_state ? 1.0 : 0.0, Controllable::UseGroup);
5836 Editor::toggle_mute ()
5838 bool new_state = false;
5840 boost::shared_ptr<RouteList> rl (new RouteList);
5842 for (TrackSelection::iterator i = selection->tracks.begin(); i != selection->tracks.end(); ++i) {
5843 RouteTimeAxisView *rtav = dynamic_cast<RouteTimeAxisView *>(*i);
5850 new_state = !rtav->route()->muted();
5854 rl->push_back (rtav->route());
5857 _session->set_controls (route_list_to_control_list (rl, &Stripable::mute_control), new_state, Controllable::UseGroup);
5861 Editor::toggle_solo_isolate ()
5867 Editor::fade_range ()
5869 TrackViewList ts = selection->tracks.filter_to_unique_playlists ();
5871 begin_reversible_command (_("fade range"));
5873 for (TrackViewList::iterator i = ts.begin(); i != ts.end(); ++i) {
5874 (*i)->fade_range (selection->time);
5877 commit_reversible_command ();
5882 Editor::set_fade_length (bool in)
5884 RegionSelection rs = get_regions_from_selection_and_entered ();
5890 /* we need a region to measure the offset from the start */
5892 RegionView* rv = rs.front ();
5894 framepos_t pos = get_preferred_edit_position();
5898 if (pos > rv->region()->last_frame() || pos < rv->region()->first_frame()) {
5899 /* edit point is outside the relevant region */
5904 if (pos <= rv->region()->position()) {
5908 len = pos - rv->region()->position();
5909 cmd = _("set fade in length");
5911 if (pos >= rv->region()->last_frame()) {
5915 len = rv->region()->last_frame() - pos;
5916 cmd = _("set fade out length");
5919 bool in_command = false;
5921 for (RegionSelection::iterator x = rs.begin(); x != rs.end(); ++x) {
5922 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (*x);
5928 boost::shared_ptr<AutomationList> alist;
5930 alist = tmp->audio_region()->fade_in();
5932 alist = tmp->audio_region()->fade_out();
5935 XMLNode &before = alist->get_state();
5938 tmp->audio_region()->set_fade_in_length (len);
5939 tmp->audio_region()->set_fade_in_active (true);
5941 tmp->audio_region()->set_fade_out_length (len);
5942 tmp->audio_region()->set_fade_out_active (true);
5946 begin_reversible_command (cmd);
5949 XMLNode &after = alist->get_state();
5950 _session->add_command(new MementoCommand<AutomationList>(*alist, &before, &after));
5954 commit_reversible_command ();
5959 Editor::set_fade_in_shape (FadeShape shape)
5961 RegionSelection rs = get_regions_from_selection_and_entered ();
5966 bool in_command = false;
5968 for (RegionSelection::iterator x = rs.begin(); x != rs.end(); ++x) {
5969 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (*x);
5975 boost::shared_ptr<AutomationList> alist = tmp->audio_region()->fade_in();
5976 XMLNode &before = alist->get_state();
5978 tmp->audio_region()->set_fade_in_shape (shape);
5981 begin_reversible_command (_("set fade in shape"));
5984 XMLNode &after = alist->get_state();
5985 _session->add_command(new MementoCommand<AutomationList>(*alist.get(), &before, &after));
5989 commit_reversible_command ();
5994 Editor::set_fade_out_shape (FadeShape shape)
5996 RegionSelection rs = get_regions_from_selection_and_entered ();
6001 bool in_command = false;
6003 for (RegionSelection::iterator x = rs.begin(); x != rs.end(); ++x) {
6004 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (*x);
6010 boost::shared_ptr<AutomationList> alist = tmp->audio_region()->fade_out();
6011 XMLNode &before = alist->get_state();
6013 tmp->audio_region()->set_fade_out_shape (shape);
6016 begin_reversible_command (_("set fade out shape"));
6019 XMLNode &after = alist->get_state();
6020 _session->add_command(new MementoCommand<AutomationList>(*alist.get(), &before, &after));
6024 commit_reversible_command ();
6029 Editor::set_fade_in_active (bool yn)
6031 RegionSelection rs = get_regions_from_selection_and_entered ();
6036 bool in_command = false;
6038 for (RegionSelection::iterator x = rs.begin(); x != rs.end(); ++x) {
6039 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (*x);
6046 boost::shared_ptr<AudioRegion> ar (tmp->audio_region());
6048 ar->clear_changes ();
6049 ar->set_fade_in_active (yn);
6052 begin_reversible_command (_("set fade in active"));
6055 _session->add_command (new StatefulDiffCommand (ar));
6059 commit_reversible_command ();
6064 Editor::set_fade_out_active (bool yn)
6066 RegionSelection rs = get_regions_from_selection_and_entered ();
6071 bool in_command = false;
6073 for (RegionSelection::iterator x = rs.begin(); x != rs.end(); ++x) {
6074 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (*x);
6080 boost::shared_ptr<AudioRegion> ar (tmp->audio_region());
6082 ar->clear_changes ();
6083 ar->set_fade_out_active (yn);
6086 begin_reversible_command (_("set fade out active"));
6089 _session->add_command(new StatefulDiffCommand (ar));
6093 commit_reversible_command ();
6098 Editor::toggle_region_fades (int dir)
6100 if (_ignore_region_action) {
6104 boost::shared_ptr<AudioRegion> ar;
6107 RegionSelection rs = get_regions_from_selection_and_entered ();
6113 RegionSelection::iterator i;
6114 for (i = rs.begin(); i != rs.end(); ++i) {
6115 if ((ar = boost::dynamic_pointer_cast<AudioRegion>((*i)->region())) != 0) {
6117 yn = ar->fade_out_active ();
6119 yn = ar->fade_in_active ();
6125 if (i == rs.end()) {
6129 /* XXX should this undo-able? */
6130 bool in_command = false;
6132 for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
6133 if ((ar = boost::dynamic_pointer_cast<AudioRegion>((*i)->region())) == 0) {
6136 ar->clear_changes ();
6138 if (dir == 1 || dir == 0) {
6139 ar->set_fade_in_active (!yn);
6142 if (dir == -1 || dir == 0) {
6143 ar->set_fade_out_active (!yn);
6146 begin_reversible_command (_("toggle fade active"));
6149 _session->add_command(new StatefulDiffCommand (ar));
6153 commit_reversible_command ();
6158 /** Update region fade visibility after its configuration has been changed */
6160 Editor::update_region_fade_visibility ()
6162 bool _fade_visibility = _session->config.get_show_region_fades ();
6164 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
6165 AudioTimeAxisView* v = dynamic_cast<AudioTimeAxisView*>(*i);
6167 if (_fade_visibility) {
6168 v->audio_view()->show_all_fades ();
6170 v->audio_view()->hide_all_fades ();
6177 Editor::set_edit_point ()
6182 if (!mouse_frame (where, ignored)) {
6188 if (selection->markers.empty()) {
6190 mouse_add_new_marker (where);
6195 Location* loc = find_location_from_marker (selection->markers.front(), ignored);
6198 loc->move_to (where, get_grid_music_divisions(0));
6204 Editor::set_playhead_cursor ()
6206 if (entered_marker) {
6207 _session->request_locate (entered_marker->position(), _session->transport_rolling());
6212 if (!mouse_frame (where, ignored)) {
6219 _session->request_locate (where, _session->transport_rolling());
6223 //not sure what this was for; remove it for now.
6224 // if (UIConfiguration::instance().get_follow_edits() && (!_session || !_session->config.get_external_sync())) {
6225 // cancel_time_selection();
6231 Editor::split_region ()
6233 if (_drags->active ()) {
6237 //if a range is selected, separate it
6238 if ( !selection->time.empty()) {
6239 separate_regions_between (selection->time);
6243 //if no range was selected, try to find some regions to split
6244 if (current_mouse_mode() == MouseObject) { //don't try this for Internal Edit, Stretch, Draw, etc.
6246 RegionSelection rs = get_regions_from_selection_and_edit_point ();
6248 framepos_t where = get_preferred_edit_position ();
6254 if (snap_musical()) {
6255 split_regions_at (where, rs, get_grid_music_divisions (0));
6257 split_regions_at (where, rs, 0);
6263 Editor::select_next_route()
6265 if (selection->tracks.empty()) {
6266 selection->set (track_views.front());
6270 TimeAxisView* current = selection->tracks.front();
6274 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
6276 if (*i == current) {
6278 if (i != track_views.end()) {
6281 current = (*(track_views.begin()));
6282 //selection->set (*(track_views.begin()));
6288 rui = dynamic_cast<RouteUI *>(current);
6290 } while (current->hidden() || (rui == NULL) || !rui->route()->active());
6292 selection->set (current);
6294 ensure_time_axis_view_is_visible (*current, false);
6298 Editor::select_prev_route()
6300 if (selection->tracks.empty()) {
6301 selection->set (track_views.front());
6305 TimeAxisView* current = selection->tracks.front();
6309 for (TrackViewList::reverse_iterator i = track_views.rbegin(); i != track_views.rend(); ++i) {
6311 if (*i == current) {
6313 if (i != track_views.rend()) {
6316 current = *(track_views.rbegin());
6321 rui = dynamic_cast<RouteUI *>(current);
6323 } while (current->hidden() || (rui == NULL) || !rui->route()->active());
6325 selection->set (current);
6327 ensure_time_axis_view_is_visible (*current, false);
6331 Editor::set_loop_from_selection (bool play)
6333 if (_session == 0) {
6337 framepos_t start, end;
6338 if (!get_selection_extents ( start, end))
6341 set_loop_range (start, end, _("set loop range from selection"));
6344 _session->request_play_loop (true, true);
6349 Editor::set_loop_from_region (bool play)
6351 framepos_t start, end;
6352 if (!get_selection_extents ( start, end))
6355 set_loop_range (start, end, _("set loop range from region"));
6358 _session->request_locate (start, true);
6359 _session->request_play_loop (true);
6364 Editor::set_punch_from_selection ()
6366 if (_session == 0) {
6370 framepos_t start, end;
6371 if (!get_selection_extents ( start, end))
6374 set_punch_range (start, end, _("set punch range from selection"));
6378 Editor::set_auto_punch_range ()
6380 // auto punch in/out button from a single button
6381 // If Punch In is unset, set punch range from playhead to end, enable punch in
6382 // If Punch In is set, the next punch sets Punch Out, unless the playhead has been
6383 // rewound beyond the Punch In marker, in which case that marker will be moved back
6384 // to the current playhead position.
6385 // If punch out is set, it clears the punch range and Punch In/Out buttons
6387 if (_session == 0) {
6391 Location* tpl = transport_punch_location();
6392 framepos_t now = playhead_cursor->current_frame();
6393 framepos_t begin = now;
6394 framepos_t end = _session->current_end_frame();
6396 if (!_session->config.get_punch_in()) {
6397 // First Press - set punch in and create range from here to eternity
6398 set_punch_range (begin, end, _("Auto Punch In"));
6399 _session->config.set_punch_in(true);
6400 } else if (tpl && !_session->config.get_punch_out()) {
6401 // Second press - update end range marker and set punch_out
6402 if (now < tpl->start()) {
6403 // playhead has been rewound - move start back and pretend nothing happened
6405 set_punch_range (begin, end, _("Auto Punch In/Out"));
6407 // normal case for 2nd press - set the punch out
6408 end = playhead_cursor->current_frame ();
6409 set_punch_range (tpl->start(), now, _("Auto Punch In/Out"));
6410 _session->config.set_punch_out(true);
6413 if (_session->config.get_punch_out()) {
6414 _session->config.set_punch_out(false);
6417 if (_session->config.get_punch_in()) {
6418 _session->config.set_punch_in(false);
6423 // third press - unset punch in/out and remove range
6424 _session->locations()->remove(tpl);
6431 Editor::set_session_extents_from_selection ()
6433 if (_session == 0) {
6437 framepos_t start, end;
6438 if (!get_selection_extents ( start, end))
6442 if ((loc = _session->locations()->session_range_location()) == 0) {
6443 _session->set_session_extents (start, end); // this will create a new session range; no need for UNDO
6445 XMLNode &before = loc->get_state();
6447 _session->set_session_extents (start, end);
6449 XMLNode &after = loc->get_state();
6451 begin_reversible_command (_("set session start/end from selection"));
6453 _session->add_command (new MementoCommand<Location>(*loc, &before, &after));
6455 commit_reversible_command ();
6458 _session->set_end_is_free (false);
6462 Editor::set_punch_start_from_edit_point ()
6466 framepos_t start = 0;
6467 framepos_t end = max_framepos;
6469 //use the existing punch end, if any
6470 Location* tpl = transport_punch_location();
6475 if ((_edit_point == EditAtPlayhead) && _session->transport_rolling()) {
6476 start = _session->audible_frame();
6478 start = get_preferred_edit_position();
6481 //snap the selection start/end
6484 //if there's not already a sensible selection endpoint, go "forever"
6485 if ( start > end ) {
6489 set_punch_range (start, end, _("set punch start from EP"));
6495 Editor::set_punch_end_from_edit_point ()
6499 framepos_t start = 0;
6500 framepos_t end = max_framepos;
6502 //use the existing punch start, if any
6503 Location* tpl = transport_punch_location();
6505 start = tpl->start();
6508 if ((_edit_point == EditAtPlayhead) && _session->transport_rolling()) {
6509 end = _session->audible_frame();
6511 end = get_preferred_edit_position();
6514 //snap the selection start/end
6517 set_punch_range (start, end, _("set punch end from EP"));
6523 Editor::set_loop_start_from_edit_point ()
6527 framepos_t start = 0;
6528 framepos_t end = max_framepos;
6530 //use the existing loop end, if any
6531 Location* tpl = transport_loop_location();
6536 if ((_edit_point == EditAtPlayhead) && _session->transport_rolling()) {
6537 start = _session->audible_frame();
6539 start = get_preferred_edit_position();
6542 //snap the selection start/end
6545 //if there's not already a sensible selection endpoint, go "forever"
6546 if ( start > end ) {
6550 set_loop_range (start, end, _("set loop start from EP"));
6556 Editor::set_loop_end_from_edit_point ()
6560 framepos_t start = 0;
6561 framepos_t end = max_framepos;
6563 //use the existing loop start, if any
6564 Location* tpl = transport_loop_location();
6566 start = tpl->start();
6569 if ((_edit_point == EditAtPlayhead) && _session->transport_rolling()) {
6570 end = _session->audible_frame();
6572 end = get_preferred_edit_position();
6575 //snap the selection start/end
6578 set_loop_range (start, end, _("set loop end from EP"));
6583 Editor::set_punch_from_region ()
6585 framepos_t start, end;
6586 if (!get_selection_extents ( start, end))
6589 set_punch_range (start, end, _("set punch range from region"));
6593 Editor::pitch_shift_region ()
6595 RegionSelection rs = get_regions_from_selection_and_entered ();
6597 RegionSelection audio_rs;
6598 for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
6599 if (dynamic_cast<AudioRegionView*> (*i)) {
6600 audio_rs.push_back (*i);
6604 if (audio_rs.empty()) {
6608 pitch_shift (audio_rs, 1.2);
6612 Editor::set_tempo_from_region ()
6614 RegionSelection rs = get_regions_from_selection_and_entered ();
6616 if (!_session || rs.empty()) {
6620 RegionView* rv = rs.front();
6622 define_one_bar (rv->region()->position(), rv->region()->last_frame() + 1);
6626 Editor::use_range_as_bar ()
6628 framepos_t start, end;
6629 if (get_edit_op_range (start, end)) {
6630 define_one_bar (start, end);
6635 Editor::define_one_bar (framepos_t start, framepos_t end)
6637 framepos_t length = end - start;
6639 const Meter& m (_session->tempo_map().meter_at_frame (start));
6641 /* length = 1 bar */
6643 /* We're going to deliver a constant tempo here,
6644 so we can use frames per beat to determine length.
6645 now we want frames per beat.
6646 we have frames per bar, and beats per bar, so ...
6649 /* XXXX METER MATH */
6651 double frames_per_beat = length / m.divisions_per_bar();
6653 /* beats per minute = */
6655 double beats_per_minute = (_session->frame_rate() * 60.0) / frames_per_beat;
6657 /* now decide whether to:
6659 (a) set global tempo
6660 (b) add a new tempo marker
6664 const TempoSection& t (_session->tempo_map().tempo_section_at_frame (start));
6666 bool do_global = false;
6668 if ((_session->tempo_map().n_tempos() == 1) && (_session->tempo_map().n_meters() == 1)) {
6670 /* only 1 tempo & 1 meter: ask if the user wants to set the tempo
6671 at the start, or create a new marker
6674 vector<string> options;
6675 options.push_back (_("Cancel"));
6676 options.push_back (_("Add new marker"));
6677 options.push_back (_("Set global tempo"));
6680 _("Define one bar"),
6681 _("Do you want to set the global tempo or add a new tempo marker?"),
6685 c.set_default_response (2);
6701 /* more than 1 tempo and/or meter section already, go ahead do the "usual":
6702 if the marker is at the region starter, change it, otherwise add
6707 begin_reversible_command (_("set tempo from region"));
6708 XMLNode& before (_session->tempo_map().get_state());
6711 _session->tempo_map().change_initial_tempo (beats_per_minute, t.note_type());
6712 } else if (t.frame() == start) {
6713 _session->tempo_map().change_existing_tempo_at (start, beats_per_minute, t.note_type());
6715 const Tempo tempo (beats_per_minute, t.note_type());
6716 _session->tempo_map().add_tempo (tempo, 0.0, start, TempoSection::Constant, AudioTime);
6719 XMLNode& after (_session->tempo_map().get_state());
6721 _session->add_command (new MementoCommand<TempoMap>(_session->tempo_map(), &before, &after));
6722 commit_reversible_command ();
6726 Editor::split_region_at_transients ()
6728 AnalysisFeatureList positions;
6730 RegionSelection rs = get_regions_from_selection_and_entered ();
6732 if (!_session || rs.empty()) {
6736 begin_reversible_command (_("split regions"));
6738 for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ) {
6740 RegionSelection::iterator tmp;
6745 boost::shared_ptr<AudioRegion> ar = boost::dynamic_pointer_cast<AudioRegion> ((*i)->region());
6748 ar->transients (positions);
6749 split_region_at_points ((*i)->region(), positions, true);
6756 commit_reversible_command ();
6761 Editor::split_region_at_points (boost::shared_ptr<Region> r, AnalysisFeatureList& positions, bool can_ferret, bool select_new)
6763 bool use_rhythmic_rodent = false;
6765 boost::shared_ptr<Playlist> pl = r->playlist();
6767 list<boost::shared_ptr<Region> > new_regions;
6773 if (positions.empty()) {
6777 if (positions.size() > 20 && can_ferret) {
6778 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);
6779 MessageDialog msg (msgstr,
6782 Gtk::BUTTONS_OK_CANCEL);
6785 msg.add_button (_("Call for the Ferret!"), RESPONSE_APPLY);
6786 msg.set_secondary_text (_("Press OK to continue with this split operation\nor ask the Ferret dialog to tune the analysis"));
6788 msg.set_secondary_text (_("Press OK to continue with this split operation"));
6791 msg.set_title (_("Excessive split?"));
6794 int response = msg.run();
6800 case RESPONSE_APPLY:
6801 use_rhythmic_rodent = true;
6808 if (use_rhythmic_rodent) {
6809 show_rhythm_ferret ();
6813 AnalysisFeatureList::const_iterator x;
6815 pl->clear_changes ();
6816 pl->clear_owned_changes ();
6818 x = positions.begin();
6820 if (x == positions.end()) {
6825 pl->remove_region (r);
6829 framepos_t rstart = r->first_frame ();
6830 framepos_t rend = r->last_frame ();
6832 while (x != positions.end()) {
6834 /* deal with positons that are out of scope of present region bounds */
6835 if (*x <= rstart || *x > rend) {
6840 /* file start = original start + how far we from the initial position ? */
6842 framepos_t file_start = r->start() + pos;
6844 /* length = next position - current position */
6846 framepos_t len = (*x) - pos - rstart;
6848 /* XXX we do we really want to allow even single-sample regions?
6849 * shouldn't we have some kind of lower limit on region size?
6858 if (RegionFactory::region_name (new_name, r->name())) {
6862 /* do NOT announce new regions 1 by one, just wait till they are all done */
6866 plist.add (ARDOUR::Properties::start, file_start);
6867 plist.add (ARDOUR::Properties::length, len);
6868 plist.add (ARDOUR::Properties::name, new_name);
6869 plist.add (ARDOUR::Properties::layer, 0);
6870 // TODO set transients_offset
6872 boost::shared_ptr<Region> nr = RegionFactory::create (r->sources(), plist, false);
6873 /* because we set annouce to false, manually add the new region to the
6876 RegionFactory::map_add (nr);
6878 pl->add_region (nr, rstart + pos);
6881 new_regions.push_front(nr);
6890 RegionFactory::region_name (new_name, r->name());
6892 /* Add the final region */
6895 plist.add (ARDOUR::Properties::start, r->start() + pos);
6896 plist.add (ARDOUR::Properties::length, r->last_frame() - (r->position() + pos) + 1);
6897 plist.add (ARDOUR::Properties::name, new_name);
6898 plist.add (ARDOUR::Properties::layer, 0);
6900 boost::shared_ptr<Region> nr = RegionFactory::create (r->sources(), plist, false);
6901 /* because we set annouce to false, manually add the new region to the
6904 RegionFactory::map_add (nr);
6905 pl->add_region (nr, r->position() + pos);
6908 new_regions.push_front(nr);
6913 /* We might have removed regions, which alters other regions' layering_index,
6914 so we need to do a recursive diff here.
6916 vector<Command*> cmds;
6918 _session->add_commands (cmds);
6920 _session->add_command (new StatefulDiffCommand (pl));
6924 for (list<boost::shared_ptr<Region> >::iterator i = new_regions.begin(); i != new_regions.end(); ++i){
6925 set_selected_regionview_from_region_list ((*i), Selection::Add);
6931 Editor::place_transient()
6937 RegionSelection rs = get_regions_from_selection_and_edit_point ();
6943 framepos_t where = get_preferred_edit_position();
6945 begin_reversible_command (_("place transient"));
6947 for (RegionSelection::iterator r = rs.begin(); r != rs.end(); ++r) {
6948 (*r)->region()->add_transient(where);
6951 commit_reversible_command ();
6955 Editor::remove_transient(ArdourCanvas::Item* item)
6961 ArdourCanvas::Line* _line = reinterpret_cast<ArdourCanvas::Line*> (item);
6964 AudioRegionView* _arv = reinterpret_cast<AudioRegionView*> (item->get_data ("regionview"));
6965 _arv->remove_transient (*(float*) _line->get_data ("position"));
6969 Editor::snap_regions_to_grid ()
6971 list <boost::shared_ptr<Playlist > > used_playlists;
6973 RegionSelection rs = get_regions_from_selection_and_entered ();
6975 if (!_session || rs.empty()) {
6979 begin_reversible_command (_("snap regions to grid"));
6981 for (RegionSelection::iterator r = rs.begin(); r != rs.end(); ++r) {
6983 boost::shared_ptr<Playlist> pl = (*r)->region()->playlist();
6985 if (!pl->frozen()) {
6986 /* we haven't seen this playlist before */
6988 /* remember used playlists so we can thaw them later */
6989 used_playlists.push_back(pl);
6993 framepos_t start_frame = (*r)->region()->first_frame ();
6994 snap_to (start_frame);
6995 (*r)->region()->set_position (start_frame);
6998 while (used_playlists.size() > 0) {
6999 list <boost::shared_ptr<Playlist > >::iterator i = used_playlists.begin();
7001 used_playlists.pop_front();
7004 commit_reversible_command ();
7008 Editor::close_region_gaps ()
7010 list <boost::shared_ptr<Playlist > > used_playlists;
7012 RegionSelection rs = get_regions_from_selection_and_entered ();
7014 if (!_session || rs.empty()) {
7018 Dialog dialog (_("Close Region Gaps"));
7021 table.set_spacings (12);
7022 table.set_border_width (12);
7023 Label* l = manage (left_aligned_label (_("Crossfade length")));
7024 table.attach (*l, 0, 1, 0, 1);
7026 SpinButton spin_crossfade (1, 0);
7027 spin_crossfade.set_range (0, 15);
7028 spin_crossfade.set_increments (1, 1);
7029 spin_crossfade.set_value (5);
7030 table.attach (spin_crossfade, 1, 2, 0, 1);
7032 table.attach (*manage (new Label (_("ms"))), 2, 3, 0, 1);
7034 l = manage (left_aligned_label (_("Pull-back length")));
7035 table.attach (*l, 0, 1, 1, 2);
7037 SpinButton spin_pullback (1, 0);
7038 spin_pullback.set_range (0, 100);
7039 spin_pullback.set_increments (1, 1);
7040 spin_pullback.set_value(30);
7041 table.attach (spin_pullback, 1, 2, 1, 2);
7043 table.attach (*manage (new Label (_("ms"))), 2, 3, 1, 2);
7045 dialog.get_vbox()->pack_start (table);
7046 dialog.add_button (Stock::CANCEL, RESPONSE_CANCEL);
7047 dialog.add_button (_("Ok"), RESPONSE_ACCEPT);
7050 if (dialog.run () == RESPONSE_CANCEL) {
7054 framepos_t crossfade_len = spin_crossfade.get_value();
7055 framepos_t pull_back_frames = spin_pullback.get_value();
7057 crossfade_len = lrintf (crossfade_len * _session->frame_rate()/1000);
7058 pull_back_frames = lrintf (pull_back_frames * _session->frame_rate()/1000);
7060 /* Iterate over the region list and make adjacent regions overlap by crossfade_len_ms */
7062 begin_reversible_command (_("close region gaps"));
7065 boost::shared_ptr<Region> last_region;
7067 rs.sort_by_position_and_track();
7069 for (RegionSelection::iterator r = rs.begin(); r != rs.end(); ++r) {
7071 boost::shared_ptr<Playlist> pl = (*r)->region()->playlist();
7073 if (!pl->frozen()) {
7074 /* we haven't seen this playlist before */
7076 /* remember used playlists so we can thaw them later */
7077 used_playlists.push_back(pl);
7081 framepos_t position = (*r)->region()->position();
7083 if (idx == 0 || position < last_region->position()){
7084 last_region = (*r)->region();
7089 (*r)->region()->trim_front( (position - pull_back_frames));
7090 last_region->trim_end( (position - pull_back_frames + crossfade_len));
7092 last_region = (*r)->region();
7097 while (used_playlists.size() > 0) {
7098 list <boost::shared_ptr<Playlist > >::iterator i = used_playlists.begin();
7100 used_playlists.pop_front();
7103 commit_reversible_command ();
7107 Editor::tab_to_transient (bool forward)
7109 AnalysisFeatureList positions;
7111 RegionSelection rs = get_regions_from_selection_and_entered ();
7117 framepos_t pos = _session->audible_frame ();
7119 if (!selection->tracks.empty()) {
7121 /* don't waste time searching for transients in duplicate playlists.
7124 TrackViewList ts = selection->tracks.filter_to_unique_playlists ();
7126 for (TrackViewList::iterator t = ts.begin(); t != ts.end(); ++t) {
7128 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (*t);
7131 boost::shared_ptr<Track> tr = rtv->track();
7133 boost::shared_ptr<Playlist> pl = tr->playlist ();
7135 framepos_t result = pl->find_next_transient (pos, forward ? 1 : -1);
7138 positions.push_back (result);
7151 for (RegionSelection::iterator r = rs.begin(); r != rs.end(); ++r) {
7152 (*r)->region()->get_transients (positions);
7156 TransientDetector::cleanup_transients (positions, _session->frame_rate(), 3.0);
7159 AnalysisFeatureList::iterator x;
7161 for (x = positions.begin(); x != positions.end(); ++x) {
7167 if (x != positions.end ()) {
7168 _session->request_locate (*x);
7172 AnalysisFeatureList::reverse_iterator x;
7174 for (x = positions.rbegin(); x != positions.rend(); ++x) {
7180 if (x != positions.rend ()) {
7181 _session->request_locate (*x);
7187 Editor::playhead_forward_to_grid ()
7193 framepos_t pos = playhead_cursor->current_frame ();
7194 if (pos < max_framepos - 1) {
7196 snap_to_internal (pos, RoundUpAlways, false);
7197 _session->request_locate (pos);
7203 Editor::playhead_backward_to_grid ()
7209 framepos_t pos = playhead_cursor->current_frame ();
7212 snap_to_internal (pos, RoundDownAlways, false);
7213 _session->request_locate (pos);
7218 Editor::set_track_height (Height h)
7220 TrackSelection& ts (selection->tracks);
7222 for (TrackSelection::iterator x = ts.begin(); x != ts.end(); ++x) {
7223 (*x)->set_height_enum (h);
7228 Editor::toggle_tracks_active ()
7230 TrackSelection& ts (selection->tracks);
7232 bool target = false;
7238 for (TrackSelection::iterator x = ts.begin(); x != ts.end(); ++x) {
7239 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*>(*x);
7243 target = !rtv->_route->active();
7246 rtv->_route->set_active (target, this);
7252 Editor::remove_tracks ()
7254 /* this will delete GUI objects that may be the subject of an event
7255 handler in which this method is called. Defer actual deletion to the
7256 next idle callback, when all event handling is finished.
7258 Glib::signal_idle().connect (sigc::mem_fun (*this, &Editor::idle_remove_tracks));
7262 Editor::idle_remove_tracks ()
7264 Session::StateProtector sp (_session);
7266 return false; /* do not call again */
7270 Editor::_remove_tracks ()
7272 TrackSelection& ts (selection->tracks);
7278 vector<string> choices;
7282 const char* trackstr;
7284 vector<boost::shared_ptr<Route> > routes;
7285 bool special_bus = false;
7287 for (TrackSelection::iterator x = ts.begin(); x != ts.end(); ++x) {
7288 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (*x);
7292 if (rtv->is_track()) {
7297 routes.push_back (rtv->_route);
7299 if (rtv->route()->is_master() || rtv->route()->is_monitor()) {
7304 if (special_bus && !Config->get_allow_special_bus_removal()) {
7305 MessageDialog msg (_("That would be bad news ...."),
7309 msg.set_secondary_text (string_compose (_(
7310 "Removing the master or monitor bus is such a bad idea\n\
7311 that %1 is not going to allow it.\n\
7313 If you really want to do this sort of thing\n\
7314 edit your ardour.rc file to set the\n\
7315 \"allow-special-bus-removal\" option to be \"yes\""), PROGRAM_NAME));
7322 if (ntracks + nbusses == 0) {
7326 trackstr = P_("track", "tracks", ntracks);
7327 busstr = P_("bus", "busses", nbusses);
7331 prompt = string_compose (_("Do you really want to remove %1 %2 and %3 %4?\n"
7332 "(You may also lose the playlists associated with the %2)\n\n"
7333 "This action cannot be undone, and the session file will be overwritten!"),
7334 ntracks, trackstr, nbusses, busstr);
7336 prompt = string_compose (_("Do you really want to remove %1 %2?\n"
7337 "(You may also lose the playlists associated with the %2)\n\n"
7338 "This action cannot be undone, and the session file will be overwritten!"),
7341 } else if (nbusses) {
7342 prompt = string_compose (_("Do you really want to remove %1 %2?\n\n"
7343 "This action cannot be undone, and the session file will be overwritten"),
7347 choices.push_back (_("No, do nothing."));
7348 if (ntracks + nbusses > 1) {
7349 choices.push_back (_("Yes, remove them."));
7351 choices.push_back (_("Yes, remove it."));
7356 title = string_compose (_("Remove %1"), trackstr);
7358 title = string_compose (_("Remove %1"), busstr);
7361 Choice prompter (title, prompt, choices);
7363 if (prompter.run () != 1) {
7367 if (current_mixer_strip && routes.size () > 1 && std::find (routes.begin(), routes.end(), current_mixer_strip->route()) != routes.end ()) {
7368 /* Route deletion calls Editor::timeaxisview_deleted() iteratively (for each deleted
7369 * route). If the deleted route is currently displayed in the Editor-Mixer (highly
7370 * likely because deletion requires selection) this will call
7371 * Editor::set_selected_mixer_strip () which is expensive ( MixerStrip::set_route() ).
7372 * It's likewise likely that the route that has just been displayed in the
7373 * Editor-Mixer will be next in line for deletion.
7375 * So simply switch to the master-bus (if present)
7377 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
7378 if ((*i)->stripable ()->is_master ()) {
7379 set_selected_mixer_strip (*(*i));
7385 Mixer_UI::instance()->selection().block_routes_changed (true);
7386 selection->block_tracks_changed (true);
7388 DisplaySuspender ds;
7389 boost::shared_ptr<RouteList> rl (new RouteList);
7390 for (vector<boost::shared_ptr<Route> >::iterator x = routes.begin(); x != routes.end(); ++x) {
7393 _session->remove_routes (rl);
7395 /* TrackSelection and RouteList leave scope,
7396 * destructors are called,
7397 * diskstream drops references, save_state is called (again for every track)
7399 selection->block_tracks_changed (false);
7400 Mixer_UI::instance()->selection().block_routes_changed (false);
7401 selection->TracksChanged (); /* EMIT SIGNAL */
7405 Editor::do_insert_time ()
7407 if (selection->tracks.empty()) {
7411 InsertRemoveTimeDialog d (*this);
7412 int response = d.run ();
7414 if (response != RESPONSE_OK) {
7418 if (d.distance() == 0) {
7425 d.intersected_region_action (),
7429 d.move_glued_markers(),
7430 d.move_locked_markers(),
7436 Editor::insert_time (
7437 framepos_t pos, framecnt_t frames, InsertTimeOption opt,
7438 bool all_playlists, bool ignore_music_glue, bool markers_too, bool glued_markers_too, bool locked_markers_too, bool tempo_too
7442 if (Config->get_edit_mode() == Lock) {
7445 bool in_command = false;
7447 TrackViewList ts = selection->tracks.filter_to_unique_playlists ();
7449 for (TrackViewList::iterator x = ts.begin(); x != ts.end(); ++x) {
7453 /* don't operate on any playlist more than once, which could
7454 * happen if "all playlists" is enabled, but there is more
7455 * than 1 track using playlists "from" a given track.
7458 set<boost::shared_ptr<Playlist> > pl;
7460 if (all_playlists) {
7461 RouteTimeAxisView* rtav = dynamic_cast<RouteTimeAxisView*> (*x);
7462 if (rtav && rtav->track ()) {
7463 vector<boost::shared_ptr<Playlist> > all = _session->playlists->playlists_for_track (rtav->track ());
7464 for (vector<boost::shared_ptr<Playlist> >::iterator p = all.begin(); p != all.end(); ++p) {
7469 if ((*x)->playlist ()) {
7470 pl.insert ((*x)->playlist ());
7474 for (set<boost::shared_ptr<Playlist> >::iterator i = pl.begin(); i != pl.end(); ++i) {
7476 (*i)->clear_changes ();
7477 (*i)->clear_owned_changes ();
7479 if (opt == SplitIntersected) {
7480 /* non musical split */
7481 (*i)->split (pos, 0);
7484 (*i)->shift (pos, frames, (opt == MoveIntersected), ignore_music_glue);
7487 begin_reversible_command (_("insert time"));
7490 vector<Command*> cmds;
7492 _session->add_commands (cmds);
7494 _session->add_command (new StatefulDiffCommand (*i));
7498 RouteTimeAxisView* rtav = dynamic_cast<RouteTimeAxisView*> (*x);
7501 begin_reversible_command (_("insert time"));
7504 rtav->route ()->shift (pos, frames);
7511 const int32_t divisions = get_grid_music_divisions (0);
7512 XMLNode& before (_session->locations()->get_state());
7513 Locations::LocationList copy (_session->locations()->list());
7515 for (Locations::LocationList::iterator i = copy.begin(); i != copy.end(); ++i) {
7517 Locations::LocationList::const_iterator tmp;
7519 if ((*i)->position_lock_style() == AudioTime || glued_markers_too) {
7520 bool const was_locked = (*i)->locked ();
7521 if (locked_markers_too) {
7525 if ((*i)->start() >= pos) {
7526 // move end first, in case we're moving by more than the length of the range
7527 if (!(*i)->is_mark()) {
7528 (*i)->set_end ((*i)->end() + frames, false, true, divisions);
7530 (*i)->set_start ((*i)->start() + frames, false, true, divisions);
7542 begin_reversible_command (_("insert time"));
7545 XMLNode& after (_session->locations()->get_state());
7546 _session->add_command (new MementoCommand<Locations>(*_session->locations(), &before, &after));
7552 begin_reversible_command (_("insert time"));
7555 XMLNode& before (_session->tempo_map().get_state());
7556 _session->tempo_map().insert_time (pos, frames);
7557 XMLNode& after (_session->tempo_map().get_state());
7558 _session->add_command (new MementoCommand<TempoMap>(_session->tempo_map(), &before, &after));
7562 commit_reversible_command ();
7567 Editor::do_remove_time ()
7569 if (selection->tracks.empty()) {
7573 InsertRemoveTimeDialog d (*this, true);
7575 int response = d.run ();
7577 if (response != RESPONSE_OK) {
7581 framecnt_t distance = d.distance();
7583 if (distance == 0) {
7593 d.move_glued_markers(),
7594 d.move_locked_markers(),
7600 Editor::remove_time (framepos_t pos, framecnt_t frames, InsertTimeOption opt,
7601 bool ignore_music_glue, bool markers_too, bool glued_markers_too, bool locked_markers_too, bool tempo_too)
7603 if (Config->get_edit_mode() == Lock) {
7604 error << (_("Cannot insert or delete time when in Lock edit.")) << endmsg;
7607 bool in_command = false;
7609 for (TrackSelection::iterator x = selection->tracks.begin(); x != selection->tracks.end(); ++x) {
7611 boost::shared_ptr<Playlist> pl = (*x)->playlist();
7615 XMLNode &before = pl->get_state();
7617 std::list<AudioRange> rl;
7618 AudioRange ar(pos, pos+frames, 0);
7621 pl->shift (pos, -frames, true, ignore_music_glue);
7624 begin_reversible_command (_("remove time"));
7627 XMLNode &after = pl->get_state();
7629 _session->add_command (new MementoCommand<Playlist> (*pl, &before, &after));
7633 RouteTimeAxisView* rtav = dynamic_cast<RouteTimeAxisView*> (*x);
7636 begin_reversible_command (_("remove time"));
7639 rtav->route ()->shift (pos, -frames);
7643 const int32_t divisions = get_grid_music_divisions (0);
7644 std::list<Location*> loc_kill_list;
7649 XMLNode& before (_session->locations()->get_state());
7650 Locations::LocationList copy (_session->locations()->list());
7652 for (Locations::LocationList::iterator i = copy.begin(); i != copy.end(); ++i) {
7653 if ((*i)->position_lock_style() == AudioTime || glued_markers_too) {
7655 bool const was_locked = (*i)->locked ();
7656 if (locked_markers_too) {
7660 if (!(*i)->is_mark()) { // it's a range; have to handle both start and end
7661 if ((*i)->end() >= pos
7662 && (*i)->end() < pos+frames
7663 && (*i)->start() >= pos
7664 && (*i)->end() < pos+frames) { // range is completely enclosed; kill it
7666 loc_kill_list.push_back(*i);
7667 } else { // only start or end is included, try to do the right thing
7668 // move start before moving end, to avoid trying to move the end to before the start
7669 // if we're removing more time than the length of the range
7670 if ((*i)->start() >= pos && (*i)->start() < pos+frames) {
7671 // start is within cut
7672 (*i)->set_start (pos, false, true,divisions); // bring the start marker to the beginning of the cut
7674 } else if ((*i)->start() >= pos+frames) {
7675 // start (and thus entire range) lies beyond end of cut
7676 (*i)->set_start ((*i)->start() - frames, false, true, divisions); // slip the start marker back
7679 if ((*i)->end() >= pos && (*i)->end() < pos+frames) {
7680 // end is inside cut
7681 (*i)->set_end (pos, false, true, divisions); // bring the end to the cut
7683 } else if ((*i)->end() >= pos+frames) {
7684 // end is beyond end of cut
7685 (*i)->set_end ((*i)->end() - frames, false, true, divisions); // slip the end marker back
7690 } else if ((*i)->start() >= pos && (*i)->start() < pos+frames ) {
7691 loc_kill_list.push_back(*i);
7693 } else if ((*i)->start() >= pos) {
7694 (*i)->set_start ((*i)->start() -frames, false, true, divisions);
7704 for (list<Location*>::iterator i = loc_kill_list.begin(); i != loc_kill_list.end(); ++i) {
7705 _session->locations()->remove( *i );
7710 begin_reversible_command (_("remove time"));
7713 XMLNode& after (_session->locations()->get_state());
7714 _session->add_command (new MementoCommand<Locations>(*_session->locations(), &before, &after));
7719 XMLNode& before (_session->tempo_map().get_state());
7721 if (_session->tempo_map().remove_time (pos, frames) ) {
7723 begin_reversible_command (_("remove time"));
7726 XMLNode& after (_session->tempo_map().get_state());
7727 _session->add_command (new MementoCommand<TempoMap>(_session->tempo_map(), &before, &after));
7732 commit_reversible_command ();
7737 Editor::fit_selection ()
7739 if (!selection->tracks.empty()) {
7740 fit_tracks (selection->tracks);
7744 /* no selected tracks - use tracks with selected regions */
7746 if (!selection->regions.empty()) {
7747 for (RegionSelection::iterator r = selection->regions.begin(); r != selection->regions.end(); ++r) {
7748 tvl.push_back (&(*r)->get_time_axis_view ());
7754 } else if (internal_editing()) {
7755 /* no selected tracks, or regions, but in internal edit mode, so follow the mouse and use
7758 if (entered_track) {
7759 tvl.push_back (entered_track);
7768 Editor::fit_tracks (TrackViewList & tracks)
7770 if (tracks.empty()) {
7774 uint32_t child_heights = 0;
7775 int visible_tracks = 0;
7777 for (TrackSelection::iterator t = tracks.begin(); t != tracks.end(); ++t) {
7779 if (!(*t)->marked_for_display()) {
7783 child_heights += (*t)->effective_height() - (*t)->current_height();
7787 /* compute the per-track height from:
7789 total canvas visible height -
7790 height that will be taken by visible children of selected
7791 tracks - height of the ruler/hscroll area
7793 uint32_t h = (uint32_t) floor ((trackviews_height() - child_heights) / visible_tracks);
7794 double first_y_pos = DBL_MAX;
7796 if (h < TimeAxisView::preset_height (HeightSmall)) {
7797 MessageDialog msg (_("There are too many tracks to fit in the current window"));
7798 /* too small to be displayed */
7802 undo_visual_stack.push_back (current_visual_state (true));
7803 PBD::Unwinder<bool> nsv (no_save_visual, true);
7805 /* build a list of all tracks, including children */
7808 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
7810 TimeAxisView::Children c = (*i)->get_child_list ();
7811 for (TimeAxisView::Children::iterator j = c.begin(); j != c.end(); ++j) {
7812 all.push_back (j->get());
7817 // find selection range.
7818 // if someone knows how to user TrackViewList::iterator for this
7820 int selected_top = -1;
7821 int selected_bottom = -1;
7823 for (TrackViewList::iterator t = all.begin(); t != all.end(); ++t, ++i) {
7824 if ((*t)->marked_for_display ()) {
7825 if (tracks.contains(*t)) {
7826 if (selected_top == -1) {
7829 selected_bottom = i;
7835 for (TrackViewList::iterator t = all.begin(); t != all.end(); ++t, ++i) {
7836 if ((*t)->marked_for_display ()) {
7837 if (tracks.contains(*t)) {
7838 (*t)->set_height (h);
7839 first_y_pos = std::min ((*t)->y_position (), first_y_pos);
7841 if (i > selected_top && i < selected_bottom) {
7842 hide_track_in_display (*t);
7849 set the controls_layout height now, because waiting for its size
7850 request signal handler will cause the vertical adjustment setting to fail
7853 controls_layout.property_height () = _full_canvas_height;
7854 vertical_adjustment.set_value (first_y_pos);
7856 redo_visual_stack.push_back (current_visual_state (true));
7858 visible_tracks_selector.set_text (_("Sel"));
7862 Editor::save_visual_state (uint32_t n)
7864 while (visual_states.size() <= n) {
7865 visual_states.push_back (0);
7868 if (visual_states[n] != 0) {
7869 delete visual_states[n];
7872 visual_states[n] = current_visual_state (true);
7877 Editor::goto_visual_state (uint32_t n)
7879 if (visual_states.size() <= n) {
7883 if (visual_states[n] == 0) {
7887 use_visual_state (*visual_states[n]);
7891 Editor::start_visual_state_op (uint32_t n)
7893 save_visual_state (n);
7895 PopUp* pup = new PopUp (WIN_POS_MOUSE, 1000, true);
7897 snprintf (buf, sizeof (buf), _("Saved view %u"), n+1);
7898 pup->set_text (buf);
7903 Editor::cancel_visual_state_op (uint32_t n)
7905 goto_visual_state (n);
7909 Editor::toggle_region_mute ()
7911 if (_ignore_region_action) {
7915 RegionSelection rs = get_regions_from_selection_and_entered ();
7921 if (rs.size() > 1) {
7922 begin_reversible_command (_("mute regions"));
7924 begin_reversible_command (_("mute region"));
7927 for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
7929 (*i)->region()->playlist()->clear_changes ();
7930 (*i)->region()->set_muted (!(*i)->region()->muted ());
7931 _session->add_command (new StatefulDiffCommand ((*i)->region()));
7935 commit_reversible_command ();
7939 Editor::combine_regions ()
7941 /* foreach track with selected regions, take all selected regions
7942 and join them into a new region containing the subregions (as a
7946 typedef set<RouteTimeAxisView*> RTVS;
7949 if (selection->regions.empty()) {
7953 for (RegionSelection::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
7954 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*>(&(*i)->get_time_axis_view());
7957 tracks.insert (rtv);
7961 begin_reversible_command (_("combine regions"));
7963 vector<RegionView*> new_selection;
7965 for (RTVS::iterator i = tracks.begin(); i != tracks.end(); ++i) {
7968 if ((rv = (*i)->combine_regions ()) != 0) {
7969 new_selection.push_back (rv);
7973 selection->clear_regions ();
7974 for (vector<RegionView*>::iterator i = new_selection.begin(); i != new_selection.end(); ++i) {
7975 selection->add (*i);
7978 commit_reversible_command ();
7982 Editor::uncombine_regions ()
7984 typedef set<RouteTimeAxisView*> RTVS;
7987 if (selection->regions.empty()) {
7991 for (RegionSelection::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
7992 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*>(&(*i)->get_time_axis_view());
7995 tracks.insert (rtv);
7999 begin_reversible_command (_("uncombine regions"));
8001 for (RTVS::iterator i = tracks.begin(); i != tracks.end(); ++i) {
8002 (*i)->uncombine_regions ();
8005 commit_reversible_command ();
8009 Editor::toggle_midi_input_active (bool flip_others)
8012 boost::shared_ptr<RouteList> rl (new RouteList);
8014 for (TrackSelection::iterator i = selection->tracks.begin(); i != selection->tracks.end(); ++i) {
8015 RouteTimeAxisView *rtav = dynamic_cast<RouteTimeAxisView *>(*i);
8021 boost::shared_ptr<MidiTrack> mt = rtav->midi_track();
8024 rl->push_back (rtav->route());
8025 onoff = !mt->input_active();
8029 _session->set_exclusive_input_active (rl, onoff, flip_others);
8032 static bool ok_fine (GdkEventAny*) { return true; }
8038 lock_dialog = new Gtk::Dialog (string_compose (_("%1: Locked"), PROGRAM_NAME), true);
8040 Gtk::Image* padlock = manage (new Gtk::Image (ARDOUR_UI_UTILS::get_icon ("padlock_closed")));
8041 lock_dialog->get_vbox()->pack_start (*padlock);
8042 lock_dialog->signal_delete_event ().connect (sigc::ptr_fun (ok_fine));
8044 ArdourButton* b = manage (new ArdourButton);
8045 b->set_name ("lock button");
8046 b->set_text (_("Click to unlock"));
8047 b->signal_clicked.connect (sigc::mem_fun (*this, &Editor::unlock));
8048 lock_dialog->get_vbox()->pack_start (*b);
8050 lock_dialog->get_vbox()->show_all ();
8051 lock_dialog->set_size_request (200, 200);
8054 delete _main_menu_disabler;
8055 _main_menu_disabler = new MainMenuDisabler;
8057 lock_dialog->present ();
8059 lock_dialog->get_window()->set_decorations (Gdk::WMDecoration (0));
8065 lock_dialog->hide ();
8067 delete _main_menu_disabler;
8068 _main_menu_disabler = 0;
8070 if (UIConfiguration::instance().get_lock_gui_after_seconds()) {
8071 start_lock_event_timing ();
8076 Editor::bring_in_callback (Gtk::Label* label, uint32_t n, uint32_t total, string name)
8078 Gtkmm2ext::UI::instance()->call_slot (invalidator (*this), boost::bind (&Editor::update_bring_in_message, this, label, n, total, name));
8082 Editor::update_bring_in_message (Gtk::Label* label, uint32_t n, uint32_t total, string name)
8084 Timers::TimerSuspender t;
8085 label->set_text (string_compose ("Copying %1, %2 of %3", name, n, total));
8086 Gtkmm2ext::UI::instance()->flush_pending (1);
8090 Editor::bring_all_sources_into_session ()
8097 ArdourDialog w (_("Moving embedded files into session folder"));
8098 w.get_vbox()->pack_start (msg);
8101 /* flush all pending GUI events because we're about to start copying
8105 Timers::TimerSuspender t;
8106 Gtkmm2ext::UI::instance()->flush_pending (3);
8110 _session->bring_all_sources_into_session (boost::bind (&Editor::bring_in_callback, this, &msg, _1, _2, _3));