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);
2675 Editor::play_location (Location& location)
2677 if (location.start() <= location.end()) {
2681 _session->request_bounded_roll (location.start(), location.end());
2685 Editor::loop_location (Location& location)
2687 if (location.start() <= location.end()) {
2693 if ((tll = transport_loop_location()) != 0) {
2694 tll->set (location.start(), location.end());
2696 // enable looping, reposition and start rolling
2697 _session->request_locate (tll->start(), true);
2698 _session->request_play_loop (true);
2703 Editor::do_layer_operation (LayerOperation op)
2705 if (selection->regions.empty ()) {
2709 bool const multiple = selection->regions.size() > 1;
2713 begin_reversible_command (_("raise regions"));
2715 begin_reversible_command (_("raise region"));
2721 begin_reversible_command (_("raise regions to top"));
2723 begin_reversible_command (_("raise region to top"));
2729 begin_reversible_command (_("lower regions"));
2731 begin_reversible_command (_("lower region"));
2737 begin_reversible_command (_("lower regions to bottom"));
2739 begin_reversible_command (_("lower region"));
2744 set<boost::shared_ptr<Playlist> > playlists = selection->regions.playlists ();
2745 for (set<boost::shared_ptr<Playlist> >::iterator i = playlists.begin(); i != playlists.end(); ++i) {
2746 (*i)->clear_owned_changes ();
2749 for (RegionSelection::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
2750 boost::shared_ptr<Region> r = (*i)->region ();
2762 r->lower_to_bottom ();
2766 for (set<boost::shared_ptr<Playlist> >::iterator i = playlists.begin(); i != playlists.end(); ++i) {
2767 vector<Command*> cmds;
2769 _session->add_commands (cmds);
2772 commit_reversible_command ();
2776 Editor::raise_region ()
2778 do_layer_operation (Raise);
2782 Editor::raise_region_to_top ()
2784 do_layer_operation (RaiseToTop);
2788 Editor::lower_region ()
2790 do_layer_operation (Lower);
2794 Editor::lower_region_to_bottom ()
2796 do_layer_operation (LowerToBottom);
2799 /** Show the region editor for the selected regions */
2801 Editor::show_region_properties ()
2803 selection->foreach_regionview (&RegionView::show_region_editor);
2806 /** Show the midi list editor for the selected MIDI regions */
2808 Editor::show_midi_list_editor ()
2810 selection->foreach_midi_regionview (&MidiRegionView::show_list_editor);
2814 Editor::rename_region ()
2816 RegionSelection rs = get_regions_from_selection_and_entered ();
2822 ArdourDialog d (_("Rename Region"), true, false);
2824 Label label (_("New name:"));
2827 hbox.set_spacing (6);
2828 hbox.pack_start (label, false, false);
2829 hbox.pack_start (entry, true, true);
2831 d.get_vbox()->set_border_width (12);
2832 d.get_vbox()->pack_start (hbox, false, false);
2834 d.add_button(Gtk::Stock::CANCEL, Gtk::RESPONSE_CANCEL);
2835 d.add_button(Gtk::Stock::OK, Gtk::RESPONSE_OK);
2837 d.set_size_request (300, -1);
2839 entry.set_text (rs.front()->region()->name());
2840 entry.select_region (0, -1);
2842 entry.signal_activate().connect (sigc::bind (sigc::mem_fun (d, &Dialog::response), RESPONSE_OK));
2848 int const ret = d.run();
2852 if (ret != RESPONSE_OK) {
2856 std::string str = entry.get_text();
2857 strip_whitespace_edges (str);
2859 rs.front()->region()->set_name (str);
2860 _regions->redisplay ();
2864 /** Start an audition of the first selected region */
2866 Editor::play_edit_range ()
2868 framepos_t start, end;
2870 if (get_edit_op_range (start, end)) {
2871 _session->request_bounded_roll (start, end);
2876 Editor::play_selected_region ()
2878 framepos_t start = max_framepos;
2881 RegionSelection rs = get_regions_from_selection_and_entered ();
2887 for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
2888 if ((*i)->region()->position() < start) {
2889 start = (*i)->region()->position();
2891 if ((*i)->region()->last_frame() + 1 > end) {
2892 end = (*i)->region()->last_frame() + 1;
2896 _session->request_bounded_roll (start, end);
2900 Editor::audition_playlist_region_standalone (boost::shared_ptr<Region> region)
2902 _session->audition_region (region);
2906 Editor::region_from_selection ()
2908 if (clicked_axisview == 0) {
2912 if (selection->time.empty()) {
2916 framepos_t start = selection->time[clicked_selection].start;
2917 framepos_t end = selection->time[clicked_selection].end;
2919 TrackViewList tracks = get_tracks_for_range_action ();
2921 framepos_t selection_cnt = end - start + 1;
2923 for (TrackSelection::iterator i = tracks.begin(); i != tracks.end(); ++i) {
2924 boost::shared_ptr<Region> current;
2925 boost::shared_ptr<Playlist> pl;
2926 framepos_t internal_start;
2929 if ((pl = (*i)->playlist()) == 0) {
2933 if ((current = pl->top_region_at (start)) == 0) {
2937 internal_start = start - current->position();
2938 RegionFactory::region_name (new_name, current->name(), true);
2942 plist.add (ARDOUR::Properties::start, current->start() + internal_start);
2943 plist.add (ARDOUR::Properties::length, selection_cnt);
2944 plist.add (ARDOUR::Properties::name, new_name);
2945 plist.add (ARDOUR::Properties::layer, 0);
2947 boost::shared_ptr<Region> region (RegionFactory::create (current, plist));
2952 Editor::create_region_from_selection (vector<boost::shared_ptr<Region> >& new_regions)
2954 if (selection->time.empty() || selection->tracks.empty()) {
2958 framepos_t start, end;
2959 if (clicked_selection) {
2960 start = selection->time[clicked_selection].start;
2961 end = selection->time[clicked_selection].end;
2963 start = selection->time.start();
2964 end = selection->time.end_frame();
2967 TrackViewList ts = selection->tracks.filter_to_unique_playlists ();
2968 sort_track_selection (ts);
2970 for (TrackSelection::iterator i = ts.begin(); i != ts.end(); ++i) {
2971 boost::shared_ptr<Region> current;
2972 boost::shared_ptr<Playlist> playlist;
2973 framepos_t internal_start;
2976 if ((playlist = (*i)->playlist()) == 0) {
2980 if ((current = playlist->top_region_at(start)) == 0) {
2984 internal_start = start - current->position();
2985 RegionFactory::region_name (new_name, current->name(), true);
2989 plist.add (ARDOUR::Properties::start, current->start() + internal_start);
2990 plist.add (ARDOUR::Properties::length, end - start + 1);
2991 plist.add (ARDOUR::Properties::name, new_name);
2993 new_regions.push_back (RegionFactory::create (current, plist));
2998 Editor::split_multichannel_region ()
3000 RegionSelection rs = get_regions_from_selection_and_entered ();
3006 vector< boost::shared_ptr<Region> > v;
3008 for (list<RegionView*>::iterator x = rs.begin(); x != rs.end(); ++x) {
3009 (*x)->region()->separate_by_channel (*_session, v);
3014 Editor::new_region_from_selection ()
3016 region_from_selection ();
3017 cancel_selection ();
3021 add_if_covered (RegionView* rv, const AudioRange* ar, RegionSelection* rs)
3023 switch (rv->region()->coverage (ar->start, ar->end - 1)) {
3024 // n.b. -1 because AudioRange::end is one past the end, but coverage expects inclusive ranges
3025 case Evoral::OverlapNone:
3033 * - selected tracks, or if there are none...
3034 * - tracks containing selected regions, or if there are none...
3039 Editor::get_tracks_for_range_action () const
3043 if (selection->tracks.empty()) {
3045 /* use tracks with selected regions */
3047 RegionSelection rs = selection->regions;
3049 for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
3050 TimeAxisView* tv = &(*i)->get_time_axis_view();
3052 if (!t.contains (tv)) {
3058 /* no regions and no tracks: use all tracks */
3064 t = selection->tracks;
3067 return t.filter_to_unique_playlists();
3071 Editor::separate_regions_between (const TimeSelection& ts)
3073 bool in_command = false;
3074 boost::shared_ptr<Playlist> playlist;
3075 RegionSelection new_selection;
3077 TrackViewList tmptracks = get_tracks_for_range_action ();
3078 sort_track_selection (tmptracks);
3080 for (TrackSelection::iterator i = tmptracks.begin(); i != tmptracks.end(); ++i) {
3082 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> ((*i));
3088 if (!rtv->is_track()) {
3092 /* no edits to destructive tracks */
3094 if (rtv->track()->destructive()) {
3098 if ((playlist = rtv->playlist()) != 0) {
3100 playlist->clear_changes ();
3102 /* XXX need to consider musical time selections here at some point */
3104 double speed = rtv->track()->speed();
3106 for (list<AudioRange>::const_iterator t = ts.begin(); t != ts.end(); ++t) {
3108 sigc::connection c = rtv->view()->RegionViewAdded.connect (
3109 sigc::mem_fun(*this, &Editor::collect_new_region_view));
3111 latest_regionviews.clear ();
3113 playlist->partition ((framepos_t)((*t).start * speed),
3114 (framepos_t)((*t).end * speed), false);
3118 if (!latest_regionviews.empty()) {
3120 rtv->view()->foreach_regionview (sigc::bind (
3121 sigc::ptr_fun (add_if_covered),
3122 &(*t), &new_selection));
3125 begin_reversible_command (_("separate"));
3129 /* pick up changes to existing regions */
3131 vector<Command*> cmds;
3132 playlist->rdiff (cmds);
3133 _session->add_commands (cmds);
3135 /* pick up changes to the playlist itself (adds/removes)
3138 _session->add_command(new StatefulDiffCommand (playlist));
3145 // selection->set (new_selection);
3147 commit_reversible_command ();
3151 struct PlaylistState {
3152 boost::shared_ptr<Playlist> playlist;
3156 /** Take tracks from get_tracks_for_range_action and cut any regions
3157 * on those tracks so that the tracks are empty over the time
3161 Editor::separate_region_from_selection ()
3163 /* preferentially use *all* ranges in the time selection if we're in range mode
3164 to allow discontiguous operation, since get_edit_op_range() currently
3165 returns a single range.
3168 if (!selection->time.empty()) {
3170 separate_regions_between (selection->time);
3177 if (get_edit_op_range (start, end)) {
3179 AudioRange ar (start, end, 1);
3183 separate_regions_between (ts);
3189 Editor::separate_region_from_punch ()
3191 Location* loc = _session->locations()->auto_punch_location();
3193 separate_regions_using_location (*loc);
3198 Editor::separate_region_from_loop ()
3200 Location* loc = _session->locations()->auto_loop_location();
3202 separate_regions_using_location (*loc);
3207 Editor::separate_regions_using_location (Location& loc)
3209 if (loc.is_mark()) {
3213 AudioRange ar (loc.start(), loc.end(), 1);
3218 separate_regions_between (ts);
3221 /** Separate regions under the selected region */
3223 Editor::separate_under_selected_regions ()
3225 vector<PlaylistState> playlists;
3229 rs = get_regions_from_selection_and_entered();
3231 if (!_session || rs.empty()) {
3235 begin_reversible_command (_("separate region under"));
3237 list<boost::shared_ptr<Region> > regions_to_remove;
3239 for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
3240 // we can't just remove the region(s) in this loop because
3241 // this removes them from the RegionSelection, and they thus
3242 // disappear from underneath the iterator, and the ++i above
3243 // SEGVs in a puzzling fashion.
3245 // so, first iterate over the regions to be removed from rs and
3246 // add them to the regions_to_remove list, and then
3247 // iterate over the list to actually remove them.
3249 regions_to_remove.push_back ((*i)->region());
3252 for (list<boost::shared_ptr<Region> >::iterator rl = regions_to_remove.begin(); rl != regions_to_remove.end(); ++rl) {
3254 boost::shared_ptr<Playlist> playlist = (*rl)->playlist();
3257 // is this check necessary?
3261 vector<PlaylistState>::iterator i;
3263 //only take state if this is a new playlist.
3264 for (i = playlists.begin(); i != playlists.end(); ++i) {
3265 if ((*i).playlist == playlist) {
3270 if (i == playlists.end()) {
3272 PlaylistState before;
3273 before.playlist = playlist;
3274 before.before = &playlist->get_state();
3276 playlist->freeze ();
3277 playlists.push_back(before);
3280 //Partition on the region bounds
3281 playlist->partition ((*rl)->first_frame() - 1, (*rl)->last_frame() + 1, true);
3283 //Re-add region that was just removed due to the partition operation
3284 playlist->add_region( (*rl), (*rl)->first_frame() );
3287 vector<PlaylistState>::iterator pl;
3289 for (pl = playlists.begin(); pl != playlists.end(); ++pl) {
3290 (*pl).playlist->thaw ();
3291 _session->add_command(new MementoCommand<Playlist>(*(*pl).playlist, (*pl).before, &(*pl).playlist->get_state()));
3294 commit_reversible_command ();
3298 Editor::crop_region_to_selection ()
3300 if (!selection->time.empty()) {
3302 crop_region_to (selection->time.start(), selection->time.end_frame());
3309 if (get_edit_op_range (start, end)) {
3310 crop_region_to (start, end);
3317 Editor::crop_region_to (framepos_t start, framepos_t end)
3319 vector<boost::shared_ptr<Playlist> > playlists;
3320 boost::shared_ptr<Playlist> playlist;
3323 if (selection->tracks.empty()) {
3324 ts = track_views.filter_to_unique_playlists();
3326 ts = selection->tracks.filter_to_unique_playlists ();
3329 sort_track_selection (ts);
3331 for (TrackSelection::iterator i = ts.begin(); i != ts.end(); ++i) {
3333 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> ((*i));
3339 boost::shared_ptr<Track> t = rtv->track();
3341 if (t != 0 && ! t->destructive()) {
3343 if ((playlist = rtv->playlist()) != 0) {
3344 playlists.push_back (playlist);
3349 if (playlists.empty()) {
3354 framepos_t new_start;
3356 framecnt_t new_length;
3357 bool in_command = false;
3359 for (vector<boost::shared_ptr<Playlist> >::iterator i = playlists.begin(); i != playlists.end(); ++i) {
3361 /* Only the top regions at start and end have to be cropped */
3362 boost::shared_ptr<Region> region_at_start = (*i)->top_region_at(start);
3363 boost::shared_ptr<Region> region_at_end = (*i)->top_region_at(end);
3365 vector<boost::shared_ptr<Region> > regions;
3367 if (region_at_start != 0) {
3368 regions.push_back (region_at_start);
3370 if (region_at_end != 0) {
3371 regions.push_back (region_at_end);
3374 /* now adjust lengths */
3375 for (vector<boost::shared_ptr<Region> >::iterator i = regions.begin(); i != regions.end(); ++i) {
3377 pos = (*i)->position();
3378 new_start = max (start, pos);
3379 if (max_framepos - pos > (*i)->length()) {
3380 new_end = pos + (*i)->length() - 1;
3382 new_end = max_framepos;
3384 new_end = min (end, new_end);
3385 new_length = new_end - new_start + 1;
3388 begin_reversible_command (_("trim to selection"));
3391 (*i)->clear_changes ();
3392 (*i)->trim_to (new_start, new_length);
3393 _session->add_command (new StatefulDiffCommand (*i));
3398 commit_reversible_command ();
3403 Editor::region_fill_track ()
3405 boost::shared_ptr<Playlist> playlist;
3406 RegionSelection regions = get_regions_from_selection_and_entered ();
3407 RegionSelection foo;
3409 framepos_t const end = _session->current_end_frame ();
3411 if (regions.empty () || regions.end_frame () + 1 >= end) {
3415 framepos_t const start_frame = regions.start ();
3416 framepos_t const end_frame = regions.end_frame ();
3417 framecnt_t const gap = end_frame - start_frame + 1;
3419 begin_reversible_command (Operations::region_fill);
3421 selection->clear_regions ();
3423 for (RegionSelection::iterator i = regions.begin(); i != regions.end(); ++i) {
3425 boost::shared_ptr<Region> r ((*i)->region());
3427 TimeAxisView& tv = (*i)->get_time_axis_view();
3428 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (&tv);
3429 latest_regionviews.clear ();
3430 sigc::connection c = rtv->view()->RegionViewAdded.connect (sigc::mem_fun(*this, &Editor::collect_new_region_view));
3432 framepos_t const position = end_frame + (r->first_frame() - start_frame + 1);
3433 playlist = (*i)->region()->playlist();
3434 playlist->clear_changes ();
3435 playlist->duplicate_until (r, position, gap, end);
3436 _session->add_command(new StatefulDiffCommand (playlist));
3440 foo.insert (foo.end(), latest_regionviews.begin(), latest_regionviews.end());
3444 selection->set (foo);
3447 commit_reversible_command ();
3451 Editor::set_region_sync_position ()
3453 set_sync_point (get_preferred_edit_position (), get_regions_from_selection_and_edit_point ());
3457 Editor::set_sync_point (framepos_t where, const RegionSelection& rs)
3459 bool in_command = false;
3461 for (RegionSelection::const_iterator r = rs.begin(); r != rs.end(); ++r) {
3463 if (!(*r)->region()->covers (where)) {
3467 boost::shared_ptr<Region> region ((*r)->region());
3470 begin_reversible_command (_("set sync point"));
3474 region->clear_changes ();
3475 region->set_sync_position (where);
3476 _session->add_command(new StatefulDiffCommand (region));
3480 commit_reversible_command ();
3484 /** Remove the sync positions of the selection */
3486 Editor::remove_region_sync ()
3488 RegionSelection rs = get_regions_from_selection_and_entered ();
3494 begin_reversible_command (_("remove region sync"));
3496 for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
3498 (*i)->region()->clear_changes ();
3499 (*i)->region()->clear_sync_position ();
3500 _session->add_command(new StatefulDiffCommand ((*i)->region()));
3503 commit_reversible_command ();
3507 Editor::naturalize_region ()
3509 RegionSelection rs = get_regions_from_selection_and_entered ();
3515 if (rs.size() > 1) {
3516 begin_reversible_command (_("move regions to original position"));
3518 begin_reversible_command (_("move region to original position"));
3521 for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
3522 (*i)->region()->clear_changes ();
3523 (*i)->region()->move_to_natural_position ();
3524 _session->add_command (new StatefulDiffCommand ((*i)->region()));
3527 commit_reversible_command ();
3531 Editor::align_regions (RegionPoint what)
3533 RegionSelection const rs = get_regions_from_selection_and_edit_point ();
3539 begin_reversible_command (_("align selection"));
3541 framepos_t const position = get_preferred_edit_position ();
3543 for (RegionSelection::const_iterator i = rs.begin(); i != rs.end(); ++i) {
3544 align_region_internal ((*i)->region(), what, position);
3547 commit_reversible_command ();
3550 struct RegionSortByTime {
3551 bool operator() (const RegionView* a, const RegionView* b) {
3552 return a->region()->position() < b->region()->position();
3557 Editor::align_regions_relative (RegionPoint point)
3559 RegionSelection const rs = get_regions_from_selection_and_edit_point ();
3565 framepos_t const position = get_preferred_edit_position ();
3567 framepos_t distance = 0;
3571 list<RegionView*> sorted;
3572 rs.by_position (sorted);
3574 boost::shared_ptr<Region> r ((*sorted.begin())->region());
3579 if (position > r->position()) {
3580 distance = position - r->position();
3582 distance = r->position() - position;
3588 if (position > r->last_frame()) {
3589 distance = position - r->last_frame();
3590 pos = r->position() + distance;
3592 distance = r->last_frame() - position;
3593 pos = r->position() - distance;
3599 pos = r->adjust_to_sync (position);
3600 if (pos > r->position()) {
3601 distance = pos - r->position();
3603 distance = r->position() - pos;
3609 if (pos == r->position()) {
3613 begin_reversible_command (_("align selection (relative)"));
3615 /* move first one specially */
3617 r->clear_changes ();
3618 r->set_position (pos);
3619 _session->add_command(new StatefulDiffCommand (r));
3621 /* move rest by the same amount */
3625 for (list<RegionView*>::iterator i = sorted.begin(); i != sorted.end(); ++i) {
3627 boost::shared_ptr<Region> region ((*i)->region());
3629 region->clear_changes ();
3632 region->set_position (region->position() + distance);
3634 region->set_position (region->position() - distance);
3637 _session->add_command(new StatefulDiffCommand (region));
3641 commit_reversible_command ();
3645 Editor::align_region (boost::shared_ptr<Region> region, RegionPoint point, framepos_t position)
3647 begin_reversible_command (_("align region"));
3648 align_region_internal (region, point, position);
3649 commit_reversible_command ();
3653 Editor::align_region_internal (boost::shared_ptr<Region> region, RegionPoint point, framepos_t position)
3655 region->clear_changes ();
3659 region->set_position (region->adjust_to_sync (position));
3663 if (position > region->length()) {
3664 region->set_position (position - region->length());
3669 region->set_position (position);
3673 _session->add_command(new StatefulDiffCommand (region));
3677 Editor::trim_region_front ()
3683 Editor::trim_region_back ()
3685 trim_region (false);
3689 Editor::trim_region (bool front)
3691 framepos_t where = get_preferred_edit_position();
3692 RegionSelection rs = get_regions_from_selection_and_edit_point ();
3698 begin_reversible_command (front ? _("trim front") : _("trim back"));
3700 for (list<RegionView*>::const_iterator i = rs.by_layer().begin(); i != rs.by_layer().end(); ++i) {
3701 if (!(*i)->region()->locked()) {
3703 (*i)->region()->clear_changes ();
3706 (*i)->region()->trim_front (where);
3708 (*i)->region()->trim_end (where);
3711 _session->add_command (new StatefulDiffCommand ((*i)->region()));
3715 commit_reversible_command ();
3718 /** Trim the end of the selected regions to the position of the edit cursor */
3720 Editor::trim_region_to_loop ()
3722 Location* loc = _session->locations()->auto_loop_location();
3726 trim_region_to_location (*loc, _("trim to loop"));
3730 Editor::trim_region_to_punch ()
3732 Location* loc = _session->locations()->auto_punch_location();
3736 trim_region_to_location (*loc, _("trim to punch"));
3740 Editor::trim_region_to_location (const Location& loc, const char* str)
3742 RegionSelection rs = get_regions_from_selection_and_entered ();
3743 bool in_command = false;
3745 for (RegionSelection::iterator x = rs.begin(); x != rs.end(); ++x) {
3746 RegionView* rv = (*x);
3748 /* require region to span proposed trim */
3749 switch (rv->region()->coverage (loc.start(), loc.end())) {
3750 case Evoral::OverlapInternal:
3756 RouteTimeAxisView* tav = dynamic_cast<RouteTimeAxisView*> (&rv->get_time_axis_view());
3765 if (tav->track() != 0) {
3766 speed = tav->track()->speed();
3769 start = session_frame_to_track_frame (loc.start(), speed);
3770 end = session_frame_to_track_frame (loc.end(), speed);
3772 rv->region()->clear_changes ();
3773 rv->region()->trim_to (start, (end - start));
3776 begin_reversible_command (str);
3779 _session->add_command(new StatefulDiffCommand (rv->region()));
3783 commit_reversible_command ();
3788 Editor::trim_region_to_previous_region_end ()
3790 return trim_to_region(false);
3794 Editor::trim_region_to_next_region_start ()
3796 return trim_to_region(true);
3800 Editor::trim_to_region(bool forward)
3802 RegionSelection rs = get_regions_from_selection_and_entered ();
3803 bool in_command = false;
3805 boost::shared_ptr<Region> next_region;
3807 for (RegionSelection::iterator x = rs.begin(); x != rs.end(); ++x) {
3809 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (*x);
3815 AudioTimeAxisView* atav = dynamic_cast<AudioTimeAxisView*> (&arv->get_time_axis_view());
3823 if (atav->track() != 0) {
3824 speed = atav->track()->speed();
3828 boost::shared_ptr<Region> region = arv->region();
3829 boost::shared_ptr<Playlist> playlist (region->playlist());
3831 region->clear_changes ();
3835 next_region = playlist->find_next_region (region->first_frame(), Start, 1);
3841 region->trim_end((framepos_t) ( (next_region->first_frame() - 1) * speed));
3842 arv->region_changed (PropertyChange (ARDOUR::Properties::length));
3846 next_region = playlist->find_next_region (region->first_frame(), Start, 0);
3852 region->trim_front((framepos_t) ((next_region->last_frame() + 1) * speed));
3854 arv->region_changed (ARDOUR::bounds_change);
3858 begin_reversible_command (_("trim to region"));
3861 _session->add_command(new StatefulDiffCommand (region));
3865 commit_reversible_command ();
3870 Editor::unfreeze_route ()
3872 if (clicked_routeview == 0 || !clicked_routeview->is_track()) {
3876 clicked_routeview->track()->unfreeze ();
3880 Editor::_freeze_thread (void* arg)
3882 return static_cast<Editor*>(arg)->freeze_thread ();
3886 Editor::freeze_thread ()
3888 /* create event pool because we may need to talk to the session */
3889 SessionEvent::create_per_thread_pool ("freeze events", 64);
3890 /* create per-thread buffers for process() tree to use */
3891 clicked_routeview->audio_track()->freeze_me (*current_interthread_info);
3892 current_interthread_info->done = true;
3897 Editor::freeze_route ()
3903 /* stop transport before we start. this is important */
3905 _session->request_transport_speed (0.0);
3907 /* wait for just a little while, because the above call is asynchronous */
3909 Glib::usleep (250000);
3911 if (clicked_routeview == 0 || !clicked_routeview->is_audio_track()) {
3915 if (!clicked_routeview->track()->bounceable (clicked_routeview->track()->main_outs(), true)) {
3917 _("This track/bus cannot be frozen because the signal adds or loses channels before reaching the outputs.\n"
3918 "This is typically caused by plugins that generate stereo output from mono input or vice versa.")
3920 d.set_title (_("Cannot freeze"));
3925 if (clicked_routeview->track()->has_external_redirects()) {
3926 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"
3927 "Freezing will only process the signal as far as the first send/insert/return."),
3928 clicked_routeview->track()->name()), true, MESSAGE_INFO, BUTTONS_NONE, true);
3930 d.add_button (_("Freeze anyway"), Gtk::RESPONSE_OK);
3931 d.add_button (_("Don't freeze"), Gtk::RESPONSE_CANCEL);
3932 d.set_title (_("Freeze Limits"));
3934 int response = d.run ();
3937 case Gtk::RESPONSE_CANCEL:
3944 InterThreadInfo itt;
3945 current_interthread_info = &itt;
3947 InterthreadProgressWindow ipw (current_interthread_info, _("Freeze"), _("Cancel Freeze"));
3949 pthread_create_and_store (X_("freezer"), &itt.thread, _freeze_thread, this);
3951 CursorContext::Handle cursor_ctx = CursorContext::create(*this, _cursors->wait);
3953 while (!itt.done && !itt.cancel) {
3954 gtk_main_iteration ();
3957 pthread_join (itt.thread, 0);
3958 current_interthread_info = 0;
3962 Editor::bounce_range_selection (bool replace, bool enable_processing)
3964 if (selection->time.empty()) {
3968 TrackSelection views = selection->tracks;
3970 for (TrackViewList::iterator i = views.begin(); i != views.end(); ++i) {
3972 if (enable_processing) {
3974 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (*i);
3976 if (rtv && rtv->track() && replace && enable_processing && !rtv->track()->bounceable (rtv->track()->main_outs(), false)) {
3978 _("You can't perform this operation because the processing of the signal "
3979 "will cause one or more of the tracks to end up with a region with more channels than this track has inputs.\n\n"
3980 "You can do this without processing, which is a different operation.")
3982 d.set_title (_("Cannot bounce"));
3989 framepos_t start = selection->time[clicked_selection].start;
3990 framepos_t end = selection->time[clicked_selection].end;
3991 framepos_t cnt = end - start + 1;
3992 bool in_command = false;
3994 for (TrackViewList::iterator i = views.begin(); i != views.end(); ++i) {
3996 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (*i);
4002 boost::shared_ptr<Playlist> playlist;
4004 if ((playlist = rtv->playlist()) == 0) {
4008 InterThreadInfo itt;
4010 playlist->clear_changes ();
4011 playlist->clear_owned_changes ();
4013 boost::shared_ptr<Region> r;
4015 if (enable_processing) {
4016 r = rtv->track()->bounce_range (start, start+cnt, itt, rtv->track()->main_outs(), false);
4018 r = rtv->track()->bounce_range (start, start+cnt, itt, boost::shared_ptr<Processor>(), false);
4026 list<AudioRange> ranges;
4027 ranges.push_back (AudioRange (start, start+cnt, 0));
4028 playlist->cut (ranges); // discard result
4029 playlist->add_region (r, start);
4033 begin_reversible_command (_("bounce range"));
4036 vector<Command*> cmds;
4037 playlist->rdiff (cmds);
4038 _session->add_commands (cmds);
4040 _session->add_command (new StatefulDiffCommand (playlist));
4044 commit_reversible_command ();
4048 /** Delete selected regions, automation points or a time range */
4052 //special case: if the user is pointing in the editor/mixer strip, they may be trying to delete a plugin.
4053 //we need this because the editor-mixer strip is in the editor window, so it doesn't get the bindings from the mix window
4054 bool deleted = false;
4055 if ( current_mixer_strip && current_mixer_strip == MixerStrip::entered_mixer_strip() )
4056 deleted = current_mixer_strip->delete_processors ();
4062 /** Cut selected regions, automation points or a time range */
4069 /** Copy selected regions, automation points or a time range */
4077 /** @return true if a Cut, Copy or Clear is possible */
4079 Editor::can_cut_copy () const
4081 if (!selection->time.empty() || !selection->regions.empty() || !selection->points.empty())
4088 /** Cut, copy or clear selected regions, automation points or a time range.
4089 * @param op Operation (Delete, Cut, Copy or Clear)
4092 Editor::cut_copy (CutCopyOp op)
4094 /* only cancel selection if cut/copy is successful.*/
4100 opname = _("delete");
4109 opname = _("clear");
4113 /* if we're deleting something, and the mouse is still pressed,
4114 the thing we started a drag for will be gone when we release
4115 the mouse button(s). avoid this. see part 2 at the end of
4119 if (op == Delete || op == Cut || op == Clear) {
4120 if (_drags->active ()) {
4125 if ( op != Delete ) //"Delete" doesn't change copy/paste buf
4126 cut_buffer->clear ();
4128 if (entered_marker) {
4130 /* cut/delete op while pointing at a marker */
4133 Location* loc = find_location_from_marker (entered_marker, ignored);
4135 if (_session && loc) {
4136 entered_marker = NULL;
4137 Glib::signal_idle().connect (sigc::bind (sigc::mem_fun(*this, &Editor::really_remove_marker), loc));
4144 switch (mouse_mode) {
4147 begin_reversible_command (opname + ' ' + X_("MIDI"));
4149 commit_reversible_command ();
4155 bool did_edit = false;
4157 if (!selection->regions.empty() || !selection->points.empty()) {
4158 begin_reversible_command (opname + ' ' + _("objects"));
4161 if (!selection->regions.empty()) {
4162 cut_copy_regions (op, selection->regions);
4164 if (op == Cut || op == Delete) {
4165 selection->clear_regions ();
4169 if (!selection->points.empty()) {
4170 cut_copy_points (op);
4172 if (op == Cut || op == Delete) {
4173 selection->clear_points ();
4176 } else if (selection->time.empty()) {
4177 framepos_t start, end;
4178 /* no time selection, see if we can get an edit range
4181 if (get_edit_op_range (start, end)) {
4182 selection->set (start, end);
4184 } else if (!selection->time.empty()) {
4185 begin_reversible_command (opname + ' ' + _("range"));
4188 cut_copy_ranges (op);
4190 if (op == Cut || op == Delete) {
4191 selection->clear_time ();
4196 /* reset repeated paste state */
4199 commit_reversible_command ();
4202 if (op == Delete || op == Cut || op == Clear) {
4208 struct AutomationRecord {
4209 AutomationRecord () : state (0) , line(NULL) {}
4210 AutomationRecord (XMLNode* s, const AutomationLine* l) : state (s) , line (l) {}
4212 XMLNode* state; ///< state before any operation
4213 const AutomationLine* line; ///< line this came from
4214 boost::shared_ptr<Evoral::ControlList> copy; ///< copied events for the cut buffer
4216 struct PointsSelectionPositionSorter {
4217 bool operator() (ControlPoint* a, ControlPoint* b) {
4218 return (*(a->model()))->when < (*(b->model()))->when;
4221 /** Cut, copy or clear selected automation points.
4222 * @param op Operation (Cut, Copy or Clear)
4225 Editor::cut_copy_points (Editing::CutCopyOp op, Evoral::Beats earliest, bool midi)
4227 if (selection->points.empty ()) {
4231 /* XXX: not ideal, as there may be more than one track involved in the point selection */
4232 _last_cut_copy_source_track = &selection->points.front()->line().trackview;
4234 /* Keep a record of the AutomationLists that we end up using in this operation */
4235 typedef std::map<boost::shared_ptr<AutomationList>, AutomationRecord> Lists;
4238 /* user could select points in any order */
4239 selection->points.sort(PointsSelectionPositionSorter ());
4241 /* Go through all selected points, making an AutomationRecord for each distinct AutomationList */
4242 for (PointSelection::iterator sel_point = selection->points.begin(); sel_point != selection->points.end(); ++sel_point) {
4243 const AutomationLine& line = (*sel_point)->line();
4244 const boost::shared_ptr<AutomationList> al = line.the_list();
4245 if (lists.find (al) == lists.end ()) {
4246 /* We haven't seen this list yet, so make a record for it. This includes
4247 taking a copy of its current state, in case this is needed for undo later.
4249 lists[al] = AutomationRecord (&al->get_state (), &line);
4253 if (op == Cut || op == Copy) {
4254 /* This operation will involve putting things in the cut buffer, so create an empty
4255 ControlList for each of our source lists to put the cut buffer data in.
4257 for (Lists::iterator i = lists.begin(); i != lists.end(); ++i) {
4258 i->second.copy = i->first->create (i->first->parameter (), i->first->descriptor());
4261 /* Add all selected points to the relevant copy ControlLists */
4262 framepos_t start = std::numeric_limits<framepos_t>::max();
4263 for (PointSelection::iterator sel_point = selection->points.begin(); sel_point != selection->points.end(); ++sel_point) {
4264 boost::shared_ptr<AutomationList> al = (*sel_point)->line().the_list();
4265 AutomationList::const_iterator ctrl_evt = (*sel_point)->model ();
4267 lists[al].copy->fast_simple_add ((*ctrl_evt)->when, (*ctrl_evt)->value);
4269 /* Update earliest MIDI start time in beats */
4270 earliest = std::min(earliest, Evoral::Beats((*ctrl_evt)->when));
4272 /* Update earliest session start time in frames */
4273 start = std::min(start, (*sel_point)->line().session_position(ctrl_evt));
4277 /* Snap start time backwards, so copy/paste is snap aligned. */
4279 if (earliest == Evoral::Beats::max()) {
4280 earliest = Evoral::Beats(); // Weird... don't offset
4282 earliest.round_down_to_beat();
4284 if (start == std::numeric_limits<double>::max()) {
4285 start = 0; // Weird... don't offset
4287 snap_to(start, RoundDownMaybe);
4290 const double line_offset = midi ? earliest.to_double() : start;
4291 for (Lists::iterator i = lists.begin(); i != lists.end(); ++i) {
4292 /* Correct this copy list so that it is relative to the earliest
4293 start time, so relative ordering between points is preserved
4294 when copying from several lists and the paste starts at the
4295 earliest copied piece of data. */
4296 boost::shared_ptr<Evoral::ControlList> &al_cpy = i->second.copy;
4297 for (AutomationList::iterator ctrl_evt = al_cpy->begin(); ctrl_evt != al_cpy->end(); ++ctrl_evt) {
4298 (*ctrl_evt)->when -= line_offset;
4301 /* And add it to the cut buffer */
4302 cut_buffer->add (al_cpy);
4306 if (op == Delete || op == Cut) {
4307 /* This operation needs to remove things from the main AutomationList, so do that now */
4309 for (Lists::iterator i = lists.begin(); i != lists.end(); ++i) {
4310 i->first->freeze ();
4313 /* Remove each selected point from its AutomationList */
4314 for (PointSelection::iterator sel_point = selection->points.begin(); sel_point != selection->points.end(); ++sel_point) {
4315 AutomationLine& line = (*sel_point)->line ();
4316 boost::shared_ptr<AutomationList> al = line.the_list();
4320 if (dynamic_cast<AudioRegionGainLine*> (&line)) {
4321 /* removing of first and last gain point in region gain lines is prohibited*/
4322 if (line.is_last_point (*(*sel_point)) || line.is_first_point (*(*sel_point))) {
4328 al->erase ((*sel_point)->model ());
4332 /* Thaw the lists and add undo records for them */
4333 for (Lists::iterator i = lists.begin(); i != lists.end(); ++i) {
4334 boost::shared_ptr<AutomationList> al = i->first;
4336 _session->add_command (new MementoCommand<AutomationList> (*al.get(), i->second.state, &(al->get_state ())));
4341 /** Cut, copy or clear selected automation points.
4342 * @param op Operation (Cut, Copy or Clear)
4345 Editor::cut_copy_midi (CutCopyOp op)
4347 Evoral::Beats earliest = Evoral::Beats::max();
4348 for (MidiRegionSelection::iterator i = selection->midi_regions.begin(); i != selection->midi_regions.end(); ++i) {
4349 MidiRegionView* mrv = dynamic_cast<MidiRegionView*>(*i);
4351 if (!mrv->selection().empty()) {
4352 earliest = std::min(earliest, (*mrv->selection().begin())->note()->time());
4354 mrv->cut_copy_clear (op);
4356 /* XXX: not ideal, as there may be more than one track involved in the selection */
4357 _last_cut_copy_source_track = &mrv->get_time_axis_view();
4361 if (!selection->points.empty()) {
4362 cut_copy_points (op, earliest, true);
4363 if (op == Cut || op == Delete) {
4364 selection->clear_points ();
4369 struct lt_playlist {
4370 bool operator () (const PlaylistState& a, const PlaylistState& b) {
4371 return a.playlist < b.playlist;
4375 struct PlaylistMapping {
4377 boost::shared_ptr<Playlist> pl;
4379 PlaylistMapping (TimeAxisView* tvp) : tv (tvp) {}
4382 /** Remove `clicked_regionview' */
4384 Editor::remove_clicked_region ()
4386 if (clicked_routeview == 0 || clicked_regionview == 0) {
4390 begin_reversible_command (_("remove region"));
4392 boost::shared_ptr<Playlist> playlist = clicked_routeview->playlist();
4394 playlist->clear_changes ();
4395 playlist->clear_owned_changes ();
4396 playlist->remove_region (clicked_regionview->region());
4397 if (Config->get_edit_mode() == Ripple)
4398 playlist->ripple (clicked_regionview->region()->position(), -clicked_regionview->region()->length(), boost::shared_ptr<Region>());
4400 /* We might have removed regions, which alters other regions' layering_index,
4401 so we need to do a recursive diff here.
4403 vector<Command*> cmds;
4404 playlist->rdiff (cmds);
4405 _session->add_commands (cmds);
4407 _session->add_command(new StatefulDiffCommand (playlist));
4408 commit_reversible_command ();
4412 /** Remove the selected regions */
4414 Editor::remove_selected_regions ()
4416 RegionSelection rs = get_regions_from_selection_and_entered ();
4418 if (!_session || rs.empty()) {
4422 list<boost::shared_ptr<Region> > regions_to_remove;
4424 for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
4425 // we can't just remove the region(s) in this loop because
4426 // this removes them from the RegionSelection, and they thus
4427 // disappear from underneath the iterator, and the ++i above
4428 // SEGVs in a puzzling fashion.
4430 // so, first iterate over the regions to be removed from rs and
4431 // add them to the regions_to_remove list, and then
4432 // iterate over the list to actually remove them.
4434 regions_to_remove.push_back ((*i)->region());
4437 vector<boost::shared_ptr<Playlist> > playlists;
4439 for (list<boost::shared_ptr<Region> >::iterator rl = regions_to_remove.begin(); rl != regions_to_remove.end(); ++rl) {
4441 boost::shared_ptr<Playlist> playlist = (*rl)->playlist();
4444 // is this check necessary?
4448 /* get_regions_from_selection_and_entered() guarantees that
4449 the playlists involved are unique, so there is no need
4453 playlists.push_back (playlist);
4455 playlist->clear_changes ();
4456 playlist->clear_owned_changes ();
4457 playlist->freeze ();
4458 playlist->remove_region (*rl);
4459 if (Config->get_edit_mode() == Ripple)
4460 playlist->ripple ((*rl)->position(), -(*rl)->length(), boost::shared_ptr<Region>());
4464 vector<boost::shared_ptr<Playlist> >::iterator pl;
4465 bool in_command = false;
4467 for (pl = playlists.begin(); pl != playlists.end(); ++pl) {
4470 /* We might have removed regions, which alters other regions' layering_index,
4471 so we need to do a recursive diff here.
4475 begin_reversible_command (_("remove region"));
4478 vector<Command*> cmds;
4479 (*pl)->rdiff (cmds);
4480 _session->add_commands (cmds);
4482 _session->add_command(new StatefulDiffCommand (*pl));
4486 commit_reversible_command ();
4490 /** Cut, copy or clear selected regions.
4491 * @param op Operation (Cut, Copy or Clear)
4494 Editor::cut_copy_regions (CutCopyOp op, RegionSelection& rs)
4496 /* we can't use a std::map here because the ordering is important, and we can't trivially sort
4497 a map when we want ordered access to both elements. i think.
4500 vector<PlaylistMapping> pmap;
4502 framepos_t first_position = max_framepos;
4504 typedef set<boost::shared_ptr<Playlist> > FreezeList;
4505 FreezeList freezelist;
4507 /* get ordering correct before we cut/copy */
4509 rs.sort_by_position_and_track ();
4511 for (RegionSelection::iterator x = rs.begin(); x != rs.end(); ++x) {
4513 first_position = min ((framepos_t) (*x)->region()->position(), first_position);
4515 if (op == Cut || op == Clear || op == Delete) {
4516 boost::shared_ptr<Playlist> pl = (*x)->region()->playlist();
4519 FreezeList::iterator fl;
4521 // only take state if this is a new playlist.
4522 for (fl = freezelist.begin(); fl != freezelist.end(); ++fl) {
4528 if (fl == freezelist.end()) {
4529 pl->clear_changes();
4530 pl->clear_owned_changes ();
4532 freezelist.insert (pl);
4537 TimeAxisView* tv = &(*x)->get_time_axis_view();
4538 vector<PlaylistMapping>::iterator z;
4540 for (z = pmap.begin(); z != pmap.end(); ++z) {
4541 if ((*z).tv == tv) {
4546 if (z == pmap.end()) {
4547 pmap.push_back (PlaylistMapping (tv));
4551 for (RegionSelection::iterator x = rs.begin(); x != rs.end(); ) {
4553 boost::shared_ptr<Playlist> pl = (*x)->region()->playlist();
4556 /* region not yet associated with a playlist (e.g. unfinished
4563 TimeAxisView& tv = (*x)->get_time_axis_view();
4564 boost::shared_ptr<Playlist> npl;
4565 RegionSelection::iterator tmp;
4572 vector<PlaylistMapping>::iterator z;
4574 for (z = pmap.begin(); z != pmap.end(); ++z) {
4575 if ((*z).tv == &tv) {
4580 assert (z != pmap.end());
4583 npl = PlaylistFactory::create (pl->data_type(), *_session, "cutlist", true);
4591 boost::shared_ptr<Region> r = (*x)->region();
4592 boost::shared_ptr<Region> _xx;
4598 pl->remove_region (r);
4599 if (Config->get_edit_mode() == Ripple)
4600 pl->ripple (r->position(), -r->length(), boost::shared_ptr<Region>());
4604 _xx = RegionFactory::create (r);
4605 npl->add_region (_xx, r->position() - first_position);
4606 pl->remove_region (r);
4607 if (Config->get_edit_mode() == Ripple)
4608 pl->ripple (r->position(), -r->length(), boost::shared_ptr<Region>());
4612 /* copy region before adding, so we're not putting same object into two different playlists */
4613 npl->add_region (RegionFactory::create (r), r->position() - first_position);
4617 pl->remove_region (r);
4618 if (Config->get_edit_mode() == Ripple)
4619 pl->ripple (r->position(), -r->length(), boost::shared_ptr<Region>());
4628 list<boost::shared_ptr<Playlist> > foo;
4630 /* the pmap is in the same order as the tracks in which selected regions occurred */
4632 for (vector<PlaylistMapping>::iterator i = pmap.begin(); i != pmap.end(); ++i) {
4635 foo.push_back ((*i).pl);
4640 cut_buffer->set (foo);
4644 _last_cut_copy_source_track = 0;
4646 _last_cut_copy_source_track = pmap.front().tv;
4650 for (FreezeList::iterator pl = freezelist.begin(); pl != freezelist.end(); ++pl) {
4653 /* We might have removed regions, which alters other regions' layering_index,
4654 so we need to do a recursive diff here.
4656 vector<Command*> cmds;
4657 (*pl)->rdiff (cmds);
4658 _session->add_commands (cmds);
4660 _session->add_command (new StatefulDiffCommand (*pl));
4665 Editor::cut_copy_ranges (CutCopyOp op)
4667 TrackViewList ts = selection->tracks.filter_to_unique_playlists ();
4669 /* Sort the track selection now, so that it if is used, the playlists
4670 selected by the calls below to cut_copy_clear are in the order that
4671 their tracks appear in the editor. This makes things like paste
4672 of ranges work properly.
4675 sort_track_selection (ts);
4678 if (!entered_track) {
4681 ts.push_back (entered_track);
4684 for (TrackViewList::iterator i = ts.begin(); i != ts.end(); ++i) {
4685 (*i)->cut_copy_clear (*selection, op);
4690 Editor::paste (float times, bool from_context)
4692 DEBUG_TRACE (DEBUG::CutNPaste, "paste to preferred edit pos\n");
4694 paste_internal (get_preferred_edit_position (EDIT_IGNORE_NONE, from_context), times, get_grid_music_divisions (0));
4698 Editor::mouse_paste ()
4703 if (!mouse_frame (where, ignored)) {
4708 paste_internal (where, 1, get_grid_music_divisions (0));
4712 Editor::paste_internal (framepos_t position, float times, const int32_t sub_num)
4714 DEBUG_TRACE (DEBUG::CutNPaste, string_compose ("apparent paste position is %1\n", position));
4716 if (cut_buffer->empty(internal_editing())) {
4720 if (position == max_framepos) {
4721 position = get_preferred_edit_position();
4722 DEBUG_TRACE (DEBUG::CutNPaste, string_compose ("preferred edit position is %1\n", position));
4725 if (position == last_paste_pos) {
4726 /* repeated paste in the same position */
4729 /* paste in new location, reset repeated paste state */
4731 last_paste_pos = position;
4734 /* get everything in the correct order */
4737 if (!selection->tracks.empty()) {
4738 /* If there is a track selection, paste into exactly those tracks and
4739 only those tracks. This allows the user to be explicit and override
4740 the below "do the reasonable thing" logic. */
4741 ts = selection->tracks.filter_to_unique_playlists ();
4742 sort_track_selection (ts);
4744 /* Figure out which track to base the paste at. */
4745 TimeAxisView* base_track = NULL;
4746 if (_edit_point == Editing::EditAtMouse && entered_track) {
4747 /* With the mouse edit point, paste onto the track under the mouse. */
4748 base_track = entered_track;
4749 } else if (_edit_point == Editing::EditAtMouse && entered_regionview) {
4750 /* With the mouse edit point, paste onto the track of the region under the mouse. */
4751 base_track = &entered_regionview->get_time_axis_view();
4752 } else if (_last_cut_copy_source_track) {
4753 /* Paste to the track that the cut/copy came from (see mantis #333). */
4754 base_track = _last_cut_copy_source_track;
4756 /* This is "impossible" since we've copied... well, do nothing. */
4760 /* Walk up to parent if necessary, so base track is a route. */
4761 while (base_track->get_parent()) {
4762 base_track = base_track->get_parent();
4765 /* Add base track and all tracks below it. The paste logic will select
4766 the appropriate object types from the cut buffer in relative order. */
4767 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
4768 if ((*i)->order() >= base_track->order()) {
4773 /* Sort tracks so the nth track of type T will pick the nth object of type T. */
4774 sort_track_selection (ts);
4776 /* Add automation children of each track in order, for pasting several lines. */
4777 for (TrackViewList::iterator i = ts.begin(); i != ts.end();) {
4778 /* Add any automation children for pasting several lines */
4779 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*>(*i++);
4784 typedef RouteTimeAxisView::AutomationTracks ATracks;
4785 const ATracks& atracks = rtv->automation_tracks();
4786 for (ATracks::const_iterator a = atracks.begin(); a != atracks.end(); ++a) {
4787 i = ts.insert(i, a->second.get());
4792 /* We now have a list of trackviews starting at base_track, including
4793 automation children, in the order shown in the editor, e.g. R1,
4794 R1.A1, R1.A2, R2, R2.A1, ... */
4797 begin_reversible_command (Operations::paste);
4799 if (ts.size() == 1 && cut_buffer->lines.size() == 1 &&
4800 dynamic_cast<AutomationTimeAxisView*>(ts.front())) {
4801 /* Only one line copied, and one automation track selected. Do a
4802 "greedy" paste from one automation type to another. */
4804 PasteContext ctx(paste_count, times, ItemCounts(), true);
4805 ts.front()->paste (position, *cut_buffer, ctx, sub_num);
4809 /* Paste into tracks */
4811 PasteContext ctx(paste_count, times, ItemCounts(), false);
4812 for (TrackViewList::iterator i = ts.begin(); i != ts.end(); ++i) {
4813 (*i)->paste (position, *cut_buffer, ctx, sub_num);
4817 commit_reversible_command ();
4821 Editor::duplicate_regions (float times)
4823 RegionSelection rs (get_regions_from_selection_and_entered());
4824 duplicate_some_regions (rs, times);
4828 Editor::duplicate_some_regions (RegionSelection& regions, float times)
4830 if (regions.empty ()) {
4834 boost::shared_ptr<Playlist> playlist;
4835 RegionSelection sel = regions; // clear (below) may clear the argument list if its the current region selection
4836 RegionSelection foo;
4838 framepos_t const start_frame = regions.start ();
4839 framepos_t const end_frame = regions.end_frame ();
4840 framecnt_t const gap = end_frame - start_frame + 1;
4842 begin_reversible_command (Operations::duplicate_region);
4844 selection->clear_regions ();
4846 for (RegionSelection::iterator i = sel.begin(); i != sel.end(); ++i) {
4848 boost::shared_ptr<Region> r ((*i)->region());
4850 TimeAxisView& tv = (*i)->get_time_axis_view();
4851 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (&tv);
4852 latest_regionviews.clear ();
4853 sigc::connection c = rtv->view()->RegionViewAdded.connect (sigc::mem_fun(*this, &Editor::collect_new_region_view));
4855 framepos_t const position = end_frame + (r->first_frame() - start_frame + 1);
4856 playlist = (*i)->region()->playlist();
4857 playlist->clear_changes ();
4858 playlist->duplicate (r, position, gap, times);
4859 _session->add_command(new StatefulDiffCommand (playlist));
4863 foo.insert (foo.end(), latest_regionviews.begin(), latest_regionviews.end());
4867 selection->set (foo);
4870 commit_reversible_command ();
4874 Editor::duplicate_selection (float times)
4876 if (selection->time.empty() || selection->tracks.empty()) {
4880 boost::shared_ptr<Playlist> playlist;
4882 TrackViewList ts = selection->tracks.filter_to_unique_playlists ();
4884 bool in_command = false;
4886 for (TrackViewList::iterator i = ts.begin(); i != ts.end(); ++i) {
4887 if ((playlist = (*i)->playlist()) == 0) {
4890 playlist->clear_changes ();
4892 if (clicked_selection) {
4893 playlist->duplicate_range (selection->time[clicked_selection], times);
4895 playlist->duplicate_ranges (selection->time, times);
4899 begin_reversible_command (_("duplicate range selection"));
4902 _session->add_command (new StatefulDiffCommand (playlist));
4907 if (times == 1.0f) {
4908 // now "move" range selection to after the current range selection
4909 framecnt_t distance = 0;
4911 if (clicked_selection) {
4913 selection->time[clicked_selection].end - selection->time[clicked_selection].start;
4915 distance = selection->time.end_frame () - selection->time.start ();
4918 selection->move_time (distance);
4920 commit_reversible_command ();
4924 /** Reset all selected points to the relevant default value */
4926 Editor::reset_point_selection ()
4928 for (PointSelection::iterator i = selection->points.begin(); i != selection->points.end(); ++i) {
4929 ARDOUR::AutomationList::iterator j = (*i)->model ();
4930 (*j)->value = (*i)->line().the_list()->default_value ();
4935 Editor::center_playhead ()
4937 float const page = _visible_canvas_width * samples_per_pixel;
4938 center_screen_internal (playhead_cursor->current_frame (), page);
4942 Editor::center_edit_point ()
4944 float const page = _visible_canvas_width * samples_per_pixel;
4945 center_screen_internal (get_preferred_edit_position(), page);
4948 /** Caller must begin and commit a reversible command */
4950 Editor::clear_playlist (boost::shared_ptr<Playlist> playlist)
4952 playlist->clear_changes ();
4954 _session->add_command (new StatefulDiffCommand (playlist));
4958 Editor::nudge_track (bool use_edit, bool forwards)
4960 boost::shared_ptr<Playlist> playlist;
4961 framepos_t distance;
4962 framepos_t next_distance;
4966 start = get_preferred_edit_position();
4971 if ((distance = get_nudge_distance (start, next_distance)) == 0) {
4975 if (selection->tracks.empty()) {
4979 TrackViewList ts = selection->tracks.filter_to_unique_playlists ();
4980 bool in_command = false;
4982 for (TrackViewList::iterator i = ts.begin(); i != ts.end(); ++i) {
4984 if ((playlist = (*i)->playlist()) == 0) {
4988 playlist->clear_changes ();
4989 playlist->clear_owned_changes ();
4991 playlist->nudge_after (start, distance, forwards);
4994 begin_reversible_command (_("nudge track"));
4997 vector<Command*> cmds;
4999 playlist->rdiff (cmds);
5000 _session->add_commands (cmds);
5002 _session->add_command (new StatefulDiffCommand (playlist));
5006 commit_reversible_command ();
5011 Editor::remove_last_capture ()
5013 vector<string> choices;
5020 if (Config->get_verify_remove_last_capture()) {
5021 prompt = _("Do you really want to destroy the last capture?"
5022 "\n(This is destructive and cannot be undone)");
5024 choices.push_back (_("No, do nothing."));
5025 choices.push_back (_("Yes, destroy it."));
5027 Gtkmm2ext::Choice prompter (_("Destroy last capture"), prompt, choices);
5029 if (prompter.run () == 1) {
5030 _session->remove_last_capture ();
5031 _regions->redisplay ();
5035 _session->remove_last_capture();
5036 _regions->redisplay ();
5041 Editor::normalize_region ()
5047 RegionSelection rs = get_regions_from_selection_and_entered ();
5053 NormalizeDialog dialog (rs.size() > 1);
5055 if (dialog.run () != RESPONSE_ACCEPT) {
5059 CursorContext::Handle cursor_ctx = CursorContext::create(*this, _cursors->wait);
5062 /* XXX: should really only count audio regions here */
5063 int const regions = rs.size ();
5065 /* Make a list of the selected audio regions' maximum amplitudes, and also
5066 obtain the maximum amplitude of them all.
5068 list<double> max_amps;
5069 list<double> rms_vals;
5072 bool use_rms = dialog.constrain_rms ();
5074 for (RegionSelection::const_iterator i = rs.begin(); i != rs.end(); ++i) {
5075 AudioRegionView const * arv = dynamic_cast<AudioRegionView const *> (*i);
5079 dialog.descend (1.0 / regions);
5080 double const a = arv->audio_region()->maximum_amplitude (&dialog);
5082 double r = arv->audio_region()->rms (&dialog);
5083 max_rms = max (max_rms, r);
5084 rms_vals.push_back (r);
5088 /* the user cancelled the operation */
5092 max_amps.push_back (a);
5093 max_amp = max (max_amp, a);
5097 list<double>::const_iterator a = max_amps.begin ();
5098 list<double>::const_iterator l = rms_vals.begin ();
5099 bool in_command = false;
5101 for (RegionSelection::iterator r = rs.begin(); r != rs.end(); ++r) {
5102 AudioRegionView* const arv = dynamic_cast<AudioRegionView*> (*r);
5107 arv->region()->clear_changes ();
5109 double amp = dialog.normalize_individually() ? *a : max_amp;
5110 double target = dialog.target_peak (); // dB
5113 double const amp_rms = dialog.normalize_individually() ? *l : max_rms;
5114 const double t_rms = dialog.target_rms ();
5115 const gain_t c_peak = dB_to_coefficient (target);
5116 const gain_t c_rms = dB_to_coefficient (t_rms);
5117 if ((amp_rms / c_rms) > (amp / c_peak)) {
5123 arv->audio_region()->normalize (amp, target);
5126 begin_reversible_command (_("normalize"));
5129 _session->add_command (new StatefulDiffCommand (arv->region()));
5136 commit_reversible_command ();
5142 Editor::reset_region_scale_amplitude ()
5148 RegionSelection rs = get_regions_from_selection_and_entered ();
5154 bool in_command = false;
5156 for (RegionSelection::iterator r = rs.begin(); r != rs.end(); ++r) {
5157 AudioRegionView* const arv = dynamic_cast<AudioRegionView*>(*r);
5160 arv->region()->clear_changes ();
5161 arv->audio_region()->set_scale_amplitude (1.0f);
5164 begin_reversible_command ("reset gain");
5167 _session->add_command (new StatefulDiffCommand (arv->region()));
5171 commit_reversible_command ();
5176 Editor::adjust_region_gain (bool up)
5178 RegionSelection rs = get_regions_from_selection_and_entered ();
5180 if (!_session || rs.empty()) {
5184 bool in_command = false;
5186 for (RegionSelection::iterator r = rs.begin(); r != rs.end(); ++r) {
5187 AudioRegionView* const arv = dynamic_cast<AudioRegionView*>(*r);
5192 arv->region()->clear_changes ();
5194 double dB = accurate_coefficient_to_dB (arv->audio_region()->scale_amplitude ());
5202 arv->audio_region()->set_scale_amplitude (dB_to_coefficient (dB));
5205 begin_reversible_command ("adjust region gain");
5208 _session->add_command (new StatefulDiffCommand (arv->region()));
5212 commit_reversible_command ();
5218 Editor::reverse_region ()
5224 Reverse rev (*_session);
5225 apply_filter (rev, _("reverse regions"));
5229 Editor::strip_region_silence ()
5235 RegionSelection rs = get_regions_from_selection_and_entered ();
5241 std::list<RegionView*> audio_only;
5243 for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
5244 AudioRegionView* const arv = dynamic_cast<AudioRegionView*> (*i);
5246 audio_only.push_back (arv);
5250 assert (!audio_only.empty());
5252 StripSilenceDialog d (_session, audio_only);
5253 int const r = d.run ();
5257 if (r == Gtk::RESPONSE_OK) {
5258 ARDOUR::AudioIntervalMap silences;
5259 d.silences (silences);
5260 StripSilence s (*_session, silences, d.fade_length());
5262 apply_filter (s, _("strip silence"), &d);
5267 Editor::apply_midi_note_edit_op_to_region (MidiOperator& op, MidiRegionView& mrv)
5269 Evoral::Sequence<Evoral::Beats>::Notes selected;
5270 mrv.selection_as_notelist (selected, true);
5272 vector<Evoral::Sequence<Evoral::Beats>::Notes> v;
5273 v.push_back (selected);
5275 Evoral::Beats pos_beats = Evoral::Beats (mrv.midi_region()->beat()) - mrv.midi_region()->start_beats();
5277 return op (mrv.midi_region()->model(), pos_beats, v);
5281 Editor::apply_midi_note_edit_op (MidiOperator& op, const RegionSelection& rs)
5287 bool in_command = false;
5289 for (RegionSelection::const_iterator r = rs.begin(); r != rs.end(); ) {
5290 RegionSelection::const_iterator tmp = r;
5293 MidiRegionView* const mrv = dynamic_cast<MidiRegionView*> (*r);
5296 Command* cmd = apply_midi_note_edit_op_to_region (op, *mrv);
5299 begin_reversible_command (op.name ());
5303 _session->add_command (cmd);
5311 commit_reversible_command ();
5316 Editor::fork_region ()
5318 RegionSelection rs = get_regions_from_selection_and_entered ();
5324 CursorContext::Handle cursor_ctx = CursorContext::create(*this, _cursors->wait);
5325 bool in_command = false;
5329 for (RegionSelection::iterator r = rs.begin(); r != rs.end(); ) {
5330 RegionSelection::iterator tmp = r;
5333 MidiRegionView* const mrv = dynamic_cast<MidiRegionView*>(*r);
5337 boost::shared_ptr<Playlist> playlist = mrv->region()->playlist();
5338 boost::shared_ptr<MidiSource> new_source = _session->create_midi_source_by_stealing_name (mrv->midi_view()->track());
5339 boost::shared_ptr<MidiRegion> newregion = mrv->midi_region()->clone (new_source);
5342 begin_reversible_command (_("Fork Region(s)"));
5345 playlist->clear_changes ();
5346 playlist->replace_region (mrv->region(), newregion, mrv->region()->position());
5347 _session->add_command(new StatefulDiffCommand (playlist));
5349 error << string_compose (_("Could not unlink %1"), mrv->region()->name()) << endmsg;
5357 commit_reversible_command ();
5362 Editor::quantize_region ()
5365 quantize_regions(get_regions_from_selection_and_entered ());
5370 Editor::quantize_regions (const RegionSelection& rs)
5372 if (rs.n_midi_regions() == 0) {
5376 if (!quantize_dialog) {
5377 quantize_dialog = new QuantizeDialog (*this);
5380 if (quantize_dialog->is_mapped()) {
5381 /* in progress already */
5385 quantize_dialog->present ();
5386 const int r = quantize_dialog->run ();
5387 quantize_dialog->hide ();
5389 if (r == Gtk::RESPONSE_OK) {
5390 Quantize quant (quantize_dialog->snap_start(),
5391 quantize_dialog->snap_end(),
5392 quantize_dialog->start_grid_size(),
5393 quantize_dialog->end_grid_size(),
5394 quantize_dialog->strength(),
5395 quantize_dialog->swing(),
5396 quantize_dialog->threshold());
5398 apply_midi_note_edit_op (quant, rs);
5403 Editor::legatize_region (bool shrink_only)
5406 legatize_regions(get_regions_from_selection_and_entered (), shrink_only);
5411 Editor::legatize_regions (const RegionSelection& rs, bool shrink_only)
5413 if (rs.n_midi_regions() == 0) {
5417 Legatize legatize(shrink_only);
5418 apply_midi_note_edit_op (legatize, rs);
5422 Editor::transform_region ()
5425 transform_regions(get_regions_from_selection_and_entered ());
5430 Editor::transform_regions (const RegionSelection& rs)
5432 if (rs.n_midi_regions() == 0) {
5439 const int r = td.run();
5442 if (r == Gtk::RESPONSE_OK) {
5443 Transform transform(td.get());
5444 apply_midi_note_edit_op(transform, rs);
5449 Editor::transpose_region ()
5452 transpose_regions(get_regions_from_selection_and_entered ());
5457 Editor::transpose_regions (const RegionSelection& rs)
5459 if (rs.n_midi_regions() == 0) {
5464 int const r = d.run ();
5466 if (r == RESPONSE_ACCEPT) {
5467 Transpose transpose(d.semitones ());
5468 apply_midi_note_edit_op (transpose, rs);
5473 Editor::insert_patch_change (bool from_context)
5475 RegionSelection rs = get_regions_from_selection_and_entered ();
5481 const framepos_t p = get_preferred_edit_position (EDIT_IGNORE_NONE, from_context);
5483 /* XXX: bit of a hack; use the MIDNAM from the first selected region;
5484 there may be more than one, but the PatchChangeDialog can only offer
5485 one set of patch menus.
5487 MidiRegionView* first = dynamic_cast<MidiRegionView*> (rs.front ());
5489 Evoral::PatchChange<Evoral::Beats> empty (Evoral::Beats(), 0, 0, 0);
5490 PatchChangeDialog d (0, _session, empty, first->instrument_info(), Gtk::Stock::ADD);
5492 if (d.run() == RESPONSE_CANCEL) {
5496 for (RegionSelection::iterator i = rs.begin (); i != rs.end(); ++i) {
5497 MidiRegionView* const mrv = dynamic_cast<MidiRegionView*> (*i);
5499 if (p >= mrv->region()->first_frame() && p <= mrv->region()->last_frame()) {
5500 mrv->add_patch_change (p - mrv->region()->position(), d.patch ());
5507 Editor::apply_filter (Filter& filter, string command, ProgressReporter* progress)
5509 RegionSelection rs = get_regions_from_selection_and_entered ();
5515 CursorContext::Handle cursor_ctx = CursorContext::create(*this, _cursors->wait);
5516 bool in_command = false;
5521 int const N = rs.size ();
5523 for (RegionSelection::iterator r = rs.begin(); r != rs.end(); ) {
5524 RegionSelection::iterator tmp = r;
5527 AudioRegionView* const arv = dynamic_cast<AudioRegionView*>(*r);
5529 boost::shared_ptr<Playlist> playlist = arv->region()->playlist();
5532 progress->descend (1.0 / N);
5535 if (arv->audio_region()->apply (filter, progress) == 0) {
5537 playlist->clear_changes ();
5538 playlist->clear_owned_changes ();
5541 begin_reversible_command (command);
5545 if (filter.results.empty ()) {
5547 /* no regions returned; remove the old one */
5548 playlist->remove_region (arv->region ());
5552 std::vector<boost::shared_ptr<Region> >::iterator res = filter.results.begin ();
5554 /* first region replaces the old one */
5555 playlist->replace_region (arv->region(), *res, (*res)->position());
5559 while (res != filter.results.end()) {
5560 playlist->add_region (*res, (*res)->position());
5566 /* We might have removed regions, which alters other regions' layering_index,
5567 so we need to do a recursive diff here.
5569 vector<Command*> cmds;
5570 playlist->rdiff (cmds);
5571 _session->add_commands (cmds);
5573 _session->add_command(new StatefulDiffCommand (playlist));
5577 progress->ascend ();
5586 commit_reversible_command ();
5591 Editor::external_edit_region ()
5597 Editor::reset_region_gain_envelopes ()
5599 RegionSelection rs = get_regions_from_selection_and_entered ();
5601 if (!_session || rs.empty()) {
5605 bool in_command = false;
5607 for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
5608 AudioRegionView* const arv = dynamic_cast<AudioRegionView*>(*i);
5610 boost::shared_ptr<AutomationList> alist (arv->audio_region()->envelope());
5611 XMLNode& before (alist->get_state());
5613 arv->audio_region()->set_default_envelope ();
5616 begin_reversible_command (_("reset region gain"));
5619 _session->add_command (new MementoCommand<AutomationList>(*arv->audio_region()->envelope().get(), &before, &alist->get_state()));
5624 commit_reversible_command ();
5629 Editor::set_region_gain_visibility (RegionView* rv)
5631 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (rv);
5633 arv->update_envelope_visibility();
5638 Editor::set_gain_envelope_visibility ()
5644 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
5645 AudioTimeAxisView* v = dynamic_cast<AudioTimeAxisView*>(*i);
5647 v->audio_view()->foreach_regionview (sigc::mem_fun (this, &Editor::set_region_gain_visibility));
5653 Editor::toggle_gain_envelope_active ()
5655 if (_ignore_region_action) {
5659 RegionSelection rs = get_regions_from_selection_and_entered ();
5661 if (!_session || rs.empty()) {
5665 bool in_command = false;
5667 for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
5668 AudioRegionView* const arv = dynamic_cast<AudioRegionView*>(*i);
5670 arv->region()->clear_changes ();
5671 arv->audio_region()->set_envelope_active (!arv->audio_region()->envelope_active());
5674 begin_reversible_command (_("region gain envelope active"));
5677 _session->add_command (new StatefulDiffCommand (arv->region()));
5682 commit_reversible_command ();
5687 Editor::toggle_region_lock ()
5689 if (_ignore_region_action) {
5693 RegionSelection rs = get_regions_from_selection_and_entered ();
5695 if (!_session || rs.empty()) {
5699 begin_reversible_command (_("toggle region lock"));
5701 for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
5702 (*i)->region()->clear_changes ();
5703 (*i)->region()->set_locked (!(*i)->region()->locked());
5704 _session->add_command (new StatefulDiffCommand ((*i)->region()));
5707 commit_reversible_command ();
5711 Editor::toggle_region_video_lock ()
5713 if (_ignore_region_action) {
5717 RegionSelection rs = get_regions_from_selection_and_entered ();
5719 if (!_session || rs.empty()) {
5723 begin_reversible_command (_("Toggle Video Lock"));
5725 for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
5726 (*i)->region()->clear_changes ();
5727 (*i)->region()->set_video_locked (!(*i)->region()->video_locked());
5728 _session->add_command (new StatefulDiffCommand ((*i)->region()));
5731 commit_reversible_command ();
5735 Editor::toggle_region_lock_style ()
5737 if (_ignore_region_action) {
5741 RegionSelection rs = get_regions_from_selection_and_entered ();
5743 if (!_session || rs.empty()) {
5747 begin_reversible_command (_("region lock style"));
5749 for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
5750 (*i)->region()->clear_changes ();
5751 PositionLockStyle const ns = (*i)->region()->position_lock_style() == AudioTime ? MusicTime : AudioTime;
5752 (*i)->region()->set_position_lock_style (ns);
5753 _session->add_command (new StatefulDiffCommand ((*i)->region()));
5756 commit_reversible_command ();
5760 Editor::toggle_opaque_region ()
5762 if (_ignore_region_action) {
5766 RegionSelection rs = get_regions_from_selection_and_entered ();
5768 if (!_session || rs.empty()) {
5772 begin_reversible_command (_("change region opacity"));
5774 for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
5775 (*i)->region()->clear_changes ();
5776 (*i)->region()->set_opaque (!(*i)->region()->opaque());
5777 _session->add_command (new StatefulDiffCommand ((*i)->region()));
5780 commit_reversible_command ();
5784 Editor::toggle_record_enable ()
5786 bool new_state = false;
5788 for (TrackSelection::iterator i = selection->tracks.begin(); i != selection->tracks.end(); ++i) {
5789 RouteTimeAxisView *rtav = dynamic_cast<RouteTimeAxisView *>(*i);
5792 if (!rtav->is_track())
5796 new_state = !rtav->track()->rec_enable_control()->get_value();
5800 rtav->track()->rec_enable_control()->set_value (new_state, Controllable::UseGroup);
5805 Editor::toggle_solo ()
5807 bool new_state = false;
5809 boost::shared_ptr<ControlList> cl (new ControlList);
5811 for (TrackSelection::iterator i = selection->tracks.begin(); i != selection->tracks.end(); ++i) {
5812 RouteTimeAxisView *rtav = dynamic_cast<RouteTimeAxisView *>(*i);
5819 new_state = !rtav->route()->soloed ();
5823 cl->push_back (rtav->route()->solo_control());
5826 _session->set_controls (cl, new_state ? 1.0 : 0.0, Controllable::UseGroup);
5830 Editor::toggle_mute ()
5832 bool new_state = false;
5834 boost::shared_ptr<RouteList> rl (new RouteList);
5836 for (TrackSelection::iterator i = selection->tracks.begin(); i != selection->tracks.end(); ++i) {
5837 RouteTimeAxisView *rtav = dynamic_cast<RouteTimeAxisView *>(*i);
5844 new_state = !rtav->route()->muted();
5848 rl->push_back (rtav->route());
5851 _session->set_controls (route_list_to_control_list (rl, &Stripable::mute_control), new_state, Controllable::UseGroup);
5855 Editor::toggle_solo_isolate ()
5861 Editor::fade_range ()
5863 TrackViewList ts = selection->tracks.filter_to_unique_playlists ();
5865 begin_reversible_command (_("fade range"));
5867 for (TrackViewList::iterator i = ts.begin(); i != ts.end(); ++i) {
5868 (*i)->fade_range (selection->time);
5871 commit_reversible_command ();
5876 Editor::set_fade_length (bool in)
5878 RegionSelection rs = get_regions_from_selection_and_entered ();
5884 /* we need a region to measure the offset from the start */
5886 RegionView* rv = rs.front ();
5888 framepos_t pos = get_preferred_edit_position();
5892 if (pos > rv->region()->last_frame() || pos < rv->region()->first_frame()) {
5893 /* edit point is outside the relevant region */
5898 if (pos <= rv->region()->position()) {
5902 len = pos - rv->region()->position();
5903 cmd = _("set fade in length");
5905 if (pos >= rv->region()->last_frame()) {
5909 len = rv->region()->last_frame() - pos;
5910 cmd = _("set fade out length");
5913 bool in_command = false;
5915 for (RegionSelection::iterator x = rs.begin(); x != rs.end(); ++x) {
5916 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (*x);
5922 boost::shared_ptr<AutomationList> alist;
5924 alist = tmp->audio_region()->fade_in();
5926 alist = tmp->audio_region()->fade_out();
5929 XMLNode &before = alist->get_state();
5932 tmp->audio_region()->set_fade_in_length (len);
5933 tmp->audio_region()->set_fade_in_active (true);
5935 tmp->audio_region()->set_fade_out_length (len);
5936 tmp->audio_region()->set_fade_out_active (true);
5940 begin_reversible_command (cmd);
5943 XMLNode &after = alist->get_state();
5944 _session->add_command(new MementoCommand<AutomationList>(*alist, &before, &after));
5948 commit_reversible_command ();
5953 Editor::set_fade_in_shape (FadeShape shape)
5955 RegionSelection rs = get_regions_from_selection_and_entered ();
5960 bool in_command = false;
5962 for (RegionSelection::iterator x = rs.begin(); x != rs.end(); ++x) {
5963 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (*x);
5969 boost::shared_ptr<AutomationList> alist = tmp->audio_region()->fade_in();
5970 XMLNode &before = alist->get_state();
5972 tmp->audio_region()->set_fade_in_shape (shape);
5975 begin_reversible_command (_("set fade in shape"));
5978 XMLNode &after = alist->get_state();
5979 _session->add_command(new MementoCommand<AutomationList>(*alist.get(), &before, &after));
5983 commit_reversible_command ();
5988 Editor::set_fade_out_shape (FadeShape shape)
5990 RegionSelection rs = get_regions_from_selection_and_entered ();
5995 bool in_command = false;
5997 for (RegionSelection::iterator x = rs.begin(); x != rs.end(); ++x) {
5998 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (*x);
6004 boost::shared_ptr<AutomationList> alist = tmp->audio_region()->fade_out();
6005 XMLNode &before = alist->get_state();
6007 tmp->audio_region()->set_fade_out_shape (shape);
6010 begin_reversible_command (_("set fade out shape"));
6013 XMLNode &after = alist->get_state();
6014 _session->add_command(new MementoCommand<AutomationList>(*alist.get(), &before, &after));
6018 commit_reversible_command ();
6023 Editor::set_fade_in_active (bool yn)
6025 RegionSelection rs = get_regions_from_selection_and_entered ();
6030 bool in_command = false;
6032 for (RegionSelection::iterator x = rs.begin(); x != rs.end(); ++x) {
6033 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (*x);
6040 boost::shared_ptr<AudioRegion> ar (tmp->audio_region());
6042 ar->clear_changes ();
6043 ar->set_fade_in_active (yn);
6046 begin_reversible_command (_("set fade in active"));
6049 _session->add_command (new StatefulDiffCommand (ar));
6053 commit_reversible_command ();
6058 Editor::set_fade_out_active (bool yn)
6060 RegionSelection rs = get_regions_from_selection_and_entered ();
6065 bool in_command = false;
6067 for (RegionSelection::iterator x = rs.begin(); x != rs.end(); ++x) {
6068 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (*x);
6074 boost::shared_ptr<AudioRegion> ar (tmp->audio_region());
6076 ar->clear_changes ();
6077 ar->set_fade_out_active (yn);
6080 begin_reversible_command (_("set fade out active"));
6083 _session->add_command(new StatefulDiffCommand (ar));
6087 commit_reversible_command ();
6092 Editor::toggle_region_fades (int dir)
6094 if (_ignore_region_action) {
6098 boost::shared_ptr<AudioRegion> ar;
6101 RegionSelection rs = get_regions_from_selection_and_entered ();
6107 RegionSelection::iterator i;
6108 for (i = rs.begin(); i != rs.end(); ++i) {
6109 if ((ar = boost::dynamic_pointer_cast<AudioRegion>((*i)->region())) != 0) {
6111 yn = ar->fade_out_active ();
6113 yn = ar->fade_in_active ();
6119 if (i == rs.end()) {
6123 /* XXX should this undo-able? */
6124 bool in_command = false;
6126 for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
6127 if ((ar = boost::dynamic_pointer_cast<AudioRegion>((*i)->region())) == 0) {
6130 ar->clear_changes ();
6132 if (dir == 1 || dir == 0) {
6133 ar->set_fade_in_active (!yn);
6136 if (dir == -1 || dir == 0) {
6137 ar->set_fade_out_active (!yn);
6140 begin_reversible_command (_("toggle fade active"));
6143 _session->add_command(new StatefulDiffCommand (ar));
6147 commit_reversible_command ();
6152 /** Update region fade visibility after its configuration has been changed */
6154 Editor::update_region_fade_visibility ()
6156 bool _fade_visibility = _session->config.get_show_region_fades ();
6158 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
6159 AudioTimeAxisView* v = dynamic_cast<AudioTimeAxisView*>(*i);
6161 if (_fade_visibility) {
6162 v->audio_view()->show_all_fades ();
6164 v->audio_view()->hide_all_fades ();
6171 Editor::set_edit_point ()
6176 if (!mouse_frame (where, ignored)) {
6182 if (selection->markers.empty()) {
6184 mouse_add_new_marker (where);
6189 Location* loc = find_location_from_marker (selection->markers.front(), ignored);
6192 loc->move_to (where, get_grid_music_divisions(0));
6198 Editor::set_playhead_cursor ()
6200 if (entered_marker) {
6201 _session->request_locate (entered_marker->position(), _session->transport_rolling());
6206 if (!mouse_frame (where, ignored)) {
6213 _session->request_locate (where, _session->transport_rolling());
6217 //not sure what this was for; remove it for now.
6218 // if (UIConfiguration::instance().get_follow_edits() && (!_session || !_session->config.get_external_sync())) {
6219 // cancel_time_selection();
6225 Editor::split_region ()
6227 if (_drags->active ()) {
6231 //if a range is selected, separate it
6232 if ( !selection->time.empty()) {
6233 separate_regions_between (selection->time);
6237 //if no range was selected, try to find some regions to split
6238 if (current_mouse_mode() == MouseObject) { //don't try this for Internal Edit, Stretch, Draw, etc.
6240 RegionSelection rs = get_regions_from_selection_and_edit_point ();
6242 framepos_t where = get_preferred_edit_position ();
6248 if (snap_musical()) {
6249 split_regions_at (where, rs, get_grid_music_divisions (0));
6251 split_regions_at (where, rs, 0);
6257 Editor::select_next_route()
6259 if (selection->tracks.empty()) {
6260 selection->set (track_views.front());
6264 TimeAxisView* current = selection->tracks.front();
6268 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
6270 if (*i == current) {
6272 if (i != track_views.end()) {
6275 current = (*(track_views.begin()));
6276 //selection->set (*(track_views.begin()));
6282 rui = dynamic_cast<RouteUI *>(current);
6284 } while (current->hidden() || (rui == NULL) || !rui->route()->active());
6286 selection->set (current);
6288 ensure_time_axis_view_is_visible (*current, false);
6292 Editor::select_prev_route()
6294 if (selection->tracks.empty()) {
6295 selection->set (track_views.front());
6299 TimeAxisView* current = selection->tracks.front();
6303 for (TrackViewList::reverse_iterator i = track_views.rbegin(); i != track_views.rend(); ++i) {
6305 if (*i == current) {
6307 if (i != track_views.rend()) {
6310 current = *(track_views.rbegin());
6315 rui = dynamic_cast<RouteUI *>(current);
6317 } while (current->hidden() || (rui == NULL) || !rui->route()->active());
6319 selection->set (current);
6321 ensure_time_axis_view_is_visible (*current, false);
6325 Editor::set_loop_from_selection (bool play)
6327 if (_session == 0) {
6331 framepos_t start, end;
6332 if (!get_selection_extents ( start, end))
6335 set_loop_range (start, end, _("set loop range from selection"));
6338 _session->request_play_loop (true, true);
6343 Editor::set_loop_from_region (bool play)
6345 framepos_t start, end;
6346 if (!get_selection_extents ( start, end))
6349 set_loop_range (start, end, _("set loop range from region"));
6352 _session->request_locate (start, true);
6353 _session->request_play_loop (true);
6358 Editor::set_punch_from_selection ()
6360 if (_session == 0) {
6364 framepos_t start, end;
6365 if (!get_selection_extents ( start, end))
6368 set_punch_range (start, end, _("set punch range from selection"));
6372 Editor::set_auto_punch_range ()
6374 // auto punch in/out button from a single button
6375 // If Punch In is unset, set punch range from playhead to end, enable punch in
6376 // If Punch In is set, the next punch sets Punch Out, unless the playhead has been
6377 // rewound beyond the Punch In marker, in which case that marker will be moved back
6378 // to the current playhead position.
6379 // If punch out is set, it clears the punch range and Punch In/Out buttons
6381 if (_session == 0) {
6385 Location* tpl = transport_punch_location();
6386 framepos_t now = playhead_cursor->current_frame();
6387 framepos_t begin = now;
6388 framepos_t end = _session->current_end_frame();
6390 if (!_session->config.get_punch_in()) {
6391 // First Press - set punch in and create range from here to eternity
6392 set_punch_range (begin, end, _("Auto Punch In"));
6393 _session->config.set_punch_in(true);
6394 } else if (tpl && !_session->config.get_punch_out()) {
6395 // Second press - update end range marker and set punch_out
6396 if (now < tpl->start()) {
6397 // playhead has been rewound - move start back and pretend nothing happened
6399 set_punch_range (begin, end, _("Auto Punch In/Out"));
6401 // normal case for 2nd press - set the punch out
6402 end = playhead_cursor->current_frame ();
6403 set_punch_range (tpl->start(), now, _("Auto Punch In/Out"));
6404 _session->config.set_punch_out(true);
6407 if (_session->config.get_punch_out()) {
6408 _session->config.set_punch_out(false);
6411 if (_session->config.get_punch_in()) {
6412 _session->config.set_punch_in(false);
6417 // third press - unset punch in/out and remove range
6418 _session->locations()->remove(tpl);
6425 Editor::set_session_extents_from_selection ()
6427 if (_session == 0) {
6431 framepos_t start, end;
6432 if (!get_selection_extents ( start, end))
6436 if ((loc = _session->locations()->session_range_location()) == 0) {
6437 _session->set_session_extents (start, end); // this will create a new session range; no need for UNDO
6439 XMLNode &before = loc->get_state();
6441 _session->set_session_extents (start, end);
6443 XMLNode &after = loc->get_state();
6445 begin_reversible_command (_("set session start/end from selection"));
6447 _session->add_command (new MementoCommand<Location>(*loc, &before, &after));
6449 commit_reversible_command ();
6452 _session->set_end_is_free (false);
6456 Editor::set_punch_start_from_edit_point ()
6460 framepos_t start = 0;
6461 framepos_t end = max_framepos;
6463 //use the existing punch end, if any
6464 Location* tpl = transport_punch_location();
6469 if ((_edit_point == EditAtPlayhead) && _session->transport_rolling()) {
6470 start = _session->audible_frame();
6472 start = get_preferred_edit_position();
6475 //snap the selection start/end
6478 //if there's not already a sensible selection endpoint, go "forever"
6479 if ( start > end ) {
6483 set_punch_range (start, end, _("set punch start from EP"));
6489 Editor::set_punch_end_from_edit_point ()
6493 framepos_t start = 0;
6494 framepos_t end = max_framepos;
6496 //use the existing punch start, if any
6497 Location* tpl = transport_punch_location();
6499 start = tpl->start();
6502 if ((_edit_point == EditAtPlayhead) && _session->transport_rolling()) {
6503 end = _session->audible_frame();
6505 end = get_preferred_edit_position();
6508 //snap the selection start/end
6511 set_punch_range (start, end, _("set punch end from EP"));
6517 Editor::set_loop_start_from_edit_point ()
6521 framepos_t start = 0;
6522 framepos_t end = max_framepos;
6524 //use the existing loop end, if any
6525 Location* tpl = transport_loop_location();
6530 if ((_edit_point == EditAtPlayhead) && _session->transport_rolling()) {
6531 start = _session->audible_frame();
6533 start = get_preferred_edit_position();
6536 //snap the selection start/end
6539 //if there's not already a sensible selection endpoint, go "forever"
6540 if ( start > end ) {
6544 set_loop_range (start, end, _("set loop start from EP"));
6550 Editor::set_loop_end_from_edit_point ()
6554 framepos_t start = 0;
6555 framepos_t end = max_framepos;
6557 //use the existing loop start, if any
6558 Location* tpl = transport_loop_location();
6560 start = tpl->start();
6563 if ((_edit_point == EditAtPlayhead) && _session->transport_rolling()) {
6564 end = _session->audible_frame();
6566 end = get_preferred_edit_position();
6569 //snap the selection start/end
6572 set_loop_range (start, end, _("set loop end from EP"));
6577 Editor::set_punch_from_region ()
6579 framepos_t start, end;
6580 if (!get_selection_extents ( start, end))
6583 set_punch_range (start, end, _("set punch range from region"));
6587 Editor::pitch_shift_region ()
6589 RegionSelection rs = get_regions_from_selection_and_entered ();
6591 RegionSelection audio_rs;
6592 for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
6593 if (dynamic_cast<AudioRegionView*> (*i)) {
6594 audio_rs.push_back (*i);
6598 if (audio_rs.empty()) {
6602 pitch_shift (audio_rs, 1.2);
6606 Editor::set_tempo_from_region ()
6608 RegionSelection rs = get_regions_from_selection_and_entered ();
6610 if (!_session || rs.empty()) {
6614 RegionView* rv = rs.front();
6616 define_one_bar (rv->region()->position(), rv->region()->last_frame() + 1);
6620 Editor::use_range_as_bar ()
6622 framepos_t start, end;
6623 if (get_edit_op_range (start, end)) {
6624 define_one_bar (start, end);
6629 Editor::define_one_bar (framepos_t start, framepos_t end)
6631 framepos_t length = end - start;
6633 const Meter& m (_session->tempo_map().meter_at_frame (start));
6635 /* length = 1 bar */
6637 /* We're going to deliver a constant tempo here,
6638 so we can use frames per beat to determine length.
6639 now we want frames per beat.
6640 we have frames per bar, and beats per bar, so ...
6643 /* XXXX METER MATH */
6645 double frames_per_beat = length / m.divisions_per_bar();
6647 /* beats per minute = */
6649 double beats_per_minute = (_session->frame_rate() * 60.0) / frames_per_beat;
6651 /* now decide whether to:
6653 (a) set global tempo
6654 (b) add a new tempo marker
6658 const TempoSection& t (_session->tempo_map().tempo_section_at_frame (start));
6660 bool do_global = false;
6662 if ((_session->tempo_map().n_tempos() == 1) && (_session->tempo_map().n_meters() == 1)) {
6664 /* only 1 tempo & 1 meter: ask if the user wants to set the tempo
6665 at the start, or create a new marker
6668 vector<string> options;
6669 options.push_back (_("Cancel"));
6670 options.push_back (_("Add new marker"));
6671 options.push_back (_("Set global tempo"));
6674 _("Define one bar"),
6675 _("Do you want to set the global tempo or add a new tempo marker?"),
6679 c.set_default_response (2);
6695 /* more than 1 tempo and/or meter section already, go ahead do the "usual":
6696 if the marker is at the region starter, change it, otherwise add
6701 begin_reversible_command (_("set tempo from region"));
6702 XMLNode& before (_session->tempo_map().get_state());
6705 _session->tempo_map().change_initial_tempo (beats_per_minute, t.note_type());
6706 } else if (t.frame() == start) {
6707 _session->tempo_map().change_existing_tempo_at (start, beats_per_minute, t.note_type());
6709 const Tempo tempo (beats_per_minute, t.note_type());
6710 _session->tempo_map().add_tempo (tempo, 0.0, start, TempoSection::Constant, AudioTime);
6713 XMLNode& after (_session->tempo_map().get_state());
6715 _session->add_command (new MementoCommand<TempoMap>(_session->tempo_map(), &before, &after));
6716 commit_reversible_command ();
6720 Editor::split_region_at_transients ()
6722 AnalysisFeatureList positions;
6724 RegionSelection rs = get_regions_from_selection_and_entered ();
6726 if (!_session || rs.empty()) {
6730 begin_reversible_command (_("split regions"));
6732 for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ) {
6734 RegionSelection::iterator tmp;
6739 boost::shared_ptr<AudioRegion> ar = boost::dynamic_pointer_cast<AudioRegion> ((*i)->region());
6742 ar->transients (positions);
6743 split_region_at_points ((*i)->region(), positions, true);
6750 commit_reversible_command ();
6755 Editor::split_region_at_points (boost::shared_ptr<Region> r, AnalysisFeatureList& positions, bool can_ferret, bool select_new)
6757 bool use_rhythmic_rodent = false;
6759 boost::shared_ptr<Playlist> pl = r->playlist();
6761 list<boost::shared_ptr<Region> > new_regions;
6767 if (positions.empty()) {
6771 if (positions.size() > 20 && can_ferret) {
6772 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);
6773 MessageDialog msg (msgstr,
6776 Gtk::BUTTONS_OK_CANCEL);
6779 msg.add_button (_("Call for the Ferret!"), RESPONSE_APPLY);
6780 msg.set_secondary_text (_("Press OK to continue with this split operation\nor ask the Ferret dialog to tune the analysis"));
6782 msg.set_secondary_text (_("Press OK to continue with this split operation"));
6785 msg.set_title (_("Excessive split?"));
6788 int response = msg.run();
6794 case RESPONSE_APPLY:
6795 use_rhythmic_rodent = true;
6802 if (use_rhythmic_rodent) {
6803 show_rhythm_ferret ();
6807 AnalysisFeatureList::const_iterator x;
6809 pl->clear_changes ();
6810 pl->clear_owned_changes ();
6812 x = positions.begin();
6814 if (x == positions.end()) {
6819 pl->remove_region (r);
6823 framepos_t rstart = r->first_frame ();
6824 framepos_t rend = r->last_frame ();
6826 while (x != positions.end()) {
6828 /* deal with positons that are out of scope of present region bounds */
6829 if (*x <= rstart || *x > rend) {
6834 /* file start = original start + how far we from the initial position ? */
6836 framepos_t file_start = r->start() + pos;
6838 /* length = next position - current position */
6840 framepos_t len = (*x) - pos - rstart;
6842 /* XXX we do we really want to allow even single-sample regions?
6843 * shouldn't we have some kind of lower limit on region size?
6852 if (RegionFactory::region_name (new_name, r->name())) {
6856 /* do NOT announce new regions 1 by one, just wait till they are all done */
6860 plist.add (ARDOUR::Properties::start, file_start);
6861 plist.add (ARDOUR::Properties::length, len);
6862 plist.add (ARDOUR::Properties::name, new_name);
6863 plist.add (ARDOUR::Properties::layer, 0);
6864 // TODO set transients_offset
6866 boost::shared_ptr<Region> nr = RegionFactory::create (r->sources(), plist, false);
6867 /* because we set annouce to false, manually add the new region to the
6870 RegionFactory::map_add (nr);
6872 pl->add_region (nr, rstart + pos);
6875 new_regions.push_front(nr);
6884 RegionFactory::region_name (new_name, r->name());
6886 /* Add the final region */
6889 plist.add (ARDOUR::Properties::start, r->start() + pos);
6890 plist.add (ARDOUR::Properties::length, r->last_frame() - (r->position() + pos) + 1);
6891 plist.add (ARDOUR::Properties::name, new_name);
6892 plist.add (ARDOUR::Properties::layer, 0);
6894 boost::shared_ptr<Region> nr = RegionFactory::create (r->sources(), plist, false);
6895 /* because we set annouce to false, manually add the new region to the
6898 RegionFactory::map_add (nr);
6899 pl->add_region (nr, r->position() + pos);
6902 new_regions.push_front(nr);
6907 /* We might have removed regions, which alters other regions' layering_index,
6908 so we need to do a recursive diff here.
6910 vector<Command*> cmds;
6912 _session->add_commands (cmds);
6914 _session->add_command (new StatefulDiffCommand (pl));
6918 for (list<boost::shared_ptr<Region> >::iterator i = new_regions.begin(); i != new_regions.end(); ++i){
6919 set_selected_regionview_from_region_list ((*i), Selection::Add);
6925 Editor::place_transient()
6931 RegionSelection rs = get_regions_from_selection_and_edit_point ();
6937 framepos_t where = get_preferred_edit_position();
6939 begin_reversible_command (_("place transient"));
6941 for (RegionSelection::iterator r = rs.begin(); r != rs.end(); ++r) {
6942 (*r)->region()->add_transient(where);
6945 commit_reversible_command ();
6949 Editor::remove_transient(ArdourCanvas::Item* item)
6955 ArdourCanvas::Line* _line = reinterpret_cast<ArdourCanvas::Line*> (item);
6958 AudioRegionView* _arv = reinterpret_cast<AudioRegionView*> (item->get_data ("regionview"));
6959 _arv->remove_transient (*(float*) _line->get_data ("position"));
6963 Editor::snap_regions_to_grid ()
6965 list <boost::shared_ptr<Playlist > > used_playlists;
6967 RegionSelection rs = get_regions_from_selection_and_entered ();
6969 if (!_session || rs.empty()) {
6973 begin_reversible_command (_("snap regions to grid"));
6975 for (RegionSelection::iterator r = rs.begin(); r != rs.end(); ++r) {
6977 boost::shared_ptr<Playlist> pl = (*r)->region()->playlist();
6979 if (!pl->frozen()) {
6980 /* we haven't seen this playlist before */
6982 /* remember used playlists so we can thaw them later */
6983 used_playlists.push_back(pl);
6987 framepos_t start_frame = (*r)->region()->first_frame ();
6988 snap_to (start_frame);
6989 (*r)->region()->set_position (start_frame);
6992 while (used_playlists.size() > 0) {
6993 list <boost::shared_ptr<Playlist > >::iterator i = used_playlists.begin();
6995 used_playlists.pop_front();
6998 commit_reversible_command ();
7002 Editor::close_region_gaps ()
7004 list <boost::shared_ptr<Playlist > > used_playlists;
7006 RegionSelection rs = get_regions_from_selection_and_entered ();
7008 if (!_session || rs.empty()) {
7012 Dialog dialog (_("Close Region Gaps"));
7015 table.set_spacings (12);
7016 table.set_border_width (12);
7017 Label* l = manage (left_aligned_label (_("Crossfade length")));
7018 table.attach (*l, 0, 1, 0, 1);
7020 SpinButton spin_crossfade (1, 0);
7021 spin_crossfade.set_range (0, 15);
7022 spin_crossfade.set_increments (1, 1);
7023 spin_crossfade.set_value (5);
7024 table.attach (spin_crossfade, 1, 2, 0, 1);
7026 table.attach (*manage (new Label (_("ms"))), 2, 3, 0, 1);
7028 l = manage (left_aligned_label (_("Pull-back length")));
7029 table.attach (*l, 0, 1, 1, 2);
7031 SpinButton spin_pullback (1, 0);
7032 spin_pullback.set_range (0, 100);
7033 spin_pullback.set_increments (1, 1);
7034 spin_pullback.set_value(30);
7035 table.attach (spin_pullback, 1, 2, 1, 2);
7037 table.attach (*manage (new Label (_("ms"))), 2, 3, 1, 2);
7039 dialog.get_vbox()->pack_start (table);
7040 dialog.add_button (Stock::CANCEL, RESPONSE_CANCEL);
7041 dialog.add_button (_("Ok"), RESPONSE_ACCEPT);
7044 if (dialog.run () == RESPONSE_CANCEL) {
7048 framepos_t crossfade_len = spin_crossfade.get_value();
7049 framepos_t pull_back_frames = spin_pullback.get_value();
7051 crossfade_len = lrintf (crossfade_len * _session->frame_rate()/1000);
7052 pull_back_frames = lrintf (pull_back_frames * _session->frame_rate()/1000);
7054 /* Iterate over the region list and make adjacent regions overlap by crossfade_len_ms */
7056 begin_reversible_command (_("close region gaps"));
7059 boost::shared_ptr<Region> last_region;
7061 rs.sort_by_position_and_track();
7063 for (RegionSelection::iterator r = rs.begin(); r != rs.end(); ++r) {
7065 boost::shared_ptr<Playlist> pl = (*r)->region()->playlist();
7067 if (!pl->frozen()) {
7068 /* we haven't seen this playlist before */
7070 /* remember used playlists so we can thaw them later */
7071 used_playlists.push_back(pl);
7075 framepos_t position = (*r)->region()->position();
7077 if (idx == 0 || position < last_region->position()){
7078 last_region = (*r)->region();
7083 (*r)->region()->trim_front( (position - pull_back_frames));
7084 last_region->trim_end( (position - pull_back_frames + crossfade_len));
7086 last_region = (*r)->region();
7091 while (used_playlists.size() > 0) {
7092 list <boost::shared_ptr<Playlist > >::iterator i = used_playlists.begin();
7094 used_playlists.pop_front();
7097 commit_reversible_command ();
7101 Editor::tab_to_transient (bool forward)
7103 AnalysisFeatureList positions;
7105 RegionSelection rs = get_regions_from_selection_and_entered ();
7111 framepos_t pos = _session->audible_frame ();
7113 if (!selection->tracks.empty()) {
7115 /* don't waste time searching for transients in duplicate playlists.
7118 TrackViewList ts = selection->tracks.filter_to_unique_playlists ();
7120 for (TrackViewList::iterator t = ts.begin(); t != ts.end(); ++t) {
7122 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (*t);
7125 boost::shared_ptr<Track> tr = rtv->track();
7127 boost::shared_ptr<Playlist> pl = tr->playlist ();
7129 framepos_t result = pl->find_next_transient (pos, forward ? 1 : -1);
7132 positions.push_back (result);
7145 for (RegionSelection::iterator r = rs.begin(); r != rs.end(); ++r) {
7146 (*r)->region()->get_transients (positions);
7150 TransientDetector::cleanup_transients (positions, _session->frame_rate(), 3.0);
7153 AnalysisFeatureList::iterator x;
7155 for (x = positions.begin(); x != positions.end(); ++x) {
7161 if (x != positions.end ()) {
7162 _session->request_locate (*x);
7166 AnalysisFeatureList::reverse_iterator x;
7168 for (x = positions.rbegin(); x != positions.rend(); ++x) {
7174 if (x != positions.rend ()) {
7175 _session->request_locate (*x);
7181 Editor::playhead_forward_to_grid ()
7187 framepos_t pos = playhead_cursor->current_frame ();
7188 if (pos < max_framepos - 1) {
7190 snap_to_internal (pos, RoundUpAlways, false);
7191 _session->request_locate (pos);
7197 Editor::playhead_backward_to_grid ()
7203 framepos_t pos = playhead_cursor->current_frame ();
7206 snap_to_internal (pos, RoundDownAlways, false);
7207 _session->request_locate (pos);
7212 Editor::set_track_height (Height h)
7214 TrackSelection& ts (selection->tracks);
7216 for (TrackSelection::iterator x = ts.begin(); x != ts.end(); ++x) {
7217 (*x)->set_height_enum (h);
7222 Editor::toggle_tracks_active ()
7224 TrackSelection& ts (selection->tracks);
7226 bool target = false;
7232 for (TrackSelection::iterator x = ts.begin(); x != ts.end(); ++x) {
7233 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*>(*x);
7237 target = !rtv->_route->active();
7240 rtv->_route->set_active (target, this);
7246 Editor::remove_tracks ()
7248 /* this will delete GUI objects that may be the subject of an event
7249 handler in which this method is called. Defer actual deletion to the
7250 next idle callback, when all event handling is finished.
7252 Glib::signal_idle().connect (sigc::mem_fun (*this, &Editor::idle_remove_tracks));
7256 Editor::idle_remove_tracks ()
7258 Session::StateProtector sp (_session);
7260 return false; /* do not call again */
7264 Editor::_remove_tracks ()
7266 TrackSelection& ts (selection->tracks);
7272 vector<string> choices;
7276 const char* trackstr;
7278 vector<boost::shared_ptr<Route> > routes;
7279 bool special_bus = false;
7281 for (TrackSelection::iterator x = ts.begin(); x != ts.end(); ++x) {
7282 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (*x);
7286 if (rtv->is_track()) {
7291 routes.push_back (rtv->_route);
7293 if (rtv->route()->is_master() || rtv->route()->is_monitor()) {
7298 if (special_bus && !Config->get_allow_special_bus_removal()) {
7299 MessageDialog msg (_("That would be bad news ...."),
7303 msg.set_secondary_text (string_compose (_(
7304 "Removing the master or monitor bus is such a bad idea\n\
7305 that %1 is not going to allow it.\n\
7307 If you really want to do this sort of thing\n\
7308 edit your ardour.rc file to set the\n\
7309 \"allow-special-bus-removal\" option to be \"yes\""), PROGRAM_NAME));
7316 if (ntracks + nbusses == 0) {
7320 trackstr = P_("track", "tracks", ntracks);
7321 busstr = P_("bus", "busses", nbusses);
7325 prompt = string_compose (_("Do you really want to remove %1 %2 and %3 %4?\n"
7326 "(You may also lose the playlists associated with the %2)\n\n"
7327 "This action cannot be undone, and the session file will be overwritten!"),
7328 ntracks, trackstr, nbusses, busstr);
7330 prompt = string_compose (_("Do you really want to remove %1 %2?\n"
7331 "(You may also lose the playlists associated with the %2)\n\n"
7332 "This action cannot be undone, and the session file will be overwritten!"),
7335 } else if (nbusses) {
7336 prompt = string_compose (_("Do you really want to remove %1 %2?\n\n"
7337 "This action cannot be undone, and the session file will be overwritten"),
7341 choices.push_back (_("No, do nothing."));
7342 if (ntracks + nbusses > 1) {
7343 choices.push_back (_("Yes, remove them."));
7345 choices.push_back (_("Yes, remove it."));
7350 title = string_compose (_("Remove %1"), trackstr);
7352 title = string_compose (_("Remove %1"), busstr);
7355 Choice prompter (title, prompt, choices);
7357 if (prompter.run () != 1) {
7361 if (current_mixer_strip && routes.size () > 1 && std::find (routes.begin(), routes.end(), current_mixer_strip->route()) != routes.end ()) {
7362 /* Route deletion calls Editor::timeaxisview_deleted() iteratively (for each deleted
7363 * route). If the deleted route is currently displayed in the Editor-Mixer (highly
7364 * likely because deletion requires selection) this will call
7365 * Editor::set_selected_mixer_strip () which is expensive ( MixerStrip::set_route() ).
7366 * It's likewise likely that the route that has just been displayed in the
7367 * Editor-Mixer will be next in line for deletion.
7369 * So simply switch to the master-bus (if present)
7371 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
7372 if ((*i)->stripable ()->is_master ()) {
7373 set_selected_mixer_strip (*(*i));
7379 Mixer_UI::instance()->selection().block_routes_changed (true);
7380 selection->block_tracks_changed (true);
7382 DisplaySuspender ds;
7383 boost::shared_ptr<RouteList> rl (new RouteList);
7384 for (vector<boost::shared_ptr<Route> >::iterator x = routes.begin(); x != routes.end(); ++x) {
7387 _session->remove_routes (rl);
7389 /* TrackSelection and RouteList leave scope,
7390 * destructors are called,
7391 * diskstream drops references, save_state is called (again for every track)
7393 selection->block_tracks_changed (false);
7394 Mixer_UI::instance()->selection().block_routes_changed (false);
7395 selection->TracksChanged (); /* EMIT SIGNAL */
7399 Editor::do_insert_time ()
7401 if (selection->tracks.empty()) {
7405 InsertRemoveTimeDialog d (*this);
7406 int response = d.run ();
7408 if (response != RESPONSE_OK) {
7412 if (d.distance() == 0) {
7419 d.intersected_region_action (),
7423 d.move_glued_markers(),
7424 d.move_locked_markers(),
7430 Editor::insert_time (
7431 framepos_t pos, framecnt_t frames, InsertTimeOption opt,
7432 bool all_playlists, bool ignore_music_glue, bool markers_too, bool glued_markers_too, bool locked_markers_too, bool tempo_too
7436 if (Config->get_edit_mode() == Lock) {
7439 bool in_command = false;
7441 TrackViewList ts = selection->tracks.filter_to_unique_playlists ();
7443 for (TrackViewList::iterator x = ts.begin(); x != ts.end(); ++x) {
7447 /* don't operate on any playlist more than once, which could
7448 * happen if "all playlists" is enabled, but there is more
7449 * than 1 track using playlists "from" a given track.
7452 set<boost::shared_ptr<Playlist> > pl;
7454 if (all_playlists) {
7455 RouteTimeAxisView* rtav = dynamic_cast<RouteTimeAxisView*> (*x);
7456 if (rtav && rtav->track ()) {
7457 vector<boost::shared_ptr<Playlist> > all = _session->playlists->playlists_for_track (rtav->track ());
7458 for (vector<boost::shared_ptr<Playlist> >::iterator p = all.begin(); p != all.end(); ++p) {
7463 if ((*x)->playlist ()) {
7464 pl.insert ((*x)->playlist ());
7468 for (set<boost::shared_ptr<Playlist> >::iterator i = pl.begin(); i != pl.end(); ++i) {
7470 (*i)->clear_changes ();
7471 (*i)->clear_owned_changes ();
7473 if (opt == SplitIntersected) {
7474 /* non musical split */
7475 (*i)->split (pos, 0);
7478 (*i)->shift (pos, frames, (opt == MoveIntersected), ignore_music_glue);
7481 begin_reversible_command (_("insert time"));
7484 vector<Command*> cmds;
7486 _session->add_commands (cmds);
7488 _session->add_command (new StatefulDiffCommand (*i));
7492 RouteTimeAxisView* rtav = dynamic_cast<RouteTimeAxisView*> (*x);
7495 begin_reversible_command (_("insert time"));
7498 rtav->route ()->shift (pos, frames);
7505 const int32_t divisions = get_grid_music_divisions (0);
7506 XMLNode& before (_session->locations()->get_state());
7507 Locations::LocationList copy (_session->locations()->list());
7509 for (Locations::LocationList::iterator i = copy.begin(); i != copy.end(); ++i) {
7511 Locations::LocationList::const_iterator tmp;
7513 if ((*i)->position_lock_style() == AudioTime || glued_markers_too) {
7514 bool const was_locked = (*i)->locked ();
7515 if (locked_markers_too) {
7519 if ((*i)->start() >= pos) {
7520 // move end first, in case we're moving by more than the length of the range
7521 if (!(*i)->is_mark()) {
7522 (*i)->set_end ((*i)->end() + frames, false, true, divisions);
7524 (*i)->set_start ((*i)->start() + frames, false, true, divisions);
7536 begin_reversible_command (_("insert time"));
7539 XMLNode& after (_session->locations()->get_state());
7540 _session->add_command (new MementoCommand<Locations>(*_session->locations(), &before, &after));
7546 begin_reversible_command (_("insert time"));
7549 XMLNode& before (_session->tempo_map().get_state());
7550 _session->tempo_map().insert_time (pos, frames);
7551 XMLNode& after (_session->tempo_map().get_state());
7552 _session->add_command (new MementoCommand<TempoMap>(_session->tempo_map(), &before, &after));
7556 commit_reversible_command ();
7561 Editor::do_remove_time ()
7563 if (selection->tracks.empty()) {
7567 InsertRemoveTimeDialog d (*this, true);
7569 int response = d.run ();
7571 if (response != RESPONSE_OK) {
7575 framecnt_t distance = d.distance();
7577 if (distance == 0) {
7587 d.move_glued_markers(),
7588 d.move_locked_markers(),
7594 Editor::remove_time (framepos_t pos, framecnt_t frames, InsertTimeOption opt,
7595 bool ignore_music_glue, bool markers_too, bool glued_markers_too, bool locked_markers_too, bool tempo_too)
7597 if (Config->get_edit_mode() == Lock) {
7598 error << (_("Cannot insert or delete time when in Lock edit.")) << endmsg;
7601 bool in_command = false;
7603 for (TrackSelection::iterator x = selection->tracks.begin(); x != selection->tracks.end(); ++x) {
7605 boost::shared_ptr<Playlist> pl = (*x)->playlist();
7609 XMLNode &before = pl->get_state();
7611 std::list<AudioRange> rl;
7612 AudioRange ar(pos, pos+frames, 0);
7615 pl->shift (pos, -frames, true, ignore_music_glue);
7618 begin_reversible_command (_("remove time"));
7621 XMLNode &after = pl->get_state();
7623 _session->add_command (new MementoCommand<Playlist> (*pl, &before, &after));
7627 RouteTimeAxisView* rtav = dynamic_cast<RouteTimeAxisView*> (*x);
7630 begin_reversible_command (_("remove time"));
7633 rtav->route ()->shift (pos, -frames);
7637 const int32_t divisions = get_grid_music_divisions (0);
7638 std::list<Location*> loc_kill_list;
7643 XMLNode& before (_session->locations()->get_state());
7644 Locations::LocationList copy (_session->locations()->list());
7646 for (Locations::LocationList::iterator i = copy.begin(); i != copy.end(); ++i) {
7647 if ((*i)->position_lock_style() == AudioTime || glued_markers_too) {
7649 bool const was_locked = (*i)->locked ();
7650 if (locked_markers_too) {
7654 if (!(*i)->is_mark()) { // it's a range; have to handle both start and end
7655 if ((*i)->end() >= pos
7656 && (*i)->end() < pos+frames
7657 && (*i)->start() >= pos
7658 && (*i)->end() < pos+frames) { // range is completely enclosed; kill it
7660 loc_kill_list.push_back(*i);
7661 } else { // only start or end is included, try to do the right thing
7662 // move start before moving end, to avoid trying to move the end to before the start
7663 // if we're removing more time than the length of the range
7664 if ((*i)->start() >= pos && (*i)->start() < pos+frames) {
7665 // start is within cut
7666 (*i)->set_start (pos, false, true,divisions); // bring the start marker to the beginning of the cut
7668 } else if ((*i)->start() >= pos+frames) {
7669 // start (and thus entire range) lies beyond end of cut
7670 (*i)->set_start ((*i)->start() - frames, false, true, divisions); // slip the start marker back
7673 if ((*i)->end() >= pos && (*i)->end() < pos+frames) {
7674 // end is inside cut
7675 (*i)->set_end (pos, false, true, divisions); // bring the end to the cut
7677 } else if ((*i)->end() >= pos+frames) {
7678 // end is beyond end of cut
7679 (*i)->set_end ((*i)->end() - frames, false, true, divisions); // slip the end marker back
7684 } else if ((*i)->start() >= pos && (*i)->start() < pos+frames ) {
7685 loc_kill_list.push_back(*i);
7687 } else if ((*i)->start() >= pos) {
7688 (*i)->set_start ((*i)->start() -frames, false, true, divisions);
7698 for (list<Location*>::iterator i = loc_kill_list.begin(); i != loc_kill_list.end(); ++i) {
7699 _session->locations()->remove( *i );
7704 begin_reversible_command (_("remove time"));
7707 XMLNode& after (_session->locations()->get_state());
7708 _session->add_command (new MementoCommand<Locations>(*_session->locations(), &before, &after));
7713 XMLNode& before (_session->tempo_map().get_state());
7715 if (_session->tempo_map().remove_time (pos, frames) ) {
7717 begin_reversible_command (_("remove time"));
7720 XMLNode& after (_session->tempo_map().get_state());
7721 _session->add_command (new MementoCommand<TempoMap>(_session->tempo_map(), &before, &after));
7726 commit_reversible_command ();
7731 Editor::fit_selection ()
7733 if (!selection->tracks.empty()) {
7734 fit_tracks (selection->tracks);
7738 /* no selected tracks - use tracks with selected regions */
7740 if (!selection->regions.empty()) {
7741 for (RegionSelection::iterator r = selection->regions.begin(); r != selection->regions.end(); ++r) {
7742 tvl.push_back (&(*r)->get_time_axis_view ());
7748 } else if (internal_editing()) {
7749 /* no selected tracks, or regions, but in internal edit mode, so follow the mouse and use
7752 if (entered_track) {
7753 tvl.push_back (entered_track);
7762 Editor::fit_tracks (TrackViewList & tracks)
7764 if (tracks.empty()) {
7768 uint32_t child_heights = 0;
7769 int visible_tracks = 0;
7771 for (TrackSelection::iterator t = tracks.begin(); t != tracks.end(); ++t) {
7773 if (!(*t)->marked_for_display()) {
7777 child_heights += (*t)->effective_height() - (*t)->current_height();
7781 /* compute the per-track height from:
7783 total canvas visible height -
7784 height that will be taken by visible children of selected
7785 tracks - height of the ruler/hscroll area
7787 uint32_t h = (uint32_t) floor ((trackviews_height() - child_heights) / visible_tracks);
7788 double first_y_pos = DBL_MAX;
7790 if (h < TimeAxisView::preset_height (HeightSmall)) {
7791 MessageDialog msg (_("There are too many tracks to fit in the current window"));
7792 /* too small to be displayed */
7796 undo_visual_stack.push_back (current_visual_state (true));
7797 PBD::Unwinder<bool> nsv (no_save_visual, true);
7799 /* build a list of all tracks, including children */
7802 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
7804 TimeAxisView::Children c = (*i)->get_child_list ();
7805 for (TimeAxisView::Children::iterator j = c.begin(); j != c.end(); ++j) {
7806 all.push_back (j->get());
7811 // find selection range.
7812 // if someone knows how to user TrackViewList::iterator for this
7814 int selected_top = -1;
7815 int selected_bottom = -1;
7817 for (TrackViewList::iterator t = all.begin(); t != all.end(); ++t, ++i) {
7818 if ((*t)->marked_for_display ()) {
7819 if (tracks.contains(*t)) {
7820 if (selected_top == -1) {
7823 selected_bottom = i;
7829 for (TrackViewList::iterator t = all.begin(); t != all.end(); ++t, ++i) {
7830 if ((*t)->marked_for_display ()) {
7831 if (tracks.contains(*t)) {
7832 (*t)->set_height (h);
7833 first_y_pos = std::min ((*t)->y_position (), first_y_pos);
7835 if (i > selected_top && i < selected_bottom) {
7836 hide_track_in_display (*t);
7843 set the controls_layout height now, because waiting for its size
7844 request signal handler will cause the vertical adjustment setting to fail
7847 controls_layout.property_height () = _full_canvas_height;
7848 vertical_adjustment.set_value (first_y_pos);
7850 redo_visual_stack.push_back (current_visual_state (true));
7852 visible_tracks_selector.set_text (_("Sel"));
7856 Editor::save_visual_state (uint32_t n)
7858 while (visual_states.size() <= n) {
7859 visual_states.push_back (0);
7862 if (visual_states[n] != 0) {
7863 delete visual_states[n];
7866 visual_states[n] = current_visual_state (true);
7871 Editor::goto_visual_state (uint32_t n)
7873 if (visual_states.size() <= n) {
7877 if (visual_states[n] == 0) {
7881 use_visual_state (*visual_states[n]);
7885 Editor::start_visual_state_op (uint32_t n)
7887 save_visual_state (n);
7889 PopUp* pup = new PopUp (WIN_POS_MOUSE, 1000, true);
7891 snprintf (buf, sizeof (buf), _("Saved view %u"), n+1);
7892 pup->set_text (buf);
7897 Editor::cancel_visual_state_op (uint32_t n)
7899 goto_visual_state (n);
7903 Editor::toggle_region_mute ()
7905 if (_ignore_region_action) {
7909 RegionSelection rs = get_regions_from_selection_and_entered ();
7915 if (rs.size() > 1) {
7916 begin_reversible_command (_("mute regions"));
7918 begin_reversible_command (_("mute region"));
7921 for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
7923 (*i)->region()->playlist()->clear_changes ();
7924 (*i)->region()->set_muted (!(*i)->region()->muted ());
7925 _session->add_command (new StatefulDiffCommand ((*i)->region()));
7929 commit_reversible_command ();
7933 Editor::combine_regions ()
7935 /* foreach track with selected regions, take all selected regions
7936 and join them into a new region containing the subregions (as a
7940 typedef set<RouteTimeAxisView*> RTVS;
7943 if (selection->regions.empty()) {
7947 for (RegionSelection::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
7948 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*>(&(*i)->get_time_axis_view());
7951 tracks.insert (rtv);
7955 begin_reversible_command (_("combine regions"));
7957 vector<RegionView*> new_selection;
7959 for (RTVS::iterator i = tracks.begin(); i != tracks.end(); ++i) {
7962 if ((rv = (*i)->combine_regions ()) != 0) {
7963 new_selection.push_back (rv);
7967 selection->clear_regions ();
7968 for (vector<RegionView*>::iterator i = new_selection.begin(); i != new_selection.end(); ++i) {
7969 selection->add (*i);
7972 commit_reversible_command ();
7976 Editor::uncombine_regions ()
7978 typedef set<RouteTimeAxisView*> RTVS;
7981 if (selection->regions.empty()) {
7985 for (RegionSelection::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
7986 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*>(&(*i)->get_time_axis_view());
7989 tracks.insert (rtv);
7993 begin_reversible_command (_("uncombine regions"));
7995 for (RTVS::iterator i = tracks.begin(); i != tracks.end(); ++i) {
7996 (*i)->uncombine_regions ();
7999 commit_reversible_command ();
8003 Editor::toggle_midi_input_active (bool flip_others)
8006 boost::shared_ptr<RouteList> rl (new RouteList);
8008 for (TrackSelection::iterator i = selection->tracks.begin(); i != selection->tracks.end(); ++i) {
8009 RouteTimeAxisView *rtav = dynamic_cast<RouteTimeAxisView *>(*i);
8015 boost::shared_ptr<MidiTrack> mt = rtav->midi_track();
8018 rl->push_back (rtav->route());
8019 onoff = !mt->input_active();
8023 _session->set_exclusive_input_active (rl, onoff, flip_others);
8026 static bool ok_fine (GdkEventAny*) { return true; }
8032 lock_dialog = new Gtk::Dialog (string_compose (_("%1: Locked"), PROGRAM_NAME), true);
8034 Gtk::Image* padlock = manage (new Gtk::Image (ARDOUR_UI_UTILS::get_icon ("padlock_closed")));
8035 lock_dialog->get_vbox()->pack_start (*padlock);
8036 lock_dialog->signal_delete_event ().connect (sigc::ptr_fun (ok_fine));
8038 ArdourButton* b = manage (new ArdourButton);
8039 b->set_name ("lock button");
8040 b->set_text (_("Click to unlock"));
8041 b->signal_clicked.connect (sigc::mem_fun (*this, &Editor::unlock));
8042 lock_dialog->get_vbox()->pack_start (*b);
8044 lock_dialog->get_vbox()->show_all ();
8045 lock_dialog->set_size_request (200, 200);
8048 delete _main_menu_disabler;
8049 _main_menu_disabler = new MainMenuDisabler;
8051 lock_dialog->present ();
8053 lock_dialog->get_window()->set_decorations (Gdk::WMDecoration (0));
8059 lock_dialog->hide ();
8061 delete _main_menu_disabler;
8062 _main_menu_disabler = 0;
8064 if (UIConfiguration::instance().get_lock_gui_after_seconds()) {
8065 start_lock_event_timing ();
8070 Editor::bring_in_callback (Gtk::Label* label, uint32_t n, uint32_t total, string name)
8072 Gtkmm2ext::UI::instance()->call_slot (invalidator (*this), boost::bind (&Editor::update_bring_in_message, this, label, n, total, name));
8076 Editor::update_bring_in_message (Gtk::Label* label, uint32_t n, uint32_t total, string name)
8078 Timers::TimerSuspender t;
8079 label->set_text (string_compose ("Copying %1, %2 of %3", name, n, total));
8080 Gtkmm2ext::UI::instance()->flush_pending (1);
8084 Editor::bring_all_sources_into_session ()
8091 ArdourDialog w (_("Moving embedded files into session folder"));
8092 w.get_vbox()->pack_start (msg);
8095 /* flush all pending GUI events because we're about to start copying
8099 Timers::TimerSuspender t;
8100 Gtkmm2ext::UI::instance()->flush_pending (3);
8104 _session->bring_all_sources_into_session (boost::bind (&Editor::bring_in_callback, this, &msg, _1, _2, _3));