2 Copyright (C) 2000-2004 Paul Davis
4 This program is free software; you can redistribute it and/or modify
5 it under the terms of the GNU General Public License as published by
6 the Free Software Foundation; either version 2 of the License, or
7 (at your option) any later version.
9 This program is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 GNU General Public License for more details.
14 You should have received a copy of the GNU General Public License
15 along with this program; if not, write to the Free Software
16 Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
20 /* Note: public Editor methods are documented in public_editor.h */
31 #include "pbd/error.h"
32 #include "pbd/basename.h"
33 #include "pbd/pthread_utils.h"
34 #include "pbd/memento_command.h"
35 #include "pbd/unwind.h"
36 #include "pbd/whitespace.h"
37 #include "pbd/stateful_diff_command.h"
39 #include <gtkmm2ext/utils.h>
40 #include <gtkmm2ext/choice.h>
41 #include <gtkmm2ext/popup.h>
43 #include "ardour/audio_track.h"
44 #include "ardour/audioregion.h"
45 #include "ardour/boost_debug.h"
46 #include "ardour/dB.h"
47 #include "ardour/location.h"
48 #include "ardour/midi_region.h"
49 #include "ardour/midi_track.h"
50 #include "ardour/operations.h"
51 #include "ardour/playlist_factory.h"
52 #include "ardour/profile.h"
53 #include "ardour/quantize.h"
54 #include "ardour/legatize.h"
55 #include "ardour/region_factory.h"
56 #include "ardour/reverse.h"
57 #include "ardour/session.h"
58 #include "ardour/session_playlists.h"
59 #include "ardour/strip_silence.h"
60 #include "ardour/transient_detector.h"
61 #include "ardour/transpose.h"
63 #include "canvas/canvas.h"
66 #include "audio_region_view.h"
67 #include "audio_streamview.h"
68 #include "audio_time_axis.h"
69 #include "automation_region_view.h"
70 #include "automation_time_axis.h"
71 #include "control_point.h"
75 #include "editor_cursors.h"
76 #include "editor_drag.h"
77 #include "editor_regions.h"
78 #include "editor_routes.h"
79 #include "gui_thread.h"
80 #include "insert_remove_time_dialog.h"
81 #include "interthread_progress_window.h"
82 #include "item_counts.h"
84 #include "midi_region_view.h"
86 #include "mixer_strip.h"
87 #include "mouse_cursors.h"
88 #include "normalize_dialog.h"
90 #include "paste_context.h"
91 #include "patch_change_dialog.h"
92 #include "quantize_dialog.h"
93 #include "region_gain_line.h"
94 #include "rgb_macros.h"
95 #include "route_time_axis.h"
96 #include "selection.h"
97 #include "selection_templates.h"
98 #include "streamview.h"
99 #include "strip_silence_dialog.h"
100 #include "time_axis_view.h"
102 #include "transpose_dialog.h"
103 #include "transform_dialog.h"
104 #include "ui_config.h"
106 #include "pbd/i18n.h"
109 using namespace ARDOUR;
112 using namespace Gtkmm2ext;
113 using namespace Editing;
114 using Gtkmm2ext::Keyboard;
116 /***********************************************************************
118 ***********************************************************************/
121 Editor::undo (uint32_t n)
123 if (_session && _session->actively_recording()) {
124 /* no undo allowed while recording. Session will check also,
125 but we don't even want to get to that.
130 if (_drags->active ()) {
136 if (_session->undo_depth() == 0) {
137 undo_action->set_sensitive(false);
139 redo_action->set_sensitive(true);
140 begin_selection_op_history ();
145 Editor::redo (uint32_t n)
147 if (_session && _session->actively_recording()) {
148 /* no redo allowed while recording. Session will check also,
149 but we don't even want to get to that.
154 if (_drags->active ()) {
160 if (_session->redo_depth() == 0) {
161 redo_action->set_sensitive(false);
163 undo_action->set_sensitive(true);
164 begin_selection_op_history ();
169 Editor::split_regions_at (framepos_t where, RegionSelection& regions, const int32_t sub_num,
174 RegionSelection pre_selected_regions = selection->regions;
175 bool working_on_selection = !pre_selected_regions.empty();
177 list<boost::shared_ptr<Playlist> > used_playlists;
178 list<RouteTimeAxisView*> used_trackviews;
180 if (regions.empty()) {
184 begin_reversible_command (_("split"));
186 // if splitting a single region, and snap-to is using
187 // region boundaries, don't pay attention to them
189 if (regions.size() == 1) {
190 switch (_snap_type) {
191 case SnapToRegionStart:
192 case SnapToRegionSync:
193 case SnapToRegionEnd:
206 EditorFreeze(); /* Emit Signal */
209 for (RegionSelection::iterator a = regions.begin(); a != regions.end(); ) {
211 RegionSelection::iterator tmp;
213 /* XXX this test needs to be more complicated, to make sure we really
214 have something to split.
217 if (!(*a)->region()->covers (where)) {
225 boost::shared_ptr<Playlist> pl = (*a)->region()->playlist();
233 /* we haven't seen this playlist before */
235 /* remember used playlists so we can thaw them later */
236 used_playlists.push_back(pl);
238 TimeAxisView& tv = (*a)->get_time_axis_view();
239 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (&tv);
241 used_trackviews.push_back (rtv);
248 pl->clear_changes ();
249 pl->split_region ((*a)->region(), where, sub_num);
250 _session->add_command (new StatefulDiffCommand (pl));
256 latest_regionviews.clear ();
258 vector<sigc::connection> region_added_connections;
260 for (list<RouteTimeAxisView*>::iterator i = used_trackviews.begin(); i != used_trackviews.end(); ++i) {
261 region_added_connections.push_back ((*i)->view()->RegionViewAdded.connect (sigc::mem_fun(*this, &Editor::collect_new_region_view)));
264 while (used_playlists.size() > 0) {
265 list <boost::shared_ptr<Playlist > >::iterator i = used_playlists.begin();
267 used_playlists.pop_front();
270 for (vector<sigc::connection>::iterator c = region_added_connections.begin(); c != region_added_connections.end(); ++c) {
275 EditorThaw(); /* Emit Signal */
278 if (working_on_selection) {
279 // IFF we were working on selected regions, try to reinstate the other region selections that existed before the freeze/thaw.
281 RegionSelectionAfterSplit rsas = Config->get_region_selection_after_split();
282 /* There are three classes of regions that we might want selected after
283 splitting selected regions:
284 - regions selected before the split operation, and unaffected by it
285 - newly-created regions before the split
286 - newly-created regions after the split
289 if (rsas & Existing) {
290 // region selections that existed before the split.
291 selection->add ( pre_selected_regions );
294 for (RegionSelection::iterator ri = latest_regionviews.begin(); ri != latest_regionviews.end(); ri++) {
295 if ((*ri)->region()->position() < where) {
296 // new regions created before the split
297 if (rsas & NewlyCreatedLeft) {
298 selection->add (*ri);
301 // new regions created after the split
302 if (rsas & NewlyCreatedRight) {
303 selection->add (*ri);
308 if( working_on_selection ) {
309 selection->add (latest_regionviews); //these are the new regions created after the split
313 commit_reversible_command ();
316 /** Move one extreme of the current range selection. If more than one range is selected,
317 * the start of the earliest range or the end of the latest range is moved.
319 * @param move_end true to move the end of the current range selection, false to move
321 * @param next true to move the extreme to the next region boundary, false to move to
325 Editor::move_range_selection_start_or_end_to_region_boundary (bool move_end, bool next)
327 if (selection->time.start() == selection->time.end_frame()) {
331 framepos_t start = selection->time.start ();
332 framepos_t end = selection->time.end_frame ();
334 /* the position of the thing we may move */
335 framepos_t pos = move_end ? end : start;
336 int dir = next ? 1 : -1;
338 /* so we don't find the current region again */
339 if (dir > 0 || pos > 0) {
343 framepos_t const target = get_region_boundary (pos, dir, true, false);
358 begin_reversible_selection_op (_("alter selection"));
359 selection->set_preserving_all_ranges (start, end);
360 commit_reversible_selection_op ();
364 Editor::nudge_forward_release (GdkEventButton* ev)
366 if (ev->state & Keyboard::PrimaryModifier) {
367 nudge_forward (false, true);
369 nudge_forward (false, false);
375 Editor::nudge_backward_release (GdkEventButton* ev)
377 if (ev->state & Keyboard::PrimaryModifier) {
378 nudge_backward (false, true);
380 nudge_backward (false, false);
387 Editor::nudge_forward (bool next, bool force_playhead)
390 framepos_t next_distance;
396 RegionSelection rs = get_regions_from_selection_and_entered ();
398 if (!force_playhead && !rs.empty()) {
400 begin_reversible_command (_("nudge regions forward"));
402 for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
403 boost::shared_ptr<Region> r ((*i)->region());
405 distance = get_nudge_distance (r->position(), next_distance);
408 distance = next_distance;
412 r->set_position (r->position() + distance);
413 _session->add_command (new StatefulDiffCommand (r));
416 commit_reversible_command ();
419 } else if (!force_playhead && !selection->markers.empty()) {
422 bool in_command = false;
423 const int32_t divisions = get_grid_music_divisions (0);
425 for (MarkerSelection::iterator i = selection->markers.begin(); i != selection->markers.end(); ++i) {
427 Location* loc = find_location_from_marker ((*i), is_start);
431 XMLNode& before (loc->get_state());
434 distance = get_nudge_distance (loc->start(), next_distance);
436 distance = next_distance;
438 if (max_framepos - distance > loc->start() + loc->length()) {
439 loc->set_start (loc->start() + distance, false, true, divisions);
441 loc->set_start (max_framepos - loc->length(), false, true, divisions);
444 distance = get_nudge_distance (loc->end(), next_distance);
446 distance = next_distance;
448 if (max_framepos - distance > loc->end()) {
449 loc->set_end (loc->end() + distance, false, true, divisions);
451 loc->set_end (max_framepos, false, true, divisions);
453 if (loc->is_session_range()) {
454 _session->set_end_is_free (false);
458 begin_reversible_command (_("nudge location forward"));
461 XMLNode& after (loc->get_state());
462 _session->add_command (new MementoCommand<Location>(*loc, &before, &after));
467 commit_reversible_command ();
470 distance = get_nudge_distance (playhead_cursor->current_frame (), next_distance);
471 _session->request_locate (playhead_cursor->current_frame () + distance);
476 Editor::nudge_backward (bool next, bool force_playhead)
479 framepos_t next_distance;
485 RegionSelection rs = get_regions_from_selection_and_entered ();
487 if (!force_playhead && !rs.empty()) {
489 begin_reversible_command (_("nudge regions backward"));
491 for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
492 boost::shared_ptr<Region> r ((*i)->region());
494 distance = get_nudge_distance (r->position(), next_distance);
497 distance = next_distance;
502 if (r->position() > distance) {
503 r->set_position (r->position() - distance);
507 _session->add_command (new StatefulDiffCommand (r));
510 commit_reversible_command ();
512 } else if (!force_playhead && !selection->markers.empty()) {
515 bool in_command = false;
517 for (MarkerSelection::iterator i = selection->markers.begin(); i != selection->markers.end(); ++i) {
519 Location* loc = find_location_from_marker ((*i), is_start);
523 XMLNode& before (loc->get_state());
526 distance = get_nudge_distance (loc->start(), next_distance);
528 distance = next_distance;
530 if (distance < loc->start()) {
531 loc->set_start (loc->start() - distance, false, true, get_grid_music_divisions(0));
533 loc->set_start (0, false, true, get_grid_music_divisions(0));
536 distance = get_nudge_distance (loc->end(), next_distance);
539 distance = next_distance;
542 if (distance < loc->end() - loc->length()) {
543 loc->set_end (loc->end() - distance, false, true, get_grid_music_divisions(0));
545 loc->set_end (loc->length(), false, true, get_grid_music_divisions(0));
547 if (loc->is_session_range()) {
548 _session->set_end_is_free (false);
552 begin_reversible_command (_("nudge location forward"));
555 XMLNode& after (loc->get_state());
556 _session->add_command (new MementoCommand<Location>(*loc, &before, &after));
560 commit_reversible_command ();
565 distance = get_nudge_distance (playhead_cursor->current_frame (), next_distance);
567 if (playhead_cursor->current_frame () > distance) {
568 _session->request_locate (playhead_cursor->current_frame () - distance);
570 _session->goto_start();
576 Editor::nudge_forward_capture_offset ()
578 RegionSelection rs = get_regions_from_selection_and_entered ();
580 if (!_session || rs.empty()) {
584 begin_reversible_command (_("nudge forward"));
586 framepos_t const distance = _session->worst_output_latency();
588 for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
589 boost::shared_ptr<Region> r ((*i)->region());
592 r->set_position (r->position() + distance);
593 _session->add_command(new StatefulDiffCommand (r));
596 commit_reversible_command ();
600 Editor::nudge_backward_capture_offset ()
602 RegionSelection rs = get_regions_from_selection_and_entered ();
604 if (!_session || rs.empty()) {
608 begin_reversible_command (_("nudge backward"));
610 framepos_t const distance = _session->worst_output_latency();
612 for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
613 boost::shared_ptr<Region> r ((*i)->region());
617 if (r->position() > distance) {
618 r->set_position (r->position() - distance);
622 _session->add_command(new StatefulDiffCommand (r));
625 commit_reversible_command ();
628 struct RegionSelectionPositionSorter {
629 bool operator() (RegionView* a, RegionView* b) {
630 return a->region()->position() < b->region()->position();
635 Editor::sequence_regions ()
638 framepos_t r_end_prev;
646 RegionSelection rs = get_regions_from_selection_and_entered ();
647 rs.sort(RegionSelectionPositionSorter());
651 bool in_command = false;
653 for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
654 boost::shared_ptr<Region> r ((*i)->region());
662 if(r->position_locked())
669 r->set_position(r_end_prev);
673 begin_reversible_command (_("sequence regions"));
676 _session->add_command (new StatefulDiffCommand (r));
678 r_end=r->position() + r->length();
684 commit_reversible_command ();
693 Editor::move_to_start ()
695 _session->goto_start ();
699 Editor::move_to_end ()
702 _session->request_locate (_session->current_end_frame());
706 Editor::build_region_boundary_cache ()
709 vector<RegionPoint> interesting_points;
710 boost::shared_ptr<Region> r;
711 TrackViewList tracks;
714 region_boundary_cache.clear ();
720 switch (_snap_type) {
721 case SnapToRegionStart:
722 interesting_points.push_back (Start);
724 case SnapToRegionEnd:
725 interesting_points.push_back (End);
727 case SnapToRegionSync:
728 interesting_points.push_back (SyncPoint);
730 case SnapToRegionBoundary:
731 interesting_points.push_back (Start);
732 interesting_points.push_back (End);
735 fatal << string_compose (_("build_region_boundary_cache called with snap_type = %1"), _snap_type) << endmsg;
736 abort(); /*NOTREACHED*/
740 TimeAxisView *ontrack = 0;
743 if (!selection->tracks.empty()) {
744 tlist = selection->tracks.filter_to_unique_playlists ();
746 tlist = track_views.filter_to_unique_playlists ();
749 while (pos < _session->current_end_frame() && !at_end) {
752 framepos_t lpos = max_framepos;
754 for (vector<RegionPoint>::iterator p = interesting_points.begin(); p != interesting_points.end(); ++p) {
756 if ((r = find_next_region (pos, *p, 1, tlist, &ontrack)) == 0) {
757 if (*p == interesting_points.back()) {
760 /* move to next point type */
766 rpos = r->first_frame();
770 rpos = r->last_frame();
774 rpos = r->sync_position ();
782 RouteTimeAxisView *rtav;
784 if (ontrack != 0 && (rtav = dynamic_cast<RouteTimeAxisView*>(ontrack)) != 0 ) {
785 if (rtav->track() != 0) {
786 speed = rtav->track()->speed();
790 rpos = track_frame_to_session_frame (rpos, speed);
796 /* prevent duplicates, but we don't use set<> because we want to be able
800 vector<framepos_t>::iterator ri;
802 for (ri = region_boundary_cache.begin(); ri != region_boundary_cache.end(); ++ri) {
808 if (ri == region_boundary_cache.end()) {
809 region_boundary_cache.push_back (rpos);
816 /* finally sort to be sure that the order is correct */
818 sort (region_boundary_cache.begin(), region_boundary_cache.end());
821 boost::shared_ptr<Region>
822 Editor::find_next_region (framepos_t frame, RegionPoint point, int32_t dir, TrackViewList& tracks, TimeAxisView **ontrack)
824 TrackViewList::iterator i;
825 framepos_t closest = max_framepos;
826 boost::shared_ptr<Region> ret;
830 framepos_t track_frame;
831 RouteTimeAxisView *rtav;
833 for (i = tracks.begin(); i != tracks.end(); ++i) {
836 boost::shared_ptr<Region> r;
839 if ( (rtav = dynamic_cast<RouteTimeAxisView*>(*i)) != 0 ) {
840 if (rtav->track()!=0)
841 track_speed = rtav->track()->speed();
844 track_frame = session_frame_to_track_frame(frame, track_speed);
846 if ((r = (*i)->find_next_region (track_frame, point, dir)) == 0) {
852 rpos = r->first_frame ();
856 rpos = r->last_frame ();
860 rpos = r->sync_position ();
864 // rpos is a "track frame", converting it to "_session frame"
865 rpos = track_frame_to_session_frame(rpos, track_speed);
868 distance = rpos - frame;
870 distance = frame - rpos;
873 if (distance < closest) {
885 Editor::find_next_region_boundary (framepos_t pos, int32_t dir, const TrackViewList& tracks)
887 framecnt_t distance = max_framepos;
888 framepos_t current_nearest = -1;
890 for (TrackViewList::const_iterator i = tracks.begin(); i != tracks.end(); ++i) {
891 framepos_t contender;
894 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (*i);
900 if ((contender = rtv->find_next_region_boundary (pos, dir)) < 0) {
904 d = ::llabs (pos - contender);
907 current_nearest = contender;
912 return current_nearest;
916 Editor::get_region_boundary (framepos_t pos, int32_t dir, bool with_selection, bool only_onscreen)
921 if (with_selection && Config->get_region_boundaries_from_selected_tracks()) {
923 if (!selection->tracks.empty()) {
925 target = find_next_region_boundary (pos, dir, selection->tracks);
929 if (only_onscreen || Config->get_region_boundaries_from_onscreen_tracks()) {
930 get_onscreen_tracks (tvl);
931 target = find_next_region_boundary (pos, dir, tvl);
933 target = find_next_region_boundary (pos, dir, track_views);
939 if (only_onscreen || Config->get_region_boundaries_from_onscreen_tracks()) {
940 get_onscreen_tracks (tvl);
941 target = find_next_region_boundary (pos, dir, tvl);
943 target = find_next_region_boundary (pos, dir, track_views);
951 Editor::cursor_to_region_boundary (bool with_selection, int32_t dir)
953 framepos_t pos = playhead_cursor->current_frame ();
960 // so we don't find the current region again..
961 if (dir > 0 || pos > 0) {
965 if ((target = get_region_boundary (pos, dir, with_selection, false)) < 0) {
969 _session->request_locate (target);
973 Editor::cursor_to_next_region_boundary (bool with_selection)
975 cursor_to_region_boundary (with_selection, 1);
979 Editor::cursor_to_previous_region_boundary (bool with_selection)
981 cursor_to_region_boundary (with_selection, -1);
985 Editor::cursor_to_region_point (EditorCursor* cursor, RegionPoint point, int32_t dir)
987 boost::shared_ptr<Region> r;
988 framepos_t pos = cursor->current_frame ();
994 TimeAxisView *ontrack = 0;
996 // so we don't find the current region again..
1000 if (!selection->tracks.empty()) {
1002 r = find_next_region (pos, point, dir, selection->tracks, &ontrack);
1004 } else if (clicked_axisview) {
1007 t.push_back (clicked_axisview);
1009 r = find_next_region (pos, point, dir, t, &ontrack);
1013 r = find_next_region (pos, point, dir, track_views, &ontrack);
1022 pos = r->first_frame ();
1026 pos = r->last_frame ();
1030 pos = r->sync_position ();
1035 RouteTimeAxisView *rtav;
1037 if ( ontrack != 0 && (rtav = dynamic_cast<RouteTimeAxisView*>(ontrack)) != 0 ) {
1038 if (rtav->track() != 0) {
1039 speed = rtav->track()->speed();
1043 pos = track_frame_to_session_frame(pos, speed);
1045 if (cursor == playhead_cursor) {
1046 _session->request_locate (pos);
1048 cursor->set_position (pos);
1053 Editor::cursor_to_next_region_point (EditorCursor* cursor, RegionPoint point)
1055 cursor_to_region_point (cursor, point, 1);
1059 Editor::cursor_to_previous_region_point (EditorCursor* cursor, RegionPoint point)
1061 cursor_to_region_point (cursor, point, -1);
1065 Editor::cursor_to_selection_start (EditorCursor *cursor)
1069 switch (mouse_mode) {
1071 if (!selection->regions.empty()) {
1072 pos = selection->regions.start();
1077 if (!selection->time.empty()) {
1078 pos = selection->time.start ();
1086 if (cursor == playhead_cursor) {
1087 _session->request_locate (pos);
1089 cursor->set_position (pos);
1094 Editor::cursor_to_selection_end (EditorCursor *cursor)
1098 switch (mouse_mode) {
1100 if (!selection->regions.empty()) {
1101 pos = selection->regions.end_frame();
1106 if (!selection->time.empty()) {
1107 pos = selection->time.end_frame ();
1115 if (cursor == playhead_cursor) {
1116 _session->request_locate (pos);
1118 cursor->set_position (pos);
1123 Editor::selected_marker_to_region_boundary (bool with_selection, int32_t dir)
1133 if (selection->markers.empty()) {
1137 if (!mouse_frame (mouse, ignored)) {
1141 add_location_mark (mouse);
1144 if ((loc = find_location_from_marker (selection->markers.front(), ignored)) == 0) {
1148 framepos_t pos = loc->start();
1150 // so we don't find the current region again..
1151 if (dir > 0 || pos > 0) {
1155 if ((target = get_region_boundary (pos, dir, with_selection, false)) < 0) {
1159 loc->move_to (target, 0);
1163 Editor::selected_marker_to_next_region_boundary (bool with_selection)
1165 selected_marker_to_region_boundary (with_selection, 1);
1169 Editor::selected_marker_to_previous_region_boundary (bool with_selection)
1171 selected_marker_to_region_boundary (with_selection, -1);
1175 Editor::selected_marker_to_region_point (RegionPoint point, int32_t dir)
1177 boost::shared_ptr<Region> r;
1182 if (!_session || selection->markers.empty()) {
1186 if ((loc = find_location_from_marker (selection->markers.front(), ignored)) == 0) {
1190 TimeAxisView *ontrack = 0;
1194 // so we don't find the current region again..
1198 if (!selection->tracks.empty()) {
1200 r = find_next_region (pos, point, dir, selection->tracks, &ontrack);
1204 r = find_next_region (pos, point, dir, track_views, &ontrack);
1213 pos = r->first_frame ();
1217 pos = r->last_frame ();
1221 pos = r->adjust_to_sync (r->first_frame());
1226 RouteTimeAxisView *rtav;
1228 if (ontrack != 0 && (rtav = dynamic_cast<RouteTimeAxisView*>(ontrack)) != 0) {
1229 if (rtav->track() != 0) {
1230 speed = rtav->track()->speed();
1234 pos = track_frame_to_session_frame(pos, speed);
1236 loc->move_to (pos, 0);
1240 Editor::selected_marker_to_next_region_point (RegionPoint point)
1242 selected_marker_to_region_point (point, 1);
1246 Editor::selected_marker_to_previous_region_point (RegionPoint point)
1248 selected_marker_to_region_point (point, -1);
1252 Editor::selected_marker_to_selection_start ()
1258 if (!_session || selection->markers.empty()) {
1262 if ((loc = find_location_from_marker (selection->markers.front(), ignored)) == 0) {
1266 switch (mouse_mode) {
1268 if (!selection->regions.empty()) {
1269 pos = selection->regions.start();
1274 if (!selection->time.empty()) {
1275 pos = selection->time.start ();
1283 loc->move_to (pos, 0);
1287 Editor::selected_marker_to_selection_end ()
1293 if (!_session || selection->markers.empty()) {
1297 if ((loc = find_location_from_marker (selection->markers.front(), ignored)) == 0) {
1301 switch (mouse_mode) {
1303 if (!selection->regions.empty()) {
1304 pos = selection->regions.end_frame();
1309 if (!selection->time.empty()) {
1310 pos = selection->time.end_frame ();
1318 loc->move_to (pos, 0);
1322 Editor::scroll_playhead (bool forward)
1324 framepos_t pos = playhead_cursor->current_frame ();
1325 framecnt_t delta = (framecnt_t) floor (current_page_samples() / 0.8);
1328 if (pos == max_framepos) {
1332 if (pos < max_framepos - delta) {
1351 _session->request_locate (pos);
1355 Editor::cursor_align (bool playhead_to_edit)
1361 if (playhead_to_edit) {
1363 if (selection->markers.empty()) {
1367 _session->request_locate (selection->markers.front()->position(), _session->transport_rolling());
1370 const int32_t divisions = get_grid_music_divisions (0);
1371 /* move selected markers to playhead */
1373 for (MarkerSelection::iterator i = selection->markers.begin(); i != selection->markers.end(); ++i) {
1376 Location* loc = find_location_from_marker (*i, ignored);
1378 if (loc->is_mark()) {
1379 loc->set_start (playhead_cursor->current_frame (), false, true, divisions);
1381 loc->set (playhead_cursor->current_frame (),
1382 playhead_cursor->current_frame () + loc->length(), true, divisions);
1389 Editor::scroll_backward (float pages)
1391 framepos_t const one_page = (framepos_t) rint (_visible_canvas_width * samples_per_pixel);
1392 framepos_t const cnt = (framepos_t) floor (pages * one_page);
1395 if (leftmost_frame < cnt) {
1398 frame = leftmost_frame - cnt;
1401 reset_x_origin (frame);
1405 Editor::scroll_forward (float pages)
1407 framepos_t const one_page = (framepos_t) rint (_visible_canvas_width * samples_per_pixel);
1408 framepos_t const cnt = (framepos_t) floor (pages * one_page);
1411 if (max_framepos - cnt < leftmost_frame) {
1412 frame = max_framepos - cnt;
1414 frame = leftmost_frame + cnt;
1417 reset_x_origin (frame);
1421 Editor::scroll_tracks_down ()
1423 double vert_value = vertical_adjustment.get_value() + vertical_adjustment.get_page_size();
1424 if (vert_value > vertical_adjustment.get_upper() - _visible_canvas_height) {
1425 vert_value = vertical_adjustment.get_upper() - _visible_canvas_height;
1428 vertical_adjustment.set_value (vert_value);
1432 Editor::scroll_tracks_up ()
1434 vertical_adjustment.set_value (vertical_adjustment.get_value() - vertical_adjustment.get_page_size());
1438 Editor::scroll_tracks_down_line ()
1440 double vert_value = vertical_adjustment.get_value() + 60;
1442 if (vert_value > vertical_adjustment.get_upper() - _visible_canvas_height) {
1443 vert_value = vertical_adjustment.get_upper() - _visible_canvas_height;
1446 vertical_adjustment.set_value (vert_value);
1450 Editor::scroll_tracks_up_line ()
1452 reset_y_origin (vertical_adjustment.get_value() - 60);
1456 Editor::scroll_down_one_track (bool skip_child_views)
1458 TrackViewList::reverse_iterator next = track_views.rend();
1459 const double top_of_trackviews = vertical_adjustment.get_value();
1461 for (TrackViewList::reverse_iterator t = track_views.rbegin(); t != track_views.rend(); ++t) {
1462 if ((*t)->hidden()) {
1466 /* If this is the upper-most visible trackview, we want to display
1467 * the one above it (next)
1469 * Note that covers_y_position() is recursive and includes child views
1471 std::pair<TimeAxisView*,double> res = (*t)->covers_y_position (top_of_trackviews);
1474 if (skip_child_views) {
1477 /* automation lane (one level, non-recursive)
1479 * - if no automation lane exists -> move to next tack
1480 * - if the first (here: bottom-most) matches -> move to next tack
1481 * - if no y-axis match is found -> the current track is at the top
1482 * -> move to last (here: top-most) automation lane
1484 TimeAxisView::Children kids = (*t)->get_child_list();
1485 TimeAxisView::Children::reverse_iterator nkid = kids.rend();
1487 for (TimeAxisView::Children::reverse_iterator ci = kids.rbegin(); ci != kids.rend(); ++ci) {
1488 if ((*ci)->hidden()) {
1492 std::pair<TimeAxisView*,double> dev;
1493 dev = (*ci)->covers_y_position (top_of_trackviews);
1495 /* some automation lane is currently at the top */
1496 if (ci == kids.rbegin()) {
1497 /* first (bottom-most) autmation lane is at the top.
1498 * -> move to next track
1507 if (nkid != kids.rend()) {
1508 ensure_time_axis_view_is_visible (**nkid, true);
1516 /* move to the track below the first one that covers the */
1518 if (next != track_views.rend()) {
1519 ensure_time_axis_view_is_visible (**next, true);
1527 Editor::scroll_up_one_track (bool skip_child_views)
1529 TrackViewList::iterator prev = track_views.end();
1530 double top_of_trackviews = vertical_adjustment.get_value ();
1532 for (TrackViewList::iterator t = track_views.begin(); t != track_views.end(); ++t) {
1534 if ((*t)->hidden()) {
1538 /* find the trackview at the top of the trackview group
1540 * Note that covers_y_position() is recursive and includes child views
1542 std::pair<TimeAxisView*,double> res = (*t)->covers_y_position (top_of_trackviews);
1545 if (skip_child_views) {
1548 /* automation lane (one level, non-recursive)
1550 * - if no automation lane exists -> move to prev tack
1551 * - if no y-axis match is found -> the current track is at the top -> move to prev track
1552 * (actually last automation lane of previous track, see below)
1553 * - if first (top-most) lane is at the top -> move to this track
1554 * - else move up one lane
1556 TimeAxisView::Children kids = (*t)->get_child_list();
1557 TimeAxisView::Children::iterator pkid = kids.end();
1559 for (TimeAxisView::Children::iterator ci = kids.begin(); ci != kids.end(); ++ci) {
1560 if ((*ci)->hidden()) {
1564 std::pair<TimeAxisView*,double> dev;
1565 dev = (*ci)->covers_y_position (top_of_trackviews);
1567 /* some automation lane is currently at the top */
1568 if (ci == kids.begin()) {
1569 /* first (top-most) autmation lane is at the top.
1570 * jump directly to this track's top
1572 ensure_time_axis_view_is_visible (**t, true);
1575 else if (pkid != kids.end()) {
1576 /* some other automation lane is at the top.
1577 * move up to prev automation lane.
1579 ensure_time_axis_view_is_visible (**pkid, true);
1582 assert(0); // not reached
1593 if (prev != track_views.end()) {
1594 // move to bottom-most automation-lane of the previous track
1595 TimeAxisView::Children kids = (*prev)->get_child_list();
1596 TimeAxisView::Children::reverse_iterator pkid = kids.rend();
1597 if (!skip_child_views) {
1598 // find the last visible lane
1599 for (TimeAxisView::Children::reverse_iterator ci = kids.rbegin(); ci != kids.rend(); ++ci) {
1600 if (!(*ci)->hidden()) {
1606 if (pkid != kids.rend()) {
1607 ensure_time_axis_view_is_visible (**pkid, true);
1609 ensure_time_axis_view_is_visible (**prev, true);
1618 Editor::scroll_left_step ()
1620 framepos_t xdelta = (current_page_samples() / 8);
1622 if (leftmost_frame > xdelta) {
1623 reset_x_origin (leftmost_frame - xdelta);
1631 Editor::scroll_right_step ()
1633 framepos_t xdelta = (current_page_samples() / 8);
1635 if (max_framepos - xdelta > leftmost_frame) {
1636 reset_x_origin (leftmost_frame + xdelta);
1638 reset_x_origin (max_framepos - current_page_samples());
1643 Editor::scroll_left_half_page ()
1645 framepos_t xdelta = (current_page_samples() / 2);
1646 if (leftmost_frame > xdelta) {
1647 reset_x_origin (leftmost_frame - xdelta);
1654 Editor::scroll_right_half_page ()
1656 framepos_t xdelta = (current_page_samples() / 2);
1657 if (max_framepos - xdelta > leftmost_frame) {
1658 reset_x_origin (leftmost_frame + xdelta);
1660 reset_x_origin (max_framepos - current_page_samples());
1667 Editor::tav_zoom_step (bool coarser)
1669 DisplaySuspender ds;
1673 if (selection->tracks.empty()) {
1676 ts = &selection->tracks;
1679 for (TrackViewList::iterator i = ts->begin(); i != ts->end(); ++i) {
1680 TimeAxisView *tv = (static_cast<TimeAxisView*>(*i));
1681 tv->step_height (coarser);
1686 Editor::tav_zoom_smooth (bool coarser, bool force_all)
1688 DisplaySuspender ds;
1692 if (selection->tracks.empty() || force_all) {
1695 ts = &selection->tracks;
1698 for (TrackViewList::iterator i = ts->begin(); i != ts->end(); ++i) {
1699 TimeAxisView *tv = (static_cast<TimeAxisView*>(*i));
1700 uint32_t h = tv->current_height ();
1705 if (h >= TimeAxisView::preset_height (HeightSmall)) {
1710 tv->set_height (h + 5);
1716 Editor::temporal_zoom_step_mouse_focus_scale (bool zoom_out, double scale)
1718 Editing::ZoomFocus temp_focus = zoom_focus;
1719 zoom_focus = Editing::ZoomFocusMouse;
1720 temporal_zoom_step_scale (zoom_out, scale);
1721 zoom_focus = temp_focus;
1725 Editor::temporal_zoom_step_mouse_focus (bool zoom_out)
1727 temporal_zoom_step_mouse_focus_scale (zoom_out, 2.0);
1731 Editor::temporal_zoom_step (bool zoom_out)
1733 temporal_zoom_step_scale (zoom_out, 2.0);
1737 Editor::temporal_zoom_step_scale (bool zoom_out, double scale)
1739 ENSURE_GUI_THREAD (*this, &Editor::temporal_zoom_step, zoom_out, scale)
1741 framecnt_t nspp = samples_per_pixel;
1745 if (nspp == samples_per_pixel) {
1750 if (nspp == samples_per_pixel) {
1755 temporal_zoom (nspp);
1759 Editor::temporal_zoom (framecnt_t fpp)
1765 framepos_t current_page = current_page_samples();
1766 framepos_t current_leftmost = leftmost_frame;
1767 framepos_t current_rightmost;
1768 framepos_t current_center;
1769 framepos_t new_page_size;
1770 framepos_t half_page_size;
1771 framepos_t leftmost_after_zoom = 0;
1773 bool in_track_canvas;
1774 bool use_mouse_frame = true;
1778 if (fpp == samples_per_pixel) {
1782 // Imposing an arbitrary limit to zoom out as too much zoom out produces
1783 // segfaults for lack of memory. If somebody decides this is not high enough I
1784 // believe it can be raisen to higher values but some limit must be in place.
1786 // This constant represents 1 day @ 48kHz on a 1600 pixel wide display
1787 // all of which is used for the editor track displays. The whole day
1788 // would be 4147200000 samples, so 2592000 samples per pixel.
1790 nfpp = min (fpp, (framecnt_t) 2592000);
1791 nfpp = max ((framecnt_t) 1, nfpp);
1793 new_page_size = (framepos_t) floor (_visible_canvas_width * nfpp);
1794 half_page_size = new_page_size / 2;
1796 switch (zoom_focus) {
1798 leftmost_after_zoom = current_leftmost;
1801 case ZoomFocusRight:
1802 current_rightmost = leftmost_frame + current_page;
1803 if (current_rightmost < new_page_size) {
1804 leftmost_after_zoom = 0;
1806 leftmost_after_zoom = current_rightmost - new_page_size;
1810 case ZoomFocusCenter:
1811 current_center = current_leftmost + (current_page/2);
1812 if (current_center < half_page_size) {
1813 leftmost_after_zoom = 0;
1815 leftmost_after_zoom = current_center - half_page_size;
1819 case ZoomFocusPlayhead:
1820 /* centre playhead */
1821 l = playhead_cursor->current_frame () - (new_page_size * 0.5);
1824 leftmost_after_zoom = 0;
1825 } else if (l > max_framepos) {
1826 leftmost_after_zoom = max_framepos - new_page_size;
1828 leftmost_after_zoom = (framepos_t) l;
1832 case ZoomFocusMouse:
1833 /* try to keep the mouse over the same point in the display */
1835 if (_drags->active()) {
1836 where = _drags->current_pointer_frame ();
1837 } else if (!mouse_frame (where, in_track_canvas)) {
1838 use_mouse_frame = false;
1841 if (use_mouse_frame) {
1842 l = - ((new_page_size * ((where - current_leftmost)/(double)current_page)) - where);
1845 leftmost_after_zoom = 0;
1846 } else if (l > max_framepos) {
1847 leftmost_after_zoom = max_framepos - new_page_size;
1849 leftmost_after_zoom = (framepos_t) l;
1852 /* use playhead instead */
1853 where = playhead_cursor->current_frame ();
1855 if (where < half_page_size) {
1856 leftmost_after_zoom = 0;
1858 leftmost_after_zoom = where - half_page_size;
1864 /* try to keep the edit point in the same place */
1865 where = get_preferred_edit_position ();
1869 double l = - ((new_page_size * ((where - current_leftmost)/(double)current_page)) - where);
1872 leftmost_after_zoom = 0;
1873 } else if (l > max_framepos) {
1874 leftmost_after_zoom = max_framepos - new_page_size;
1876 leftmost_after_zoom = (framepos_t) l;
1880 /* edit point not defined */
1887 // leftmost_after_zoom = min (leftmost_after_zoom, _session->current_end_frame());
1889 reposition_and_zoom (leftmost_after_zoom, nfpp);
1893 Editor::calc_extra_zoom_edges(framepos_t &start, framepos_t &end)
1895 /* this func helps make sure we leave a little space
1896 at each end of the editor so that the zoom doesn't fit the region
1897 precisely to the screen.
1900 GdkScreen* screen = gdk_screen_get_default ();
1901 const gint pixwidth = gdk_screen_get_width (screen);
1902 const gint mmwidth = gdk_screen_get_width_mm (screen);
1903 const double pix_per_mm = (double) pixwidth/ (double) mmwidth;
1904 const double one_centimeter_in_pixels = pix_per_mm * 10.0;
1906 const framepos_t range = end - start;
1907 const framecnt_t new_fpp = (framecnt_t) ceil ((double) range / (double) _visible_canvas_width);
1908 const framepos_t extra_samples = (framepos_t) floor (one_centimeter_in_pixels * new_fpp);
1910 if (start > extra_samples) {
1911 start -= extra_samples;
1916 if (max_framepos - extra_samples > end) {
1917 end += extra_samples;
1924 Editor::get_selection_extents (framepos_t &start, framepos_t &end) const
1926 start = max_framepos;
1930 //ToDo: if notes are selected, set extents to that selection
1932 //ToDo: if control points are selected, set extents to that selection
1934 if ( !selection->regions.empty() ) {
1935 RegionSelection rs = get_regions_from_selection_and_entered ();
1937 for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
1939 if ((*i)->region()->position() < start) {
1940 start = (*i)->region()->position();
1943 if ((*i)->region()->last_frame() + 1 > end) {
1944 end = (*i)->region()->last_frame() + 1;
1948 } else if (!selection->time.empty()) {
1949 start = selection->time.start();
1950 end = selection->time.end_frame();
1952 ret = false; //no selection found
1955 if ((start == 0 && end == 0) || end < start) {
1964 Editor::temporal_zoom_selection (Editing::ZoomAxis axes)
1966 if (!selection) return;
1968 //ToDo: if notes are selected, zoom to that
1970 //ToDo: if control points are selected, zoom to that
1972 if (axes == Horizontal || axes == Both) {
1974 framepos_t start, end;
1975 if (get_selection_extents (start, end)) {
1976 calc_extra_zoom_edges (start, end);
1977 temporal_zoom_by_frame (start, end);
1981 if (axes == Vertical || axes == Both) {
1987 Editor::temporal_zoom_session ()
1989 ENSURE_GUI_THREAD (*this, &Editor::temporal_zoom_session)
1992 framecnt_t start = _session->current_start_frame();
1993 framecnt_t end = _session->current_end_frame();
1995 if (_session->actively_recording () ) {
1996 framepos_t cur = playhead_cursor->current_frame ();
1998 /* recording beyond the end marker; zoom out
1999 * by 5 seconds more so that if 'follow
2000 * playhead' is active we don't immediately
2003 end = cur + _session->frame_rate() * 5;
2007 if ((start == 0 && end == 0) || end < start) {
2011 calc_extra_zoom_edges(start, end);
2013 temporal_zoom_by_frame (start, end);
2018 Editor::temporal_zoom_by_frame (framepos_t start, framepos_t end)
2020 if (!_session) return;
2022 if ((start == 0 && end == 0) || end < start) {
2026 framepos_t range = end - start;
2028 const framecnt_t new_fpp = (framecnt_t) ceil ((double) range / (double) _visible_canvas_width);
2030 framepos_t new_page = range;
2031 framepos_t middle = (framepos_t) floor ((double) start + ((double) range / 2.0f));
2032 framepos_t new_leftmost = (framepos_t) floor ((double) middle - ((double) new_page / 2.0f));
2034 if (new_leftmost > middle) {
2038 if (new_leftmost < 0) {
2042 reposition_and_zoom (new_leftmost, new_fpp);
2046 Editor::temporal_zoom_to_frame (bool coarser, framepos_t frame)
2052 framecnt_t range_before = frame - leftmost_frame;
2056 if (samples_per_pixel <= 1) {
2059 new_spp = samples_per_pixel + (samples_per_pixel/2);
2061 range_before += range_before/2;
2063 if (samples_per_pixel >= 1) {
2064 new_spp = samples_per_pixel - (samples_per_pixel/2);
2066 /* could bail out here since we cannot zoom any finer,
2067 but leave that to the equality test below
2069 new_spp = samples_per_pixel;
2072 range_before -= range_before/2;
2075 if (new_spp == samples_per_pixel) {
2079 /* zoom focus is automatically taken as @param frame when this
2083 framepos_t new_leftmost = frame - (framepos_t)range_before;
2085 if (new_leftmost > frame) {
2089 if (new_leftmost < 0) {
2093 reposition_and_zoom (new_leftmost, new_spp);
2098 Editor::choose_new_marker_name(string &name) {
2100 if (!UIConfiguration::instance().get_name_new_markers()) {
2101 /* don't prompt user for a new name */
2105 ArdourPrompter dialog (true);
2107 dialog.set_prompt (_("New Name:"));
2109 dialog.set_title (_("New Location Marker"));
2111 dialog.set_name ("MarkNameWindow");
2112 dialog.set_size_request (250, -1);
2113 dialog.set_position (Gtk::WIN_POS_MOUSE);
2115 dialog.add_button (Stock::OK, RESPONSE_ACCEPT);
2116 dialog.set_initial_text (name);
2120 switch (dialog.run ()) {
2121 case RESPONSE_ACCEPT:
2127 dialog.get_result(name);
2134 Editor::add_location_from_selection ()
2138 if (selection->time.empty()) {
2142 if (_session == 0 || clicked_axisview == 0) {
2146 framepos_t start = selection->time[clicked_selection].start;
2147 framepos_t end = selection->time[clicked_selection].end;
2149 _session->locations()->next_available_name(rangename,"selection");
2150 Location *location = new Location (*_session, start, end, rangename, Location::IsRangeMarker, get_grid_music_divisions(0));
2152 begin_reversible_command (_("add marker"));
2154 XMLNode &before = _session->locations()->get_state();
2155 _session->locations()->add (location, true);
2156 XMLNode &after = _session->locations()->get_state();
2157 _session->add_command(new MementoCommand<Locations>(*(_session->locations()), &before, &after));
2159 commit_reversible_command ();
2163 Editor::add_location_mark (framepos_t where)
2167 select_new_marker = true;
2169 _session->locations()->next_available_name(markername,"mark");
2170 if (!choose_new_marker_name(markername)) {
2173 Location *location = new Location (*_session, where, where, markername, Location::IsMark, get_grid_music_divisions (0));
2174 begin_reversible_command (_("add marker"));
2176 XMLNode &before = _session->locations()->get_state();
2177 _session->locations()->add (location, true);
2178 XMLNode &after = _session->locations()->get_state();
2179 _session->add_command(new MementoCommand<Locations>(*(_session->locations()), &before, &after));
2181 commit_reversible_command ();
2185 Editor::set_session_start_from_playhead ()
2191 if ((loc = _session->locations()->session_range_location()) == 0) { //should never happen
2192 _session->set_session_extents ( _session->audible_frame(), _session->audible_frame() );
2194 XMLNode &before = loc->get_state();
2196 _session->set_session_extents ( _session->audible_frame(), loc->end() );
2198 XMLNode &after = loc->get_state();
2200 begin_reversible_command (_("Set session start"));
2202 _session->add_command (new MementoCommand<Location>(*loc, &before, &after));
2204 commit_reversible_command ();
2209 Editor::set_session_end_from_playhead ()
2215 if ((loc = _session->locations()->session_range_location()) == 0) { //should never happen
2216 _session->set_session_extents ( _session->audible_frame(), _session->audible_frame() );
2218 XMLNode &before = loc->get_state();
2220 _session->set_session_extents ( loc->start(), _session->audible_frame() );
2222 XMLNode &after = loc->get_state();
2224 begin_reversible_command (_("Set session start"));
2226 _session->add_command (new MementoCommand<Location>(*loc, &before, &after));
2228 commit_reversible_command ();
2231 _session->set_end_is_free (false);
2236 Editor::toggle_location_at_playhead_cursor ()
2238 if (!do_remove_location_at_playhead_cursor())
2240 add_location_from_playhead_cursor();
2245 Editor::add_location_from_playhead_cursor ()
2247 add_location_mark (_session->audible_frame());
2251 Editor::do_remove_location_at_playhead_cursor ()
2253 bool removed = false;
2256 XMLNode &before = _session->locations()->get_state();
2258 //find location(s) at this time
2259 Locations::LocationList locs;
2260 _session->locations()->find_all_between (_session->audible_frame(), _session->audible_frame()+1, locs, Location::Flags(0));
2261 for (Locations::LocationList::iterator i = locs.begin(); i != locs.end(); ++i) {
2262 if ((*i)->is_mark()) {
2263 _session->locations()->remove (*i);
2270 begin_reversible_command (_("remove marker"));
2271 XMLNode &after = _session->locations()->get_state();
2272 _session->add_command(new MementoCommand<Locations>(*(_session->locations()), &before, &after));
2273 commit_reversible_command ();
2280 Editor::remove_location_at_playhead_cursor ()
2282 do_remove_location_at_playhead_cursor ();
2285 /** Add a range marker around each selected region */
2287 Editor::add_locations_from_region ()
2289 RegionSelection rs = get_regions_from_selection_and_entered ();
2294 bool commit = false;
2296 XMLNode &before = _session->locations()->get_state();
2298 for (RegionSelection::iterator i = rs.begin (); i != rs.end (); ++i) {
2300 boost::shared_ptr<Region> region = (*i)->region ();
2302 Location *location = new Location (*_session, region->position(), region->last_frame(), region->name(), Location::IsRangeMarker, 0);
2304 _session->locations()->add (location, true);
2309 begin_reversible_command (selection->regions.size () > 1 ? _("add markers") : _("add marker"));
2310 XMLNode &after = _session->locations()->get_state();
2311 _session->add_command (new MementoCommand<Locations>(*(_session->locations()), &before, &after));
2312 commit_reversible_command ();
2316 /** Add a single range marker around all selected regions */
2318 Editor::add_location_from_region ()
2320 RegionSelection rs = get_regions_from_selection_and_entered ();
2326 XMLNode &before = _session->locations()->get_state();
2330 if (rs.size() > 1) {
2331 _session->locations()->next_available_name(markername, "regions");
2333 RegionView* rv = *(rs.begin());
2334 boost::shared_ptr<Region> region = rv->region();
2335 markername = region->name();
2338 if (!choose_new_marker_name(markername)) {
2342 // single range spanning all selected
2343 Location *location = new Location (*_session, selection->regions.start(), selection->regions.end_frame(), markername, Location::IsRangeMarker, 0);
2344 _session->locations()->add (location, true);
2346 begin_reversible_command (_("add marker"));
2347 XMLNode &after = _session->locations()->get_state();
2348 _session->add_command (new MementoCommand<Locations>(*(_session->locations()), &before, &after));
2349 commit_reversible_command ();
2355 Editor::jump_forward_to_mark ()
2361 framepos_t pos = _session->locations()->first_mark_after (playhead_cursor->current_frame());
2367 _session->request_locate (pos, _session->transport_rolling());
2371 Editor::jump_backward_to_mark ()
2377 framepos_t pos = _session->locations()->first_mark_before (playhead_cursor->current_frame());
2379 //handle the case where we are rolling, and we're less than one-half second past the mark, we want to go to the prior mark...
2380 if ( _session->transport_rolling() ) {
2381 if ( (playhead_cursor->current_frame() - pos) < _session->frame_rate()/2 ) {
2382 framepos_t prior = _session->locations()->first_mark_before ( pos );
2391 _session->request_locate (pos, _session->transport_rolling());
2397 framepos_t const pos = _session->audible_frame ();
2400 _session->locations()->next_available_name (markername, "mark");
2402 if (!choose_new_marker_name (markername)) {
2406 _session->locations()->add (new Location (*_session, pos, 0, markername, Location::IsMark, 0), true);
2410 Editor::clear_markers ()
2413 begin_reversible_command (_("clear markers"));
2415 XMLNode &before = _session->locations()->get_state();
2416 _session->locations()->clear_markers ();
2417 XMLNode &after = _session->locations()->get_state();
2418 _session->add_command(new MementoCommand<Locations>(*(_session->locations()), &before, &after));
2420 commit_reversible_command ();
2425 Editor::clear_ranges ()
2428 begin_reversible_command (_("clear ranges"));
2430 XMLNode &before = _session->locations()->get_state();
2432 _session->locations()->clear_ranges ();
2434 XMLNode &after = _session->locations()->get_state();
2435 _session->add_command(new MementoCommand<Locations>(*(_session->locations()), &before, &after));
2437 commit_reversible_command ();
2442 Editor::clear_locations ()
2444 begin_reversible_command (_("clear locations"));
2446 XMLNode &before = _session->locations()->get_state();
2447 _session->locations()->clear ();
2448 XMLNode &after = _session->locations()->get_state();
2449 _session->add_command(new MementoCommand<Locations>(*(_session->locations()), &before, &after));
2451 commit_reversible_command ();
2455 Editor::unhide_markers ()
2457 for (LocationMarkerMap::iterator i = location_markers.begin(); i != location_markers.end(); ++i) {
2458 Location *l = (*i).first;
2459 if (l->is_hidden() && l->is_mark()) {
2460 l->set_hidden(false, this);
2466 Editor::unhide_ranges ()
2468 for (LocationMarkerMap::iterator i = location_markers.begin(); i != location_markers.end(); ++i) {
2469 Location *l = (*i).first;
2470 if (l->is_hidden() && l->is_range_marker()) {
2471 l->set_hidden(false, this);
2476 /* INSERT/REPLACE */
2479 Editor::insert_region_list_selection (float times)
2481 RouteTimeAxisView *tv = 0;
2482 boost::shared_ptr<Playlist> playlist;
2484 if (clicked_routeview != 0) {
2485 tv = clicked_routeview;
2486 } else if (!selection->tracks.empty()) {
2487 if ((tv = dynamic_cast<RouteTimeAxisView*>(selection->tracks.front())) == 0) {
2490 } else if (entered_track != 0) {
2491 if ((tv = dynamic_cast<RouteTimeAxisView*>(entered_track)) == 0) {
2498 if ((playlist = tv->playlist()) == 0) {
2502 boost::shared_ptr<Region> region = _regions->get_single_selection ();
2507 begin_reversible_command (_("insert region"));
2508 playlist->clear_changes ();
2509 playlist->add_region ((RegionFactory::create (region, true)), get_preferred_edit_position(), times);
2510 if (Config->get_edit_mode() == Ripple)
2511 playlist->ripple (get_preferred_edit_position(), region->length() * times, boost::shared_ptr<Region>());
2513 _session->add_command(new StatefulDiffCommand (playlist));
2514 commit_reversible_command ();
2517 /* BUILT-IN EFFECTS */
2520 Editor::reverse_selection ()
2525 /* GAIN ENVELOPE EDITING */
2528 Editor::edit_envelope ()
2535 Editor::transition_to_rolling (bool fwd)
2541 if (_session->config.get_external_sync()) {
2542 switch (Config->get_sync_source()) {
2546 /* transport controlled by the master */
2551 if (_session->is_auditioning()) {
2552 _session->cancel_audition ();
2556 _session->request_transport_speed (fwd ? 1.0f : -1.0f);
2560 Editor::play_from_start ()
2562 _session->request_locate (_session->current_start_frame(), true);
2566 Editor::play_from_edit_point ()
2568 _session->request_locate (get_preferred_edit_position(), true);
2572 Editor::play_from_edit_point_and_return ()
2574 framepos_t start_frame;
2575 framepos_t return_frame;
2577 start_frame = get_preferred_edit_position ( EDIT_IGNORE_PHEAD );
2579 if (_session->transport_rolling()) {
2580 _session->request_locate (start_frame, false);
2584 /* don't reset the return frame if its already set */
2586 if ((return_frame = _session->requested_return_frame()) < 0) {
2587 return_frame = _session->audible_frame();
2590 if (start_frame >= 0) {
2591 _session->request_roll_at_and_return (start_frame, return_frame);
2596 Editor::play_selection ()
2598 framepos_t start, end;
2599 if (!get_selection_extents ( start, end))
2602 AudioRange ar (start, end, 0);
2603 list<AudioRange> lar;
2606 _session->request_play_range (&lar, true);
2611 Editor::maybe_locate_with_edit_preroll (framepos_t location)
2613 if ( _session->transport_rolling() || !UIConfiguration::instance().get_follow_edits() || _session->config.get_external_sync() )
2616 location -= _session->preroll_samples (location);
2618 //don't try to locate before the beginning of time
2623 //if follow_playhead is on, keep the playhead on the screen
2624 if ( _follow_playhead )
2625 if ( location < leftmost_frame )
2626 location = leftmost_frame;
2628 _session->request_locate( location );
2632 Editor::play_with_preroll ()
2634 framepos_t start, end;
2635 if ( UIConfiguration::instance().get_follow_edits() && get_selection_extents ( start, end) ) {
2636 const framepos_t preroll = _session->preroll_samples (start);
2638 framepos_t ret = start;
2640 if (start > preroll) {
2641 start = start - preroll;
2644 end = end + preroll; //"post-roll"
2646 AudioRange ar (start, end, 0);
2647 list<AudioRange> lar;
2650 _session->request_play_range (&lar, true);
2651 _session->set_requested_return_frame (ret); //force auto-return to return to range start, without the preroll
2653 framepos_t ph = playhead_cursor->current_frame ();
2654 const framepos_t preroll = _session->preroll_samples (ph);
2657 start = ph - preroll;
2661 _session->request_locate (start, true);
2662 _session->set_requested_return_frame (ph); //force auto-return to return to playhead location, without the preroll
2667 Editor::rec_with_preroll ()
2669 framepos_t ph = playhead_cursor->current_frame ();
2670 framepos_t preroll = _session->preroll_samples (ph);
2671 _session->request_preroll_record_trim (ph, preroll);
2676 Editor::play_location (Location& location)
2678 if (location.start() <= location.end()) {
2682 _session->request_bounded_roll (location.start(), location.end());
2686 Editor::loop_location (Location& location)
2688 if (location.start() <= location.end()) {
2694 if ((tll = transport_loop_location()) != 0) {
2695 tll->set (location.start(), location.end());
2697 // enable looping, reposition and start rolling
2698 _session->request_locate (tll->start(), true);
2699 _session->request_play_loop (true);
2704 Editor::do_layer_operation (LayerOperation op)
2706 if (selection->regions.empty ()) {
2710 bool const multiple = selection->regions.size() > 1;
2714 begin_reversible_command (_("raise regions"));
2716 begin_reversible_command (_("raise region"));
2722 begin_reversible_command (_("raise regions to top"));
2724 begin_reversible_command (_("raise region to top"));
2730 begin_reversible_command (_("lower regions"));
2732 begin_reversible_command (_("lower region"));
2738 begin_reversible_command (_("lower regions to bottom"));
2740 begin_reversible_command (_("lower region"));
2745 set<boost::shared_ptr<Playlist> > playlists = selection->regions.playlists ();
2746 for (set<boost::shared_ptr<Playlist> >::iterator i = playlists.begin(); i != playlists.end(); ++i) {
2747 (*i)->clear_owned_changes ();
2750 for (RegionSelection::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
2751 boost::shared_ptr<Region> r = (*i)->region ();
2763 r->lower_to_bottom ();
2767 for (set<boost::shared_ptr<Playlist> >::iterator i = playlists.begin(); i != playlists.end(); ++i) {
2768 vector<Command*> cmds;
2770 _session->add_commands (cmds);
2773 commit_reversible_command ();
2777 Editor::raise_region ()
2779 do_layer_operation (Raise);
2783 Editor::raise_region_to_top ()
2785 do_layer_operation (RaiseToTop);
2789 Editor::lower_region ()
2791 do_layer_operation (Lower);
2795 Editor::lower_region_to_bottom ()
2797 do_layer_operation (LowerToBottom);
2800 /** Show the region editor for the selected regions */
2802 Editor::show_region_properties ()
2804 selection->foreach_regionview (&RegionView::show_region_editor);
2807 /** Show the midi list editor for the selected MIDI regions */
2809 Editor::show_midi_list_editor ()
2811 selection->foreach_midi_regionview (&MidiRegionView::show_list_editor);
2815 Editor::rename_region ()
2817 RegionSelection rs = get_regions_from_selection_and_entered ();
2823 ArdourDialog d (_("Rename Region"), true, false);
2825 Label label (_("New name:"));
2828 hbox.set_spacing (6);
2829 hbox.pack_start (label, false, false);
2830 hbox.pack_start (entry, true, true);
2832 d.get_vbox()->set_border_width (12);
2833 d.get_vbox()->pack_start (hbox, false, false);
2835 d.add_button(Gtk::Stock::CANCEL, Gtk::RESPONSE_CANCEL);
2836 d.add_button(Gtk::Stock::OK, Gtk::RESPONSE_OK);
2838 d.set_size_request (300, -1);
2840 entry.set_text (rs.front()->region()->name());
2841 entry.select_region (0, -1);
2843 entry.signal_activate().connect (sigc::bind (sigc::mem_fun (d, &Dialog::response), RESPONSE_OK));
2849 int const ret = d.run();
2853 if (ret != RESPONSE_OK) {
2857 std::string str = entry.get_text();
2858 strip_whitespace_edges (str);
2860 rs.front()->region()->set_name (str);
2861 _regions->redisplay ();
2865 /** Start an audition of the first selected region */
2867 Editor::play_edit_range ()
2869 framepos_t start, end;
2871 if (get_edit_op_range (start, end)) {
2872 _session->request_bounded_roll (start, end);
2877 Editor::play_selected_region ()
2879 framepos_t start = max_framepos;
2882 RegionSelection rs = get_regions_from_selection_and_entered ();
2888 for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
2889 if ((*i)->region()->position() < start) {
2890 start = (*i)->region()->position();
2892 if ((*i)->region()->last_frame() + 1 > end) {
2893 end = (*i)->region()->last_frame() + 1;
2897 _session->request_bounded_roll (start, end);
2901 Editor::audition_playlist_region_standalone (boost::shared_ptr<Region> region)
2903 _session->audition_region (region);
2907 Editor::region_from_selection ()
2909 if (clicked_axisview == 0) {
2913 if (selection->time.empty()) {
2917 framepos_t start = selection->time[clicked_selection].start;
2918 framepos_t end = selection->time[clicked_selection].end;
2920 TrackViewList tracks = get_tracks_for_range_action ();
2922 framepos_t selection_cnt = end - start + 1;
2924 for (TrackSelection::iterator i = tracks.begin(); i != tracks.end(); ++i) {
2925 boost::shared_ptr<Region> current;
2926 boost::shared_ptr<Playlist> pl;
2927 framepos_t internal_start;
2930 if ((pl = (*i)->playlist()) == 0) {
2934 if ((current = pl->top_region_at (start)) == 0) {
2938 internal_start = start - current->position();
2939 RegionFactory::region_name (new_name, current->name(), true);
2943 plist.add (ARDOUR::Properties::start, current->start() + internal_start);
2944 plist.add (ARDOUR::Properties::length, selection_cnt);
2945 plist.add (ARDOUR::Properties::name, new_name);
2946 plist.add (ARDOUR::Properties::layer, 0);
2948 boost::shared_ptr<Region> region (RegionFactory::create (current, plist));
2953 Editor::create_region_from_selection (vector<boost::shared_ptr<Region> >& new_regions)
2955 if (selection->time.empty() || selection->tracks.empty()) {
2959 framepos_t start, end;
2960 if (clicked_selection) {
2961 start = selection->time[clicked_selection].start;
2962 end = selection->time[clicked_selection].end;
2964 start = selection->time.start();
2965 end = selection->time.end_frame();
2968 TrackViewList ts = selection->tracks.filter_to_unique_playlists ();
2969 sort_track_selection (ts);
2971 for (TrackSelection::iterator i = ts.begin(); i != ts.end(); ++i) {
2972 boost::shared_ptr<Region> current;
2973 boost::shared_ptr<Playlist> playlist;
2974 framepos_t internal_start;
2977 if ((playlist = (*i)->playlist()) == 0) {
2981 if ((current = playlist->top_region_at(start)) == 0) {
2985 internal_start = start - current->position();
2986 RegionFactory::region_name (new_name, current->name(), true);
2990 plist.add (ARDOUR::Properties::start, current->start() + internal_start);
2991 plist.add (ARDOUR::Properties::length, end - start + 1);
2992 plist.add (ARDOUR::Properties::name, new_name);
2994 new_regions.push_back (RegionFactory::create (current, plist));
2999 Editor::split_multichannel_region ()
3001 RegionSelection rs = get_regions_from_selection_and_entered ();
3007 vector< boost::shared_ptr<Region> > v;
3009 for (list<RegionView*>::iterator x = rs.begin(); x != rs.end(); ++x) {
3010 (*x)->region()->separate_by_channel (*_session, v);
3015 Editor::new_region_from_selection ()
3017 region_from_selection ();
3018 cancel_selection ();
3022 add_if_covered (RegionView* rv, const AudioRange* ar, RegionSelection* rs)
3024 switch (rv->region()->coverage (ar->start, ar->end - 1)) {
3025 // n.b. -1 because AudioRange::end is one past the end, but coverage expects inclusive ranges
3026 case Evoral::OverlapNone:
3034 * - selected tracks, or if there are none...
3035 * - tracks containing selected regions, or if there are none...
3040 Editor::get_tracks_for_range_action () const
3044 if (selection->tracks.empty()) {
3046 /* use tracks with selected regions */
3048 RegionSelection rs = selection->regions;
3050 for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
3051 TimeAxisView* tv = &(*i)->get_time_axis_view();
3053 if (!t.contains (tv)) {
3059 /* no regions and no tracks: use all tracks */
3065 t = selection->tracks;
3068 return t.filter_to_unique_playlists();
3072 Editor::separate_regions_between (const TimeSelection& ts)
3074 bool in_command = false;
3075 boost::shared_ptr<Playlist> playlist;
3076 RegionSelection new_selection;
3078 TrackViewList tmptracks = get_tracks_for_range_action ();
3079 sort_track_selection (tmptracks);
3081 for (TrackSelection::iterator i = tmptracks.begin(); i != tmptracks.end(); ++i) {
3083 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> ((*i));
3089 if (!rtv->is_track()) {
3093 /* no edits to destructive tracks */
3095 if (rtv->track()->destructive()) {
3099 if ((playlist = rtv->playlist()) != 0) {
3101 playlist->clear_changes ();
3103 /* XXX need to consider musical time selections here at some point */
3105 double speed = rtv->track()->speed();
3107 for (list<AudioRange>::const_iterator t = ts.begin(); t != ts.end(); ++t) {
3109 sigc::connection c = rtv->view()->RegionViewAdded.connect (
3110 sigc::mem_fun(*this, &Editor::collect_new_region_view));
3112 latest_regionviews.clear ();
3114 playlist->partition ((framepos_t)((*t).start * speed),
3115 (framepos_t)((*t).end * speed), false);
3119 if (!latest_regionviews.empty()) {
3121 rtv->view()->foreach_regionview (sigc::bind (
3122 sigc::ptr_fun (add_if_covered),
3123 &(*t), &new_selection));
3126 begin_reversible_command (_("separate"));
3130 /* pick up changes to existing regions */
3132 vector<Command*> cmds;
3133 playlist->rdiff (cmds);
3134 _session->add_commands (cmds);
3136 /* pick up changes to the playlist itself (adds/removes)
3139 _session->add_command(new StatefulDiffCommand (playlist));
3146 // selection->set (new_selection);
3148 commit_reversible_command ();
3152 struct PlaylistState {
3153 boost::shared_ptr<Playlist> playlist;
3157 /** Take tracks from get_tracks_for_range_action and cut any regions
3158 * on those tracks so that the tracks are empty over the time
3162 Editor::separate_region_from_selection ()
3164 /* preferentially use *all* ranges in the time selection if we're in range mode
3165 to allow discontiguous operation, since get_edit_op_range() currently
3166 returns a single range.
3169 if (!selection->time.empty()) {
3171 separate_regions_between (selection->time);
3178 if (get_edit_op_range (start, end)) {
3180 AudioRange ar (start, end, 1);
3184 separate_regions_between (ts);
3190 Editor::separate_region_from_punch ()
3192 Location* loc = _session->locations()->auto_punch_location();
3194 separate_regions_using_location (*loc);
3199 Editor::separate_region_from_loop ()
3201 Location* loc = _session->locations()->auto_loop_location();
3203 separate_regions_using_location (*loc);
3208 Editor::separate_regions_using_location (Location& loc)
3210 if (loc.is_mark()) {
3214 AudioRange ar (loc.start(), loc.end(), 1);
3219 separate_regions_between (ts);
3222 /** Separate regions under the selected region */
3224 Editor::separate_under_selected_regions ()
3226 vector<PlaylistState> playlists;
3230 rs = get_regions_from_selection_and_entered();
3232 if (!_session || rs.empty()) {
3236 begin_reversible_command (_("separate region under"));
3238 list<boost::shared_ptr<Region> > regions_to_remove;
3240 for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
3241 // we can't just remove the region(s) in this loop because
3242 // this removes them from the RegionSelection, and they thus
3243 // disappear from underneath the iterator, and the ++i above
3244 // SEGVs in a puzzling fashion.
3246 // so, first iterate over the regions to be removed from rs and
3247 // add them to the regions_to_remove list, and then
3248 // iterate over the list to actually remove them.
3250 regions_to_remove.push_back ((*i)->region());
3253 for (list<boost::shared_ptr<Region> >::iterator rl = regions_to_remove.begin(); rl != regions_to_remove.end(); ++rl) {
3255 boost::shared_ptr<Playlist> playlist = (*rl)->playlist();
3258 // is this check necessary?
3262 vector<PlaylistState>::iterator i;
3264 //only take state if this is a new playlist.
3265 for (i = playlists.begin(); i != playlists.end(); ++i) {
3266 if ((*i).playlist == playlist) {
3271 if (i == playlists.end()) {
3273 PlaylistState before;
3274 before.playlist = playlist;
3275 before.before = &playlist->get_state();
3277 playlist->freeze ();
3278 playlists.push_back(before);
3281 //Partition on the region bounds
3282 playlist->partition ((*rl)->first_frame() - 1, (*rl)->last_frame() + 1, true);
3284 //Re-add region that was just removed due to the partition operation
3285 playlist->add_region( (*rl), (*rl)->first_frame() );
3288 vector<PlaylistState>::iterator pl;
3290 for (pl = playlists.begin(); pl != playlists.end(); ++pl) {
3291 (*pl).playlist->thaw ();
3292 _session->add_command(new MementoCommand<Playlist>(*(*pl).playlist, (*pl).before, &(*pl).playlist->get_state()));
3295 commit_reversible_command ();
3299 Editor::crop_region_to_selection ()
3301 if (!selection->time.empty()) {
3303 crop_region_to (selection->time.start(), selection->time.end_frame());
3310 if (get_edit_op_range (start, end)) {
3311 crop_region_to (start, end);
3318 Editor::crop_region_to (framepos_t start, framepos_t end)
3320 vector<boost::shared_ptr<Playlist> > playlists;
3321 boost::shared_ptr<Playlist> playlist;
3324 if (selection->tracks.empty()) {
3325 ts = track_views.filter_to_unique_playlists();
3327 ts = selection->tracks.filter_to_unique_playlists ();
3330 sort_track_selection (ts);
3332 for (TrackSelection::iterator i = ts.begin(); i != ts.end(); ++i) {
3334 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> ((*i));
3340 boost::shared_ptr<Track> t = rtv->track();
3342 if (t != 0 && ! t->destructive()) {
3344 if ((playlist = rtv->playlist()) != 0) {
3345 playlists.push_back (playlist);
3350 if (playlists.empty()) {
3355 framepos_t new_start;
3357 framecnt_t new_length;
3358 bool in_command = false;
3360 for (vector<boost::shared_ptr<Playlist> >::iterator i = playlists.begin(); i != playlists.end(); ++i) {
3362 /* Only the top regions at start and end have to be cropped */
3363 boost::shared_ptr<Region> region_at_start = (*i)->top_region_at(start);
3364 boost::shared_ptr<Region> region_at_end = (*i)->top_region_at(end);
3366 vector<boost::shared_ptr<Region> > regions;
3368 if (region_at_start != 0) {
3369 regions.push_back (region_at_start);
3371 if (region_at_end != 0) {
3372 regions.push_back (region_at_end);
3375 /* now adjust lengths */
3376 for (vector<boost::shared_ptr<Region> >::iterator i = regions.begin(); i != regions.end(); ++i) {
3378 pos = (*i)->position();
3379 new_start = max (start, pos);
3380 if (max_framepos - pos > (*i)->length()) {
3381 new_end = pos + (*i)->length() - 1;
3383 new_end = max_framepos;
3385 new_end = min (end, new_end);
3386 new_length = new_end - new_start + 1;
3389 begin_reversible_command (_("trim to selection"));
3392 (*i)->clear_changes ();
3393 (*i)->trim_to (new_start, new_length);
3394 _session->add_command (new StatefulDiffCommand (*i));
3399 commit_reversible_command ();
3404 Editor::region_fill_track ()
3406 boost::shared_ptr<Playlist> playlist;
3407 RegionSelection regions = get_regions_from_selection_and_entered ();
3408 RegionSelection foo;
3410 framepos_t const end = _session->current_end_frame ();
3412 if (regions.empty () || regions.end_frame () + 1 >= end) {
3416 framepos_t const start_frame = regions.start ();
3417 framepos_t const end_frame = regions.end_frame ();
3418 framecnt_t const gap = end_frame - start_frame + 1;
3420 begin_reversible_command (Operations::region_fill);
3422 selection->clear_regions ();
3424 for (RegionSelection::iterator i = regions.begin(); i != regions.end(); ++i) {
3426 boost::shared_ptr<Region> r ((*i)->region());
3428 TimeAxisView& tv = (*i)->get_time_axis_view();
3429 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (&tv);
3430 latest_regionviews.clear ();
3431 sigc::connection c = rtv->view()->RegionViewAdded.connect (sigc::mem_fun(*this, &Editor::collect_new_region_view));
3433 framepos_t const position = end_frame + (r->first_frame() - start_frame + 1);
3434 playlist = (*i)->region()->playlist();
3435 playlist->clear_changes ();
3436 playlist->duplicate_until (r, position, gap, end);
3437 _session->add_command(new StatefulDiffCommand (playlist));
3441 foo.insert (foo.end(), latest_regionviews.begin(), latest_regionviews.end());
3445 selection->set (foo);
3448 commit_reversible_command ();
3452 Editor::set_region_sync_position ()
3454 set_sync_point (get_preferred_edit_position (), get_regions_from_selection_and_edit_point ());
3458 Editor::set_sync_point (framepos_t where, const RegionSelection& rs)
3460 bool in_command = false;
3462 for (RegionSelection::const_iterator r = rs.begin(); r != rs.end(); ++r) {
3464 if (!(*r)->region()->covers (where)) {
3468 boost::shared_ptr<Region> region ((*r)->region());
3471 begin_reversible_command (_("set sync point"));
3475 region->clear_changes ();
3476 region->set_sync_position (where);
3477 _session->add_command(new StatefulDiffCommand (region));
3481 commit_reversible_command ();
3485 /** Remove the sync positions of the selection */
3487 Editor::remove_region_sync ()
3489 RegionSelection rs = get_regions_from_selection_and_entered ();
3495 begin_reversible_command (_("remove region sync"));
3497 for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
3499 (*i)->region()->clear_changes ();
3500 (*i)->region()->clear_sync_position ();
3501 _session->add_command(new StatefulDiffCommand ((*i)->region()));
3504 commit_reversible_command ();
3508 Editor::naturalize_region ()
3510 RegionSelection rs = get_regions_from_selection_and_entered ();
3516 if (rs.size() > 1) {
3517 begin_reversible_command (_("move regions to original position"));
3519 begin_reversible_command (_("move region to original position"));
3522 for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
3523 (*i)->region()->clear_changes ();
3524 (*i)->region()->move_to_natural_position ();
3525 _session->add_command (new StatefulDiffCommand ((*i)->region()));
3528 commit_reversible_command ();
3532 Editor::align_regions (RegionPoint what)
3534 RegionSelection const rs = get_regions_from_selection_and_edit_point ();
3540 begin_reversible_command (_("align selection"));
3542 framepos_t const position = get_preferred_edit_position ();
3544 for (RegionSelection::const_iterator i = rs.begin(); i != rs.end(); ++i) {
3545 align_region_internal ((*i)->region(), what, position);
3548 commit_reversible_command ();
3551 struct RegionSortByTime {
3552 bool operator() (const RegionView* a, const RegionView* b) {
3553 return a->region()->position() < b->region()->position();
3558 Editor::align_regions_relative (RegionPoint point)
3560 RegionSelection const rs = get_regions_from_selection_and_edit_point ();
3566 framepos_t const position = get_preferred_edit_position ();
3568 framepos_t distance = 0;
3572 list<RegionView*> sorted;
3573 rs.by_position (sorted);
3575 boost::shared_ptr<Region> r ((*sorted.begin())->region());
3580 if (position > r->position()) {
3581 distance = position - r->position();
3583 distance = r->position() - position;
3589 if (position > r->last_frame()) {
3590 distance = position - r->last_frame();
3591 pos = r->position() + distance;
3593 distance = r->last_frame() - position;
3594 pos = r->position() - distance;
3600 pos = r->adjust_to_sync (position);
3601 if (pos > r->position()) {
3602 distance = pos - r->position();
3604 distance = r->position() - pos;
3610 if (pos == r->position()) {
3614 begin_reversible_command (_("align selection (relative)"));
3616 /* move first one specially */
3618 r->clear_changes ();
3619 r->set_position (pos);
3620 _session->add_command(new StatefulDiffCommand (r));
3622 /* move rest by the same amount */
3626 for (list<RegionView*>::iterator i = sorted.begin(); i != sorted.end(); ++i) {
3628 boost::shared_ptr<Region> region ((*i)->region());
3630 region->clear_changes ();
3633 region->set_position (region->position() + distance);
3635 region->set_position (region->position() - distance);
3638 _session->add_command(new StatefulDiffCommand (region));
3642 commit_reversible_command ();
3646 Editor::align_region (boost::shared_ptr<Region> region, RegionPoint point, framepos_t position)
3648 begin_reversible_command (_("align region"));
3649 align_region_internal (region, point, position);
3650 commit_reversible_command ();
3654 Editor::align_region_internal (boost::shared_ptr<Region> region, RegionPoint point, framepos_t position)
3656 region->clear_changes ();
3660 region->set_position (region->adjust_to_sync (position));
3664 if (position > region->length()) {
3665 region->set_position (position - region->length());
3670 region->set_position (position);
3674 _session->add_command(new StatefulDiffCommand (region));
3678 Editor::trim_region_front ()
3684 Editor::trim_region_back ()
3686 trim_region (false);
3690 Editor::trim_region (bool front)
3692 framepos_t where = get_preferred_edit_position();
3693 RegionSelection rs = get_regions_from_selection_and_edit_point ();
3699 begin_reversible_command (front ? _("trim front") : _("trim back"));
3701 for (list<RegionView*>::const_iterator i = rs.by_layer().begin(); i != rs.by_layer().end(); ++i) {
3702 if (!(*i)->region()->locked()) {
3704 (*i)->region()->clear_changes ();
3707 (*i)->region()->trim_front (where);
3709 (*i)->region()->trim_end (where);
3712 _session->add_command (new StatefulDiffCommand ((*i)->region()));
3716 commit_reversible_command ();
3719 /** Trim the end of the selected regions to the position of the edit cursor */
3721 Editor::trim_region_to_loop ()
3723 Location* loc = _session->locations()->auto_loop_location();
3727 trim_region_to_location (*loc, _("trim to loop"));
3731 Editor::trim_region_to_punch ()
3733 Location* loc = _session->locations()->auto_punch_location();
3737 trim_region_to_location (*loc, _("trim to punch"));
3741 Editor::trim_region_to_location (const Location& loc, const char* str)
3743 RegionSelection rs = get_regions_from_selection_and_entered ();
3744 bool in_command = false;
3746 for (RegionSelection::iterator x = rs.begin(); x != rs.end(); ++x) {
3747 RegionView* rv = (*x);
3749 /* require region to span proposed trim */
3750 switch (rv->region()->coverage (loc.start(), loc.end())) {
3751 case Evoral::OverlapInternal:
3757 RouteTimeAxisView* tav = dynamic_cast<RouteTimeAxisView*> (&rv->get_time_axis_view());
3766 if (tav->track() != 0) {
3767 speed = tav->track()->speed();
3770 start = session_frame_to_track_frame (loc.start(), speed);
3771 end = session_frame_to_track_frame (loc.end(), speed);
3773 rv->region()->clear_changes ();
3774 rv->region()->trim_to (start, (end - start));
3777 begin_reversible_command (str);
3780 _session->add_command(new StatefulDiffCommand (rv->region()));
3784 commit_reversible_command ();
3789 Editor::trim_region_to_previous_region_end ()
3791 return trim_to_region(false);
3795 Editor::trim_region_to_next_region_start ()
3797 return trim_to_region(true);
3801 Editor::trim_to_region(bool forward)
3803 RegionSelection rs = get_regions_from_selection_and_entered ();
3804 bool in_command = false;
3806 boost::shared_ptr<Region> next_region;
3808 for (RegionSelection::iterator x = rs.begin(); x != rs.end(); ++x) {
3810 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (*x);
3816 AudioTimeAxisView* atav = dynamic_cast<AudioTimeAxisView*> (&arv->get_time_axis_view());
3824 if (atav->track() != 0) {
3825 speed = atav->track()->speed();
3829 boost::shared_ptr<Region> region = arv->region();
3830 boost::shared_ptr<Playlist> playlist (region->playlist());
3832 region->clear_changes ();
3836 next_region = playlist->find_next_region (region->first_frame(), Start, 1);
3842 region->trim_end((framepos_t) ( (next_region->first_frame() - 1) * speed));
3843 arv->region_changed (PropertyChange (ARDOUR::Properties::length));
3847 next_region = playlist->find_next_region (region->first_frame(), Start, 0);
3853 region->trim_front((framepos_t) ((next_region->last_frame() + 1) * speed));
3855 arv->region_changed (ARDOUR::bounds_change);
3859 begin_reversible_command (_("trim to region"));
3862 _session->add_command(new StatefulDiffCommand (region));
3866 commit_reversible_command ();
3871 Editor::unfreeze_route ()
3873 if (clicked_routeview == 0 || !clicked_routeview->is_track()) {
3877 clicked_routeview->track()->unfreeze ();
3881 Editor::_freeze_thread (void* arg)
3883 return static_cast<Editor*>(arg)->freeze_thread ();
3887 Editor::freeze_thread ()
3889 /* create event pool because we may need to talk to the session */
3890 SessionEvent::create_per_thread_pool ("freeze events", 64);
3891 /* create per-thread buffers for process() tree to use */
3892 clicked_routeview->audio_track()->freeze_me (*current_interthread_info);
3893 current_interthread_info->done = true;
3898 Editor::freeze_route ()
3904 /* stop transport before we start. this is important */
3906 _session->request_transport_speed (0.0);
3908 /* wait for just a little while, because the above call is asynchronous */
3910 Glib::usleep (250000);
3912 if (clicked_routeview == 0 || !clicked_routeview->is_audio_track()) {
3916 if (!clicked_routeview->track()->bounceable (clicked_routeview->track()->main_outs(), true)) {
3918 _("This track/bus cannot be frozen because the signal adds or loses channels before reaching the outputs.\n"
3919 "This is typically caused by plugins that generate stereo output from mono input or vice versa.")
3921 d.set_title (_("Cannot freeze"));
3926 if (clicked_routeview->track()->has_external_redirects()) {
3927 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"
3928 "Freezing will only process the signal as far as the first send/insert/return."),
3929 clicked_routeview->track()->name()), true, MESSAGE_INFO, BUTTONS_NONE, true);
3931 d.add_button (_("Freeze anyway"), Gtk::RESPONSE_OK);
3932 d.add_button (_("Don't freeze"), Gtk::RESPONSE_CANCEL);
3933 d.set_title (_("Freeze Limits"));
3935 int response = d.run ();
3938 case Gtk::RESPONSE_CANCEL:
3945 InterThreadInfo itt;
3946 current_interthread_info = &itt;
3948 InterthreadProgressWindow ipw (current_interthread_info, _("Freeze"), _("Cancel Freeze"));
3950 pthread_create_and_store (X_("freezer"), &itt.thread, _freeze_thread, this);
3952 CursorContext::Handle cursor_ctx = CursorContext::create(*this, _cursors->wait);
3954 while (!itt.done && !itt.cancel) {
3955 gtk_main_iteration ();
3958 pthread_join (itt.thread, 0);
3959 current_interthread_info = 0;
3963 Editor::bounce_range_selection (bool replace, bool enable_processing)
3965 if (selection->time.empty()) {
3969 TrackSelection views = selection->tracks;
3971 for (TrackViewList::iterator i = views.begin(); i != views.end(); ++i) {
3973 if (enable_processing) {
3975 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (*i);
3977 if (rtv && rtv->track() && replace && enable_processing && !rtv->track()->bounceable (rtv->track()->main_outs(), false)) {
3979 _("You can't perform this operation because the processing of the signal "
3980 "will cause one or more of the tracks to end up with a region with more channels than this track has inputs.\n\n"
3981 "You can do this without processing, which is a different operation.")
3983 d.set_title (_("Cannot bounce"));
3990 framepos_t start = selection->time[clicked_selection].start;
3991 framepos_t end = selection->time[clicked_selection].end;
3992 framepos_t cnt = end - start + 1;
3993 bool in_command = false;
3995 for (TrackViewList::iterator i = views.begin(); i != views.end(); ++i) {
3997 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (*i);
4003 boost::shared_ptr<Playlist> playlist;
4005 if ((playlist = rtv->playlist()) == 0) {
4009 InterThreadInfo itt;
4011 playlist->clear_changes ();
4012 playlist->clear_owned_changes ();
4014 boost::shared_ptr<Region> r;
4016 if (enable_processing) {
4017 r = rtv->track()->bounce_range (start, start+cnt, itt, rtv->track()->main_outs(), false);
4019 r = rtv->track()->bounce_range (start, start+cnt, itt, boost::shared_ptr<Processor>(), false);
4027 list<AudioRange> ranges;
4028 ranges.push_back (AudioRange (start, start+cnt, 0));
4029 playlist->cut (ranges); // discard result
4030 playlist->add_region (r, start);
4034 begin_reversible_command (_("bounce range"));
4037 vector<Command*> cmds;
4038 playlist->rdiff (cmds);
4039 _session->add_commands (cmds);
4041 _session->add_command (new StatefulDiffCommand (playlist));
4045 commit_reversible_command ();
4049 /** Delete selected regions, automation points or a time range */
4053 //special case: if the user is pointing in the editor/mixer strip, they may be trying to delete a plugin.
4054 //we need this because the editor-mixer strip is in the editor window, so it doesn't get the bindings from the mix window
4055 bool deleted = false;
4056 if ( current_mixer_strip && current_mixer_strip == MixerStrip::entered_mixer_strip() )
4057 deleted = current_mixer_strip->delete_processors ();
4063 /** Cut selected regions, automation points or a time range */
4070 /** Copy selected regions, automation points or a time range */
4078 /** @return true if a Cut, Copy or Clear is possible */
4080 Editor::can_cut_copy () const
4082 if (!selection->time.empty() || !selection->regions.empty() || !selection->points.empty())
4089 /** Cut, copy or clear selected regions, automation points or a time range.
4090 * @param op Operation (Delete, Cut, Copy or Clear)
4093 Editor::cut_copy (CutCopyOp op)
4095 /* only cancel selection if cut/copy is successful.*/
4101 opname = _("delete");
4110 opname = _("clear");
4114 /* if we're deleting something, and the mouse is still pressed,
4115 the thing we started a drag for will be gone when we release
4116 the mouse button(s). avoid this. see part 2 at the end of
4120 if (op == Delete || op == Cut || op == Clear) {
4121 if (_drags->active ()) {
4126 if ( op != Delete ) //"Delete" doesn't change copy/paste buf
4127 cut_buffer->clear ();
4129 if (entered_marker) {
4131 /* cut/delete op while pointing at a marker */
4134 Location* loc = find_location_from_marker (entered_marker, ignored);
4136 if (_session && loc) {
4137 entered_marker = NULL;
4138 Glib::signal_idle().connect (sigc::bind (sigc::mem_fun(*this, &Editor::really_remove_marker), loc));
4145 switch (mouse_mode) {
4148 begin_reversible_command (opname + ' ' + X_("MIDI"));
4150 commit_reversible_command ();
4156 bool did_edit = false;
4158 if (!selection->regions.empty() || !selection->points.empty()) {
4159 begin_reversible_command (opname + ' ' + _("objects"));
4162 if (!selection->regions.empty()) {
4163 cut_copy_regions (op, selection->regions);
4165 if (op == Cut || op == Delete) {
4166 selection->clear_regions ();
4170 if (!selection->points.empty()) {
4171 cut_copy_points (op);
4173 if (op == Cut || op == Delete) {
4174 selection->clear_points ();
4177 } else if (selection->time.empty()) {
4178 framepos_t start, end;
4179 /* no time selection, see if we can get an edit range
4182 if (get_edit_op_range (start, end)) {
4183 selection->set (start, end);
4185 } else if (!selection->time.empty()) {
4186 begin_reversible_command (opname + ' ' + _("range"));
4189 cut_copy_ranges (op);
4191 if (op == Cut || op == Delete) {
4192 selection->clear_time ();
4197 /* reset repeated paste state */
4200 commit_reversible_command ();
4203 if (op == Delete || op == Cut || op == Clear) {
4209 struct AutomationRecord {
4210 AutomationRecord () : state (0) , line(NULL) {}
4211 AutomationRecord (XMLNode* s, const AutomationLine* l) : state (s) , line (l) {}
4213 XMLNode* state; ///< state before any operation
4214 const AutomationLine* line; ///< line this came from
4215 boost::shared_ptr<Evoral::ControlList> copy; ///< copied events for the cut buffer
4217 struct PointsSelectionPositionSorter {
4218 bool operator() (ControlPoint* a, ControlPoint* b) {
4219 return (*(a->model()))->when < (*(b->model()))->when;
4222 /** Cut, copy or clear selected automation points.
4223 * @param op Operation (Cut, Copy or Clear)
4226 Editor::cut_copy_points (Editing::CutCopyOp op, Evoral::Beats earliest, bool midi)
4228 if (selection->points.empty ()) {
4232 /* XXX: not ideal, as there may be more than one track involved in the point selection */
4233 _last_cut_copy_source_track = &selection->points.front()->line().trackview;
4235 /* Keep a record of the AutomationLists that we end up using in this operation */
4236 typedef std::map<boost::shared_ptr<AutomationList>, AutomationRecord> Lists;
4239 /* user could select points in any order */
4240 selection->points.sort(PointsSelectionPositionSorter ());
4242 /* Go through all selected points, making an AutomationRecord for each distinct AutomationList */
4243 for (PointSelection::iterator sel_point = selection->points.begin(); sel_point != selection->points.end(); ++sel_point) {
4244 const AutomationLine& line = (*sel_point)->line();
4245 const boost::shared_ptr<AutomationList> al = line.the_list();
4246 if (lists.find (al) == lists.end ()) {
4247 /* We haven't seen this list yet, so make a record for it. This includes
4248 taking a copy of its current state, in case this is needed for undo later.
4250 lists[al] = AutomationRecord (&al->get_state (), &line);
4254 if (op == Cut || op == Copy) {
4255 /* This operation will involve putting things in the cut buffer, so create an empty
4256 ControlList for each of our source lists to put the cut buffer data in.
4258 for (Lists::iterator i = lists.begin(); i != lists.end(); ++i) {
4259 i->second.copy = i->first->create (i->first->parameter (), i->first->descriptor());
4262 /* Add all selected points to the relevant copy ControlLists */
4263 framepos_t start = std::numeric_limits<framepos_t>::max();
4264 for (PointSelection::iterator sel_point = selection->points.begin(); sel_point != selection->points.end(); ++sel_point) {
4265 boost::shared_ptr<AutomationList> al = (*sel_point)->line().the_list();
4266 AutomationList::const_iterator ctrl_evt = (*sel_point)->model ();
4268 lists[al].copy->fast_simple_add ((*ctrl_evt)->when, (*ctrl_evt)->value);
4270 /* Update earliest MIDI start time in beats */
4271 earliest = std::min(earliest, Evoral::Beats((*ctrl_evt)->when));
4273 /* Update earliest session start time in frames */
4274 start = std::min(start, (*sel_point)->line().session_position(ctrl_evt));
4278 /* Snap start time backwards, so copy/paste is snap aligned. */
4280 if (earliest == Evoral::Beats::max()) {
4281 earliest = Evoral::Beats(); // Weird... don't offset
4283 earliest.round_down_to_beat();
4285 if (start == std::numeric_limits<double>::max()) {
4286 start = 0; // Weird... don't offset
4288 snap_to(start, RoundDownMaybe);
4291 const double line_offset = midi ? earliest.to_double() : start;
4292 for (Lists::iterator i = lists.begin(); i != lists.end(); ++i) {
4293 /* Correct this copy list so that it is relative to the earliest
4294 start time, so relative ordering between points is preserved
4295 when copying from several lists and the paste starts at the
4296 earliest copied piece of data. */
4297 boost::shared_ptr<Evoral::ControlList> &al_cpy = i->second.copy;
4298 for (AutomationList::iterator ctrl_evt = al_cpy->begin(); ctrl_evt != al_cpy->end(); ++ctrl_evt) {
4299 (*ctrl_evt)->when -= line_offset;
4302 /* And add it to the cut buffer */
4303 cut_buffer->add (al_cpy);
4307 if (op == Delete || op == Cut) {
4308 /* This operation needs to remove things from the main AutomationList, so do that now */
4310 for (Lists::iterator i = lists.begin(); i != lists.end(); ++i) {
4311 i->first->freeze ();
4314 /* Remove each selected point from its AutomationList */
4315 for (PointSelection::iterator sel_point = selection->points.begin(); sel_point != selection->points.end(); ++sel_point) {
4316 AutomationLine& line = (*sel_point)->line ();
4317 boost::shared_ptr<AutomationList> al = line.the_list();
4321 if (dynamic_cast<AudioRegionGainLine*> (&line)) {
4322 /* removing of first and last gain point in region gain lines is prohibited*/
4323 if (line.is_last_point (*(*sel_point)) || line.is_first_point (*(*sel_point))) {
4329 al->erase ((*sel_point)->model ());
4333 /* Thaw the lists and add undo records for them */
4334 for (Lists::iterator i = lists.begin(); i != lists.end(); ++i) {
4335 boost::shared_ptr<AutomationList> al = i->first;
4337 _session->add_command (new MementoCommand<AutomationList> (*al.get(), i->second.state, &(al->get_state ())));
4342 /** Cut, copy or clear selected automation points.
4343 * @param op Operation (Cut, Copy or Clear)
4346 Editor::cut_copy_midi (CutCopyOp op)
4348 Evoral::Beats earliest = Evoral::Beats::max();
4349 for (MidiRegionSelection::iterator i = selection->midi_regions.begin(); i != selection->midi_regions.end(); ++i) {
4350 MidiRegionView* mrv = dynamic_cast<MidiRegionView*>(*i);
4352 if (!mrv->selection().empty()) {
4353 earliest = std::min(earliest, (*mrv->selection().begin())->note()->time());
4355 mrv->cut_copy_clear (op);
4357 /* XXX: not ideal, as there may be more than one track involved in the selection */
4358 _last_cut_copy_source_track = &mrv->get_time_axis_view();
4362 if (!selection->points.empty()) {
4363 cut_copy_points (op, earliest, true);
4364 if (op == Cut || op == Delete) {
4365 selection->clear_points ();
4370 struct lt_playlist {
4371 bool operator () (const PlaylistState& a, const PlaylistState& b) {
4372 return a.playlist < b.playlist;
4376 struct PlaylistMapping {
4378 boost::shared_ptr<Playlist> pl;
4380 PlaylistMapping (TimeAxisView* tvp) : tv (tvp) {}
4383 /** Remove `clicked_regionview' */
4385 Editor::remove_clicked_region ()
4387 if (clicked_routeview == 0 || clicked_regionview == 0) {
4391 begin_reversible_command (_("remove region"));
4393 boost::shared_ptr<Playlist> playlist = clicked_routeview->playlist();
4395 playlist->clear_changes ();
4396 playlist->clear_owned_changes ();
4397 playlist->remove_region (clicked_regionview->region());
4398 if (Config->get_edit_mode() == Ripple)
4399 playlist->ripple (clicked_regionview->region()->position(), -clicked_regionview->region()->length(), boost::shared_ptr<Region>());
4401 /* We might have removed regions, which alters other regions' layering_index,
4402 so we need to do a recursive diff here.
4404 vector<Command*> cmds;
4405 playlist->rdiff (cmds);
4406 _session->add_commands (cmds);
4408 _session->add_command(new StatefulDiffCommand (playlist));
4409 commit_reversible_command ();
4413 /** Remove the selected regions */
4415 Editor::remove_selected_regions ()
4417 RegionSelection rs = get_regions_from_selection_and_entered ();
4419 if (!_session || rs.empty()) {
4423 list<boost::shared_ptr<Region> > regions_to_remove;
4425 for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
4426 // we can't just remove the region(s) in this loop because
4427 // this removes them from the RegionSelection, and they thus
4428 // disappear from underneath the iterator, and the ++i above
4429 // SEGVs in a puzzling fashion.
4431 // so, first iterate over the regions to be removed from rs and
4432 // add them to the regions_to_remove list, and then
4433 // iterate over the list to actually remove them.
4435 regions_to_remove.push_back ((*i)->region());
4438 vector<boost::shared_ptr<Playlist> > playlists;
4440 for (list<boost::shared_ptr<Region> >::iterator rl = regions_to_remove.begin(); rl != regions_to_remove.end(); ++rl) {
4442 boost::shared_ptr<Playlist> playlist = (*rl)->playlist();
4445 // is this check necessary?
4449 /* get_regions_from_selection_and_entered() guarantees that
4450 the playlists involved are unique, so there is no need
4454 playlists.push_back (playlist);
4456 playlist->clear_changes ();
4457 playlist->clear_owned_changes ();
4458 playlist->freeze ();
4459 playlist->remove_region (*rl);
4460 if (Config->get_edit_mode() == Ripple)
4461 playlist->ripple ((*rl)->position(), -(*rl)->length(), boost::shared_ptr<Region>());
4465 vector<boost::shared_ptr<Playlist> >::iterator pl;
4466 bool in_command = false;
4468 for (pl = playlists.begin(); pl != playlists.end(); ++pl) {
4471 /* We might have removed regions, which alters other regions' layering_index,
4472 so we need to do a recursive diff here.
4476 begin_reversible_command (_("remove region"));
4479 vector<Command*> cmds;
4480 (*pl)->rdiff (cmds);
4481 _session->add_commands (cmds);
4483 _session->add_command(new StatefulDiffCommand (*pl));
4487 commit_reversible_command ();
4491 /** Cut, copy or clear selected regions.
4492 * @param op Operation (Cut, Copy or Clear)
4495 Editor::cut_copy_regions (CutCopyOp op, RegionSelection& rs)
4497 /* we can't use a std::map here because the ordering is important, and we can't trivially sort
4498 a map when we want ordered access to both elements. i think.
4501 vector<PlaylistMapping> pmap;
4503 framepos_t first_position = max_framepos;
4505 typedef set<boost::shared_ptr<Playlist> > FreezeList;
4506 FreezeList freezelist;
4508 /* get ordering correct before we cut/copy */
4510 rs.sort_by_position_and_track ();
4512 for (RegionSelection::iterator x = rs.begin(); x != rs.end(); ++x) {
4514 first_position = min ((framepos_t) (*x)->region()->position(), first_position);
4516 if (op == Cut || op == Clear || op == Delete) {
4517 boost::shared_ptr<Playlist> pl = (*x)->region()->playlist();
4520 FreezeList::iterator fl;
4522 // only take state if this is a new playlist.
4523 for (fl = freezelist.begin(); fl != freezelist.end(); ++fl) {
4529 if (fl == freezelist.end()) {
4530 pl->clear_changes();
4531 pl->clear_owned_changes ();
4533 freezelist.insert (pl);
4538 TimeAxisView* tv = &(*x)->get_time_axis_view();
4539 vector<PlaylistMapping>::iterator z;
4541 for (z = pmap.begin(); z != pmap.end(); ++z) {
4542 if ((*z).tv == tv) {
4547 if (z == pmap.end()) {
4548 pmap.push_back (PlaylistMapping (tv));
4552 for (RegionSelection::iterator x = rs.begin(); x != rs.end(); ) {
4554 boost::shared_ptr<Playlist> pl = (*x)->region()->playlist();
4557 /* region not yet associated with a playlist (e.g. unfinished
4564 TimeAxisView& tv = (*x)->get_time_axis_view();
4565 boost::shared_ptr<Playlist> npl;
4566 RegionSelection::iterator tmp;
4573 vector<PlaylistMapping>::iterator z;
4575 for (z = pmap.begin(); z != pmap.end(); ++z) {
4576 if ((*z).tv == &tv) {
4581 assert (z != pmap.end());
4584 npl = PlaylistFactory::create (pl->data_type(), *_session, "cutlist", true);
4592 boost::shared_ptr<Region> r = (*x)->region();
4593 boost::shared_ptr<Region> _xx;
4599 pl->remove_region (r);
4600 if (Config->get_edit_mode() == Ripple)
4601 pl->ripple (r->position(), -r->length(), boost::shared_ptr<Region>());
4605 _xx = RegionFactory::create (r);
4606 npl->add_region (_xx, r->position() - first_position);
4607 pl->remove_region (r);
4608 if (Config->get_edit_mode() == Ripple)
4609 pl->ripple (r->position(), -r->length(), boost::shared_ptr<Region>());
4613 /* copy region before adding, so we're not putting same object into two different playlists */
4614 npl->add_region (RegionFactory::create (r), r->position() - first_position);
4618 pl->remove_region (r);
4619 if (Config->get_edit_mode() == Ripple)
4620 pl->ripple (r->position(), -r->length(), boost::shared_ptr<Region>());
4629 list<boost::shared_ptr<Playlist> > foo;
4631 /* the pmap is in the same order as the tracks in which selected regions occurred */
4633 for (vector<PlaylistMapping>::iterator i = pmap.begin(); i != pmap.end(); ++i) {
4636 foo.push_back ((*i).pl);
4641 cut_buffer->set (foo);
4645 _last_cut_copy_source_track = 0;
4647 _last_cut_copy_source_track = pmap.front().tv;
4651 for (FreezeList::iterator pl = freezelist.begin(); pl != freezelist.end(); ++pl) {
4654 /* We might have removed regions, which alters other regions' layering_index,
4655 so we need to do a recursive diff here.
4657 vector<Command*> cmds;
4658 (*pl)->rdiff (cmds);
4659 _session->add_commands (cmds);
4661 _session->add_command (new StatefulDiffCommand (*pl));
4666 Editor::cut_copy_ranges (CutCopyOp op)
4668 TrackViewList ts = selection->tracks.filter_to_unique_playlists ();
4670 /* Sort the track selection now, so that it if is used, the playlists
4671 selected by the calls below to cut_copy_clear are in the order that
4672 their tracks appear in the editor. This makes things like paste
4673 of ranges work properly.
4676 sort_track_selection (ts);
4679 if (!entered_track) {
4682 ts.push_back (entered_track);
4685 for (TrackViewList::iterator i = ts.begin(); i != ts.end(); ++i) {
4686 (*i)->cut_copy_clear (*selection, op);
4691 Editor::paste (float times, bool from_context)
4693 DEBUG_TRACE (DEBUG::CutNPaste, "paste to preferred edit pos\n");
4695 paste_internal (get_preferred_edit_position (EDIT_IGNORE_NONE, from_context), times, get_grid_music_divisions (0));
4699 Editor::mouse_paste ()
4704 if (!mouse_frame (where, ignored)) {
4709 paste_internal (where, 1, get_grid_music_divisions (0));
4713 Editor::paste_internal (framepos_t position, float times, const int32_t sub_num)
4715 DEBUG_TRACE (DEBUG::CutNPaste, string_compose ("apparent paste position is %1\n", position));
4717 if (cut_buffer->empty(internal_editing())) {
4721 if (position == max_framepos) {
4722 position = get_preferred_edit_position();
4723 DEBUG_TRACE (DEBUG::CutNPaste, string_compose ("preferred edit position is %1\n", position));
4726 if (position == last_paste_pos) {
4727 /* repeated paste in the same position */
4730 /* paste in new location, reset repeated paste state */
4732 last_paste_pos = position;
4735 /* get everything in the correct order */
4738 if (!selection->tracks.empty()) {
4739 /* If there is a track selection, paste into exactly those tracks and
4740 only those tracks. This allows the user to be explicit and override
4741 the below "do the reasonable thing" logic. */
4742 ts = selection->tracks.filter_to_unique_playlists ();
4743 sort_track_selection (ts);
4745 /* Figure out which track to base the paste at. */
4746 TimeAxisView* base_track = NULL;
4747 if (_edit_point == Editing::EditAtMouse && entered_track) {
4748 /* With the mouse edit point, paste onto the track under the mouse. */
4749 base_track = entered_track;
4750 } else if (_edit_point == Editing::EditAtMouse && entered_regionview) {
4751 /* With the mouse edit point, paste onto the track of the region under the mouse. */
4752 base_track = &entered_regionview->get_time_axis_view();
4753 } else if (_last_cut_copy_source_track) {
4754 /* Paste to the track that the cut/copy came from (see mantis #333). */
4755 base_track = _last_cut_copy_source_track;
4757 /* This is "impossible" since we've copied... well, do nothing. */
4761 /* Walk up to parent if necessary, so base track is a route. */
4762 while (base_track->get_parent()) {
4763 base_track = base_track->get_parent();
4766 /* Add base track and all tracks below it. The paste logic will select
4767 the appropriate object types from the cut buffer in relative order. */
4768 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
4769 if ((*i)->order() >= base_track->order()) {
4774 /* Sort tracks so the nth track of type T will pick the nth object of type T. */
4775 sort_track_selection (ts);
4777 /* Add automation children of each track in order, for pasting several lines. */
4778 for (TrackViewList::iterator i = ts.begin(); i != ts.end();) {
4779 /* Add any automation children for pasting several lines */
4780 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*>(*i++);
4785 typedef RouteTimeAxisView::AutomationTracks ATracks;
4786 const ATracks& atracks = rtv->automation_tracks();
4787 for (ATracks::const_iterator a = atracks.begin(); a != atracks.end(); ++a) {
4788 i = ts.insert(i, a->second.get());
4793 /* We now have a list of trackviews starting at base_track, including
4794 automation children, in the order shown in the editor, e.g. R1,
4795 R1.A1, R1.A2, R2, R2.A1, ... */
4798 begin_reversible_command (Operations::paste);
4800 if (ts.size() == 1 && cut_buffer->lines.size() == 1 &&
4801 dynamic_cast<AutomationTimeAxisView*>(ts.front())) {
4802 /* Only one line copied, and one automation track selected. Do a
4803 "greedy" paste from one automation type to another. */
4805 PasteContext ctx(paste_count, times, ItemCounts(), true);
4806 ts.front()->paste (position, *cut_buffer, ctx, sub_num);
4810 /* Paste into tracks */
4812 PasteContext ctx(paste_count, times, ItemCounts(), false);
4813 for (TrackViewList::iterator i = ts.begin(); i != ts.end(); ++i) {
4814 (*i)->paste (position, *cut_buffer, ctx, sub_num);
4818 commit_reversible_command ();
4822 Editor::duplicate_regions (float times)
4824 RegionSelection rs (get_regions_from_selection_and_entered());
4825 duplicate_some_regions (rs, times);
4829 Editor::duplicate_some_regions (RegionSelection& regions, float times)
4831 if (regions.empty ()) {
4835 boost::shared_ptr<Playlist> playlist;
4836 RegionSelection sel = regions; // clear (below) may clear the argument list if its the current region selection
4837 RegionSelection foo;
4839 framepos_t const start_frame = regions.start ();
4840 framepos_t const end_frame = regions.end_frame ();
4841 framecnt_t const gap = end_frame - start_frame + 1;
4843 begin_reversible_command (Operations::duplicate_region);
4845 selection->clear_regions ();
4847 for (RegionSelection::iterator i = sel.begin(); i != sel.end(); ++i) {
4849 boost::shared_ptr<Region> r ((*i)->region());
4851 TimeAxisView& tv = (*i)->get_time_axis_view();
4852 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (&tv);
4853 latest_regionviews.clear ();
4854 sigc::connection c = rtv->view()->RegionViewAdded.connect (sigc::mem_fun(*this, &Editor::collect_new_region_view));
4856 framepos_t const position = end_frame + (r->first_frame() - start_frame + 1);
4857 playlist = (*i)->region()->playlist();
4858 playlist->clear_changes ();
4859 playlist->duplicate (r, position, gap, times);
4860 _session->add_command(new StatefulDiffCommand (playlist));
4864 foo.insert (foo.end(), latest_regionviews.begin(), latest_regionviews.end());
4868 selection->set (foo);
4871 commit_reversible_command ();
4875 Editor::duplicate_selection (float times)
4877 if (selection->time.empty() || selection->tracks.empty()) {
4881 boost::shared_ptr<Playlist> playlist;
4883 TrackViewList ts = selection->tracks.filter_to_unique_playlists ();
4885 bool in_command = false;
4887 for (TrackViewList::iterator i = ts.begin(); i != ts.end(); ++i) {
4888 if ((playlist = (*i)->playlist()) == 0) {
4891 playlist->clear_changes ();
4893 if (clicked_selection) {
4894 playlist->duplicate_range (selection->time[clicked_selection], times);
4896 playlist->duplicate_ranges (selection->time, times);
4900 begin_reversible_command (_("duplicate range selection"));
4903 _session->add_command (new StatefulDiffCommand (playlist));
4908 if (times == 1.0f) {
4909 // now "move" range selection to after the current range selection
4910 framecnt_t distance = 0;
4912 if (clicked_selection) {
4914 selection->time[clicked_selection].end - selection->time[clicked_selection].start;
4916 distance = selection->time.end_frame () - selection->time.start ();
4919 selection->move_time (distance);
4921 commit_reversible_command ();
4925 /** Reset all selected points to the relevant default value */
4927 Editor::reset_point_selection ()
4929 for (PointSelection::iterator i = selection->points.begin(); i != selection->points.end(); ++i) {
4930 ARDOUR::AutomationList::iterator j = (*i)->model ();
4931 (*j)->value = (*i)->line().the_list()->default_value ();
4936 Editor::center_playhead ()
4938 float const page = _visible_canvas_width * samples_per_pixel;
4939 center_screen_internal (playhead_cursor->current_frame (), page);
4943 Editor::center_edit_point ()
4945 float const page = _visible_canvas_width * samples_per_pixel;
4946 center_screen_internal (get_preferred_edit_position(), page);
4949 /** Caller must begin and commit a reversible command */
4951 Editor::clear_playlist (boost::shared_ptr<Playlist> playlist)
4953 playlist->clear_changes ();
4955 _session->add_command (new StatefulDiffCommand (playlist));
4959 Editor::nudge_track (bool use_edit, bool forwards)
4961 boost::shared_ptr<Playlist> playlist;
4962 framepos_t distance;
4963 framepos_t next_distance;
4967 start = get_preferred_edit_position();
4972 if ((distance = get_nudge_distance (start, next_distance)) == 0) {
4976 if (selection->tracks.empty()) {
4980 TrackViewList ts = selection->tracks.filter_to_unique_playlists ();
4981 bool in_command = false;
4983 for (TrackViewList::iterator i = ts.begin(); i != ts.end(); ++i) {
4985 if ((playlist = (*i)->playlist()) == 0) {
4989 playlist->clear_changes ();
4990 playlist->clear_owned_changes ();
4992 playlist->nudge_after (start, distance, forwards);
4995 begin_reversible_command (_("nudge track"));
4998 vector<Command*> cmds;
5000 playlist->rdiff (cmds);
5001 _session->add_commands (cmds);
5003 _session->add_command (new StatefulDiffCommand (playlist));
5007 commit_reversible_command ();
5012 Editor::remove_last_capture ()
5014 vector<string> choices;
5021 if (Config->get_verify_remove_last_capture()) {
5022 prompt = _("Do you really want to destroy the last capture?"
5023 "\n(This is destructive and cannot be undone)");
5025 choices.push_back (_("No, do nothing."));
5026 choices.push_back (_("Yes, destroy it."));
5028 Gtkmm2ext::Choice prompter (_("Destroy last capture"), prompt, choices);
5030 if (prompter.run () == 1) {
5031 _session->remove_last_capture ();
5032 _regions->redisplay ();
5036 _session->remove_last_capture();
5037 _regions->redisplay ();
5042 Editor::normalize_region ()
5048 RegionSelection rs = get_regions_from_selection_and_entered ();
5054 NormalizeDialog dialog (rs.size() > 1);
5056 if (dialog.run () != RESPONSE_ACCEPT) {
5060 CursorContext::Handle cursor_ctx = CursorContext::create(*this, _cursors->wait);
5063 /* XXX: should really only count audio regions here */
5064 int const regions = rs.size ();
5066 /* Make a list of the selected audio regions' maximum amplitudes, and also
5067 obtain the maximum amplitude of them all.
5069 list<double> max_amps;
5070 list<double> rms_vals;
5073 bool use_rms = dialog.constrain_rms ();
5075 for (RegionSelection::const_iterator i = rs.begin(); i != rs.end(); ++i) {
5076 AudioRegionView const * arv = dynamic_cast<AudioRegionView const *> (*i);
5080 dialog.descend (1.0 / regions);
5081 double const a = arv->audio_region()->maximum_amplitude (&dialog);
5083 double r = arv->audio_region()->rms (&dialog);
5084 max_rms = max (max_rms, r);
5085 rms_vals.push_back (r);
5089 /* the user cancelled the operation */
5093 max_amps.push_back (a);
5094 max_amp = max (max_amp, a);
5098 list<double>::const_iterator a = max_amps.begin ();
5099 list<double>::const_iterator l = rms_vals.begin ();
5100 bool in_command = false;
5102 for (RegionSelection::iterator r = rs.begin(); r != rs.end(); ++r) {
5103 AudioRegionView* const arv = dynamic_cast<AudioRegionView*> (*r);
5108 arv->region()->clear_changes ();
5110 double amp = dialog.normalize_individually() ? *a : max_amp;
5111 double target = dialog.target_peak (); // dB
5114 double const amp_rms = dialog.normalize_individually() ? *l : max_rms;
5115 const double t_rms = dialog.target_rms ();
5116 const gain_t c_peak = dB_to_coefficient (target);
5117 const gain_t c_rms = dB_to_coefficient (t_rms);
5118 if ((amp_rms / c_rms) > (amp / c_peak)) {
5124 arv->audio_region()->normalize (amp, target);
5127 begin_reversible_command (_("normalize"));
5130 _session->add_command (new StatefulDiffCommand (arv->region()));
5137 commit_reversible_command ();
5143 Editor::reset_region_scale_amplitude ()
5149 RegionSelection rs = get_regions_from_selection_and_entered ();
5155 bool in_command = false;
5157 for (RegionSelection::iterator r = rs.begin(); r != rs.end(); ++r) {
5158 AudioRegionView* const arv = dynamic_cast<AudioRegionView*>(*r);
5161 arv->region()->clear_changes ();
5162 arv->audio_region()->set_scale_amplitude (1.0f);
5165 begin_reversible_command ("reset gain");
5168 _session->add_command (new StatefulDiffCommand (arv->region()));
5172 commit_reversible_command ();
5177 Editor::adjust_region_gain (bool up)
5179 RegionSelection rs = get_regions_from_selection_and_entered ();
5181 if (!_session || rs.empty()) {
5185 bool in_command = false;
5187 for (RegionSelection::iterator r = rs.begin(); r != rs.end(); ++r) {
5188 AudioRegionView* const arv = dynamic_cast<AudioRegionView*>(*r);
5193 arv->region()->clear_changes ();
5195 double dB = accurate_coefficient_to_dB (arv->audio_region()->scale_amplitude ());
5203 arv->audio_region()->set_scale_amplitude (dB_to_coefficient (dB));
5206 begin_reversible_command ("adjust region gain");
5209 _session->add_command (new StatefulDiffCommand (arv->region()));
5213 commit_reversible_command ();
5219 Editor::reverse_region ()
5225 Reverse rev (*_session);
5226 apply_filter (rev, _("reverse regions"));
5230 Editor::strip_region_silence ()
5236 RegionSelection rs = get_regions_from_selection_and_entered ();
5242 std::list<RegionView*> audio_only;
5244 for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
5245 AudioRegionView* const arv = dynamic_cast<AudioRegionView*> (*i);
5247 audio_only.push_back (arv);
5251 assert (!audio_only.empty());
5253 StripSilenceDialog d (_session, audio_only);
5254 int const r = d.run ();
5258 if (r == Gtk::RESPONSE_OK) {
5259 ARDOUR::AudioIntervalMap silences;
5260 d.silences (silences);
5261 StripSilence s (*_session, silences, d.fade_length());
5263 apply_filter (s, _("strip silence"), &d);
5268 Editor::apply_midi_note_edit_op_to_region (MidiOperator& op, MidiRegionView& mrv)
5270 Evoral::Sequence<Evoral::Beats>::Notes selected;
5271 mrv.selection_as_notelist (selected, true);
5273 vector<Evoral::Sequence<Evoral::Beats>::Notes> v;
5274 v.push_back (selected);
5276 Evoral::Beats pos_beats = Evoral::Beats (mrv.midi_region()->beat()) - mrv.midi_region()->start_beats();
5278 return op (mrv.midi_region()->model(), pos_beats, v);
5282 Editor::apply_midi_note_edit_op (MidiOperator& op, const RegionSelection& rs)
5288 bool in_command = false;
5290 for (RegionSelection::const_iterator r = rs.begin(); r != rs.end(); ) {
5291 RegionSelection::const_iterator tmp = r;
5294 MidiRegionView* const mrv = dynamic_cast<MidiRegionView*> (*r);
5297 Command* cmd = apply_midi_note_edit_op_to_region (op, *mrv);
5300 begin_reversible_command (op.name ());
5304 _session->add_command (cmd);
5312 commit_reversible_command ();
5317 Editor::fork_region ()
5319 RegionSelection rs = get_regions_from_selection_and_entered ();
5325 CursorContext::Handle cursor_ctx = CursorContext::create(*this, _cursors->wait);
5326 bool in_command = false;
5330 for (RegionSelection::iterator r = rs.begin(); r != rs.end(); ) {
5331 RegionSelection::iterator tmp = r;
5334 MidiRegionView* const mrv = dynamic_cast<MidiRegionView*>(*r);
5338 boost::shared_ptr<Playlist> playlist = mrv->region()->playlist();
5339 boost::shared_ptr<MidiSource> new_source = _session->create_midi_source_by_stealing_name (mrv->midi_view()->track());
5340 boost::shared_ptr<MidiRegion> newregion = mrv->midi_region()->clone (new_source);
5343 begin_reversible_command (_("Fork Region(s)"));
5346 playlist->clear_changes ();
5347 playlist->replace_region (mrv->region(), newregion, mrv->region()->position());
5348 _session->add_command(new StatefulDiffCommand (playlist));
5350 error << string_compose (_("Could not unlink %1"), mrv->region()->name()) << endmsg;
5358 commit_reversible_command ();
5363 Editor::quantize_region ()
5366 quantize_regions(get_regions_from_selection_and_entered ());
5371 Editor::quantize_regions (const RegionSelection& rs)
5373 if (rs.n_midi_regions() == 0) {
5377 if (!quantize_dialog) {
5378 quantize_dialog = new QuantizeDialog (*this);
5381 if (quantize_dialog->is_mapped()) {
5382 /* in progress already */
5386 quantize_dialog->present ();
5387 const int r = quantize_dialog->run ();
5388 quantize_dialog->hide ();
5390 if (r == Gtk::RESPONSE_OK) {
5391 Quantize quant (quantize_dialog->snap_start(),
5392 quantize_dialog->snap_end(),
5393 quantize_dialog->start_grid_size(),
5394 quantize_dialog->end_grid_size(),
5395 quantize_dialog->strength(),
5396 quantize_dialog->swing(),
5397 quantize_dialog->threshold());
5399 apply_midi_note_edit_op (quant, rs);
5404 Editor::legatize_region (bool shrink_only)
5407 legatize_regions(get_regions_from_selection_and_entered (), shrink_only);
5412 Editor::legatize_regions (const RegionSelection& rs, bool shrink_only)
5414 if (rs.n_midi_regions() == 0) {
5418 Legatize legatize(shrink_only);
5419 apply_midi_note_edit_op (legatize, rs);
5423 Editor::transform_region ()
5426 transform_regions(get_regions_from_selection_and_entered ());
5431 Editor::transform_regions (const RegionSelection& rs)
5433 if (rs.n_midi_regions() == 0) {
5440 const int r = td.run();
5443 if (r == Gtk::RESPONSE_OK) {
5444 Transform transform(td.get());
5445 apply_midi_note_edit_op(transform, rs);
5450 Editor::transpose_region ()
5453 transpose_regions(get_regions_from_selection_and_entered ());
5458 Editor::transpose_regions (const RegionSelection& rs)
5460 if (rs.n_midi_regions() == 0) {
5465 int const r = d.run ();
5467 if (r == RESPONSE_ACCEPT) {
5468 Transpose transpose(d.semitones ());
5469 apply_midi_note_edit_op (transpose, rs);
5474 Editor::insert_patch_change (bool from_context)
5476 RegionSelection rs = get_regions_from_selection_and_entered ();
5482 const framepos_t p = get_preferred_edit_position (EDIT_IGNORE_NONE, from_context);
5484 /* XXX: bit of a hack; use the MIDNAM from the first selected region;
5485 there may be more than one, but the PatchChangeDialog can only offer
5486 one set of patch menus.
5488 MidiRegionView* first = dynamic_cast<MidiRegionView*> (rs.front ());
5490 Evoral::PatchChange<Evoral::Beats> empty (Evoral::Beats(), 0, 0, 0);
5491 PatchChangeDialog d (0, _session, empty, first->instrument_info(), Gtk::Stock::ADD);
5493 if (d.run() == RESPONSE_CANCEL) {
5497 for (RegionSelection::iterator i = rs.begin (); i != rs.end(); ++i) {
5498 MidiRegionView* const mrv = dynamic_cast<MidiRegionView*> (*i);
5500 if (p >= mrv->region()->first_frame() && p <= mrv->region()->last_frame()) {
5501 mrv->add_patch_change (p - mrv->region()->position(), d.patch ());
5508 Editor::apply_filter (Filter& filter, string command, ProgressReporter* progress)
5510 RegionSelection rs = get_regions_from_selection_and_entered ();
5516 CursorContext::Handle cursor_ctx = CursorContext::create(*this, _cursors->wait);
5517 bool in_command = false;
5522 int const N = rs.size ();
5524 for (RegionSelection::iterator r = rs.begin(); r != rs.end(); ) {
5525 RegionSelection::iterator tmp = r;
5528 AudioRegionView* const arv = dynamic_cast<AudioRegionView*>(*r);
5530 boost::shared_ptr<Playlist> playlist = arv->region()->playlist();
5533 progress->descend (1.0 / N);
5536 if (arv->audio_region()->apply (filter, progress) == 0) {
5538 playlist->clear_changes ();
5539 playlist->clear_owned_changes ();
5542 begin_reversible_command (command);
5546 if (filter.results.empty ()) {
5548 /* no regions returned; remove the old one */
5549 playlist->remove_region (arv->region ());
5553 std::vector<boost::shared_ptr<Region> >::iterator res = filter.results.begin ();
5555 /* first region replaces the old one */
5556 playlist->replace_region (arv->region(), *res, (*res)->position());
5560 while (res != filter.results.end()) {
5561 playlist->add_region (*res, (*res)->position());
5567 /* We might have removed regions, which alters other regions' layering_index,
5568 so we need to do a recursive diff here.
5570 vector<Command*> cmds;
5571 playlist->rdiff (cmds);
5572 _session->add_commands (cmds);
5574 _session->add_command(new StatefulDiffCommand (playlist));
5578 progress->ascend ();
5587 commit_reversible_command ();
5592 Editor::external_edit_region ()
5598 Editor::reset_region_gain_envelopes ()
5600 RegionSelection rs = get_regions_from_selection_and_entered ();
5602 if (!_session || rs.empty()) {
5606 bool in_command = false;
5608 for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
5609 AudioRegionView* const arv = dynamic_cast<AudioRegionView*>(*i);
5611 boost::shared_ptr<AutomationList> alist (arv->audio_region()->envelope());
5612 XMLNode& before (alist->get_state());
5614 arv->audio_region()->set_default_envelope ();
5617 begin_reversible_command (_("reset region gain"));
5620 _session->add_command (new MementoCommand<AutomationList>(*arv->audio_region()->envelope().get(), &before, &alist->get_state()));
5625 commit_reversible_command ();
5630 Editor::set_region_gain_visibility (RegionView* rv)
5632 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (rv);
5634 arv->update_envelope_visibility();
5639 Editor::set_gain_envelope_visibility ()
5645 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
5646 AudioTimeAxisView* v = dynamic_cast<AudioTimeAxisView*>(*i);
5648 v->audio_view()->foreach_regionview (sigc::mem_fun (this, &Editor::set_region_gain_visibility));
5654 Editor::toggle_gain_envelope_active ()
5656 if (_ignore_region_action) {
5660 RegionSelection rs = get_regions_from_selection_and_entered ();
5662 if (!_session || rs.empty()) {
5666 bool in_command = false;
5668 for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
5669 AudioRegionView* const arv = dynamic_cast<AudioRegionView*>(*i);
5671 arv->region()->clear_changes ();
5672 arv->audio_region()->set_envelope_active (!arv->audio_region()->envelope_active());
5675 begin_reversible_command (_("region gain envelope active"));
5678 _session->add_command (new StatefulDiffCommand (arv->region()));
5683 commit_reversible_command ();
5688 Editor::toggle_region_lock ()
5690 if (_ignore_region_action) {
5694 RegionSelection rs = get_regions_from_selection_and_entered ();
5696 if (!_session || rs.empty()) {
5700 begin_reversible_command (_("toggle region lock"));
5702 for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
5703 (*i)->region()->clear_changes ();
5704 (*i)->region()->set_locked (!(*i)->region()->locked());
5705 _session->add_command (new StatefulDiffCommand ((*i)->region()));
5708 commit_reversible_command ();
5712 Editor::toggle_region_video_lock ()
5714 if (_ignore_region_action) {
5718 RegionSelection rs = get_regions_from_selection_and_entered ();
5720 if (!_session || rs.empty()) {
5724 begin_reversible_command (_("Toggle Video Lock"));
5726 for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
5727 (*i)->region()->clear_changes ();
5728 (*i)->region()->set_video_locked (!(*i)->region()->video_locked());
5729 _session->add_command (new StatefulDiffCommand ((*i)->region()));
5732 commit_reversible_command ();
5736 Editor::toggle_region_lock_style ()
5738 if (_ignore_region_action) {
5742 RegionSelection rs = get_regions_from_selection_and_entered ();
5744 if (!_session || rs.empty()) {
5748 begin_reversible_command (_("region lock style"));
5750 for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
5751 (*i)->region()->clear_changes ();
5752 PositionLockStyle const ns = (*i)->region()->position_lock_style() == AudioTime ? MusicTime : AudioTime;
5753 (*i)->region()->set_position_lock_style (ns);
5754 _session->add_command (new StatefulDiffCommand ((*i)->region()));
5757 commit_reversible_command ();
5761 Editor::toggle_opaque_region ()
5763 if (_ignore_region_action) {
5767 RegionSelection rs = get_regions_from_selection_and_entered ();
5769 if (!_session || rs.empty()) {
5773 begin_reversible_command (_("change region opacity"));
5775 for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
5776 (*i)->region()->clear_changes ();
5777 (*i)->region()->set_opaque (!(*i)->region()->opaque());
5778 _session->add_command (new StatefulDiffCommand ((*i)->region()));
5781 commit_reversible_command ();
5785 Editor::toggle_record_enable ()
5787 bool new_state = false;
5789 for (TrackSelection::iterator i = selection->tracks.begin(); i != selection->tracks.end(); ++i) {
5790 RouteTimeAxisView *rtav = dynamic_cast<RouteTimeAxisView *>(*i);
5793 if (!rtav->is_track())
5797 new_state = !rtav->track()->rec_enable_control()->get_value();
5801 rtav->track()->rec_enable_control()->set_value (new_state, Controllable::UseGroup);
5806 Editor::toggle_solo ()
5808 bool new_state = false;
5810 boost::shared_ptr<ControlList> cl (new ControlList);
5812 for (TrackSelection::iterator i = selection->tracks.begin(); i != selection->tracks.end(); ++i) {
5813 RouteTimeAxisView *rtav = dynamic_cast<RouteTimeAxisView *>(*i);
5820 new_state = !rtav->route()->soloed ();
5824 cl->push_back (rtav->route()->solo_control());
5827 _session->set_controls (cl, new_state ? 1.0 : 0.0, Controllable::UseGroup);
5831 Editor::toggle_mute ()
5833 bool new_state = false;
5835 boost::shared_ptr<RouteList> rl (new RouteList);
5837 for (TrackSelection::iterator i = selection->tracks.begin(); i != selection->tracks.end(); ++i) {
5838 RouteTimeAxisView *rtav = dynamic_cast<RouteTimeAxisView *>(*i);
5845 new_state = !rtav->route()->muted();
5849 rl->push_back (rtav->route());
5852 _session->set_controls (route_list_to_control_list (rl, &Stripable::mute_control), new_state, Controllable::UseGroup);
5856 Editor::toggle_solo_isolate ()
5862 Editor::fade_range ()
5864 TrackViewList ts = selection->tracks.filter_to_unique_playlists ();
5866 begin_reversible_command (_("fade range"));
5868 for (TrackViewList::iterator i = ts.begin(); i != ts.end(); ++i) {
5869 (*i)->fade_range (selection->time);
5872 commit_reversible_command ();
5877 Editor::set_fade_length (bool in)
5879 RegionSelection rs = get_regions_from_selection_and_entered ();
5885 /* we need a region to measure the offset from the start */
5887 RegionView* rv = rs.front ();
5889 framepos_t pos = get_preferred_edit_position();
5893 if (pos > rv->region()->last_frame() || pos < rv->region()->first_frame()) {
5894 /* edit point is outside the relevant region */
5899 if (pos <= rv->region()->position()) {
5903 len = pos - rv->region()->position();
5904 cmd = _("set fade in length");
5906 if (pos >= rv->region()->last_frame()) {
5910 len = rv->region()->last_frame() - pos;
5911 cmd = _("set fade out length");
5914 bool in_command = false;
5916 for (RegionSelection::iterator x = rs.begin(); x != rs.end(); ++x) {
5917 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (*x);
5923 boost::shared_ptr<AutomationList> alist;
5925 alist = tmp->audio_region()->fade_in();
5927 alist = tmp->audio_region()->fade_out();
5930 XMLNode &before = alist->get_state();
5933 tmp->audio_region()->set_fade_in_length (len);
5934 tmp->audio_region()->set_fade_in_active (true);
5936 tmp->audio_region()->set_fade_out_length (len);
5937 tmp->audio_region()->set_fade_out_active (true);
5941 begin_reversible_command (cmd);
5944 XMLNode &after = alist->get_state();
5945 _session->add_command(new MementoCommand<AutomationList>(*alist, &before, &after));
5949 commit_reversible_command ();
5954 Editor::set_fade_in_shape (FadeShape shape)
5956 RegionSelection rs = get_regions_from_selection_and_entered ();
5961 bool in_command = false;
5963 for (RegionSelection::iterator x = rs.begin(); x != rs.end(); ++x) {
5964 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (*x);
5970 boost::shared_ptr<AutomationList> alist = tmp->audio_region()->fade_in();
5971 XMLNode &before = alist->get_state();
5973 tmp->audio_region()->set_fade_in_shape (shape);
5976 begin_reversible_command (_("set fade in shape"));
5979 XMLNode &after = alist->get_state();
5980 _session->add_command(new MementoCommand<AutomationList>(*alist.get(), &before, &after));
5984 commit_reversible_command ();
5989 Editor::set_fade_out_shape (FadeShape shape)
5991 RegionSelection rs = get_regions_from_selection_and_entered ();
5996 bool in_command = false;
5998 for (RegionSelection::iterator x = rs.begin(); x != rs.end(); ++x) {
5999 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (*x);
6005 boost::shared_ptr<AutomationList> alist = tmp->audio_region()->fade_out();
6006 XMLNode &before = alist->get_state();
6008 tmp->audio_region()->set_fade_out_shape (shape);
6011 begin_reversible_command (_("set fade out shape"));
6014 XMLNode &after = alist->get_state();
6015 _session->add_command(new MementoCommand<AutomationList>(*alist.get(), &before, &after));
6019 commit_reversible_command ();
6024 Editor::set_fade_in_active (bool yn)
6026 RegionSelection rs = get_regions_from_selection_and_entered ();
6031 bool in_command = false;
6033 for (RegionSelection::iterator x = rs.begin(); x != rs.end(); ++x) {
6034 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (*x);
6041 boost::shared_ptr<AudioRegion> ar (tmp->audio_region());
6043 ar->clear_changes ();
6044 ar->set_fade_in_active (yn);
6047 begin_reversible_command (_("set fade in active"));
6050 _session->add_command (new StatefulDiffCommand (ar));
6054 commit_reversible_command ();
6059 Editor::set_fade_out_active (bool yn)
6061 RegionSelection rs = get_regions_from_selection_and_entered ();
6066 bool in_command = false;
6068 for (RegionSelection::iterator x = rs.begin(); x != rs.end(); ++x) {
6069 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (*x);
6075 boost::shared_ptr<AudioRegion> ar (tmp->audio_region());
6077 ar->clear_changes ();
6078 ar->set_fade_out_active (yn);
6081 begin_reversible_command (_("set fade out active"));
6084 _session->add_command(new StatefulDiffCommand (ar));
6088 commit_reversible_command ();
6093 Editor::toggle_region_fades (int dir)
6095 if (_ignore_region_action) {
6099 boost::shared_ptr<AudioRegion> ar;
6102 RegionSelection rs = get_regions_from_selection_and_entered ();
6108 RegionSelection::iterator i;
6109 for (i = rs.begin(); i != rs.end(); ++i) {
6110 if ((ar = boost::dynamic_pointer_cast<AudioRegion>((*i)->region())) != 0) {
6112 yn = ar->fade_out_active ();
6114 yn = ar->fade_in_active ();
6120 if (i == rs.end()) {
6124 /* XXX should this undo-able? */
6125 bool in_command = false;
6127 for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
6128 if ((ar = boost::dynamic_pointer_cast<AudioRegion>((*i)->region())) == 0) {
6131 ar->clear_changes ();
6133 if (dir == 1 || dir == 0) {
6134 ar->set_fade_in_active (!yn);
6137 if (dir == -1 || dir == 0) {
6138 ar->set_fade_out_active (!yn);
6141 begin_reversible_command (_("toggle fade active"));
6144 _session->add_command(new StatefulDiffCommand (ar));
6148 commit_reversible_command ();
6153 /** Update region fade visibility after its configuration has been changed */
6155 Editor::update_region_fade_visibility ()
6157 bool _fade_visibility = _session->config.get_show_region_fades ();
6159 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
6160 AudioTimeAxisView* v = dynamic_cast<AudioTimeAxisView*>(*i);
6162 if (_fade_visibility) {
6163 v->audio_view()->show_all_fades ();
6165 v->audio_view()->hide_all_fades ();
6172 Editor::set_edit_point ()
6177 if (!mouse_frame (where, ignored)) {
6183 if (selection->markers.empty()) {
6185 mouse_add_new_marker (where);
6190 Location* loc = find_location_from_marker (selection->markers.front(), ignored);
6193 loc->move_to (where, get_grid_music_divisions(0));
6199 Editor::set_playhead_cursor ()
6201 if (entered_marker) {
6202 _session->request_locate (entered_marker->position(), _session->transport_rolling());
6207 if (!mouse_frame (where, ignored)) {
6214 _session->request_locate (where, _session->transport_rolling());
6218 //not sure what this was for; remove it for now.
6219 // if (UIConfiguration::instance().get_follow_edits() && (!_session || !_session->config.get_external_sync())) {
6220 // cancel_time_selection();
6226 Editor::split_region ()
6228 if (_drags->active ()) {
6232 //if a range is selected, separate it
6233 if ( !selection->time.empty()) {
6234 separate_regions_between (selection->time);
6238 //if no range was selected, try to find some regions to split
6239 if (current_mouse_mode() == MouseObject) { //don't try this for Internal Edit, Stretch, Draw, etc.
6241 RegionSelection rs = get_regions_from_selection_and_edit_point ();
6243 framepos_t where = get_preferred_edit_position ();
6249 if (snap_musical()) {
6250 split_regions_at (where, rs, get_grid_music_divisions (0));
6252 split_regions_at (where, rs, 0);
6258 Editor::select_next_route()
6260 if (selection->tracks.empty()) {
6261 selection->set (track_views.front());
6265 TimeAxisView* current = selection->tracks.front();
6269 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
6271 if (*i == current) {
6273 if (i != track_views.end()) {
6276 current = (*(track_views.begin()));
6277 //selection->set (*(track_views.begin()));
6283 rui = dynamic_cast<RouteUI *>(current);
6285 } while (current->hidden() || (rui == NULL) || !rui->route()->active());
6287 selection->set (current);
6289 ensure_time_axis_view_is_visible (*current, false);
6293 Editor::select_prev_route()
6295 if (selection->tracks.empty()) {
6296 selection->set (track_views.front());
6300 TimeAxisView* current = selection->tracks.front();
6304 for (TrackViewList::reverse_iterator i = track_views.rbegin(); i != track_views.rend(); ++i) {
6306 if (*i == current) {
6308 if (i != track_views.rend()) {
6311 current = *(track_views.rbegin());
6316 rui = dynamic_cast<RouteUI *>(current);
6318 } while (current->hidden() || (rui == NULL) || !rui->route()->active());
6320 selection->set (current);
6322 ensure_time_axis_view_is_visible (*current, false);
6326 Editor::set_loop_from_selection (bool play)
6328 if (_session == 0) {
6332 framepos_t start, end;
6333 if (!get_selection_extents ( start, end))
6336 set_loop_range (start, end, _("set loop range from selection"));
6339 _session->request_play_loop (true, true);
6344 Editor::set_loop_from_region (bool play)
6346 framepos_t start, end;
6347 if (!get_selection_extents ( start, end))
6350 set_loop_range (start, end, _("set loop range from region"));
6353 _session->request_locate (start, true);
6354 _session->request_play_loop (true);
6359 Editor::set_punch_from_selection ()
6361 if (_session == 0) {
6365 framepos_t start, end;
6366 if (!get_selection_extents ( start, end))
6369 set_punch_range (start, end, _("set punch range from selection"));
6373 Editor::set_auto_punch_range ()
6375 // auto punch in/out button from a single button
6376 // If Punch In is unset, set punch range from playhead to end, enable punch in
6377 // If Punch In is set, the next punch sets Punch Out, unless the playhead has been
6378 // rewound beyond the Punch In marker, in which case that marker will be moved back
6379 // to the current playhead position.
6380 // If punch out is set, it clears the punch range and Punch In/Out buttons
6382 if (_session == 0) {
6386 Location* tpl = transport_punch_location();
6387 framepos_t now = playhead_cursor->current_frame();
6388 framepos_t begin = now;
6389 framepos_t end = _session->current_end_frame();
6391 if (!_session->config.get_punch_in()) {
6392 // First Press - set punch in and create range from here to eternity
6393 set_punch_range (begin, end, _("Auto Punch In"));
6394 _session->config.set_punch_in(true);
6395 } else if (tpl && !_session->config.get_punch_out()) {
6396 // Second press - update end range marker and set punch_out
6397 if (now < tpl->start()) {
6398 // playhead has been rewound - move start back and pretend nothing happened
6400 set_punch_range (begin, end, _("Auto Punch In/Out"));
6402 // normal case for 2nd press - set the punch out
6403 end = playhead_cursor->current_frame ();
6404 set_punch_range (tpl->start(), now, _("Auto Punch In/Out"));
6405 _session->config.set_punch_out(true);
6408 if (_session->config.get_punch_out()) {
6409 _session->config.set_punch_out(false);
6412 if (_session->config.get_punch_in()) {
6413 _session->config.set_punch_in(false);
6418 // third press - unset punch in/out and remove range
6419 _session->locations()->remove(tpl);
6426 Editor::set_session_extents_from_selection ()
6428 if (_session == 0) {
6432 framepos_t start, end;
6433 if (!get_selection_extents ( start, end))
6437 if ((loc = _session->locations()->session_range_location()) == 0) {
6438 _session->set_session_extents (start, end); // this will create a new session range; no need for UNDO
6440 XMLNode &before = loc->get_state();
6442 _session->set_session_extents (start, end);
6444 XMLNode &after = loc->get_state();
6446 begin_reversible_command (_("set session start/end from selection"));
6448 _session->add_command (new MementoCommand<Location>(*loc, &before, &after));
6450 commit_reversible_command ();
6453 _session->set_end_is_free (false);
6457 Editor::set_punch_start_from_edit_point ()
6461 framepos_t start = 0;
6462 framepos_t end = max_framepos;
6464 //use the existing punch end, if any
6465 Location* tpl = transport_punch_location();
6470 if ((_edit_point == EditAtPlayhead) && _session->transport_rolling()) {
6471 start = _session->audible_frame();
6473 start = get_preferred_edit_position();
6476 //snap the selection start/end
6479 //if there's not already a sensible selection endpoint, go "forever"
6480 if ( start > end ) {
6484 set_punch_range (start, end, _("set punch start from EP"));
6490 Editor::set_punch_end_from_edit_point ()
6494 framepos_t start = 0;
6495 framepos_t end = max_framepos;
6497 //use the existing punch start, if any
6498 Location* tpl = transport_punch_location();
6500 start = tpl->start();
6503 if ((_edit_point == EditAtPlayhead) && _session->transport_rolling()) {
6504 end = _session->audible_frame();
6506 end = get_preferred_edit_position();
6509 //snap the selection start/end
6512 set_punch_range (start, end, _("set punch end from EP"));
6518 Editor::set_loop_start_from_edit_point ()
6522 framepos_t start = 0;
6523 framepos_t end = max_framepos;
6525 //use the existing loop end, if any
6526 Location* tpl = transport_loop_location();
6531 if ((_edit_point == EditAtPlayhead) && _session->transport_rolling()) {
6532 start = _session->audible_frame();
6534 start = get_preferred_edit_position();
6537 //snap the selection start/end
6540 //if there's not already a sensible selection endpoint, go "forever"
6541 if ( start > end ) {
6545 set_loop_range (start, end, _("set loop start from EP"));
6551 Editor::set_loop_end_from_edit_point ()
6555 framepos_t start = 0;
6556 framepos_t end = max_framepos;
6558 //use the existing loop start, if any
6559 Location* tpl = transport_loop_location();
6561 start = tpl->start();
6564 if ((_edit_point == EditAtPlayhead) && _session->transport_rolling()) {
6565 end = _session->audible_frame();
6567 end = get_preferred_edit_position();
6570 //snap the selection start/end
6573 set_loop_range (start, end, _("set loop end from EP"));
6578 Editor::set_punch_from_region ()
6580 framepos_t start, end;
6581 if (!get_selection_extents ( start, end))
6584 set_punch_range (start, end, _("set punch range from region"));
6588 Editor::pitch_shift_region ()
6590 RegionSelection rs = get_regions_from_selection_and_entered ();
6592 RegionSelection audio_rs;
6593 for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
6594 if (dynamic_cast<AudioRegionView*> (*i)) {
6595 audio_rs.push_back (*i);
6599 if (audio_rs.empty()) {
6603 pitch_shift (audio_rs, 1.2);
6607 Editor::set_tempo_from_region ()
6609 RegionSelection rs = get_regions_from_selection_and_entered ();
6611 if (!_session || rs.empty()) {
6615 RegionView* rv = rs.front();
6617 define_one_bar (rv->region()->position(), rv->region()->last_frame() + 1);
6621 Editor::use_range_as_bar ()
6623 framepos_t start, end;
6624 if (get_edit_op_range (start, end)) {
6625 define_one_bar (start, end);
6630 Editor::define_one_bar (framepos_t start, framepos_t end)
6632 framepos_t length = end - start;
6634 const Meter& m (_session->tempo_map().meter_at_frame (start));
6636 /* length = 1 bar */
6638 /* We're going to deliver a constant tempo here,
6639 so we can use frames per beat to determine length.
6640 now we want frames per beat.
6641 we have frames per bar, and beats per bar, so ...
6644 /* XXXX METER MATH */
6646 double frames_per_beat = length / m.divisions_per_bar();
6648 /* beats per minute = */
6650 double beats_per_minute = (_session->frame_rate() * 60.0) / frames_per_beat;
6652 /* now decide whether to:
6654 (a) set global tempo
6655 (b) add a new tempo marker
6659 const TempoSection& t (_session->tempo_map().tempo_section_at_frame (start));
6661 bool do_global = false;
6663 if ((_session->tempo_map().n_tempos() == 1) && (_session->tempo_map().n_meters() == 1)) {
6665 /* only 1 tempo & 1 meter: ask if the user wants to set the tempo
6666 at the start, or create a new marker
6669 vector<string> options;
6670 options.push_back (_("Cancel"));
6671 options.push_back (_("Add new marker"));
6672 options.push_back (_("Set global tempo"));
6675 _("Define one bar"),
6676 _("Do you want to set the global tempo or add a new tempo marker?"),
6680 c.set_default_response (2);
6696 /* more than 1 tempo and/or meter section already, go ahead do the "usual":
6697 if the marker is at the region starter, change it, otherwise add
6702 begin_reversible_command (_("set tempo from region"));
6703 XMLNode& before (_session->tempo_map().get_state());
6706 _session->tempo_map().change_initial_tempo (beats_per_minute, t.note_type());
6707 } else if (t.frame() == start) {
6708 _session->tempo_map().change_existing_tempo_at (start, beats_per_minute, t.note_type());
6710 const Tempo tempo (beats_per_minute, t.note_type());
6711 _session->tempo_map().add_tempo (tempo, 0.0, start, TempoSection::Constant, AudioTime);
6714 XMLNode& after (_session->tempo_map().get_state());
6716 _session->add_command (new MementoCommand<TempoMap>(_session->tempo_map(), &before, &after));
6717 commit_reversible_command ();
6721 Editor::split_region_at_transients ()
6723 AnalysisFeatureList positions;
6725 RegionSelection rs = get_regions_from_selection_and_entered ();
6727 if (!_session || rs.empty()) {
6731 begin_reversible_command (_("split regions"));
6733 for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ) {
6735 RegionSelection::iterator tmp;
6740 boost::shared_ptr<AudioRegion> ar = boost::dynamic_pointer_cast<AudioRegion> ((*i)->region());
6743 ar->transients (positions);
6744 split_region_at_points ((*i)->region(), positions, true);
6751 commit_reversible_command ();
6756 Editor::split_region_at_points (boost::shared_ptr<Region> r, AnalysisFeatureList& positions, bool can_ferret, bool select_new)
6758 bool use_rhythmic_rodent = false;
6760 boost::shared_ptr<Playlist> pl = r->playlist();
6762 list<boost::shared_ptr<Region> > new_regions;
6768 if (positions.empty()) {
6772 if (positions.size() > 20 && can_ferret) {
6773 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);
6774 MessageDialog msg (msgstr,
6777 Gtk::BUTTONS_OK_CANCEL);
6780 msg.add_button (_("Call for the Ferret!"), RESPONSE_APPLY);
6781 msg.set_secondary_text (_("Press OK to continue with this split operation\nor ask the Ferret dialog to tune the analysis"));
6783 msg.set_secondary_text (_("Press OK to continue with this split operation"));
6786 msg.set_title (_("Excessive split?"));
6789 int response = msg.run();
6795 case RESPONSE_APPLY:
6796 use_rhythmic_rodent = true;
6803 if (use_rhythmic_rodent) {
6804 show_rhythm_ferret ();
6808 AnalysisFeatureList::const_iterator x;
6810 pl->clear_changes ();
6811 pl->clear_owned_changes ();
6813 x = positions.begin();
6815 if (x == positions.end()) {
6820 pl->remove_region (r);
6824 framepos_t rstart = r->first_frame ();
6825 framepos_t rend = r->last_frame ();
6827 while (x != positions.end()) {
6829 /* deal with positons that are out of scope of present region bounds */
6830 if (*x <= rstart || *x > rend) {
6835 /* file start = original start + how far we from the initial position ? */
6837 framepos_t file_start = r->start() + pos;
6839 /* length = next position - current position */
6841 framepos_t len = (*x) - pos - rstart;
6843 /* XXX we do we really want to allow even single-sample regions?
6844 * shouldn't we have some kind of lower limit on region size?
6853 if (RegionFactory::region_name (new_name, r->name())) {
6857 /* do NOT announce new regions 1 by one, just wait till they are all done */
6861 plist.add (ARDOUR::Properties::start, file_start);
6862 plist.add (ARDOUR::Properties::length, len);
6863 plist.add (ARDOUR::Properties::name, new_name);
6864 plist.add (ARDOUR::Properties::layer, 0);
6865 // TODO set transients_offset
6867 boost::shared_ptr<Region> nr = RegionFactory::create (r->sources(), plist, false);
6868 /* because we set annouce to false, manually add the new region to the
6871 RegionFactory::map_add (nr);
6873 pl->add_region (nr, rstart + pos);
6876 new_regions.push_front(nr);
6885 RegionFactory::region_name (new_name, r->name());
6887 /* Add the final region */
6890 plist.add (ARDOUR::Properties::start, r->start() + pos);
6891 plist.add (ARDOUR::Properties::length, r->last_frame() - (r->position() + pos) + 1);
6892 plist.add (ARDOUR::Properties::name, new_name);
6893 plist.add (ARDOUR::Properties::layer, 0);
6895 boost::shared_ptr<Region> nr = RegionFactory::create (r->sources(), plist, false);
6896 /* because we set annouce to false, manually add the new region to the
6899 RegionFactory::map_add (nr);
6900 pl->add_region (nr, r->position() + pos);
6903 new_regions.push_front(nr);
6908 /* We might have removed regions, which alters other regions' layering_index,
6909 so we need to do a recursive diff here.
6911 vector<Command*> cmds;
6913 _session->add_commands (cmds);
6915 _session->add_command (new StatefulDiffCommand (pl));
6919 for (list<boost::shared_ptr<Region> >::iterator i = new_regions.begin(); i != new_regions.end(); ++i){
6920 set_selected_regionview_from_region_list ((*i), Selection::Add);
6926 Editor::place_transient()
6932 RegionSelection rs = get_regions_from_selection_and_edit_point ();
6938 framepos_t where = get_preferred_edit_position();
6940 begin_reversible_command (_("place transient"));
6942 for (RegionSelection::iterator r = rs.begin(); r != rs.end(); ++r) {
6943 (*r)->region()->add_transient(where);
6946 commit_reversible_command ();
6950 Editor::remove_transient(ArdourCanvas::Item* item)
6956 ArdourCanvas::Line* _line = reinterpret_cast<ArdourCanvas::Line*> (item);
6959 AudioRegionView* _arv = reinterpret_cast<AudioRegionView*> (item->get_data ("regionview"));
6960 _arv->remove_transient (*(float*) _line->get_data ("position"));
6964 Editor::snap_regions_to_grid ()
6966 list <boost::shared_ptr<Playlist > > used_playlists;
6968 RegionSelection rs = get_regions_from_selection_and_entered ();
6970 if (!_session || rs.empty()) {
6974 begin_reversible_command (_("snap regions to grid"));
6976 for (RegionSelection::iterator r = rs.begin(); r != rs.end(); ++r) {
6978 boost::shared_ptr<Playlist> pl = (*r)->region()->playlist();
6980 if (!pl->frozen()) {
6981 /* we haven't seen this playlist before */
6983 /* remember used playlists so we can thaw them later */
6984 used_playlists.push_back(pl);
6988 framepos_t start_frame = (*r)->region()->first_frame ();
6989 snap_to (start_frame);
6990 (*r)->region()->set_position (start_frame);
6993 while (used_playlists.size() > 0) {
6994 list <boost::shared_ptr<Playlist > >::iterator i = used_playlists.begin();
6996 used_playlists.pop_front();
6999 commit_reversible_command ();
7003 Editor::close_region_gaps ()
7005 list <boost::shared_ptr<Playlist > > used_playlists;
7007 RegionSelection rs = get_regions_from_selection_and_entered ();
7009 if (!_session || rs.empty()) {
7013 Dialog dialog (_("Close Region Gaps"));
7016 table.set_spacings (12);
7017 table.set_border_width (12);
7018 Label* l = manage (left_aligned_label (_("Crossfade length")));
7019 table.attach (*l, 0, 1, 0, 1);
7021 SpinButton spin_crossfade (1, 0);
7022 spin_crossfade.set_range (0, 15);
7023 spin_crossfade.set_increments (1, 1);
7024 spin_crossfade.set_value (5);
7025 table.attach (spin_crossfade, 1, 2, 0, 1);
7027 table.attach (*manage (new Label (_("ms"))), 2, 3, 0, 1);
7029 l = manage (left_aligned_label (_("Pull-back length")));
7030 table.attach (*l, 0, 1, 1, 2);
7032 SpinButton spin_pullback (1, 0);
7033 spin_pullback.set_range (0, 100);
7034 spin_pullback.set_increments (1, 1);
7035 spin_pullback.set_value(30);
7036 table.attach (spin_pullback, 1, 2, 1, 2);
7038 table.attach (*manage (new Label (_("ms"))), 2, 3, 1, 2);
7040 dialog.get_vbox()->pack_start (table);
7041 dialog.add_button (Stock::CANCEL, RESPONSE_CANCEL);
7042 dialog.add_button (_("Ok"), RESPONSE_ACCEPT);
7045 if (dialog.run () == RESPONSE_CANCEL) {
7049 framepos_t crossfade_len = spin_crossfade.get_value();
7050 framepos_t pull_back_frames = spin_pullback.get_value();
7052 crossfade_len = lrintf (crossfade_len * _session->frame_rate()/1000);
7053 pull_back_frames = lrintf (pull_back_frames * _session->frame_rate()/1000);
7055 /* Iterate over the region list and make adjacent regions overlap by crossfade_len_ms */
7057 begin_reversible_command (_("close region gaps"));
7060 boost::shared_ptr<Region> last_region;
7062 rs.sort_by_position_and_track();
7064 for (RegionSelection::iterator r = rs.begin(); r != rs.end(); ++r) {
7066 boost::shared_ptr<Playlist> pl = (*r)->region()->playlist();
7068 if (!pl->frozen()) {
7069 /* we haven't seen this playlist before */
7071 /* remember used playlists so we can thaw them later */
7072 used_playlists.push_back(pl);
7076 framepos_t position = (*r)->region()->position();
7078 if (idx == 0 || position < last_region->position()){
7079 last_region = (*r)->region();
7084 (*r)->region()->clear_changes ();
7085 (*r)->region()->trim_front( (position - pull_back_frames));
7086 last_region->trim_end( (position - pull_back_frames + crossfade_len));
7088 _session->add_command (new StatefulDiffCommand ((*r)->region()));
7089 last_region = (*r)->region();
7094 while (used_playlists.size() > 0) {
7095 list <boost::shared_ptr<Playlist > >::iterator i = used_playlists.begin();
7097 used_playlists.pop_front();
7100 commit_reversible_command ();
7104 Editor::tab_to_transient (bool forward)
7106 AnalysisFeatureList positions;
7108 RegionSelection rs = get_regions_from_selection_and_entered ();
7114 framepos_t pos = _session->audible_frame ();
7116 if (!selection->tracks.empty()) {
7118 /* don't waste time searching for transients in duplicate playlists.
7121 TrackViewList ts = selection->tracks.filter_to_unique_playlists ();
7123 for (TrackViewList::iterator t = ts.begin(); t != ts.end(); ++t) {
7125 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (*t);
7128 boost::shared_ptr<Track> tr = rtv->track();
7130 boost::shared_ptr<Playlist> pl = tr->playlist ();
7132 framepos_t result = pl->find_next_transient (pos, forward ? 1 : -1);
7135 positions.push_back (result);
7148 for (RegionSelection::iterator r = rs.begin(); r != rs.end(); ++r) {
7149 (*r)->region()->get_transients (positions);
7153 TransientDetector::cleanup_transients (positions, _session->frame_rate(), 3.0);
7156 AnalysisFeatureList::iterator x;
7158 for (x = positions.begin(); x != positions.end(); ++x) {
7164 if (x != positions.end ()) {
7165 _session->request_locate (*x);
7169 AnalysisFeatureList::reverse_iterator x;
7171 for (x = positions.rbegin(); x != positions.rend(); ++x) {
7177 if (x != positions.rend ()) {
7178 _session->request_locate (*x);
7184 Editor::playhead_forward_to_grid ()
7190 framepos_t pos = playhead_cursor->current_frame ();
7191 if (pos < max_framepos - 1) {
7193 snap_to_internal (pos, RoundUpAlways, false);
7194 _session->request_locate (pos);
7200 Editor::playhead_backward_to_grid ()
7206 framepos_t pos = playhead_cursor->current_frame ();
7209 snap_to_internal (pos, RoundDownAlways, false);
7210 _session->request_locate (pos);
7215 Editor::set_track_height (Height h)
7217 TrackSelection& ts (selection->tracks);
7219 for (TrackSelection::iterator x = ts.begin(); x != ts.end(); ++x) {
7220 (*x)->set_height_enum (h);
7225 Editor::toggle_tracks_active ()
7227 TrackSelection& ts (selection->tracks);
7229 bool target = false;
7235 for (TrackSelection::iterator x = ts.begin(); x != ts.end(); ++x) {
7236 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*>(*x);
7240 target = !rtv->_route->active();
7243 rtv->_route->set_active (target, this);
7249 Editor::remove_tracks ()
7251 /* this will delete GUI objects that may be the subject of an event
7252 handler in which this method is called. Defer actual deletion to the
7253 next idle callback, when all event handling is finished.
7255 Glib::signal_idle().connect (sigc::mem_fun (*this, &Editor::idle_remove_tracks));
7259 Editor::idle_remove_tracks ()
7261 Session::StateProtector sp (_session);
7263 return false; /* do not call again */
7267 Editor::_remove_tracks ()
7269 TrackSelection& ts (selection->tracks);
7275 vector<string> choices;
7279 const char* trackstr;
7281 vector<boost::shared_ptr<Route> > routes;
7282 bool special_bus = false;
7284 for (TrackSelection::iterator x = ts.begin(); x != ts.end(); ++x) {
7285 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (*x);
7289 if (rtv->is_track()) {
7294 routes.push_back (rtv->_route);
7296 if (rtv->route()->is_master() || rtv->route()->is_monitor()) {
7301 if (special_bus && !Config->get_allow_special_bus_removal()) {
7302 MessageDialog msg (_("That would be bad news ...."),
7306 msg.set_secondary_text (string_compose (_(
7307 "Removing the master or monitor bus is such a bad idea\n\
7308 that %1 is not going to allow it.\n\
7310 If you really want to do this sort of thing\n\
7311 edit your ardour.rc file to set the\n\
7312 \"allow-special-bus-removal\" option to be \"yes\""), PROGRAM_NAME));
7319 if (ntracks + nbusses == 0) {
7323 trackstr = P_("track", "tracks", ntracks);
7324 busstr = P_("bus", "busses", nbusses);
7328 prompt = string_compose (_("Do you really want to remove %1 %2 and %3 %4?\n"
7329 "(You may also lose the playlists associated with the %2)\n\n"
7330 "This action cannot be undone, and the session file will be overwritten!"),
7331 ntracks, trackstr, nbusses, busstr);
7333 prompt = string_compose (_("Do you really want to remove %1 %2?\n"
7334 "(You may also lose the playlists associated with the %2)\n\n"
7335 "This action cannot be undone, and the session file will be overwritten!"),
7338 } else if (nbusses) {
7339 prompt = string_compose (_("Do you really want to remove %1 %2?\n\n"
7340 "This action cannot be undone, and the session file will be overwritten"),
7344 choices.push_back (_("No, do nothing."));
7345 if (ntracks + nbusses > 1) {
7346 choices.push_back (_("Yes, remove them."));
7348 choices.push_back (_("Yes, remove it."));
7353 title = string_compose (_("Remove %1"), trackstr);
7355 title = string_compose (_("Remove %1"), busstr);
7358 Choice prompter (title, prompt, choices);
7360 if (prompter.run () != 1) {
7364 if (current_mixer_strip && routes.size () > 1 && std::find (routes.begin(), routes.end(), current_mixer_strip->route()) != routes.end ()) {
7365 /* Route deletion calls Editor::timeaxisview_deleted() iteratively (for each deleted
7366 * route). If the deleted route is currently displayed in the Editor-Mixer (highly
7367 * likely because deletion requires selection) this will call
7368 * Editor::set_selected_mixer_strip () which is expensive ( MixerStrip::set_route() ).
7369 * It's likewise likely that the route that has just been displayed in the
7370 * Editor-Mixer will be next in line for deletion.
7372 * So simply switch to the master-bus (if present)
7374 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
7375 if ((*i)->stripable ()->is_master ()) {
7376 set_selected_mixer_strip (*(*i));
7383 PresentationInfo::ChangeSuspender cs;
7384 DisplaySuspender ds;
7386 boost::shared_ptr<RouteList> rl (new RouteList);
7387 for (vector<boost::shared_ptr<Route> >::iterator x = routes.begin(); x != routes.end(); ++x) {
7390 _session->remove_routes (rl);
7392 /* TrackSelection and RouteList leave scope,
7393 * destructors are called,
7394 * diskstream drops references, save_state is called (again for every track)
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));