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"
85 #include "mixer_strip.h"
86 #include "mouse_cursors.h"
87 #include "normalize_dialog.h"
89 #include "paste_context.h"
90 #include "patch_change_dialog.h"
91 #include "quantize_dialog.h"
92 #include "region_gain_line.h"
93 #include "rgb_macros.h"
94 #include "route_time_axis.h"
95 #include "selection.h"
96 #include "selection_templates.h"
97 #include "streamview.h"
98 #include "strip_silence_dialog.h"
99 #include "time_axis_view.h"
101 #include "transpose_dialog.h"
102 #include "transform_dialog.h"
103 #include "ui_config.h"
105 #include "pbd/i18n.h"
108 using namespace ARDOUR;
111 using namespace Gtkmm2ext;
112 using namespace Editing;
113 using Gtkmm2ext::Keyboard;
115 /***********************************************************************
117 ***********************************************************************/
120 Editor::undo (uint32_t n)
122 if (_session && _session->actively_recording()) {
123 /* no undo allowed while recording. Session will check also,
124 but we don't even want to get to that.
129 if (_drags->active ()) {
135 if (_session->undo_depth() == 0) {
136 undo_action->set_sensitive(false);
138 redo_action->set_sensitive(true);
139 begin_selection_op_history ();
144 Editor::redo (uint32_t n)
146 if (_session && _session->actively_recording()) {
147 /* no redo allowed while recording. Session will check also,
148 but we don't even want to get to that.
153 if (_drags->active ()) {
159 if (_session->redo_depth() == 0) {
160 redo_action->set_sensitive(false);
162 undo_action->set_sensitive(true);
163 begin_selection_op_history ();
168 Editor::split_regions_at (framepos_t where, RegionSelection& regions, const int32_t sub_num)
172 RegionSelection pre_selected_regions = selection->regions;
173 bool working_on_selection = !pre_selected_regions.empty();
175 list<boost::shared_ptr<Playlist> > used_playlists;
176 list<RouteTimeAxisView*> used_trackviews;
178 if (regions.empty()) {
182 begin_reversible_command (_("split"));
184 // if splitting a single region, and snap-to is using
185 // region boundaries, don't pay attention to them
187 if (regions.size() == 1) {
188 switch (_snap_type) {
189 case SnapToRegionStart:
190 case SnapToRegionSync:
191 case SnapToRegionEnd:
200 EditorFreeze(); /* Emit Signal */
203 for (RegionSelection::iterator a = regions.begin(); a != regions.end(); ) {
205 RegionSelection::iterator tmp;
207 /* XXX this test needs to be more complicated, to make sure we really
208 have something to split.
211 if (!(*a)->region()->covers (where)) {
219 boost::shared_ptr<Playlist> pl = (*a)->region()->playlist();
227 /* we haven't seen this playlist before */
229 /* remember used playlists so we can thaw them later */
230 used_playlists.push_back(pl);
232 TimeAxisView& tv = (*a)->get_time_axis_view();
233 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (&tv);
235 used_trackviews.push_back (rtv);
242 pl->clear_changes ();
243 pl->split_region ((*a)->region(), where, sub_num);
244 _session->add_command (new StatefulDiffCommand (pl));
250 latest_regionviews.clear ();
252 vector<sigc::connection> region_added_connections;
254 for (list<RouteTimeAxisView*>::iterator i = used_trackviews.begin(); i != used_trackviews.end(); ++i) {
255 region_added_connections.push_back ((*i)->view()->RegionViewAdded.connect (sigc::mem_fun(*this, &Editor::collect_new_region_view)));
258 while (used_playlists.size() > 0) {
259 list <boost::shared_ptr<Playlist > >::iterator i = used_playlists.begin();
261 used_playlists.pop_front();
264 for (vector<sigc::connection>::iterator c = region_added_connections.begin(); c != region_added_connections.end(); ++c) {
269 EditorThaw(); /* Emit Signal */
272 if (working_on_selection) {
273 // IFF we were working on selected regions, try to reinstate the other region selections that existed before the freeze/thaw.
275 _ignore_follow_edits = true; // a split will change the region selection in mysterious ways; it's not practical or wanted to follow this edit
276 RegionSelectionAfterSplit rsas = Config->get_region_selection_after_split();
277 /* There are three classes of regions that we might want selected after
278 splitting selected regions:
279 - regions selected before the split operation, and unaffected by it
280 - newly-created regions before the split
281 - newly-created regions after the split
284 if (rsas & Existing) {
285 // region selections that existed before the split.
286 selection->add ( pre_selected_regions );
289 for (RegionSelection::iterator ri = latest_regionviews.begin(); ri != latest_regionviews.end(); ri++) {
290 if ((*ri)->region()->position() < where) {
291 // new regions created before the split
292 if (rsas & NewlyCreatedLeft) {
293 selection->add (*ri);
296 // new regions created after the split
297 if (rsas & NewlyCreatedRight) {
298 selection->add (*ri);
302 _ignore_follow_edits = false;
304 _ignore_follow_edits = true;
305 if( working_on_selection ) {
306 selection->add (latest_regionviews); //these are the new regions created after the split
308 _ignore_follow_edits = false;
311 commit_reversible_command ();
314 /** Move one extreme of the current range selection. If more than one range is selected,
315 * the start of the earliest range or the end of the latest range is moved.
317 * @param move_end true to move the end of the current range selection, false to move
319 * @param next true to move the extreme to the next region boundary, false to move to
323 Editor::move_range_selection_start_or_end_to_region_boundary (bool move_end, bool next)
325 if (selection->time.start() == selection->time.end_frame()) {
329 framepos_t start = selection->time.start ();
330 framepos_t end = selection->time.end_frame ();
332 /* the position of the thing we may move */
333 framepos_t pos = move_end ? end : start;
334 int dir = next ? 1 : -1;
336 /* so we don't find the current region again */
337 if (dir > 0 || pos > 0) {
341 framepos_t const target = get_region_boundary (pos, dir, true, false);
356 begin_reversible_selection_op (_("alter selection"));
357 selection->set_preserving_all_ranges (start, end);
358 commit_reversible_selection_op ();
362 Editor::nudge_forward_release (GdkEventButton* ev)
364 if (ev->state & Keyboard::PrimaryModifier) {
365 nudge_forward (false, true);
367 nudge_forward (false, false);
373 Editor::nudge_backward_release (GdkEventButton* ev)
375 if (ev->state & Keyboard::PrimaryModifier) {
376 nudge_backward (false, true);
378 nudge_backward (false, false);
385 Editor::nudge_forward (bool next, bool force_playhead)
388 framepos_t next_distance;
394 RegionSelection rs = get_regions_from_selection_and_entered ();
396 if (!force_playhead && !rs.empty()) {
398 begin_reversible_command (_("nudge regions forward"));
400 for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
401 boost::shared_ptr<Region> r ((*i)->region());
403 distance = get_nudge_distance (r->position(), next_distance);
406 distance = next_distance;
410 r->set_position (r->position() + distance);
411 _session->add_command (new StatefulDiffCommand (r));
414 commit_reversible_command ();
417 } else if (!force_playhead && !selection->markers.empty()) {
420 bool in_command = false;
422 for (MarkerSelection::iterator i = selection->markers.begin(); i != selection->markers.end(); ++i) {
424 Location* loc = find_location_from_marker ((*i), is_start);
428 XMLNode& before (loc->get_state());
431 distance = get_nudge_distance (loc->start(), next_distance);
433 distance = next_distance;
435 if (max_framepos - distance > loc->start() + loc->length()) {
436 loc->set_start (loc->start() + distance);
438 loc->set_start (max_framepos - loc->length());
441 distance = get_nudge_distance (loc->end(), next_distance);
443 distance = next_distance;
445 if (max_framepos - distance > loc->end()) {
446 loc->set_end (loc->end() + distance);
448 loc->set_end (max_framepos);
450 if (loc->is_session_range()) {
451 _session->set_end_is_free (false);
455 begin_reversible_command (_("nudge location forward"));
458 XMLNode& after (loc->get_state());
459 _session->add_command (new MementoCommand<Location>(*loc, &before, &after));
464 commit_reversible_command ();
467 distance = get_nudge_distance (playhead_cursor->current_frame (), next_distance);
468 _session->request_locate (playhead_cursor->current_frame () + distance);
473 Editor::nudge_backward (bool next, bool force_playhead)
476 framepos_t next_distance;
482 RegionSelection rs = get_regions_from_selection_and_entered ();
484 if (!force_playhead && !rs.empty()) {
486 begin_reversible_command (_("nudge regions backward"));
488 for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
489 boost::shared_ptr<Region> r ((*i)->region());
491 distance = get_nudge_distance (r->position(), next_distance);
494 distance = next_distance;
499 if (r->position() > distance) {
500 r->set_position (r->position() - distance);
504 _session->add_command (new StatefulDiffCommand (r));
507 commit_reversible_command ();
509 } else if (!force_playhead && !selection->markers.empty()) {
512 bool in_command = false;
514 for (MarkerSelection::iterator i = selection->markers.begin(); i != selection->markers.end(); ++i) {
516 Location* loc = find_location_from_marker ((*i), is_start);
520 XMLNode& before (loc->get_state());
523 distance = get_nudge_distance (loc->start(), next_distance);
525 distance = next_distance;
527 if (distance < loc->start()) {
528 loc->set_start (loc->start() - distance);
533 distance = get_nudge_distance (loc->end(), next_distance);
536 distance = next_distance;
539 if (distance < loc->end() - loc->length()) {
540 loc->set_end (loc->end() - distance);
542 loc->set_end (loc->length());
544 if (loc->is_session_range()) {
545 _session->set_end_is_free (false);
549 begin_reversible_command (_("nudge location forward"));
552 XMLNode& after (loc->get_state());
553 _session->add_command (new MementoCommand<Location>(*loc, &before, &after));
557 commit_reversible_command ();
562 distance = get_nudge_distance (playhead_cursor->current_frame (), next_distance);
564 if (playhead_cursor->current_frame () > distance) {
565 _session->request_locate (playhead_cursor->current_frame () - distance);
567 _session->goto_start();
573 Editor::nudge_forward_capture_offset ()
575 RegionSelection rs = get_regions_from_selection_and_entered ();
577 if (!_session || rs.empty()) {
581 begin_reversible_command (_("nudge forward"));
583 framepos_t const distance = _session->worst_output_latency();
585 for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
586 boost::shared_ptr<Region> r ((*i)->region());
589 r->set_position (r->position() + distance);
590 _session->add_command(new StatefulDiffCommand (r));
593 commit_reversible_command ();
597 Editor::nudge_backward_capture_offset ()
599 RegionSelection rs = get_regions_from_selection_and_entered ();
601 if (!_session || rs.empty()) {
605 begin_reversible_command (_("nudge backward"));
607 framepos_t const distance = _session->worst_output_latency();
609 for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
610 boost::shared_ptr<Region> r ((*i)->region());
614 if (r->position() > distance) {
615 r->set_position (r->position() - distance);
619 _session->add_command(new StatefulDiffCommand (r));
622 commit_reversible_command ();
625 struct RegionSelectionPositionSorter {
626 bool operator() (RegionView* a, RegionView* b) {
627 return a->region()->position() < b->region()->position();
632 Editor::sequence_regions ()
635 framepos_t r_end_prev;
643 RegionSelection rs = get_regions_from_selection_and_entered ();
644 rs.sort(RegionSelectionPositionSorter());
648 bool in_command = false;
650 for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
651 boost::shared_ptr<Region> r ((*i)->region());
659 if(r->position_locked())
666 r->set_position(r_end_prev);
670 begin_reversible_command (_("sequence regions"));
673 _session->add_command (new StatefulDiffCommand (r));
675 r_end=r->position() + r->length();
681 commit_reversible_command ();
690 Editor::move_to_start ()
692 _session->goto_start ();
696 Editor::move_to_end ()
699 _session->request_locate (_session->current_end_frame());
703 Editor::build_region_boundary_cache ()
706 vector<RegionPoint> interesting_points;
707 boost::shared_ptr<Region> r;
708 TrackViewList tracks;
711 region_boundary_cache.clear ();
717 switch (_snap_type) {
718 case SnapToRegionStart:
719 interesting_points.push_back (Start);
721 case SnapToRegionEnd:
722 interesting_points.push_back (End);
724 case SnapToRegionSync:
725 interesting_points.push_back (SyncPoint);
727 case SnapToRegionBoundary:
728 interesting_points.push_back (Start);
729 interesting_points.push_back (End);
732 fatal << string_compose (_("build_region_boundary_cache called with snap_type = %1"), _snap_type) << endmsg;
733 abort(); /*NOTREACHED*/
737 TimeAxisView *ontrack = 0;
740 if (!selection->tracks.empty()) {
741 tlist = selection->tracks.filter_to_unique_playlists ();
743 tlist = track_views.filter_to_unique_playlists ();
746 while (pos < _session->current_end_frame() && !at_end) {
749 framepos_t lpos = max_framepos;
751 for (vector<RegionPoint>::iterator p = interesting_points.begin(); p != interesting_points.end(); ++p) {
753 if ((r = find_next_region (pos, *p, 1, tlist, &ontrack)) == 0) {
754 if (*p == interesting_points.back()) {
757 /* move to next point type */
763 rpos = r->first_frame();
767 rpos = r->last_frame();
771 rpos = r->sync_position ();
779 RouteTimeAxisView *rtav;
781 if (ontrack != 0 && (rtav = dynamic_cast<RouteTimeAxisView*>(ontrack)) != 0 ) {
782 if (rtav->track() != 0) {
783 speed = rtav->track()->speed();
787 rpos = track_frame_to_session_frame (rpos, speed);
793 /* prevent duplicates, but we don't use set<> because we want to be able
797 vector<framepos_t>::iterator ri;
799 for (ri = region_boundary_cache.begin(); ri != region_boundary_cache.end(); ++ri) {
805 if (ri == region_boundary_cache.end()) {
806 region_boundary_cache.push_back (rpos);
813 /* finally sort to be sure that the order is correct */
815 sort (region_boundary_cache.begin(), region_boundary_cache.end());
818 boost::shared_ptr<Region>
819 Editor::find_next_region (framepos_t frame, RegionPoint point, int32_t dir, TrackViewList& tracks, TimeAxisView **ontrack)
821 TrackViewList::iterator i;
822 framepos_t closest = max_framepos;
823 boost::shared_ptr<Region> ret;
827 framepos_t track_frame;
828 RouteTimeAxisView *rtav;
830 for (i = tracks.begin(); i != tracks.end(); ++i) {
833 boost::shared_ptr<Region> r;
836 if ( (rtav = dynamic_cast<RouteTimeAxisView*>(*i)) != 0 ) {
837 if (rtav->track()!=0)
838 track_speed = rtav->track()->speed();
841 track_frame = session_frame_to_track_frame(frame, track_speed);
843 if ((r = (*i)->find_next_region (track_frame, point, dir)) == 0) {
849 rpos = r->first_frame ();
853 rpos = r->last_frame ();
857 rpos = r->sync_position ();
861 // rpos is a "track frame", converting it to "_session frame"
862 rpos = track_frame_to_session_frame(rpos, track_speed);
865 distance = rpos - frame;
867 distance = frame - rpos;
870 if (distance < closest) {
882 Editor::find_next_region_boundary (framepos_t pos, int32_t dir, const TrackViewList& tracks)
884 framecnt_t distance = max_framepos;
885 framepos_t current_nearest = -1;
887 for (TrackViewList::const_iterator i = tracks.begin(); i != tracks.end(); ++i) {
888 framepos_t contender;
891 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (*i);
897 if ((contender = rtv->find_next_region_boundary (pos, dir)) < 0) {
901 d = ::llabs (pos - contender);
904 current_nearest = contender;
909 return current_nearest;
913 Editor::get_region_boundary (framepos_t pos, int32_t dir, bool with_selection, bool only_onscreen)
918 if (with_selection && Config->get_region_boundaries_from_selected_tracks()) {
920 if (!selection->tracks.empty()) {
922 target = find_next_region_boundary (pos, dir, selection->tracks);
926 if (only_onscreen || Config->get_region_boundaries_from_onscreen_tracks()) {
927 get_onscreen_tracks (tvl);
928 target = find_next_region_boundary (pos, dir, tvl);
930 target = find_next_region_boundary (pos, dir, track_views);
936 if (only_onscreen || Config->get_region_boundaries_from_onscreen_tracks()) {
937 get_onscreen_tracks (tvl);
938 target = find_next_region_boundary (pos, dir, tvl);
940 target = find_next_region_boundary (pos, dir, track_views);
948 Editor::cursor_to_region_boundary (bool with_selection, int32_t dir)
950 framepos_t pos = playhead_cursor->current_frame ();
957 // so we don't find the current region again..
958 if (dir > 0 || pos > 0) {
962 if ((target = get_region_boundary (pos, dir, with_selection, false)) < 0) {
966 _session->request_locate (target);
970 Editor::cursor_to_next_region_boundary (bool with_selection)
972 cursor_to_region_boundary (with_selection, 1);
976 Editor::cursor_to_previous_region_boundary (bool with_selection)
978 cursor_to_region_boundary (with_selection, -1);
982 Editor::cursor_to_region_point (EditorCursor* cursor, RegionPoint point, int32_t dir)
984 boost::shared_ptr<Region> r;
985 framepos_t pos = cursor->current_frame ();
991 TimeAxisView *ontrack = 0;
993 // so we don't find the current region again..
997 if (!selection->tracks.empty()) {
999 r = find_next_region (pos, point, dir, selection->tracks, &ontrack);
1001 } else if (clicked_axisview) {
1004 t.push_back (clicked_axisview);
1006 r = find_next_region (pos, point, dir, t, &ontrack);
1010 r = find_next_region (pos, point, dir, track_views, &ontrack);
1019 pos = r->first_frame ();
1023 pos = r->last_frame ();
1027 pos = r->sync_position ();
1032 RouteTimeAxisView *rtav;
1034 if ( ontrack != 0 && (rtav = dynamic_cast<RouteTimeAxisView*>(ontrack)) != 0 ) {
1035 if (rtav->track() != 0) {
1036 speed = rtav->track()->speed();
1040 pos = track_frame_to_session_frame(pos, speed);
1042 if (cursor == playhead_cursor) {
1043 _session->request_locate (pos);
1045 cursor->set_position (pos);
1050 Editor::cursor_to_next_region_point (EditorCursor* cursor, RegionPoint point)
1052 cursor_to_region_point (cursor, point, 1);
1056 Editor::cursor_to_previous_region_point (EditorCursor* cursor, RegionPoint point)
1058 cursor_to_region_point (cursor, point, -1);
1062 Editor::cursor_to_selection_start (EditorCursor *cursor)
1066 switch (mouse_mode) {
1068 if (!selection->regions.empty()) {
1069 pos = selection->regions.start();
1074 if (!selection->time.empty()) {
1075 pos = selection->time.start ();
1083 if (cursor == playhead_cursor) {
1084 _session->request_locate (pos);
1086 cursor->set_position (pos);
1091 Editor::cursor_to_selection_end (EditorCursor *cursor)
1095 switch (mouse_mode) {
1097 if (!selection->regions.empty()) {
1098 pos = selection->regions.end_frame();
1103 if (!selection->time.empty()) {
1104 pos = selection->time.end_frame ();
1112 if (cursor == playhead_cursor) {
1113 _session->request_locate (pos);
1115 cursor->set_position (pos);
1120 Editor::selected_marker_to_region_boundary (bool with_selection, int32_t dir)
1130 if (selection->markers.empty()) {
1134 if (!mouse_frame (mouse, ignored)) {
1138 add_location_mark (mouse);
1141 if ((loc = find_location_from_marker (selection->markers.front(), ignored)) == 0) {
1145 framepos_t pos = loc->start();
1147 // so we don't find the current region again..
1148 if (dir > 0 || pos > 0) {
1152 if ((target = get_region_boundary (pos, dir, with_selection, false)) < 0) {
1156 loc->move_to (target);
1160 Editor::selected_marker_to_next_region_boundary (bool with_selection)
1162 selected_marker_to_region_boundary (with_selection, 1);
1166 Editor::selected_marker_to_previous_region_boundary (bool with_selection)
1168 selected_marker_to_region_boundary (with_selection, -1);
1172 Editor::selected_marker_to_region_point (RegionPoint point, int32_t dir)
1174 boost::shared_ptr<Region> r;
1179 if (!_session || selection->markers.empty()) {
1183 if ((loc = find_location_from_marker (selection->markers.front(), ignored)) == 0) {
1187 TimeAxisView *ontrack = 0;
1191 // so we don't find the current region again..
1195 if (!selection->tracks.empty()) {
1197 r = find_next_region (pos, point, dir, selection->tracks, &ontrack);
1201 r = find_next_region (pos, point, dir, track_views, &ontrack);
1210 pos = r->first_frame ();
1214 pos = r->last_frame ();
1218 pos = r->adjust_to_sync (r->first_frame());
1223 RouteTimeAxisView *rtav;
1225 if (ontrack != 0 && (rtav = dynamic_cast<RouteTimeAxisView*>(ontrack)) != 0) {
1226 if (rtav->track() != 0) {
1227 speed = rtav->track()->speed();
1231 pos = track_frame_to_session_frame(pos, speed);
1237 Editor::selected_marker_to_next_region_point (RegionPoint point)
1239 selected_marker_to_region_point (point, 1);
1243 Editor::selected_marker_to_previous_region_point (RegionPoint point)
1245 selected_marker_to_region_point (point, -1);
1249 Editor::selected_marker_to_selection_start ()
1255 if (!_session || selection->markers.empty()) {
1259 if ((loc = find_location_from_marker (selection->markers.front(), ignored)) == 0) {
1263 switch (mouse_mode) {
1265 if (!selection->regions.empty()) {
1266 pos = selection->regions.start();
1271 if (!selection->time.empty()) {
1272 pos = selection->time.start ();
1284 Editor::selected_marker_to_selection_end ()
1290 if (!_session || selection->markers.empty()) {
1294 if ((loc = find_location_from_marker (selection->markers.front(), ignored)) == 0) {
1298 switch (mouse_mode) {
1300 if (!selection->regions.empty()) {
1301 pos = selection->regions.end_frame();
1306 if (!selection->time.empty()) {
1307 pos = selection->time.end_frame ();
1319 Editor::scroll_playhead (bool forward)
1321 framepos_t pos = playhead_cursor->current_frame ();
1322 framecnt_t delta = (framecnt_t) floor (current_page_samples() / 0.8);
1325 if (pos == max_framepos) {
1329 if (pos < max_framepos - delta) {
1348 _session->request_locate (pos);
1352 Editor::cursor_align (bool playhead_to_edit)
1358 if (playhead_to_edit) {
1360 if (selection->markers.empty()) {
1364 _session->request_locate (selection->markers.front()->position(), _session->transport_rolling());
1367 /* move selected markers to playhead */
1369 for (MarkerSelection::iterator i = selection->markers.begin(); i != selection->markers.end(); ++i) {
1372 Location* loc = find_location_from_marker (*i, ignored);
1374 if (loc->is_mark()) {
1375 loc->set_start (playhead_cursor->current_frame ());
1377 loc->set (playhead_cursor->current_frame (),
1378 playhead_cursor->current_frame () + loc->length());
1385 Editor::scroll_backward (float pages)
1387 framepos_t const one_page = (framepos_t) rint (_visible_canvas_width * samples_per_pixel);
1388 framepos_t const cnt = (framepos_t) floor (pages * one_page);
1391 if (leftmost_frame < cnt) {
1394 frame = leftmost_frame - cnt;
1397 reset_x_origin (frame);
1401 Editor::scroll_forward (float pages)
1403 framepos_t const one_page = (framepos_t) rint (_visible_canvas_width * samples_per_pixel);
1404 framepos_t const cnt = (framepos_t) floor (pages * one_page);
1407 if (max_framepos - cnt < leftmost_frame) {
1408 frame = max_framepos - cnt;
1410 frame = leftmost_frame + cnt;
1413 reset_x_origin (frame);
1417 Editor::scroll_tracks_down ()
1419 double vert_value = vertical_adjustment.get_value() + vertical_adjustment.get_page_size();
1420 if (vert_value > vertical_adjustment.get_upper() - _visible_canvas_height) {
1421 vert_value = vertical_adjustment.get_upper() - _visible_canvas_height;
1424 vertical_adjustment.set_value (vert_value);
1428 Editor::scroll_tracks_up ()
1430 vertical_adjustment.set_value (vertical_adjustment.get_value() - vertical_adjustment.get_page_size());
1434 Editor::scroll_tracks_down_line ()
1436 double vert_value = vertical_adjustment.get_value() + 60;
1438 if (vert_value > vertical_adjustment.get_upper() - _visible_canvas_height) {
1439 vert_value = vertical_adjustment.get_upper() - _visible_canvas_height;
1442 vertical_adjustment.set_value (vert_value);
1446 Editor::scroll_tracks_up_line ()
1448 reset_y_origin (vertical_adjustment.get_value() - 60);
1452 Editor::scroll_down_one_track (bool skip_child_views)
1454 TrackViewList::reverse_iterator next = track_views.rend();
1455 const double top_of_trackviews = vertical_adjustment.get_value();
1457 for (TrackViewList::reverse_iterator t = track_views.rbegin(); t != track_views.rend(); ++t) {
1458 if ((*t)->hidden()) {
1462 /* If this is the upper-most visible trackview, we want to display
1463 * the one above it (next)
1465 * Note that covers_y_position() is recursive and includes child views
1467 std::pair<TimeAxisView*,double> res = (*t)->covers_y_position (top_of_trackviews);
1470 if (skip_child_views) {
1473 /* automation lane (one level, non-recursive)
1475 * - if no automation lane exists -> move to next tack
1476 * - if the first (here: bottom-most) matches -> move to next tack
1477 * - if no y-axis match is found -> the current track is at the top
1478 * -> move to last (here: top-most) automation lane
1480 TimeAxisView::Children kids = (*t)->get_child_list();
1481 TimeAxisView::Children::reverse_iterator nkid = kids.rend();
1483 for (TimeAxisView::Children::reverse_iterator ci = kids.rbegin(); ci != kids.rend(); ++ci) {
1484 if ((*ci)->hidden()) {
1488 std::pair<TimeAxisView*,double> dev;
1489 dev = (*ci)->covers_y_position (top_of_trackviews);
1491 /* some automation lane is currently at the top */
1492 if (ci == kids.rbegin()) {
1493 /* first (bottom-most) autmation lane is at the top.
1494 * -> move to next track
1503 if (nkid != kids.rend()) {
1504 ensure_time_axis_view_is_visible (**nkid, true);
1512 /* move to the track below the first one that covers the */
1514 if (next != track_views.rend()) {
1515 ensure_time_axis_view_is_visible (**next, true);
1523 Editor::scroll_up_one_track (bool skip_child_views)
1525 TrackViewList::iterator prev = track_views.end();
1526 double top_of_trackviews = vertical_adjustment.get_value ();
1528 for (TrackViewList::iterator t = track_views.begin(); t != track_views.end(); ++t) {
1530 if ((*t)->hidden()) {
1534 /* find the trackview at the top of the trackview group
1536 * Note that covers_y_position() is recursive and includes child views
1538 std::pair<TimeAxisView*,double> res = (*t)->covers_y_position (top_of_trackviews);
1541 if (skip_child_views) {
1544 /* automation lane (one level, non-recursive)
1546 * - if no automation lane exists -> move to prev tack
1547 * - if no y-axis match is found -> the current track is at the top -> move to prev track
1548 * (actually last automation lane of previous track, see below)
1549 * - if first (top-most) lane is at the top -> move to this track
1550 * - else move up one lane
1552 TimeAxisView::Children kids = (*t)->get_child_list();
1553 TimeAxisView::Children::iterator pkid = kids.end();
1555 for (TimeAxisView::Children::iterator ci = kids.begin(); ci != kids.end(); ++ci) {
1556 if ((*ci)->hidden()) {
1560 std::pair<TimeAxisView*,double> dev;
1561 dev = (*ci)->covers_y_position (top_of_trackviews);
1563 /* some automation lane is currently at the top */
1564 if (ci == kids.begin()) {
1565 /* first (top-most) autmation lane is at the top.
1566 * jump directly to this track's top
1568 ensure_time_axis_view_is_visible (**t, true);
1571 else if (pkid != kids.end()) {
1572 /* some other automation lane is at the top.
1573 * move up to prev automation lane.
1575 ensure_time_axis_view_is_visible (**pkid, true);
1578 assert(0); // not reached
1589 if (prev != track_views.end()) {
1590 // move to bottom-most automation-lane of the previous track
1591 TimeAxisView::Children kids = (*prev)->get_child_list();
1592 TimeAxisView::Children::reverse_iterator pkid = kids.rend();
1593 if (!skip_child_views) {
1594 // find the last visible lane
1595 for (TimeAxisView::Children::reverse_iterator ci = kids.rbegin(); ci != kids.rend(); ++ci) {
1596 if (!(*ci)->hidden()) {
1602 if (pkid != kids.rend()) {
1603 ensure_time_axis_view_is_visible (**pkid, true);
1605 ensure_time_axis_view_is_visible (**prev, true);
1614 Editor::scroll_left_step ()
1616 framepos_t xdelta = (current_page_samples() / 8);
1618 if (leftmost_frame > xdelta) {
1619 reset_x_origin (leftmost_frame - xdelta);
1627 Editor::scroll_right_step ()
1629 framepos_t xdelta = (current_page_samples() / 8);
1631 if (max_framepos - xdelta > leftmost_frame) {
1632 reset_x_origin (leftmost_frame + xdelta);
1634 reset_x_origin (max_framepos - current_page_samples());
1639 Editor::scroll_left_half_page ()
1641 framepos_t xdelta = (current_page_samples() / 2);
1642 if (leftmost_frame > xdelta) {
1643 reset_x_origin (leftmost_frame - xdelta);
1650 Editor::scroll_right_half_page ()
1652 framepos_t xdelta = (current_page_samples() / 2);
1653 if (max_framepos - xdelta > leftmost_frame) {
1654 reset_x_origin (leftmost_frame + xdelta);
1656 reset_x_origin (max_framepos - current_page_samples());
1663 Editor::tav_zoom_step (bool coarser)
1665 DisplaySuspender ds;
1669 if (selection->tracks.empty()) {
1672 ts = &selection->tracks;
1675 for (TrackViewList::iterator i = ts->begin(); i != ts->end(); ++i) {
1676 TimeAxisView *tv = (static_cast<TimeAxisView*>(*i));
1677 tv->step_height (coarser);
1682 Editor::tav_zoom_smooth (bool coarser, bool force_all)
1684 DisplaySuspender ds;
1688 if (selection->tracks.empty() || force_all) {
1691 ts = &selection->tracks;
1694 for (TrackViewList::iterator i = ts->begin(); i != ts->end(); ++i) {
1695 TimeAxisView *tv = (static_cast<TimeAxisView*>(*i));
1696 uint32_t h = tv->current_height ();
1701 if (h >= TimeAxisView::preset_height (HeightSmall)) {
1706 tv->set_height (h + 5);
1712 Editor::temporal_zoom_step_mouse_focus (bool coarser)
1714 Editing::ZoomFocus temp_focus = zoom_focus;
1715 zoom_focus = Editing::ZoomFocusMouse;
1716 temporal_zoom_step (coarser);
1717 zoom_focus = temp_focus;
1721 Editor::temporal_zoom_step (bool coarser)
1723 ENSURE_GUI_THREAD (*this, &Editor::temporal_zoom_step, coarser)
1725 framecnt_t nspp = samples_per_pixel;
1733 temporal_zoom (nspp);
1737 Editor::temporal_zoom (framecnt_t fpp)
1743 framepos_t current_page = current_page_samples();
1744 framepos_t current_leftmost = leftmost_frame;
1745 framepos_t current_rightmost;
1746 framepos_t current_center;
1747 framepos_t new_page_size;
1748 framepos_t half_page_size;
1749 framepos_t leftmost_after_zoom = 0;
1751 bool in_track_canvas;
1755 if (fpp == samples_per_pixel) {
1759 // Imposing an arbitrary limit to zoom out as too much zoom out produces
1760 // segfaults for lack of memory. If somebody decides this is not high enough I
1761 // believe it can be raisen to higher values but some limit must be in place.
1763 // This constant represents 1 day @ 48kHz on a 1600 pixel wide display
1764 // all of which is used for the editor track displays. The whole day
1765 // would be 4147200000 samples, so 2592000 samples per pixel.
1767 nfpp = min (fpp, (framecnt_t) 2592000);
1768 nfpp = max ((framecnt_t) 1, nfpp);
1770 new_page_size = (framepos_t) floor (_visible_canvas_width * nfpp);
1771 half_page_size = new_page_size / 2;
1773 switch (zoom_focus) {
1775 leftmost_after_zoom = current_leftmost;
1778 case ZoomFocusRight:
1779 current_rightmost = leftmost_frame + current_page;
1780 if (current_rightmost < new_page_size) {
1781 leftmost_after_zoom = 0;
1783 leftmost_after_zoom = current_rightmost - new_page_size;
1787 case ZoomFocusCenter:
1788 current_center = current_leftmost + (current_page/2);
1789 if (current_center < half_page_size) {
1790 leftmost_after_zoom = 0;
1792 leftmost_after_zoom = current_center - half_page_size;
1796 case ZoomFocusPlayhead:
1797 /* centre playhead */
1798 l = playhead_cursor->current_frame () - (new_page_size * 0.5);
1801 leftmost_after_zoom = 0;
1802 } else if (l > max_framepos) {
1803 leftmost_after_zoom = max_framepos - new_page_size;
1805 leftmost_after_zoom = (framepos_t) l;
1809 case ZoomFocusMouse:
1810 /* try to keep the mouse over the same point in the display */
1812 if (!mouse_frame (where, in_track_canvas)) {
1813 /* use playhead instead */
1814 where = playhead_cursor->current_frame ();
1816 if (where < half_page_size) {
1817 leftmost_after_zoom = 0;
1819 leftmost_after_zoom = where - half_page_size;
1824 l = - ((new_page_size * ((where - current_leftmost)/(double)current_page)) - where);
1827 leftmost_after_zoom = 0;
1828 } else if (l > max_framepos) {
1829 leftmost_after_zoom = max_framepos - new_page_size;
1831 leftmost_after_zoom = (framepos_t) l;
1838 /* try to keep the edit point in the same place */
1839 where = get_preferred_edit_position ();
1843 double l = - ((new_page_size * ((where - current_leftmost)/(double)current_page)) - where);
1846 leftmost_after_zoom = 0;
1847 } else if (l > max_framepos) {
1848 leftmost_after_zoom = max_framepos - new_page_size;
1850 leftmost_after_zoom = (framepos_t) l;
1854 /* edit point not defined */
1861 // leftmost_after_zoom = min (leftmost_after_zoom, _session->current_end_frame());
1863 reposition_and_zoom (leftmost_after_zoom, nfpp);
1867 Editor::calc_extra_zoom_edges(framepos_t &start, framepos_t &end)
1869 /* this func helps make sure we leave a little space
1870 at each end of the editor so that the zoom doesn't fit the region
1871 precisely to the screen.
1874 GdkScreen* screen = gdk_screen_get_default ();
1875 const gint pixwidth = gdk_screen_get_width (screen);
1876 const gint mmwidth = gdk_screen_get_width_mm (screen);
1877 const double pix_per_mm = (double) pixwidth/ (double) mmwidth;
1878 const double one_centimeter_in_pixels = pix_per_mm * 10.0;
1880 const framepos_t range = end - start;
1881 const framecnt_t new_fpp = (framecnt_t) ceil ((double) range / (double) _visible_canvas_width);
1882 const framepos_t extra_samples = (framepos_t) floor (one_centimeter_in_pixels * new_fpp);
1884 if (start > extra_samples) {
1885 start -= extra_samples;
1890 if (max_framepos - extra_samples > end) {
1891 end += extra_samples;
1898 Editor::temporal_zoom_region (bool both_axes)
1900 framepos_t start = max_framepos;
1902 set<TimeAxisView*> tracks;
1904 if ( !get_selection_extents(start, end) )
1907 calc_extra_zoom_edges (start, end);
1909 /* if we're zooming on both axes we need to save track heights etc.
1912 undo_visual_stack.push_back (current_visual_state (both_axes));
1914 PBD::Unwinder<bool> nsv (no_save_visual, true);
1916 temporal_zoom_by_frame (start, end);
1919 uint32_t per_track_height = (uint32_t) floor ((_visible_canvas_height - 10.0) / tracks.size());
1921 /* set visible track heights appropriately */
1923 for (set<TimeAxisView*>::iterator t = tracks.begin(); t != tracks.end(); ++t) {
1924 (*t)->set_height (per_track_height);
1927 /* hide irrelevant tracks */
1929 DisplaySuspender ds;
1931 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
1932 if (find (tracks.begin(), tracks.end(), (*i)) == tracks.end()) {
1933 hide_track_in_display (*i);
1937 vertical_adjustment.set_value (0.0);
1940 redo_visual_stack.push_back (current_visual_state (both_axes));
1945 Editor::get_selection_extents (framepos_t &start, framepos_t &end) const
1947 start = max_framepos;
1951 //ToDo: if notes are selected, set extents to that selection
1953 //ToDo: if control points are selected, set extents to that selection
1955 if ( !selection->regions.empty() ) {
1956 RegionSelection rs = get_regions_from_selection_and_entered ();
1958 for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
1960 if ((*i)->region()->position() < start) {
1961 start = (*i)->region()->position();
1964 if ((*i)->region()->last_frame() + 1 > end) {
1965 end = (*i)->region()->last_frame() + 1;
1969 } else if (!selection->time.empty()) {
1970 start = selection->time.start();
1971 end = selection->time.end_frame();
1973 ret = false; //no selection found
1976 if ((start == 0 && end == 0) || end < start) {
1985 Editor::temporal_zoom_selection (bool both_axes)
1987 if (!selection) return;
1989 //ToDo: if notes are selected, zoom to that
1991 //ToDo: if control points are selected, zoom to that
1993 //if region(s) are selected, zoom to that
1994 if ( !selection->regions.empty() )
1995 temporal_zoom_region (both_axes);
1997 //if a range is selected, zoom to that
1998 if (!selection->time.empty()) {
2000 framepos_t start, end;
2001 if (get_selection_extents (start, end)) {
2002 calc_extra_zoom_edges(start, end);
2003 temporal_zoom_by_frame (start, end);
2013 Editor::temporal_zoom_session ()
2015 ENSURE_GUI_THREAD (*this, &Editor::temporal_zoom_session)
2018 framecnt_t start = _session->current_start_frame();
2019 framecnt_t end = _session->current_end_frame();
2021 if (_session->actively_recording () ) {
2022 framepos_t cur = playhead_cursor->current_frame ();
2024 /* recording beyond the end marker; zoom out
2025 * by 5 seconds more so that if 'follow
2026 * playhead' is active we don't immediately
2029 end = cur + _session->frame_rate() * 5;
2033 if ((start == 0 && end == 0) || end < start) {
2037 calc_extra_zoom_edges(start, end);
2039 temporal_zoom_by_frame (start, end);
2044 Editor::temporal_zoom_by_frame (framepos_t start, framepos_t end)
2046 if (!_session) return;
2048 if ((start == 0 && end == 0) || end < start) {
2052 framepos_t range = end - start;
2054 const framecnt_t new_fpp = (framecnt_t) ceil ((double) range / (double) _visible_canvas_width);
2056 framepos_t new_page = range;
2057 framepos_t middle = (framepos_t) floor ((double) start + ((double) range / 2.0f));
2058 framepos_t new_leftmost = (framepos_t) floor ((double) middle - ((double) new_page / 2.0f));
2060 if (new_leftmost > middle) {
2064 if (new_leftmost < 0) {
2068 reposition_and_zoom (new_leftmost, new_fpp);
2072 Editor::temporal_zoom_to_frame (bool coarser, framepos_t frame)
2078 framecnt_t range_before = frame - leftmost_frame;
2082 if (samples_per_pixel <= 1) {
2085 new_spp = samples_per_pixel + (samples_per_pixel/2);
2087 range_before += range_before/2;
2089 if (samples_per_pixel >= 1) {
2090 new_spp = samples_per_pixel - (samples_per_pixel/2);
2092 /* could bail out here since we cannot zoom any finer,
2093 but leave that to the equality test below
2095 new_spp = samples_per_pixel;
2098 range_before -= range_before/2;
2101 if (new_spp == samples_per_pixel) {
2105 /* zoom focus is automatically taken as @param frame when this
2109 framepos_t new_leftmost = frame - (framepos_t)range_before;
2111 if (new_leftmost > frame) {
2115 if (new_leftmost < 0) {
2119 reposition_and_zoom (new_leftmost, new_spp);
2124 Editor::choose_new_marker_name(string &name) {
2126 if (!UIConfiguration::instance().get_name_new_markers()) {
2127 /* don't prompt user for a new name */
2131 ArdourPrompter dialog (true);
2133 dialog.set_prompt (_("New Name:"));
2135 dialog.set_title (_("New Location Marker"));
2137 dialog.set_name ("MarkNameWindow");
2138 dialog.set_size_request (250, -1);
2139 dialog.set_position (Gtk::WIN_POS_MOUSE);
2141 dialog.add_button (Stock::OK, RESPONSE_ACCEPT);
2142 dialog.set_initial_text (name);
2146 switch (dialog.run ()) {
2147 case RESPONSE_ACCEPT:
2153 dialog.get_result(name);
2160 Editor::add_location_from_selection ()
2164 if (selection->time.empty()) {
2168 if (_session == 0 || clicked_axisview == 0) {
2172 framepos_t start = selection->time[clicked_selection].start;
2173 framepos_t end = selection->time[clicked_selection].end;
2175 _session->locations()->next_available_name(rangename,"selection");
2176 Location *location = new Location (*_session, start, end, rangename, Location::IsRangeMarker);
2178 begin_reversible_command (_("add marker"));
2180 XMLNode &before = _session->locations()->get_state();
2181 _session->locations()->add (location, true);
2182 XMLNode &after = _session->locations()->get_state();
2183 _session->add_command(new MementoCommand<Locations>(*(_session->locations()), &before, &after));
2185 commit_reversible_command ();
2189 Editor::add_location_mark (framepos_t where)
2193 select_new_marker = true;
2195 _session->locations()->next_available_name(markername,"mark");
2196 if (!choose_new_marker_name(markername)) {
2199 Location *location = new Location (*_session, where, where, markername, Location::IsMark);
2200 begin_reversible_command (_("add marker"));
2202 XMLNode &before = _session->locations()->get_state();
2203 _session->locations()->add (location, true);
2204 XMLNode &after = _session->locations()->get_state();
2205 _session->add_command(new MementoCommand<Locations>(*(_session->locations()), &before, &after));
2207 commit_reversible_command ();
2211 Editor::set_session_start_from_playhead ()
2217 if ((loc = _session->locations()->session_range_location()) == 0) { //should never happen
2218 _session->set_session_extents ( _session->audible_frame(), _session->audible_frame() );
2220 XMLNode &before = loc->get_state();
2222 _session->set_session_extents ( _session->audible_frame(), loc->end() );
2224 XMLNode &after = loc->get_state();
2226 begin_reversible_command (_("Set session start"));
2228 _session->add_command (new MementoCommand<Location>(*loc, &before, &after));
2230 commit_reversible_command ();
2235 Editor::set_session_end_from_playhead ()
2241 if ((loc = _session->locations()->session_range_location()) == 0) { //should never happen
2242 _session->set_session_extents ( _session->audible_frame(), _session->audible_frame() );
2244 XMLNode &before = loc->get_state();
2246 _session->set_session_extents ( loc->start(), _session->audible_frame() );
2248 XMLNode &after = loc->get_state();
2250 begin_reversible_command (_("Set session start"));
2252 _session->add_command (new MementoCommand<Location>(*loc, &before, &after));
2254 commit_reversible_command ();
2257 _session->set_end_is_free (false);
2262 Editor::toggle_location_at_playhead_cursor ()
2264 if (!do_remove_location_at_playhead_cursor())
2266 add_location_from_playhead_cursor();
2271 Editor::add_location_from_playhead_cursor ()
2273 add_location_mark (_session->audible_frame());
2277 Editor::do_remove_location_at_playhead_cursor ()
2279 bool removed = false;
2282 XMLNode &before = _session->locations()->get_state();
2284 //find location(s) at this time
2285 Locations::LocationList locs;
2286 _session->locations()->find_all_between (_session->audible_frame(), _session->audible_frame()+1, locs, Location::Flags(0));
2287 for (Locations::LocationList::iterator i = locs.begin(); i != locs.end(); ++i) {
2288 if ((*i)->is_mark()) {
2289 _session->locations()->remove (*i);
2296 begin_reversible_command (_("remove marker"));
2297 XMLNode &after = _session->locations()->get_state();
2298 _session->add_command(new MementoCommand<Locations>(*(_session->locations()), &before, &after));
2299 commit_reversible_command ();
2306 Editor::remove_location_at_playhead_cursor ()
2308 do_remove_location_at_playhead_cursor ();
2311 /** Add a range marker around each selected region */
2313 Editor::add_locations_from_region ()
2315 RegionSelection rs = get_regions_from_selection_and_entered ();
2320 bool commit = false;
2322 XMLNode &before = _session->locations()->get_state();
2324 for (RegionSelection::iterator i = rs.begin (); i != rs.end (); ++i) {
2326 boost::shared_ptr<Region> region = (*i)->region ();
2328 Location *location = new Location (*_session, region->position(), region->last_frame(), region->name(), Location::IsRangeMarker);
2330 _session->locations()->add (location, true);
2335 begin_reversible_command (selection->regions.size () > 1 ? _("add markers") : _("add marker"));
2336 XMLNode &after = _session->locations()->get_state();
2337 _session->add_command (new MementoCommand<Locations>(*(_session->locations()), &before, &after));
2338 commit_reversible_command ();
2342 /** Add a single range marker around all selected regions */
2344 Editor::add_location_from_region ()
2346 RegionSelection rs = get_regions_from_selection_and_entered ();
2352 XMLNode &before = _session->locations()->get_state();
2356 if (rs.size() > 1) {
2357 _session->locations()->next_available_name(markername, "regions");
2359 RegionView* rv = *(rs.begin());
2360 boost::shared_ptr<Region> region = rv->region();
2361 markername = region->name();
2364 if (!choose_new_marker_name(markername)) {
2368 // single range spanning all selected
2369 Location *location = new Location (*_session, selection->regions.start(), selection->regions.end_frame(), markername, Location::IsRangeMarker);
2370 _session->locations()->add (location, true);
2372 begin_reversible_command (_("add marker"));
2373 XMLNode &after = _session->locations()->get_state();
2374 _session->add_command (new MementoCommand<Locations>(*(_session->locations()), &before, &after));
2375 commit_reversible_command ();
2381 Editor::jump_forward_to_mark ()
2387 framepos_t pos = _session->locations()->first_mark_after (playhead_cursor->current_frame());
2393 _session->request_locate (pos, _session->transport_rolling());
2397 Editor::jump_backward_to_mark ()
2403 framepos_t pos = _session->locations()->first_mark_before (playhead_cursor->current_frame());
2409 _session->request_locate (pos, _session->transport_rolling());
2415 framepos_t const pos = _session->audible_frame ();
2418 _session->locations()->next_available_name (markername, "mark");
2420 if (!choose_new_marker_name (markername)) {
2424 _session->locations()->add (new Location (*_session, pos, 0, markername, Location::IsMark), true);
2428 Editor::clear_markers ()
2431 begin_reversible_command (_("clear markers"));
2433 XMLNode &before = _session->locations()->get_state();
2434 _session->locations()->clear_markers ();
2435 XMLNode &after = _session->locations()->get_state();
2436 _session->add_command(new MementoCommand<Locations>(*(_session->locations()), &before, &after));
2438 commit_reversible_command ();
2443 Editor::clear_ranges ()
2446 begin_reversible_command (_("clear ranges"));
2448 XMLNode &before = _session->locations()->get_state();
2450 _session->locations()->clear_ranges ();
2452 XMLNode &after = _session->locations()->get_state();
2453 _session->add_command(new MementoCommand<Locations>(*(_session->locations()), &before, &after));
2455 commit_reversible_command ();
2460 Editor::clear_locations ()
2462 begin_reversible_command (_("clear locations"));
2464 XMLNode &before = _session->locations()->get_state();
2465 _session->locations()->clear ();
2466 XMLNode &after = _session->locations()->get_state();
2467 _session->add_command(new MementoCommand<Locations>(*(_session->locations()), &before, &after));
2469 commit_reversible_command ();
2473 Editor::unhide_markers ()
2475 for (LocationMarkerMap::iterator i = location_markers.begin(); i != location_markers.end(); ++i) {
2476 Location *l = (*i).first;
2477 if (l->is_hidden() && l->is_mark()) {
2478 l->set_hidden(false, this);
2484 Editor::unhide_ranges ()
2486 for (LocationMarkerMap::iterator i = location_markers.begin(); i != location_markers.end(); ++i) {
2487 Location *l = (*i).first;
2488 if (l->is_hidden() && l->is_range_marker()) {
2489 l->set_hidden(false, this);
2494 /* INSERT/REPLACE */
2497 Editor::insert_region_list_selection (float times)
2499 RouteTimeAxisView *tv = 0;
2500 boost::shared_ptr<Playlist> playlist;
2502 if (clicked_routeview != 0) {
2503 tv = clicked_routeview;
2504 } else if (!selection->tracks.empty()) {
2505 if ((tv = dynamic_cast<RouteTimeAxisView*>(selection->tracks.front())) == 0) {
2508 } else if (entered_track != 0) {
2509 if ((tv = dynamic_cast<RouteTimeAxisView*>(entered_track)) == 0) {
2516 if ((playlist = tv->playlist()) == 0) {
2520 boost::shared_ptr<Region> region = _regions->get_single_selection ();
2525 begin_reversible_command (_("insert region"));
2526 playlist->clear_changes ();
2527 playlist->add_region ((RegionFactory::create (region, true)), get_preferred_edit_position(), times);
2528 if (Config->get_edit_mode() == Ripple)
2529 playlist->ripple (get_preferred_edit_position(), region->length() * times, boost::shared_ptr<Region>());
2531 _session->add_command(new StatefulDiffCommand (playlist));
2532 commit_reversible_command ();
2535 /* BUILT-IN EFFECTS */
2538 Editor::reverse_selection ()
2543 /* GAIN ENVELOPE EDITING */
2546 Editor::edit_envelope ()
2553 Editor::transition_to_rolling (bool fwd)
2559 if (_session->config.get_external_sync()) {
2560 switch (Config->get_sync_source()) {
2564 /* transport controlled by the master */
2569 if (_session->is_auditioning()) {
2570 _session->cancel_audition ();
2574 _session->request_transport_speed (fwd ? 1.0f : -1.0f);
2578 Editor::play_from_start ()
2580 _session->request_locate (_session->current_start_frame(), true);
2584 Editor::play_from_edit_point ()
2586 _session->request_locate (get_preferred_edit_position(), true);
2590 Editor::play_from_edit_point_and_return ()
2592 framepos_t start_frame;
2593 framepos_t return_frame;
2595 start_frame = get_preferred_edit_position ( EDIT_IGNORE_PHEAD );
2597 if (_session->transport_rolling()) {
2598 _session->request_locate (start_frame, false);
2602 /* don't reset the return frame if its already set */
2604 if ((return_frame = _session->requested_return_frame()) < 0) {
2605 return_frame = _session->audible_frame();
2608 if (start_frame >= 0) {
2609 _session->request_roll_at_and_return (start_frame, return_frame);
2614 Editor::play_selection ()
2616 framepos_t start, end;
2617 if (!get_selection_extents ( start, end))
2620 AudioRange ar (start, end, 0);
2621 list<AudioRange> lar;
2624 _session->request_play_range (&lar, true);
2628 Editor::get_preroll ()
2630 return Config->get_preroll_seconds() * _session->frame_rate();
2635 Editor::maybe_locate_with_edit_preroll ( framepos_t location )
2637 if ( _session->transport_rolling() || !UIConfiguration::instance().get_follow_edits() || _ignore_follow_edits || _session->config.get_external_sync() )
2640 location -= get_preroll();
2642 //don't try to locate before the beginning of time
2646 //if follow_playhead is on, keep the playhead on the screen
2647 if ( _follow_playhead )
2648 if ( location < leftmost_frame )
2649 location = leftmost_frame;
2651 _session->request_locate( location );
2655 Editor::play_with_preroll ()
2658 framepos_t preroll = get_preroll();
2660 framepos_t start, end;
2661 if (!get_selection_extents ( start, end))
2664 if (start > preroll)
2665 start = start - preroll;
2667 end = end + preroll; //"post-roll"
2669 AudioRange ar (start, end, 0);
2670 list<AudioRange> lar;
2673 _session->request_play_range (&lar, true);
2678 Editor::play_location (Location& location)
2680 if (location.start() <= location.end()) {
2684 _session->request_bounded_roll (location.start(), location.end());
2688 Editor::loop_location (Location& location)
2690 if (location.start() <= location.end()) {
2696 if ((tll = transport_loop_location()) != 0) {
2697 tll->set (location.start(), location.end());
2699 // enable looping, reposition and start rolling
2700 _session->request_locate (tll->start(), true);
2701 _session->request_play_loop (true);
2706 Editor::do_layer_operation (LayerOperation op)
2708 if (selection->regions.empty ()) {
2712 bool const multiple = selection->regions.size() > 1;
2716 begin_reversible_command (_("raise regions"));
2718 begin_reversible_command (_("raise region"));
2724 begin_reversible_command (_("raise regions to top"));
2726 begin_reversible_command (_("raise region to top"));
2732 begin_reversible_command (_("lower regions"));
2734 begin_reversible_command (_("lower region"));
2740 begin_reversible_command (_("lower regions to bottom"));
2742 begin_reversible_command (_("lower region"));
2747 set<boost::shared_ptr<Playlist> > playlists = selection->regions.playlists ();
2748 for (set<boost::shared_ptr<Playlist> >::iterator i = playlists.begin(); i != playlists.end(); ++i) {
2749 (*i)->clear_owned_changes ();
2752 for (RegionSelection::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
2753 boost::shared_ptr<Region> r = (*i)->region ();
2765 r->lower_to_bottom ();
2769 for (set<boost::shared_ptr<Playlist> >::iterator i = playlists.begin(); i != playlists.end(); ++i) {
2770 vector<Command*> cmds;
2772 _session->add_commands (cmds);
2775 commit_reversible_command ();
2779 Editor::raise_region ()
2781 do_layer_operation (Raise);
2785 Editor::raise_region_to_top ()
2787 do_layer_operation (RaiseToTop);
2791 Editor::lower_region ()
2793 do_layer_operation (Lower);
2797 Editor::lower_region_to_bottom ()
2799 do_layer_operation (LowerToBottom);
2802 /** Show the region editor for the selected regions */
2804 Editor::show_region_properties ()
2806 selection->foreach_regionview (&RegionView::show_region_editor);
2809 /** Show the midi list editor for the selected MIDI regions */
2811 Editor::show_midi_list_editor ()
2813 selection->foreach_midi_regionview (&MidiRegionView::show_list_editor);
2817 Editor::rename_region ()
2819 RegionSelection rs = get_regions_from_selection_and_entered ();
2825 ArdourDialog d (_("Rename Region"), true, false);
2827 Label label (_("New name:"));
2830 hbox.set_spacing (6);
2831 hbox.pack_start (label, false, false);
2832 hbox.pack_start (entry, true, true);
2834 d.get_vbox()->set_border_width (12);
2835 d.get_vbox()->pack_start (hbox, false, false);
2837 d.add_button(Gtk::Stock::CANCEL, Gtk::RESPONSE_CANCEL);
2838 d.add_button(Gtk::Stock::OK, Gtk::RESPONSE_OK);
2840 d.set_size_request (300, -1);
2842 entry.set_text (rs.front()->region()->name());
2843 entry.select_region (0, -1);
2845 entry.signal_activate().connect (sigc::bind (sigc::mem_fun (d, &Dialog::response), RESPONSE_OK));
2851 int const ret = d.run();
2855 if (ret != RESPONSE_OK) {
2859 std::string str = entry.get_text();
2860 strip_whitespace_edges (str);
2862 rs.front()->region()->set_name (str);
2863 _regions->redisplay ();
2867 /** Start an audition of the first selected region */
2869 Editor::play_edit_range ()
2871 framepos_t start, end;
2873 if (get_edit_op_range (start, end)) {
2874 _session->request_bounded_roll (start, end);
2879 Editor::play_selected_region ()
2881 framepos_t start = max_framepos;
2884 RegionSelection rs = get_regions_from_selection_and_entered ();
2890 for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
2891 if ((*i)->region()->position() < start) {
2892 start = (*i)->region()->position();
2894 if ((*i)->region()->last_frame() + 1 > end) {
2895 end = (*i)->region()->last_frame() + 1;
2899 _session->request_bounded_roll (start, end);
2903 Editor::audition_playlist_region_standalone (boost::shared_ptr<Region> region)
2905 _session->audition_region (region);
2909 Editor::region_from_selection ()
2911 if (clicked_axisview == 0) {
2915 if (selection->time.empty()) {
2919 framepos_t start = selection->time[clicked_selection].start;
2920 framepos_t end = selection->time[clicked_selection].end;
2922 TrackViewList tracks = get_tracks_for_range_action ();
2924 framepos_t selection_cnt = end - start + 1;
2926 for (TrackSelection::iterator i = tracks.begin(); i != tracks.end(); ++i) {
2927 boost::shared_ptr<Region> current;
2928 boost::shared_ptr<Playlist> pl;
2929 framepos_t internal_start;
2932 if ((pl = (*i)->playlist()) == 0) {
2936 if ((current = pl->top_region_at (start)) == 0) {
2940 internal_start = start - current->position();
2941 RegionFactory::region_name (new_name, current->name(), true);
2945 plist.add (ARDOUR::Properties::start, current->start() + internal_start);
2946 plist.add (ARDOUR::Properties::length, selection_cnt);
2947 plist.add (ARDOUR::Properties::name, new_name);
2948 plist.add (ARDOUR::Properties::layer, 0);
2950 boost::shared_ptr<Region> region (RegionFactory::create (current, plist));
2955 Editor::create_region_from_selection (vector<boost::shared_ptr<Region> >& new_regions)
2957 if (selection->time.empty() || selection->tracks.empty()) {
2961 framepos_t start, end;
2962 if (clicked_selection) {
2963 start = selection->time[clicked_selection].start;
2964 end = selection->time[clicked_selection].end;
2966 start = selection->time.start();
2967 end = selection->time.end_frame();
2970 TrackViewList ts = selection->tracks.filter_to_unique_playlists ();
2971 sort_track_selection (ts);
2973 for (TrackSelection::iterator i = ts.begin(); i != ts.end(); ++i) {
2974 boost::shared_ptr<Region> current;
2975 boost::shared_ptr<Playlist> playlist;
2976 framepos_t internal_start;
2979 if ((playlist = (*i)->playlist()) == 0) {
2983 if ((current = playlist->top_region_at(start)) == 0) {
2987 internal_start = start - current->position();
2988 RegionFactory::region_name (new_name, current->name(), true);
2992 plist.add (ARDOUR::Properties::start, current->start() + internal_start);
2993 plist.add (ARDOUR::Properties::length, end - start + 1);
2994 plist.add (ARDOUR::Properties::name, new_name);
2996 new_regions.push_back (RegionFactory::create (current, plist));
3001 Editor::split_multichannel_region ()
3003 RegionSelection rs = get_regions_from_selection_and_entered ();
3009 vector< boost::shared_ptr<Region> > v;
3011 for (list<RegionView*>::iterator x = rs.begin(); x != rs.end(); ++x) {
3012 (*x)->region()->separate_by_channel (*_session, v);
3017 Editor::new_region_from_selection ()
3019 region_from_selection ();
3020 cancel_selection ();
3024 add_if_covered (RegionView* rv, const AudioRange* ar, RegionSelection* rs)
3026 switch (rv->region()->coverage (ar->start, ar->end - 1)) {
3027 // n.b. -1 because AudioRange::end is one past the end, but coverage expects inclusive ranges
3028 case Evoral::OverlapNone:
3036 * - selected tracks, or if there are none...
3037 * - tracks containing selected regions, or if there are none...
3042 Editor::get_tracks_for_range_action () const
3046 if (selection->tracks.empty()) {
3048 /* use tracks with selected regions */
3050 RegionSelection rs = selection->regions;
3052 for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
3053 TimeAxisView* tv = &(*i)->get_time_axis_view();
3055 if (!t.contains (tv)) {
3061 /* no regions and no tracks: use all tracks */
3067 t = selection->tracks;
3070 return t.filter_to_unique_playlists();
3074 Editor::separate_regions_between (const TimeSelection& ts)
3076 bool in_command = false;
3077 boost::shared_ptr<Playlist> playlist;
3078 RegionSelection new_selection;
3080 TrackViewList tmptracks = get_tracks_for_range_action ();
3081 sort_track_selection (tmptracks);
3083 for (TrackSelection::iterator i = tmptracks.begin(); i != tmptracks.end(); ++i) {
3085 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> ((*i));
3091 if (!rtv->is_track()) {
3095 /* no edits to destructive tracks */
3097 if (rtv->track()->destructive()) {
3101 if ((playlist = rtv->playlist()) != 0) {
3103 playlist->clear_changes ();
3105 /* XXX need to consider musical time selections here at some point */
3107 double speed = rtv->track()->speed();
3109 for (list<AudioRange>::const_iterator t = ts.begin(); t != ts.end(); ++t) {
3111 sigc::connection c = rtv->view()->RegionViewAdded.connect (
3112 sigc::mem_fun(*this, &Editor::collect_new_region_view));
3114 latest_regionviews.clear ();
3116 playlist->partition ((framepos_t)((*t).start * speed),
3117 (framepos_t)((*t).end * speed), false);
3121 if (!latest_regionviews.empty()) {
3123 rtv->view()->foreach_regionview (sigc::bind (
3124 sigc::ptr_fun (add_if_covered),
3125 &(*t), &new_selection));
3128 begin_reversible_command (_("separate"));
3132 /* pick up changes to existing regions */
3134 vector<Command*> cmds;
3135 playlist->rdiff (cmds);
3136 _session->add_commands (cmds);
3138 /* pick up changes to the playlist itself (adds/removes)
3141 _session->add_command(new StatefulDiffCommand (playlist));
3148 // selection->set (new_selection);
3150 commit_reversible_command ();
3154 struct PlaylistState {
3155 boost::shared_ptr<Playlist> playlist;
3159 /** Take tracks from get_tracks_for_range_action and cut any regions
3160 * on those tracks so that the tracks are empty over the time
3164 Editor::separate_region_from_selection ()
3166 /* preferentially use *all* ranges in the time selection if we're in range mode
3167 to allow discontiguous operation, since get_edit_op_range() currently
3168 returns a single range.
3171 if (!selection->time.empty()) {
3173 separate_regions_between (selection->time);
3180 if (get_edit_op_range (start, end)) {
3182 AudioRange ar (start, end, 1);
3186 separate_regions_between (ts);
3192 Editor::separate_region_from_punch ()
3194 Location* loc = _session->locations()->auto_punch_location();
3196 separate_regions_using_location (*loc);
3201 Editor::separate_region_from_loop ()
3203 Location* loc = _session->locations()->auto_loop_location();
3205 separate_regions_using_location (*loc);
3210 Editor::separate_regions_using_location (Location& loc)
3212 if (loc.is_mark()) {
3216 AudioRange ar (loc.start(), loc.end(), 1);
3221 separate_regions_between (ts);
3224 /** Separate regions under the selected region */
3226 Editor::separate_under_selected_regions ()
3228 vector<PlaylistState> playlists;
3232 rs = get_regions_from_selection_and_entered();
3234 if (!_session || rs.empty()) {
3238 begin_reversible_command (_("separate region under"));
3240 list<boost::shared_ptr<Region> > regions_to_remove;
3242 for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
3243 // we can't just remove the region(s) in this loop because
3244 // this removes them from the RegionSelection, and they thus
3245 // disappear from underneath the iterator, and the ++i above
3246 // SEGVs in a puzzling fashion.
3248 // so, first iterate over the regions to be removed from rs and
3249 // add them to the regions_to_remove list, and then
3250 // iterate over the list to actually remove them.
3252 regions_to_remove.push_back ((*i)->region());
3255 for (list<boost::shared_ptr<Region> >::iterator rl = regions_to_remove.begin(); rl != regions_to_remove.end(); ++rl) {
3257 boost::shared_ptr<Playlist> playlist = (*rl)->playlist();
3260 // is this check necessary?
3264 vector<PlaylistState>::iterator i;
3266 //only take state if this is a new playlist.
3267 for (i = playlists.begin(); i != playlists.end(); ++i) {
3268 if ((*i).playlist == playlist) {
3273 if (i == playlists.end()) {
3275 PlaylistState before;
3276 before.playlist = playlist;
3277 before.before = &playlist->get_state();
3279 playlist->freeze ();
3280 playlists.push_back(before);
3283 //Partition on the region bounds
3284 playlist->partition ((*rl)->first_frame() - 1, (*rl)->last_frame() + 1, true);
3286 //Re-add region that was just removed due to the partition operation
3287 playlist->add_region( (*rl), (*rl)->first_frame() );
3290 vector<PlaylistState>::iterator pl;
3292 for (pl = playlists.begin(); pl != playlists.end(); ++pl) {
3293 (*pl).playlist->thaw ();
3294 _session->add_command(new MementoCommand<Playlist>(*(*pl).playlist, (*pl).before, &(*pl).playlist->get_state()));
3297 commit_reversible_command ();
3301 Editor::crop_region_to_selection ()
3303 if (!selection->time.empty()) {
3305 crop_region_to (selection->time.start(), selection->time.end_frame());
3312 if (get_edit_op_range (start, end)) {
3313 crop_region_to (start, end);
3320 Editor::crop_region_to (framepos_t start, framepos_t end)
3322 vector<boost::shared_ptr<Playlist> > playlists;
3323 boost::shared_ptr<Playlist> playlist;
3326 if (selection->tracks.empty()) {
3327 ts = track_views.filter_to_unique_playlists();
3329 ts = selection->tracks.filter_to_unique_playlists ();
3332 sort_track_selection (ts);
3334 for (TrackSelection::iterator i = ts.begin(); i != ts.end(); ++i) {
3336 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> ((*i));
3342 boost::shared_ptr<Track> t = rtv->track();
3344 if (t != 0 && ! t->destructive()) {
3346 if ((playlist = rtv->playlist()) != 0) {
3347 playlists.push_back (playlist);
3352 if (playlists.empty()) {
3357 framepos_t new_start;
3359 framecnt_t new_length;
3360 bool in_command = false;
3362 for (vector<boost::shared_ptr<Playlist> >::iterator i = playlists.begin(); i != playlists.end(); ++i) {
3364 /* Only the top regions at start and end have to be cropped */
3365 boost::shared_ptr<Region> region_at_start = (*i)->top_region_at(start);
3366 boost::shared_ptr<Region> region_at_end = (*i)->top_region_at(end);
3368 vector<boost::shared_ptr<Region> > regions;
3370 if (region_at_start != 0) {
3371 regions.push_back (region_at_start);
3373 if (region_at_end != 0) {
3374 regions.push_back (region_at_end);
3377 /* now adjust lengths */
3378 for (vector<boost::shared_ptr<Region> >::iterator i = regions.begin(); i != regions.end(); ++i) {
3380 pos = (*i)->position();
3381 new_start = max (start, pos);
3382 if (max_framepos - pos > (*i)->length()) {
3383 new_end = pos + (*i)->length() - 1;
3385 new_end = max_framepos;
3387 new_end = min (end, new_end);
3388 new_length = new_end - new_start + 1;
3391 begin_reversible_command (_("trim to selection"));
3394 (*i)->clear_changes ();
3395 (*i)->trim_to (new_start, new_length);
3396 _session->add_command (new StatefulDiffCommand (*i));
3401 commit_reversible_command ();
3406 Editor::region_fill_track ()
3408 boost::shared_ptr<Playlist> playlist;
3409 RegionSelection regions = get_regions_from_selection_and_entered ();
3410 RegionSelection foo;
3412 framepos_t const end = _session->current_end_frame ();
3414 if (regions.empty () || regions.end_frame () + 1 >= end) {
3418 framepos_t const start_frame = regions.start ();
3419 framepos_t const end_frame = regions.end_frame ();
3420 framecnt_t const gap = end_frame - start_frame + 1;
3422 begin_reversible_command (Operations::region_fill);
3424 selection->clear_regions ();
3426 for (RegionSelection::iterator i = regions.begin(); i != regions.end(); ++i) {
3428 boost::shared_ptr<Region> r ((*i)->region());
3430 TimeAxisView& tv = (*i)->get_time_axis_view();
3431 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (&tv);
3432 latest_regionviews.clear ();
3433 sigc::connection c = rtv->view()->RegionViewAdded.connect (sigc::mem_fun(*this, &Editor::collect_new_region_view));
3435 framepos_t const position = end_frame + (r->first_frame() - start_frame + 1);
3436 playlist = (*i)->region()->playlist();
3437 playlist->clear_changes ();
3438 playlist->duplicate_until (r, position, gap, end);
3439 _session->add_command(new StatefulDiffCommand (playlist));
3443 foo.insert (foo.end(), latest_regionviews.begin(), latest_regionviews.end());
3447 selection->set (foo);
3450 commit_reversible_command ();
3454 Editor::set_region_sync_position ()
3456 set_sync_point (get_preferred_edit_position (), get_regions_from_selection_and_edit_point ());
3460 Editor::set_sync_point (framepos_t where, const RegionSelection& rs)
3462 bool in_command = false;
3464 for (RegionSelection::const_iterator r = rs.begin(); r != rs.end(); ++r) {
3466 if (!(*r)->region()->covers (where)) {
3470 boost::shared_ptr<Region> region ((*r)->region());
3473 begin_reversible_command (_("set sync point"));
3477 region->clear_changes ();
3478 region->set_sync_position (where);
3479 _session->add_command(new StatefulDiffCommand (region));
3483 commit_reversible_command ();
3487 /** Remove the sync positions of the selection */
3489 Editor::remove_region_sync ()
3491 RegionSelection rs = get_regions_from_selection_and_entered ();
3497 begin_reversible_command (_("remove region sync"));
3499 for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
3501 (*i)->region()->clear_changes ();
3502 (*i)->region()->clear_sync_position ();
3503 _session->add_command(new StatefulDiffCommand ((*i)->region()));
3506 commit_reversible_command ();
3510 Editor::naturalize_region ()
3512 RegionSelection rs = get_regions_from_selection_and_entered ();
3518 if (rs.size() > 1) {
3519 begin_reversible_command (_("move regions to original position"));
3521 begin_reversible_command (_("move region to original position"));
3524 for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
3525 (*i)->region()->clear_changes ();
3526 (*i)->region()->move_to_natural_position ();
3527 _session->add_command (new StatefulDiffCommand ((*i)->region()));
3530 commit_reversible_command ();
3534 Editor::align_regions (RegionPoint what)
3536 RegionSelection const rs = get_regions_from_selection_and_edit_point ();
3542 begin_reversible_command (_("align selection"));
3544 framepos_t const position = get_preferred_edit_position ();
3546 for (RegionSelection::const_iterator i = rs.begin(); i != rs.end(); ++i) {
3547 align_region_internal ((*i)->region(), what, position);
3550 commit_reversible_command ();
3553 struct RegionSortByTime {
3554 bool operator() (const RegionView* a, const RegionView* b) {
3555 return a->region()->position() < b->region()->position();
3560 Editor::align_regions_relative (RegionPoint point)
3562 RegionSelection const rs = get_regions_from_selection_and_edit_point ();
3568 framepos_t const position = get_preferred_edit_position ();
3570 framepos_t distance = 0;
3574 list<RegionView*> sorted;
3575 rs.by_position (sorted);
3577 boost::shared_ptr<Region> r ((*sorted.begin())->region());
3582 if (position > r->position()) {
3583 distance = position - r->position();
3585 distance = r->position() - position;
3591 if (position > r->last_frame()) {
3592 distance = position - r->last_frame();
3593 pos = r->position() + distance;
3595 distance = r->last_frame() - position;
3596 pos = r->position() - distance;
3602 pos = r->adjust_to_sync (position);
3603 if (pos > r->position()) {
3604 distance = pos - r->position();
3606 distance = r->position() - pos;
3612 if (pos == r->position()) {
3616 begin_reversible_command (_("align selection (relative)"));
3618 /* move first one specially */
3620 r->clear_changes ();
3621 r->set_position (pos);
3622 _session->add_command(new StatefulDiffCommand (r));
3624 /* move rest by the same amount */
3628 for (list<RegionView*>::iterator i = sorted.begin(); i != sorted.end(); ++i) {
3630 boost::shared_ptr<Region> region ((*i)->region());
3632 region->clear_changes ();
3635 region->set_position (region->position() + distance);
3637 region->set_position (region->position() - distance);
3640 _session->add_command(new StatefulDiffCommand (region));
3644 commit_reversible_command ();
3648 Editor::align_region (boost::shared_ptr<Region> region, RegionPoint point, framepos_t position)
3650 begin_reversible_command (_("align region"));
3651 align_region_internal (region, point, position);
3652 commit_reversible_command ();
3656 Editor::align_region_internal (boost::shared_ptr<Region> region, RegionPoint point, framepos_t position)
3658 region->clear_changes ();
3662 region->set_position (region->adjust_to_sync (position));
3666 if (position > region->length()) {
3667 region->set_position (position - region->length());
3672 region->set_position (position);
3676 _session->add_command(new StatefulDiffCommand (region));
3680 Editor::trim_region_front ()
3686 Editor::trim_region_back ()
3688 trim_region (false);
3692 Editor::trim_region (bool front)
3694 framepos_t where = get_preferred_edit_position();
3695 RegionSelection rs = get_regions_from_selection_and_edit_point ();
3701 begin_reversible_command (front ? _("trim front") : _("trim back"));
3703 for (list<RegionView*>::const_iterator i = rs.by_layer().begin(); i != rs.by_layer().end(); ++i) {
3704 if (!(*i)->region()->locked()) {
3706 (*i)->region()->clear_changes ();
3709 (*i)->region()->trim_front (where);
3710 maybe_locate_with_edit_preroll ( where );
3712 (*i)->region()->trim_end (where);
3713 maybe_locate_with_edit_preroll ( where );
3716 _session->add_command (new StatefulDiffCommand ((*i)->region()));
3720 commit_reversible_command ();
3723 /** Trim the end of the selected regions to the position of the edit cursor */
3725 Editor::trim_region_to_loop ()
3727 Location* loc = _session->locations()->auto_loop_location();
3731 trim_region_to_location (*loc, _("trim to loop"));
3735 Editor::trim_region_to_punch ()
3737 Location* loc = _session->locations()->auto_punch_location();
3741 trim_region_to_location (*loc, _("trim to punch"));
3745 Editor::trim_region_to_location (const Location& loc, const char* str)
3747 RegionSelection rs = get_regions_from_selection_and_entered ();
3748 bool in_command = false;
3750 for (RegionSelection::iterator x = rs.begin(); x != rs.end(); ++x) {
3751 RegionView* rv = (*x);
3753 /* require region to span proposed trim */
3754 switch (rv->region()->coverage (loc.start(), loc.end())) {
3755 case Evoral::OverlapInternal:
3761 RouteTimeAxisView* tav = dynamic_cast<RouteTimeAxisView*> (&rv->get_time_axis_view());
3770 if (tav->track() != 0) {
3771 speed = tav->track()->speed();
3774 start = session_frame_to_track_frame (loc.start(), speed);
3775 end = session_frame_to_track_frame (loc.end(), speed);
3777 rv->region()->clear_changes ();
3778 rv->region()->trim_to (start, (end - start));
3781 begin_reversible_command (str);
3784 _session->add_command(new StatefulDiffCommand (rv->region()));
3788 commit_reversible_command ();
3793 Editor::trim_region_to_previous_region_end ()
3795 return trim_to_region(false);
3799 Editor::trim_region_to_next_region_start ()
3801 return trim_to_region(true);
3805 Editor::trim_to_region(bool forward)
3807 RegionSelection rs = get_regions_from_selection_and_entered ();
3808 bool in_command = false;
3810 boost::shared_ptr<Region> next_region;
3812 for (RegionSelection::iterator x = rs.begin(); x != rs.end(); ++x) {
3814 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (*x);
3820 AudioTimeAxisView* atav = dynamic_cast<AudioTimeAxisView*> (&arv->get_time_axis_view());
3828 if (atav->track() != 0) {
3829 speed = atav->track()->speed();
3833 boost::shared_ptr<Region> region = arv->region();
3834 boost::shared_ptr<Playlist> playlist (region->playlist());
3836 region->clear_changes ();
3840 next_region = playlist->find_next_region (region->first_frame(), Start, 1);
3846 region->trim_end((framepos_t) ( (next_region->first_frame() - 1) * speed));
3847 arv->region_changed (PropertyChange (ARDOUR::Properties::length));
3851 next_region = playlist->find_next_region (region->first_frame(), Start, 0);
3857 region->trim_front((framepos_t) ((next_region->last_frame() + 1) * speed));
3859 arv->region_changed (ARDOUR::bounds_change);
3863 begin_reversible_command (_("trim to region"));
3866 _session->add_command(new StatefulDiffCommand (region));
3870 commit_reversible_command ();
3875 Editor::unfreeze_route ()
3877 if (clicked_routeview == 0 || !clicked_routeview->is_track()) {
3881 clicked_routeview->track()->unfreeze ();
3885 Editor::_freeze_thread (void* arg)
3887 return static_cast<Editor*>(arg)->freeze_thread ();
3891 Editor::freeze_thread ()
3893 /* create event pool because we may need to talk to the session */
3894 SessionEvent::create_per_thread_pool ("freeze events", 64);
3895 /* create per-thread buffers for process() tree to use */
3896 clicked_routeview->audio_track()->freeze_me (*current_interthread_info);
3897 current_interthread_info->done = true;
3902 Editor::freeze_route ()
3908 /* stop transport before we start. this is important */
3910 _session->request_transport_speed (0.0);
3912 /* wait for just a little while, because the above call is asynchronous */
3914 Glib::usleep (250000);
3916 if (clicked_routeview == 0 || !clicked_routeview->is_audio_track()) {
3920 if (!clicked_routeview->track()->bounceable (clicked_routeview->track()->main_outs(), true)) {
3922 _("This track/bus cannot be frozen because the signal adds or loses channels before reaching the outputs.\n"
3923 "This is typically caused by plugins that generate stereo output from mono input or vice versa.")
3925 d.set_title (_("Cannot freeze"));
3930 if (clicked_routeview->track()->has_external_redirects()) {
3931 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"
3932 "Freezing will only process the signal as far as the first send/insert/return."),
3933 clicked_routeview->track()->name()), true, MESSAGE_INFO, BUTTONS_NONE, true);
3935 d.add_button (_("Freeze anyway"), Gtk::RESPONSE_OK);
3936 d.add_button (_("Don't freeze"), Gtk::RESPONSE_CANCEL);
3937 d.set_title (_("Freeze Limits"));
3939 int response = d.run ();
3942 case Gtk::RESPONSE_CANCEL:
3949 InterThreadInfo itt;
3950 current_interthread_info = &itt;
3952 InterthreadProgressWindow ipw (current_interthread_info, _("Freeze"), _("Cancel Freeze"));
3954 pthread_create_and_store (X_("freezer"), &itt.thread, _freeze_thread, this);
3956 CursorContext::Handle cursor_ctx = CursorContext::create(*this, _cursors->wait);
3958 while (!itt.done && !itt.cancel) {
3959 gtk_main_iteration ();
3962 pthread_join (itt.thread, 0);
3963 current_interthread_info = 0;
3967 Editor::bounce_range_selection (bool replace, bool enable_processing)
3969 if (selection->time.empty()) {
3973 TrackSelection views = selection->tracks;
3975 for (TrackViewList::iterator i = views.begin(); i != views.end(); ++i) {
3977 if (enable_processing) {
3979 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (*i);
3981 if (rtv && rtv->track() && replace && enable_processing && !rtv->track()->bounceable (rtv->track()->main_outs(), false)) {
3983 _("You can't perform this operation because the processing of the signal "
3984 "will cause one or more of the tracks to end up with a region with more channels than this track has inputs.\n\n"
3985 "You can do this without processing, which is a different operation.")
3987 d.set_title (_("Cannot bounce"));
3994 framepos_t start = selection->time[clicked_selection].start;
3995 framepos_t end = selection->time[clicked_selection].end;
3996 framepos_t cnt = end - start + 1;
3997 bool in_command = false;
3999 for (TrackViewList::iterator i = views.begin(); i != views.end(); ++i) {
4001 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (*i);
4007 boost::shared_ptr<Playlist> playlist;
4009 if ((playlist = rtv->playlist()) == 0) {
4013 InterThreadInfo itt;
4015 playlist->clear_changes ();
4016 playlist->clear_owned_changes ();
4018 boost::shared_ptr<Region> r;
4020 if (enable_processing) {
4021 r = rtv->track()->bounce_range (start, start+cnt, itt, rtv->track()->main_outs(), false);
4023 r = rtv->track()->bounce_range (start, start+cnt, itt, boost::shared_ptr<Processor>(), false);
4031 list<AudioRange> ranges;
4032 ranges.push_back (AudioRange (start, start+cnt, 0));
4033 playlist->cut (ranges); // discard result
4034 playlist->add_region (r, start);
4038 begin_reversible_command (_("bounce range"));
4041 vector<Command*> cmds;
4042 playlist->rdiff (cmds);
4043 _session->add_commands (cmds);
4045 _session->add_command (new StatefulDiffCommand (playlist));
4049 commit_reversible_command ();
4053 /** Delete selected regions, automation points or a time range */
4057 //special case: if the user is pointing in the editor/mixer strip, they may be trying to delete a plugin.
4058 //we need this because the editor-mixer strip is in the editor window, so it doesn't get the bindings from the mix window
4059 bool deleted = false;
4060 if ( current_mixer_strip && current_mixer_strip == MixerStrip::entered_mixer_strip() )
4061 deleted = current_mixer_strip->delete_processors ();
4067 /** Cut selected regions, automation points or a time range */
4074 /** Copy selected regions, automation points or a time range */
4082 /** @return true if a Cut, Copy or Clear is possible */
4084 Editor::can_cut_copy () const
4086 if (!selection->time.empty() || !selection->regions.empty() || !selection->points.empty())
4093 /** Cut, copy or clear selected regions, automation points or a time range.
4094 * @param op Operation (Delete, Cut, Copy or Clear)
4097 Editor::cut_copy (CutCopyOp op)
4099 /* only cancel selection if cut/copy is successful.*/
4105 opname = _("delete");
4114 opname = _("clear");
4118 /* if we're deleting something, and the mouse is still pressed,
4119 the thing we started a drag for will be gone when we release
4120 the mouse button(s). avoid this. see part 2 at the end of
4124 if (op == Delete || op == Cut || op == Clear) {
4125 if (_drags->active ()) {
4130 if ( op != Delete ) //"Delete" doesn't change copy/paste buf
4131 cut_buffer->clear ();
4133 if (entered_marker) {
4135 /* cut/delete op while pointing at a marker */
4138 Location* loc = find_location_from_marker (entered_marker, ignored);
4140 if (_session && loc) {
4141 entered_marker = NULL;
4142 Glib::signal_idle().connect (sigc::bind (sigc::mem_fun(*this, &Editor::really_remove_marker), loc));
4149 switch (mouse_mode) {
4152 begin_reversible_command (opname + ' ' + X_("MIDI"));
4154 commit_reversible_command ();
4160 bool did_edit = false;
4162 if (!selection->regions.empty() || !selection->points.empty()) {
4163 begin_reversible_command (opname + ' ' + _("objects"));
4166 if (!selection->regions.empty()) {
4167 cut_copy_regions (op, selection->regions);
4169 if (op == Cut || op == Delete) {
4170 selection->clear_regions ();
4174 if (!selection->points.empty()) {
4175 cut_copy_points (op);
4177 if (op == Cut || op == Delete) {
4178 selection->clear_points ();
4181 } else if (selection->time.empty()) {
4182 framepos_t start, end;
4183 /* no time selection, see if we can get an edit range
4186 if (get_edit_op_range (start, end)) {
4187 selection->set (start, end);
4189 } else if (!selection->time.empty()) {
4190 begin_reversible_command (opname + ' ' + _("range"));
4193 cut_copy_ranges (op);
4195 if (op == Cut || op == Delete) {
4196 selection->clear_time ();
4201 /* reset repeated paste state */
4204 commit_reversible_command ();
4207 if (op == Delete || op == Cut || op == Clear) {
4213 struct AutomationRecord {
4214 AutomationRecord () : state (0) , line(NULL) {}
4215 AutomationRecord (XMLNode* s, const AutomationLine* l) : state (s) , line (l) {}
4217 XMLNode* state; ///< state before any operation
4218 const AutomationLine* line; ///< line this came from
4219 boost::shared_ptr<Evoral::ControlList> copy; ///< copied events for the cut buffer
4221 struct PointsSelectionPositionSorter {
4222 bool operator() (ControlPoint* a, ControlPoint* b) {
4223 return (*(a->model()))->when < (*(b->model()))->when;
4226 /** Cut, copy or clear selected automation points.
4227 * @param op Operation (Cut, Copy or Clear)
4230 Editor::cut_copy_points (Editing::CutCopyOp op, Evoral::Beats earliest, bool midi)
4232 if (selection->points.empty ()) {
4236 /* XXX: not ideal, as there may be more than one track involved in the point selection */
4237 _last_cut_copy_source_track = &selection->points.front()->line().trackview;
4239 /* Keep a record of the AutomationLists that we end up using in this operation */
4240 typedef std::map<boost::shared_ptr<AutomationList>, AutomationRecord> Lists;
4243 /* user could select points in any order */
4244 selection->points.sort(PointsSelectionPositionSorter ());
4246 /* Go through all selected points, making an AutomationRecord for each distinct AutomationList */
4247 for (PointSelection::iterator sel_point = selection->points.begin(); sel_point != selection->points.end(); ++sel_point) {
4248 const AutomationLine& line = (*sel_point)->line();
4249 const boost::shared_ptr<AutomationList> al = line.the_list();
4250 if (lists.find (al) == lists.end ()) {
4251 /* We haven't seen this list yet, so make a record for it. This includes
4252 taking a copy of its current state, in case this is needed for undo later.
4254 lists[al] = AutomationRecord (&al->get_state (), &line);
4258 if (op == Cut || op == Copy) {
4259 /* This operation will involve putting things in the cut buffer, so create an empty
4260 ControlList for each of our source lists to put the cut buffer data in.
4262 for (Lists::iterator i = lists.begin(); i != lists.end(); ++i) {
4263 i->second.copy = i->first->create (i->first->parameter (), i->first->descriptor());
4266 /* Add all selected points to the relevant copy ControlLists */
4267 framepos_t start = std::numeric_limits<framepos_t>::max();
4268 for (PointSelection::iterator sel_point = selection->points.begin(); sel_point != selection->points.end(); ++sel_point) {
4269 boost::shared_ptr<AutomationList> al = (*sel_point)->line().the_list();
4270 AutomationList::const_iterator ctrl_evt = (*sel_point)->model ();
4272 lists[al].copy->fast_simple_add ((*ctrl_evt)->when, (*ctrl_evt)->value);
4274 /* Update earliest MIDI start time in beats */
4275 earliest = std::min(earliest, Evoral::Beats((*ctrl_evt)->when));
4277 /* Update earliest session start time in frames */
4278 start = std::min(start, (*sel_point)->line().session_position(ctrl_evt));
4282 /* Snap start time backwards, so copy/paste is snap aligned. */
4284 if (earliest == Evoral::Beats::max()) {
4285 earliest = Evoral::Beats(); // Weird... don't offset
4287 earliest.round_down_to_beat();
4289 if (start == std::numeric_limits<double>::max()) {
4290 start = 0; // Weird... don't offset
4292 snap_to(start, RoundDownMaybe);
4295 const double line_offset = midi ? earliest.to_double() : start;
4296 for (Lists::iterator i = lists.begin(); i != lists.end(); ++i) {
4297 /* Correct this copy list so that it is relative to the earliest
4298 start time, so relative ordering between points is preserved
4299 when copying from several lists and the paste starts at the
4300 earliest copied piece of data. */
4301 boost::shared_ptr<Evoral::ControlList> &al_cpy = i->second.copy;
4302 for (AutomationList::iterator ctrl_evt = al_cpy->begin(); ctrl_evt != al_cpy->end(); ++ctrl_evt) {
4303 (*ctrl_evt)->when -= line_offset;
4306 /* And add it to the cut buffer */
4307 cut_buffer->add (al_cpy);
4311 if (op == Delete || op == Cut) {
4312 /* This operation needs to remove things from the main AutomationList, so do that now */
4314 for (Lists::iterator i = lists.begin(); i != lists.end(); ++i) {
4315 i->first->freeze ();
4318 /* Remove each selected point from its AutomationList */
4319 for (PointSelection::iterator sel_point = selection->points.begin(); sel_point != selection->points.end(); ++sel_point) {
4320 AutomationLine& line = (*sel_point)->line ();
4321 boost::shared_ptr<AutomationList> al = line.the_list();
4325 if (dynamic_cast<AudioRegionGainLine*> (&line)) {
4326 /* removing of first and last gain point in region gain lines is prohibited*/
4327 if (line.is_last_point (*(*sel_point)) || line.is_first_point (*(*sel_point))) {
4333 al->erase ((*sel_point)->model ());
4337 /* Thaw the lists and add undo records for them */
4338 for (Lists::iterator i = lists.begin(); i != lists.end(); ++i) {
4339 boost::shared_ptr<AutomationList> al = i->first;
4341 _session->add_command (new MementoCommand<AutomationList> (*al.get(), i->second.state, &(al->get_state ())));
4346 /** Cut, copy or clear selected automation points.
4347 * @param op Operation (Cut, Copy or Clear)
4350 Editor::cut_copy_midi (CutCopyOp op)
4352 Evoral::Beats earliest = Evoral::Beats::max();
4353 for (MidiRegionSelection::iterator i = selection->midi_regions.begin(); i != selection->midi_regions.end(); ++i) {
4354 MidiRegionView* mrv = dynamic_cast<MidiRegionView*>(*i);
4356 if (!mrv->selection().empty()) {
4357 earliest = std::min(earliest, (*mrv->selection().begin())->note()->time());
4359 mrv->cut_copy_clear (op);
4361 /* XXX: not ideal, as there may be more than one track involved in the selection */
4362 _last_cut_copy_source_track = &mrv->get_time_axis_view();
4366 if (!selection->points.empty()) {
4367 cut_copy_points (op, earliest, true);
4368 if (op == Cut || op == Delete) {
4369 selection->clear_points ();
4374 struct lt_playlist {
4375 bool operator () (const PlaylistState& a, const PlaylistState& b) {
4376 return a.playlist < b.playlist;
4380 struct PlaylistMapping {
4382 boost::shared_ptr<Playlist> pl;
4384 PlaylistMapping (TimeAxisView* tvp) : tv (tvp) {}
4387 /** Remove `clicked_regionview' */
4389 Editor::remove_clicked_region ()
4391 if (clicked_routeview == 0 || clicked_regionview == 0) {
4395 begin_reversible_command (_("remove region"));
4397 boost::shared_ptr<Playlist> playlist = clicked_routeview->playlist();
4399 playlist->clear_changes ();
4400 playlist->clear_owned_changes ();
4401 playlist->remove_region (clicked_regionview->region());
4402 if (Config->get_edit_mode() == Ripple)
4403 playlist->ripple (clicked_regionview->region()->position(), -clicked_regionview->region()->length(), boost::shared_ptr<Region>());
4405 /* We might have removed regions, which alters other regions' layering_index,
4406 so we need to do a recursive diff here.
4408 vector<Command*> cmds;
4409 playlist->rdiff (cmds);
4410 _session->add_commands (cmds);
4412 _session->add_command(new StatefulDiffCommand (playlist));
4413 commit_reversible_command ();
4417 /** Remove the selected regions */
4419 Editor::remove_selected_regions ()
4421 RegionSelection rs = get_regions_from_selection_and_entered ();
4423 if (!_session || rs.empty()) {
4427 list<boost::shared_ptr<Region> > regions_to_remove;
4429 for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
4430 // we can't just remove the region(s) in this loop because
4431 // this removes them from the RegionSelection, and they thus
4432 // disappear from underneath the iterator, and the ++i above
4433 // SEGVs in a puzzling fashion.
4435 // so, first iterate over the regions to be removed from rs and
4436 // add them to the regions_to_remove list, and then
4437 // iterate over the list to actually remove them.
4439 regions_to_remove.push_back ((*i)->region());
4442 vector<boost::shared_ptr<Playlist> > playlists;
4444 for (list<boost::shared_ptr<Region> >::iterator rl = regions_to_remove.begin(); rl != regions_to_remove.end(); ++rl) {
4446 boost::shared_ptr<Playlist> playlist = (*rl)->playlist();
4449 // is this check necessary?
4453 /* get_regions_from_selection_and_entered() guarantees that
4454 the playlists involved are unique, so there is no need
4458 playlists.push_back (playlist);
4460 playlist->clear_changes ();
4461 playlist->clear_owned_changes ();
4462 playlist->freeze ();
4463 playlist->remove_region (*rl);
4464 if (Config->get_edit_mode() == Ripple)
4465 playlist->ripple ((*rl)->position(), -(*rl)->length(), boost::shared_ptr<Region>());
4469 vector<boost::shared_ptr<Playlist> >::iterator pl;
4470 bool in_command = false;
4472 for (pl = playlists.begin(); pl != playlists.end(); ++pl) {
4475 /* We might have removed regions, which alters other regions' layering_index,
4476 so we need to do a recursive diff here.
4480 begin_reversible_command (_("remove region"));
4483 vector<Command*> cmds;
4484 (*pl)->rdiff (cmds);
4485 _session->add_commands (cmds);
4487 _session->add_command(new StatefulDiffCommand (*pl));
4491 commit_reversible_command ();
4495 /** Cut, copy or clear selected regions.
4496 * @param op Operation (Cut, Copy or Clear)
4499 Editor::cut_copy_regions (CutCopyOp op, RegionSelection& rs)
4501 /* we can't use a std::map here because the ordering is important, and we can't trivially sort
4502 a map when we want ordered access to both elements. i think.
4505 vector<PlaylistMapping> pmap;
4507 framepos_t first_position = max_framepos;
4509 typedef set<boost::shared_ptr<Playlist> > FreezeList;
4510 FreezeList freezelist;
4512 /* get ordering correct before we cut/copy */
4514 rs.sort_by_position_and_track ();
4516 for (RegionSelection::iterator x = rs.begin(); x != rs.end(); ++x) {
4518 first_position = min ((framepos_t) (*x)->region()->position(), first_position);
4520 if (op == Cut || op == Clear || op == Delete) {
4521 boost::shared_ptr<Playlist> pl = (*x)->region()->playlist();
4524 FreezeList::iterator fl;
4526 // only take state if this is a new playlist.
4527 for (fl = freezelist.begin(); fl != freezelist.end(); ++fl) {
4533 if (fl == freezelist.end()) {
4534 pl->clear_changes();
4535 pl->clear_owned_changes ();
4537 freezelist.insert (pl);
4542 TimeAxisView* tv = &(*x)->get_time_axis_view();
4543 vector<PlaylistMapping>::iterator z;
4545 for (z = pmap.begin(); z != pmap.end(); ++z) {
4546 if ((*z).tv == tv) {
4551 if (z == pmap.end()) {
4552 pmap.push_back (PlaylistMapping (tv));
4556 for (RegionSelection::iterator x = rs.begin(); x != rs.end(); ) {
4558 boost::shared_ptr<Playlist> pl = (*x)->region()->playlist();
4561 /* region not yet associated with a playlist (e.g. unfinished
4568 TimeAxisView& tv = (*x)->get_time_axis_view();
4569 boost::shared_ptr<Playlist> npl;
4570 RegionSelection::iterator tmp;
4577 vector<PlaylistMapping>::iterator z;
4579 for (z = pmap.begin(); z != pmap.end(); ++z) {
4580 if ((*z).tv == &tv) {
4585 assert (z != pmap.end());
4588 npl = PlaylistFactory::create (pl->data_type(), *_session, "cutlist", true);
4596 boost::shared_ptr<Region> r = (*x)->region();
4597 boost::shared_ptr<Region> _xx;
4603 pl->remove_region (r);
4604 if (Config->get_edit_mode() == Ripple)
4605 pl->ripple (r->position(), -r->length(), boost::shared_ptr<Region>());
4609 _xx = RegionFactory::create (r);
4610 npl->add_region (_xx, r->position() - first_position);
4611 pl->remove_region (r);
4612 if (Config->get_edit_mode() == Ripple)
4613 pl->ripple (r->position(), -r->length(), boost::shared_ptr<Region>());
4617 /* copy region before adding, so we're not putting same object into two different playlists */
4618 npl->add_region (RegionFactory::create (r), r->position() - first_position);
4622 pl->remove_region (r);
4623 if (Config->get_edit_mode() == Ripple)
4624 pl->ripple (r->position(), -r->length(), boost::shared_ptr<Region>());
4633 list<boost::shared_ptr<Playlist> > foo;
4635 /* the pmap is in the same order as the tracks in which selected regions occurred */
4637 for (vector<PlaylistMapping>::iterator i = pmap.begin(); i != pmap.end(); ++i) {
4640 foo.push_back ((*i).pl);
4645 cut_buffer->set (foo);
4649 _last_cut_copy_source_track = 0;
4651 _last_cut_copy_source_track = pmap.front().tv;
4655 for (FreezeList::iterator pl = freezelist.begin(); pl != freezelist.end(); ++pl) {
4658 /* We might have removed regions, which alters other regions' layering_index,
4659 so we need to do a recursive diff here.
4661 vector<Command*> cmds;
4662 (*pl)->rdiff (cmds);
4663 _session->add_commands (cmds);
4665 _session->add_command (new StatefulDiffCommand (*pl));
4670 Editor::cut_copy_ranges (CutCopyOp op)
4672 TrackViewList ts = selection->tracks.filter_to_unique_playlists ();
4674 /* Sort the track selection now, so that it if is used, the playlists
4675 selected by the calls below to cut_copy_clear are in the order that
4676 their tracks appear in the editor. This makes things like paste
4677 of ranges work properly.
4680 sort_track_selection (ts);
4683 if (!entered_track) {
4686 ts.push_back (entered_track);
4689 for (TrackViewList::iterator i = ts.begin(); i != ts.end(); ++i) {
4690 (*i)->cut_copy_clear (*selection, op);
4695 Editor::paste (float times, bool from_context)
4697 DEBUG_TRACE (DEBUG::CutNPaste, "paste to preferred edit pos\n");
4699 paste_internal (get_preferred_edit_position (EDIT_IGNORE_NONE, from_context), times, get_grid_music_divisions (0));
4703 Editor::mouse_paste ()
4708 if (!mouse_frame (where, ignored)) {
4713 paste_internal (where, 1, get_grid_music_divisions (0));
4717 Editor::paste_internal (framepos_t position, float times, const int32_t sub_num)
4719 DEBUG_TRACE (DEBUG::CutNPaste, string_compose ("apparent paste position is %1\n", position));
4721 if (cut_buffer->empty(internal_editing())) {
4725 if (position == max_framepos) {
4726 position = get_preferred_edit_position();
4727 DEBUG_TRACE (DEBUG::CutNPaste, string_compose ("preferred edit position is %1\n", position));
4730 if (position == last_paste_pos) {
4731 /* repeated paste in the same position */
4734 /* paste in new location, reset repeated paste state */
4736 last_paste_pos = position;
4739 /* get everything in the correct order */
4742 if (!selection->tracks.empty()) {
4743 /* If there is a track selection, paste into exactly those tracks and
4744 only those tracks. This allows the user to be explicit and override
4745 the below "do the reasonable thing" logic. */
4746 ts = selection->tracks.filter_to_unique_playlists ();
4747 sort_track_selection (ts);
4749 /* Figure out which track to base the paste at. */
4750 TimeAxisView* base_track = NULL;
4751 if (_edit_point == Editing::EditAtMouse && entered_track) {
4752 /* With the mouse edit point, paste onto the track under the mouse. */
4753 base_track = entered_track;
4754 } else if (_edit_point == Editing::EditAtMouse && entered_regionview) {
4755 /* With the mouse edit point, paste onto the track of the region under the mouse. */
4756 base_track = &entered_regionview->get_time_axis_view();
4757 } else if (_last_cut_copy_source_track) {
4758 /* Paste to the track that the cut/copy came from (see mantis #333). */
4759 base_track = _last_cut_copy_source_track;
4761 /* This is "impossible" since we've copied... well, do nothing. */
4765 /* Walk up to parent if necessary, so base track is a route. */
4766 while (base_track->get_parent()) {
4767 base_track = base_track->get_parent();
4770 /* Add base track and all tracks below it. The paste logic will select
4771 the appropriate object types from the cut buffer in relative order. */
4772 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
4773 if ((*i)->order() >= base_track->order()) {
4778 /* Sort tracks so the nth track of type T will pick the nth object of type T. */
4779 sort_track_selection (ts);
4781 /* Add automation children of each track in order, for pasting several lines. */
4782 for (TrackViewList::iterator i = ts.begin(); i != ts.end();) {
4783 /* Add any automation children for pasting several lines */
4784 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*>(*i++);
4789 typedef RouteTimeAxisView::AutomationTracks ATracks;
4790 const ATracks& atracks = rtv->automation_tracks();
4791 for (ATracks::const_iterator a = atracks.begin(); a != atracks.end(); ++a) {
4792 i = ts.insert(i, a->second.get());
4797 /* We now have a list of trackviews starting at base_track, including
4798 automation children, in the order shown in the editor, e.g. R1,
4799 R1.A1, R1.A2, R2, R2.A1, ... */
4802 begin_reversible_command (Operations::paste);
4804 if (ts.size() == 1 && cut_buffer->lines.size() == 1 &&
4805 dynamic_cast<AutomationTimeAxisView*>(ts.front())) {
4806 /* Only one line copied, and one automation track selected. Do a
4807 "greedy" paste from one automation type to another. */
4809 PasteContext ctx(paste_count, times, ItemCounts(), true);
4810 ts.front()->paste (position, *cut_buffer, ctx, sub_num);
4814 /* Paste into tracks */
4816 PasteContext ctx(paste_count, times, ItemCounts(), false);
4817 for (TrackViewList::iterator i = ts.begin(); i != ts.end(); ++i) {
4818 (*i)->paste (position, *cut_buffer, ctx, sub_num);
4822 commit_reversible_command ();
4826 Editor::duplicate_regions (float times)
4828 RegionSelection rs (get_regions_from_selection_and_entered());
4829 duplicate_some_regions (rs, times);
4833 Editor::duplicate_some_regions (RegionSelection& regions, float times)
4835 if (regions.empty ()) {
4839 boost::shared_ptr<Playlist> playlist;
4840 RegionSelection sel = regions; // clear (below) may clear the argument list if its the current region selection
4841 RegionSelection foo;
4843 framepos_t const start_frame = regions.start ();
4844 framepos_t const end_frame = regions.end_frame ();
4845 framecnt_t const gap = end_frame - start_frame + 1;
4847 begin_reversible_command (Operations::duplicate_region);
4849 selection->clear_regions ();
4851 for (RegionSelection::iterator i = sel.begin(); i != sel.end(); ++i) {
4853 boost::shared_ptr<Region> r ((*i)->region());
4855 TimeAxisView& tv = (*i)->get_time_axis_view();
4856 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (&tv);
4857 latest_regionviews.clear ();
4858 sigc::connection c = rtv->view()->RegionViewAdded.connect (sigc::mem_fun(*this, &Editor::collect_new_region_view));
4860 framepos_t const position = end_frame + (r->first_frame() - start_frame + 1);
4861 playlist = (*i)->region()->playlist();
4862 playlist->clear_changes ();
4863 playlist->duplicate (r, position, gap, times);
4864 _session->add_command(new StatefulDiffCommand (playlist));
4868 foo.insert (foo.end(), latest_regionviews.begin(), latest_regionviews.end());
4872 selection->set (foo);
4875 commit_reversible_command ();
4879 Editor::duplicate_selection (float times)
4881 if (selection->time.empty() || selection->tracks.empty()) {
4885 boost::shared_ptr<Playlist> playlist;
4887 TrackViewList ts = selection->tracks.filter_to_unique_playlists ();
4889 bool in_command = false;
4891 for (TrackViewList::iterator i = ts.begin(); i != ts.end(); ++i) {
4892 if ((playlist = (*i)->playlist()) == 0) {
4895 playlist->clear_changes ();
4897 if (clicked_selection) {
4898 playlist->duplicate_range (selection->time[clicked_selection], times);
4900 playlist->duplicate_ranges (selection->time, times);
4904 begin_reversible_command (_("duplicate range selection"));
4907 _session->add_command (new StatefulDiffCommand (playlist));
4912 // now "move" range selection to after the current range selection
4913 framecnt_t distance = 0;
4915 if (clicked_selection) {
4916 distance = selection->time[clicked_selection].end -
4917 selection->time[clicked_selection].start;
4919 distance = selection->time.end_frame() - selection->time.start();
4922 selection->move_time (distance);
4924 commit_reversible_command ();
4928 /** Reset all selected points to the relevant default value */
4930 Editor::reset_point_selection ()
4932 for (PointSelection::iterator i = selection->points.begin(); i != selection->points.end(); ++i) {
4933 ARDOUR::AutomationList::iterator j = (*i)->model ();
4934 (*j)->value = (*i)->line().the_list()->default_value ();
4939 Editor::center_playhead ()
4941 float const page = _visible_canvas_width * samples_per_pixel;
4942 center_screen_internal (playhead_cursor->current_frame (), page);
4946 Editor::center_edit_point ()
4948 float const page = _visible_canvas_width * samples_per_pixel;
4949 center_screen_internal (get_preferred_edit_position(), page);
4952 /** Caller must begin and commit a reversible command */
4954 Editor::clear_playlist (boost::shared_ptr<Playlist> playlist)
4956 playlist->clear_changes ();
4958 _session->add_command (new StatefulDiffCommand (playlist));
4962 Editor::nudge_track (bool use_edit, bool forwards)
4964 boost::shared_ptr<Playlist> playlist;
4965 framepos_t distance;
4966 framepos_t next_distance;
4970 start = get_preferred_edit_position();
4975 if ((distance = get_nudge_distance (start, next_distance)) == 0) {
4979 if (selection->tracks.empty()) {
4983 TrackViewList ts = selection->tracks.filter_to_unique_playlists ();
4984 bool in_command = false;
4986 for (TrackViewList::iterator i = ts.begin(); i != ts.end(); ++i) {
4988 if ((playlist = (*i)->playlist()) == 0) {
4992 playlist->clear_changes ();
4993 playlist->clear_owned_changes ();
4995 playlist->nudge_after (start, distance, forwards);
4998 begin_reversible_command (_("nudge track"));
5001 vector<Command*> cmds;
5003 playlist->rdiff (cmds);
5004 _session->add_commands (cmds);
5006 _session->add_command (new StatefulDiffCommand (playlist));
5010 commit_reversible_command ();
5015 Editor::remove_last_capture ()
5017 vector<string> choices;
5024 if (Config->get_verify_remove_last_capture()) {
5025 prompt = _("Do you really want to destroy the last capture?"
5026 "\n(This is destructive and cannot be undone)");
5028 choices.push_back (_("No, do nothing."));
5029 choices.push_back (_("Yes, destroy it."));
5031 Gtkmm2ext::Choice prompter (_("Destroy last capture"), prompt, choices);
5033 if (prompter.run () == 1) {
5034 _session->remove_last_capture ();
5035 _regions->redisplay ();
5039 _session->remove_last_capture();
5040 _regions->redisplay ();
5045 Editor::normalize_region ()
5051 RegionSelection rs = get_regions_from_selection_and_entered ();
5057 NormalizeDialog dialog (rs.size() > 1);
5059 if (dialog.run () != RESPONSE_ACCEPT) {
5063 CursorContext::Handle cursor_ctx = CursorContext::create(*this, _cursors->wait);
5066 /* XXX: should really only count audio regions here */
5067 int const regions = rs.size ();
5069 /* Make a list of the selected audio regions' maximum amplitudes, and also
5070 obtain the maximum amplitude of them all.
5072 list<double> max_amps;
5073 list<double> rms_vals;
5076 bool use_rms = dialog.constrain_rms ();
5078 for (RegionSelection::const_iterator i = rs.begin(); i != rs.end(); ++i) {
5079 AudioRegionView const * arv = dynamic_cast<AudioRegionView const *> (*i);
5083 dialog.descend (1.0 / regions);
5084 double const a = arv->audio_region()->maximum_amplitude (&dialog);
5086 double r = arv->audio_region()->rms (&dialog);
5087 max_rms = max (max_rms, r);
5088 rms_vals.push_back (r);
5092 /* the user cancelled the operation */
5096 max_amps.push_back (a);
5097 max_amp = max (max_amp, a);
5101 list<double>::const_iterator a = max_amps.begin ();
5102 list<double>::const_iterator l = rms_vals.begin ();
5103 bool in_command = false;
5105 for (RegionSelection::iterator r = rs.begin(); r != rs.end(); ++r) {
5106 AudioRegionView* const arv = dynamic_cast<AudioRegionView*> (*r);
5111 arv->region()->clear_changes ();
5113 double amp = dialog.normalize_individually() ? *a : max_amp;
5114 double target = dialog.target_peak (); // dB
5117 double const amp_rms = dialog.normalize_individually() ? *l : max_rms;
5118 const double t_rms = dialog.target_rms ();
5119 const gain_t c_peak = dB_to_coefficient (target);
5120 const gain_t c_rms = dB_to_coefficient (t_rms);
5121 if ((amp_rms / c_rms) > (amp / c_peak)) {
5127 arv->audio_region()->normalize (amp, target);
5130 begin_reversible_command (_("normalize"));
5133 _session->add_command (new StatefulDiffCommand (arv->region()));
5140 commit_reversible_command ();
5146 Editor::reset_region_scale_amplitude ()
5152 RegionSelection rs = get_regions_from_selection_and_entered ();
5158 bool in_command = false;
5160 for (RegionSelection::iterator r = rs.begin(); r != rs.end(); ++r) {
5161 AudioRegionView* const arv = dynamic_cast<AudioRegionView*>(*r);
5164 arv->region()->clear_changes ();
5165 arv->audio_region()->set_scale_amplitude (1.0f);
5168 begin_reversible_command ("reset gain");
5171 _session->add_command (new StatefulDiffCommand (arv->region()));
5175 commit_reversible_command ();
5180 Editor::adjust_region_gain (bool up)
5182 RegionSelection rs = get_regions_from_selection_and_entered ();
5184 if (!_session || rs.empty()) {
5188 bool in_command = false;
5190 for (RegionSelection::iterator r = rs.begin(); r != rs.end(); ++r) {
5191 AudioRegionView* const arv = dynamic_cast<AudioRegionView*>(*r);
5196 arv->region()->clear_changes ();
5198 double dB = accurate_coefficient_to_dB (arv->audio_region()->scale_amplitude ());
5206 arv->audio_region()->set_scale_amplitude (dB_to_coefficient (dB));
5209 begin_reversible_command ("adjust region gain");
5212 _session->add_command (new StatefulDiffCommand (arv->region()));
5216 commit_reversible_command ();
5222 Editor::reverse_region ()
5228 Reverse rev (*_session);
5229 apply_filter (rev, _("reverse regions"));
5233 Editor::strip_region_silence ()
5239 RegionSelection rs = get_regions_from_selection_and_entered ();
5245 std::list<RegionView*> audio_only;
5247 for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
5248 AudioRegionView* const arv = dynamic_cast<AudioRegionView*> (*i);
5250 audio_only.push_back (arv);
5254 assert (!audio_only.empty());
5256 StripSilenceDialog d (_session, audio_only);
5257 int const r = d.run ();
5261 if (r == Gtk::RESPONSE_OK) {
5262 ARDOUR::AudioIntervalMap silences;
5263 d.silences (silences);
5264 StripSilence s (*_session, silences, d.fade_length());
5266 apply_filter (s, _("strip silence"), &d);
5271 Editor::apply_midi_note_edit_op_to_region (MidiOperator& op, MidiRegionView& mrv)
5273 Evoral::Sequence<Evoral::Beats>::Notes selected;
5274 mrv.selection_as_notelist (selected, true);
5276 vector<Evoral::Sequence<Evoral::Beats>::Notes> v;
5277 v.push_back (selected);
5279 Evoral::Beats pos_beats = Evoral::Beats (mrv.midi_region()->beat()) - mrv.midi_region()->start_beats();
5281 return op (mrv.midi_region()->model(), pos_beats, v);
5285 Editor::apply_midi_note_edit_op (MidiOperator& op, const RegionSelection& rs)
5291 bool in_command = false;
5293 for (RegionSelection::const_iterator r = rs.begin(); r != rs.end(); ) {
5294 RegionSelection::const_iterator tmp = r;
5297 MidiRegionView* const mrv = dynamic_cast<MidiRegionView*> (*r);
5300 Command* cmd = apply_midi_note_edit_op_to_region (op, *mrv);
5303 begin_reversible_command (op.name ());
5307 _session->add_command (cmd);
5315 commit_reversible_command ();
5320 Editor::fork_region ()
5322 RegionSelection rs = get_regions_from_selection_and_entered ();
5328 CursorContext::Handle cursor_ctx = CursorContext::create(*this, _cursors->wait);
5329 bool in_command = false;
5333 for (RegionSelection::iterator r = rs.begin(); r != rs.end(); ) {
5334 RegionSelection::iterator tmp = r;
5337 MidiRegionView* const mrv = dynamic_cast<MidiRegionView*>(*r);
5341 boost::shared_ptr<Playlist> playlist = mrv->region()->playlist();
5342 boost::shared_ptr<MidiSource> new_source = _session->create_midi_source_by_stealing_name (mrv->midi_view()->track());
5343 boost::shared_ptr<MidiRegion> newregion = mrv->midi_region()->clone (new_source);
5346 begin_reversible_command (_("Fork Region(s)"));
5349 playlist->clear_changes ();
5350 playlist->replace_region (mrv->region(), newregion, mrv->region()->position());
5351 _session->add_command(new StatefulDiffCommand (playlist));
5353 error << string_compose (_("Could not unlink %1"), mrv->region()->name()) << endmsg;
5361 commit_reversible_command ();
5366 Editor::quantize_region ()
5369 quantize_regions(get_regions_from_selection_and_entered ());
5374 Editor::quantize_regions (const RegionSelection& rs)
5376 if (rs.n_midi_regions() == 0) {
5380 if (!quantize_dialog) {
5381 quantize_dialog = new QuantizeDialog (*this);
5384 if (quantize_dialog->is_mapped()) {
5385 /* in progress already */
5389 quantize_dialog->present ();
5390 const int r = quantize_dialog->run ();
5391 quantize_dialog->hide ();
5393 if (r == Gtk::RESPONSE_OK) {
5394 Quantize quant (quantize_dialog->snap_start(),
5395 quantize_dialog->snap_end(),
5396 quantize_dialog->start_grid_size(),
5397 quantize_dialog->end_grid_size(),
5398 quantize_dialog->strength(),
5399 quantize_dialog->swing(),
5400 quantize_dialog->threshold());
5402 apply_midi_note_edit_op (quant, rs);
5407 Editor::legatize_region (bool shrink_only)
5410 legatize_regions(get_regions_from_selection_and_entered (), shrink_only);
5415 Editor::legatize_regions (const RegionSelection& rs, bool shrink_only)
5417 if (rs.n_midi_regions() == 0) {
5421 Legatize legatize(shrink_only);
5422 apply_midi_note_edit_op (legatize, rs);
5426 Editor::transform_region ()
5429 transform_regions(get_regions_from_selection_and_entered ());
5434 Editor::transform_regions (const RegionSelection& rs)
5436 if (rs.n_midi_regions() == 0) {
5443 const int r = td.run();
5446 if (r == Gtk::RESPONSE_OK) {
5447 Transform transform(td.get());
5448 apply_midi_note_edit_op(transform, rs);
5453 Editor::transpose_region ()
5456 transpose_regions(get_regions_from_selection_and_entered ());
5461 Editor::transpose_regions (const RegionSelection& rs)
5463 if (rs.n_midi_regions() == 0) {
5468 int const r = d.run ();
5470 if (r == RESPONSE_ACCEPT) {
5471 Transpose transpose(d.semitones ());
5472 apply_midi_note_edit_op (transpose, rs);
5477 Editor::insert_patch_change (bool from_context)
5479 RegionSelection rs = get_regions_from_selection_and_entered ();
5485 const framepos_t p = get_preferred_edit_position (EDIT_IGNORE_NONE, from_context);
5487 /* XXX: bit of a hack; use the MIDNAM from the first selected region;
5488 there may be more than one, but the PatchChangeDialog can only offer
5489 one set of patch menus.
5491 MidiRegionView* first = dynamic_cast<MidiRegionView*> (rs.front ());
5493 Evoral::PatchChange<Evoral::Beats> empty (Evoral::Beats(), 0, 0, 0);
5494 PatchChangeDialog d (0, _session, empty, first->instrument_info(), Gtk::Stock::ADD);
5496 if (d.run() == RESPONSE_CANCEL) {
5500 for (RegionSelection::iterator i = rs.begin (); i != rs.end(); ++i) {
5501 MidiRegionView* const mrv = dynamic_cast<MidiRegionView*> (*i);
5503 if (p >= mrv->region()->first_frame() && p <= mrv->region()->last_frame()) {
5504 mrv->add_patch_change (p - mrv->region()->position(), d.patch ());
5511 Editor::apply_filter (Filter& filter, string command, ProgressReporter* progress)
5513 RegionSelection rs = get_regions_from_selection_and_entered ();
5519 CursorContext::Handle cursor_ctx = CursorContext::create(*this, _cursors->wait);
5520 bool in_command = false;
5525 int const N = rs.size ();
5527 for (RegionSelection::iterator r = rs.begin(); r != rs.end(); ) {
5528 RegionSelection::iterator tmp = r;
5531 AudioRegionView* const arv = dynamic_cast<AudioRegionView*>(*r);
5533 boost::shared_ptr<Playlist> playlist = arv->region()->playlist();
5536 progress->descend (1.0 / N);
5539 if (arv->audio_region()->apply (filter, progress) == 0) {
5541 playlist->clear_changes ();
5542 playlist->clear_owned_changes ();
5545 begin_reversible_command (command);
5549 if (filter.results.empty ()) {
5551 /* no regions returned; remove the old one */
5552 playlist->remove_region (arv->region ());
5556 std::vector<boost::shared_ptr<Region> >::iterator res = filter.results.begin ();
5558 /* first region replaces the old one */
5559 playlist->replace_region (arv->region(), *res, (*res)->position());
5563 while (res != filter.results.end()) {
5564 playlist->add_region (*res, (*res)->position());
5570 /* We might have removed regions, which alters other regions' layering_index,
5571 so we need to do a recursive diff here.
5573 vector<Command*> cmds;
5574 playlist->rdiff (cmds);
5575 _session->add_commands (cmds);
5577 _session->add_command(new StatefulDiffCommand (playlist));
5581 progress->ascend ();
5590 commit_reversible_command ();
5595 Editor::external_edit_region ()
5601 Editor::reset_region_gain_envelopes ()
5603 RegionSelection rs = get_regions_from_selection_and_entered ();
5605 if (!_session || rs.empty()) {
5609 bool in_command = false;
5611 for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
5612 AudioRegionView* const arv = dynamic_cast<AudioRegionView*>(*i);
5614 boost::shared_ptr<AutomationList> alist (arv->audio_region()->envelope());
5615 XMLNode& before (alist->get_state());
5617 arv->audio_region()->set_default_envelope ();
5620 begin_reversible_command (_("reset region gain"));
5623 _session->add_command (new MementoCommand<AutomationList>(*arv->audio_region()->envelope().get(), &before, &alist->get_state()));
5628 commit_reversible_command ();
5633 Editor::set_region_gain_visibility (RegionView* rv)
5635 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (rv);
5637 arv->update_envelope_visibility();
5642 Editor::set_gain_envelope_visibility ()
5648 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
5649 AudioTimeAxisView* v = dynamic_cast<AudioTimeAxisView*>(*i);
5651 v->audio_view()->foreach_regionview (sigc::mem_fun (this, &Editor::set_region_gain_visibility));
5657 Editor::toggle_gain_envelope_active ()
5659 if (_ignore_region_action) {
5663 RegionSelection rs = get_regions_from_selection_and_entered ();
5665 if (!_session || rs.empty()) {
5669 bool in_command = false;
5671 for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
5672 AudioRegionView* const arv = dynamic_cast<AudioRegionView*>(*i);
5674 arv->region()->clear_changes ();
5675 arv->audio_region()->set_envelope_active (!arv->audio_region()->envelope_active());
5678 begin_reversible_command (_("region gain envelope active"));
5681 _session->add_command (new StatefulDiffCommand (arv->region()));
5686 commit_reversible_command ();
5691 Editor::toggle_region_lock ()
5693 if (_ignore_region_action) {
5697 RegionSelection rs = get_regions_from_selection_and_entered ();
5699 if (!_session || rs.empty()) {
5703 begin_reversible_command (_("toggle region lock"));
5705 for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
5706 (*i)->region()->clear_changes ();
5707 (*i)->region()->set_locked (!(*i)->region()->locked());
5708 _session->add_command (new StatefulDiffCommand ((*i)->region()));
5711 commit_reversible_command ();
5715 Editor::toggle_region_video_lock ()
5717 if (_ignore_region_action) {
5721 RegionSelection rs = get_regions_from_selection_and_entered ();
5723 if (!_session || rs.empty()) {
5727 begin_reversible_command (_("Toggle Video Lock"));
5729 for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
5730 (*i)->region()->clear_changes ();
5731 (*i)->region()->set_video_locked (!(*i)->region()->video_locked());
5732 _session->add_command (new StatefulDiffCommand ((*i)->region()));
5735 commit_reversible_command ();
5739 Editor::toggle_region_lock_style ()
5741 if (_ignore_region_action) {
5745 RegionSelection rs = get_regions_from_selection_and_entered ();
5747 if (!_session || rs.empty()) {
5751 begin_reversible_command (_("region lock style"));
5753 for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
5754 (*i)->region()->clear_changes ();
5755 PositionLockStyle const ns = (*i)->region()->position_lock_style() == AudioTime ? MusicTime : AudioTime;
5756 (*i)->region()->set_position_lock_style (ns);
5757 _session->add_command (new StatefulDiffCommand ((*i)->region()));
5760 commit_reversible_command ();
5764 Editor::toggle_opaque_region ()
5766 if (_ignore_region_action) {
5770 RegionSelection rs = get_regions_from_selection_and_entered ();
5772 if (!_session || rs.empty()) {
5776 begin_reversible_command (_("change region opacity"));
5778 for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
5779 (*i)->region()->clear_changes ();
5780 (*i)->region()->set_opaque (!(*i)->region()->opaque());
5781 _session->add_command (new StatefulDiffCommand ((*i)->region()));
5784 commit_reversible_command ();
5788 Editor::toggle_record_enable ()
5790 bool new_state = false;
5792 for (TrackSelection::iterator i = selection->tracks.begin(); i != selection->tracks.end(); ++i) {
5793 RouteTimeAxisView *rtav = dynamic_cast<RouteTimeAxisView *>(*i);
5796 if (!rtav->is_track())
5800 new_state = !rtav->track()->rec_enable_control()->get_value();
5804 rtav->track()->rec_enable_control()->set_value (new_state, Controllable::UseGroup);
5809 Editor::toggle_solo ()
5811 bool new_state = false;
5813 boost::shared_ptr<ControlList> cl (new ControlList);
5815 for (TrackSelection::iterator i = selection->tracks.begin(); i != selection->tracks.end(); ++i) {
5816 RouteTimeAxisView *rtav = dynamic_cast<RouteTimeAxisView *>(*i);
5823 new_state = !rtav->route()->soloed ();
5827 cl->push_back (rtav->route()->solo_control());
5830 _session->set_controls (cl, new_state ? 1.0 : 0.0, Controllable::UseGroup);
5834 Editor::toggle_mute ()
5836 bool new_state = false;
5838 boost::shared_ptr<RouteList> rl (new RouteList);
5840 for (TrackSelection::iterator i = selection->tracks.begin(); i != selection->tracks.end(); ++i) {
5841 RouteTimeAxisView *rtav = dynamic_cast<RouteTimeAxisView *>(*i);
5848 new_state = !rtav->route()->muted();
5852 rl->push_back (rtav->route());
5855 _session->set_controls (route_list_to_control_list (rl, &Stripable::mute_control), new_state, Controllable::UseGroup);
5859 Editor::toggle_solo_isolate ()
5865 Editor::fade_range ()
5867 TrackViewList ts = selection->tracks.filter_to_unique_playlists ();
5869 begin_reversible_command (_("fade range"));
5871 for (TrackViewList::iterator i = ts.begin(); i != ts.end(); ++i) {
5872 (*i)->fade_range (selection->time);
5875 commit_reversible_command ();
5880 Editor::set_fade_length (bool in)
5882 RegionSelection rs = get_regions_from_selection_and_entered ();
5888 /* we need a region to measure the offset from the start */
5890 RegionView* rv = rs.front ();
5892 framepos_t pos = get_preferred_edit_position();
5896 if (pos > rv->region()->last_frame() || pos < rv->region()->first_frame()) {
5897 /* edit point is outside the relevant region */
5902 if (pos <= rv->region()->position()) {
5906 len = pos - rv->region()->position();
5907 cmd = _("set fade in length");
5909 if (pos >= rv->region()->last_frame()) {
5913 len = rv->region()->last_frame() - pos;
5914 cmd = _("set fade out length");
5917 bool in_command = false;
5919 for (RegionSelection::iterator x = rs.begin(); x != rs.end(); ++x) {
5920 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (*x);
5926 boost::shared_ptr<AutomationList> alist;
5928 alist = tmp->audio_region()->fade_in();
5930 alist = tmp->audio_region()->fade_out();
5933 XMLNode &before = alist->get_state();
5936 tmp->audio_region()->set_fade_in_length (len);
5937 tmp->audio_region()->set_fade_in_active (true);
5939 tmp->audio_region()->set_fade_out_length (len);
5940 tmp->audio_region()->set_fade_out_active (true);
5944 begin_reversible_command (cmd);
5947 XMLNode &after = alist->get_state();
5948 _session->add_command(new MementoCommand<AutomationList>(*alist, &before, &after));
5952 commit_reversible_command ();
5957 Editor::set_fade_in_shape (FadeShape shape)
5959 RegionSelection rs = get_regions_from_selection_and_entered ();
5964 bool in_command = false;
5966 for (RegionSelection::iterator x = rs.begin(); x != rs.end(); ++x) {
5967 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (*x);
5973 boost::shared_ptr<AutomationList> alist = tmp->audio_region()->fade_in();
5974 XMLNode &before = alist->get_state();
5976 tmp->audio_region()->set_fade_in_shape (shape);
5979 begin_reversible_command (_("set fade in shape"));
5982 XMLNode &after = alist->get_state();
5983 _session->add_command(new MementoCommand<AutomationList>(*alist.get(), &before, &after));
5987 commit_reversible_command ();
5992 Editor::set_fade_out_shape (FadeShape shape)
5994 RegionSelection rs = get_regions_from_selection_and_entered ();
5999 bool in_command = false;
6001 for (RegionSelection::iterator x = rs.begin(); x != rs.end(); ++x) {
6002 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (*x);
6008 boost::shared_ptr<AutomationList> alist = tmp->audio_region()->fade_out();
6009 XMLNode &before = alist->get_state();
6011 tmp->audio_region()->set_fade_out_shape (shape);
6014 begin_reversible_command (_("set fade out shape"));
6017 XMLNode &after = alist->get_state();
6018 _session->add_command(new MementoCommand<AutomationList>(*alist.get(), &before, &after));
6022 commit_reversible_command ();
6027 Editor::set_fade_in_active (bool yn)
6029 RegionSelection rs = get_regions_from_selection_and_entered ();
6034 bool in_command = false;
6036 for (RegionSelection::iterator x = rs.begin(); x != rs.end(); ++x) {
6037 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (*x);
6044 boost::shared_ptr<AudioRegion> ar (tmp->audio_region());
6046 ar->clear_changes ();
6047 ar->set_fade_in_active (yn);
6050 begin_reversible_command (_("set fade in active"));
6053 _session->add_command (new StatefulDiffCommand (ar));
6057 commit_reversible_command ();
6062 Editor::set_fade_out_active (bool yn)
6064 RegionSelection rs = get_regions_from_selection_and_entered ();
6069 bool in_command = false;
6071 for (RegionSelection::iterator x = rs.begin(); x != rs.end(); ++x) {
6072 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (*x);
6078 boost::shared_ptr<AudioRegion> ar (tmp->audio_region());
6080 ar->clear_changes ();
6081 ar->set_fade_out_active (yn);
6084 begin_reversible_command (_("set fade out active"));
6087 _session->add_command(new StatefulDiffCommand (ar));
6091 commit_reversible_command ();
6096 Editor::toggle_region_fades (int dir)
6098 if (_ignore_region_action) {
6102 boost::shared_ptr<AudioRegion> ar;
6105 RegionSelection rs = get_regions_from_selection_and_entered ();
6111 RegionSelection::iterator i;
6112 for (i = rs.begin(); i != rs.end(); ++i) {
6113 if ((ar = boost::dynamic_pointer_cast<AudioRegion>((*i)->region())) != 0) {
6115 yn = ar->fade_out_active ();
6117 yn = ar->fade_in_active ();
6123 if (i == rs.end()) {
6127 /* XXX should this undo-able? */
6128 bool in_command = false;
6130 for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
6131 if ((ar = boost::dynamic_pointer_cast<AudioRegion>((*i)->region())) == 0) {
6134 ar->clear_changes ();
6136 if (dir == 1 || dir == 0) {
6137 ar->set_fade_in_active (!yn);
6140 if (dir == -1 || dir == 0) {
6141 ar->set_fade_out_active (!yn);
6144 begin_reversible_command (_("toggle fade active"));
6147 _session->add_command(new StatefulDiffCommand (ar));
6151 commit_reversible_command ();
6156 /** Update region fade visibility after its configuration has been changed */
6158 Editor::update_region_fade_visibility ()
6160 bool _fade_visibility = _session->config.get_show_region_fades ();
6162 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
6163 AudioTimeAxisView* v = dynamic_cast<AudioTimeAxisView*>(*i);
6165 if (_fade_visibility) {
6166 v->audio_view()->show_all_fades ();
6168 v->audio_view()->hide_all_fades ();
6175 Editor::set_edit_point ()
6180 if (!mouse_frame (where, ignored)) {
6186 if (selection->markers.empty()) {
6188 mouse_add_new_marker (where);
6193 Location* loc = find_location_from_marker (selection->markers.front(), ignored);
6196 loc->move_to (where);
6202 Editor::set_playhead_cursor ()
6204 if (entered_marker) {
6205 _session->request_locate (entered_marker->position(), _session->transport_rolling());
6210 if (!mouse_frame (where, ignored)) {
6217 _session->request_locate (where, _session->transport_rolling());
6221 if (UIConfiguration::instance().get_follow_edits() && (!_session || !_session->config.get_external_sync())) {
6222 cancel_time_selection();
6227 Editor::split_region ()
6229 if (_drags->active ()) {
6233 //if a range is selected, separate it
6234 if ( !selection->time.empty()) {
6235 separate_regions_between (selection->time);
6239 //if no range was selected, try to find some regions to split
6240 if (current_mouse_mode() == MouseObject) { //don't try this for Internal Edit, Stretch, Draw, etc.
6242 RegionSelection rs = get_regions_from_selection_and_edit_point ();
6244 framepos_t where = get_preferred_edit_position ();
6250 if (snap_musical()) {
6251 split_regions_at (where, rs, get_grid_music_divisions (0));
6253 split_regions_at (where, rs, 0);
6259 Editor::select_next_route()
6261 if (selection->tracks.empty()) {
6262 selection->set (track_views.front());
6266 TimeAxisView* current = selection->tracks.front();
6270 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
6272 if (*i == current) {
6274 if (i != track_views.end()) {
6277 current = (*(track_views.begin()));
6278 //selection->set (*(track_views.begin()));
6284 rui = dynamic_cast<RouteUI *>(current);
6286 } while (current->hidden() || (rui == NULL) || !rui->route()->active());
6288 selection->set (current);
6290 ensure_time_axis_view_is_visible (*current, false);
6294 Editor::select_prev_route()
6296 if (selection->tracks.empty()) {
6297 selection->set (track_views.front());
6301 TimeAxisView* current = selection->tracks.front();
6305 for (TrackViewList::reverse_iterator i = track_views.rbegin(); i != track_views.rend(); ++i) {
6307 if (*i == current) {
6309 if (i != track_views.rend()) {
6312 current = *(track_views.rbegin());
6317 rui = dynamic_cast<RouteUI *>(current);
6319 } while (current->hidden() || (rui == NULL) || !rui->route()->active());
6321 selection->set (current);
6323 ensure_time_axis_view_is_visible (*current, false);
6327 Editor::set_loop_from_selection (bool play)
6329 if (_session == 0) {
6333 framepos_t start, end;
6334 if (!get_selection_extents ( start, end))
6337 set_loop_range (start, end, _("set loop range from selection"));
6340 _session->request_play_loop (true, true);
6345 Editor::set_loop_from_region (bool play)
6347 framepos_t start, end;
6348 if (!get_selection_extents ( start, end))
6351 set_loop_range (start, end, _("set loop range from region"));
6354 _session->request_locate (start, true);
6355 _session->request_play_loop (true);
6360 Editor::set_punch_from_selection ()
6362 if (_session == 0) {
6366 framepos_t start, end;
6367 if (!get_selection_extents ( start, end))
6370 set_punch_range (start, end, _("set punch range from selection"));
6374 Editor::set_auto_punch_range ()
6376 // auto punch in/out button from a single button
6377 // If Punch In is unset, set punch range from playhead to end, enable punch in
6378 // If Punch In is set, the next punch sets Punch Out, unless the playhead has been
6379 // rewound beyond the Punch In marker, in which case that marker will be moved back
6380 // to the current playhead position.
6381 // If punch out is set, it clears the punch range and Punch In/Out buttons
6383 if (_session == 0) {
6387 Location* tpl = transport_punch_location();
6388 framepos_t now = playhead_cursor->current_frame();
6389 framepos_t begin = now;
6390 framepos_t end = _session->current_end_frame();
6392 if (!_session->config.get_punch_in()) {
6393 // First Press - set punch in and create range from here to eternity
6394 set_punch_range (begin, end, _("Auto Punch In"));
6395 _session->config.set_punch_in(true);
6396 } else if (tpl && !_session->config.get_punch_out()) {
6397 // Second press - update end range marker and set punch_out
6398 if (now < tpl->start()) {
6399 // playhead has been rewound - move start back and pretend nothing happened
6401 set_punch_range (begin, end, _("Auto Punch In/Out"));
6403 // normal case for 2nd press - set the punch out
6404 end = playhead_cursor->current_frame ();
6405 set_punch_range (tpl->start(), now, _("Auto Punch In/Out"));
6406 _session->config.set_punch_out(true);
6409 if (_session->config.get_punch_out()) {
6410 _session->config.set_punch_out(false);
6413 if (_session->config.get_punch_in()) {
6414 _session->config.set_punch_in(false);
6419 // third press - unset punch in/out and remove range
6420 _session->locations()->remove(tpl);
6427 Editor::set_session_extents_from_selection ()
6429 if (_session == 0) {
6433 framepos_t start, end;
6434 if (!get_selection_extents ( start, end))
6438 if ((loc = _session->locations()->session_range_location()) == 0) {
6439 _session->set_session_extents (start, end); // this will create a new session range; no need for UNDO
6441 XMLNode &before = loc->get_state();
6443 _session->set_session_extents (start, end);
6445 XMLNode &after = loc->get_state();
6447 begin_reversible_command (_("set session start/end from selection"));
6449 _session->add_command (new MementoCommand<Location>(*loc, &before, &after));
6451 commit_reversible_command ();
6454 _session->set_end_is_free (false);
6458 Editor::set_punch_start_from_edit_point ()
6462 framepos_t start = 0;
6463 framepos_t end = max_framepos;
6465 //use the existing punch end, if any
6466 Location* tpl = transport_punch_location();
6471 if ((_edit_point == EditAtPlayhead) && _session->transport_rolling()) {
6472 start = _session->audible_frame();
6474 start = get_preferred_edit_position();
6477 //snap the selection start/end
6480 //if there's not already a sensible selection endpoint, go "forever"
6481 if ( start > end ) {
6485 set_punch_range (start, end, _("set punch start from EP"));
6491 Editor::set_punch_end_from_edit_point ()
6495 framepos_t start = 0;
6496 framepos_t end = max_framepos;
6498 //use the existing punch start, if any
6499 Location* tpl = transport_punch_location();
6501 start = tpl->start();
6504 if ((_edit_point == EditAtPlayhead) && _session->transport_rolling()) {
6505 end = _session->audible_frame();
6507 end = get_preferred_edit_position();
6510 //snap the selection start/end
6513 set_punch_range (start, end, _("set punch end from EP"));
6519 Editor::set_loop_start_from_edit_point ()
6523 framepos_t start = 0;
6524 framepos_t end = max_framepos;
6526 //use the existing loop end, if any
6527 Location* tpl = transport_loop_location();
6532 if ((_edit_point == EditAtPlayhead) && _session->transport_rolling()) {
6533 start = _session->audible_frame();
6535 start = get_preferred_edit_position();
6538 //snap the selection start/end
6541 //if there's not already a sensible selection endpoint, go "forever"
6542 if ( start > end ) {
6546 set_loop_range (start, end, _("set loop start from EP"));
6552 Editor::set_loop_end_from_edit_point ()
6556 framepos_t start = 0;
6557 framepos_t end = max_framepos;
6559 //use the existing loop start, if any
6560 Location* tpl = transport_loop_location();
6562 start = tpl->start();
6565 if ((_edit_point == EditAtPlayhead) && _session->transport_rolling()) {
6566 end = _session->audible_frame();
6568 end = get_preferred_edit_position();
6571 //snap the selection start/end
6574 set_loop_range (start, end, _("set loop end from EP"));
6579 Editor::set_punch_from_region ()
6581 framepos_t start, end;
6582 if (!get_selection_extents ( start, end))
6585 set_punch_range (start, end, _("set punch range from region"));
6589 Editor::pitch_shift_region ()
6591 RegionSelection rs = get_regions_from_selection_and_entered ();
6593 RegionSelection audio_rs;
6594 for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
6595 if (dynamic_cast<AudioRegionView*> (*i)) {
6596 audio_rs.push_back (*i);
6600 if (audio_rs.empty()) {
6604 pitch_shift (audio_rs, 1.2);
6608 Editor::set_tempo_from_region ()
6610 RegionSelection rs = get_regions_from_selection_and_entered ();
6612 if (!_session || rs.empty()) {
6616 RegionView* rv = rs.front();
6618 define_one_bar (rv->region()->position(), rv->region()->last_frame() + 1);
6622 Editor::use_range_as_bar ()
6624 framepos_t start, end;
6625 if (get_edit_op_range (start, end)) {
6626 define_one_bar (start, end);
6631 Editor::define_one_bar (framepos_t start, framepos_t end)
6633 framepos_t length = end - start;
6635 const Meter& m (_session->tempo_map().meter_at_frame (start));
6637 /* length = 1 bar */
6639 /* We're going to deliver a constant tempo here,
6640 so we can use frames per beat to determine length.
6641 now we want frames per beat.
6642 we have frames per bar, and beats per bar, so ...
6645 /* XXXX METER MATH */
6647 double frames_per_beat = length / m.divisions_per_bar();
6649 /* beats per minute = */
6651 double beats_per_minute = (_session->frame_rate() * 60.0) / frames_per_beat;
6653 /* now decide whether to:
6655 (a) set global tempo
6656 (b) add a new tempo marker
6660 const TempoSection& t (_session->tempo_map().tempo_section_at_frame (start));
6662 bool do_global = false;
6664 if ((_session->tempo_map().n_tempos() == 1) && (_session->tempo_map().n_meters() == 1)) {
6666 /* only 1 tempo & 1 meter: ask if the user wants to set the tempo
6667 at the start, or create a new marker
6670 vector<string> options;
6671 options.push_back (_("Cancel"));
6672 options.push_back (_("Add new marker"));
6673 options.push_back (_("Set global tempo"));
6676 _("Define one bar"),
6677 _("Do you want to set the global tempo or add a new tempo marker?"),
6681 c.set_default_response (2);
6697 /* more than 1 tempo and/or meter section already, go ahead do the "usual":
6698 if the marker is at the region starter, change it, otherwise add
6703 begin_reversible_command (_("set tempo from region"));
6704 XMLNode& before (_session->tempo_map().get_state());
6707 _session->tempo_map().change_initial_tempo (beats_per_minute, t.note_type());
6708 } else if (t.frame() == start) {
6709 _session->tempo_map().change_existing_tempo_at (start, beats_per_minute, t.note_type());
6711 const Tempo tempo (beats_per_minute, t.note_type());
6712 _session->tempo_map().add_tempo (tempo, 0.0, start, TempoSection::Constant, AudioTime);
6715 XMLNode& after (_session->tempo_map().get_state());
6717 _session->add_command (new MementoCommand<TempoMap>(_session->tempo_map(), &before, &after));
6718 commit_reversible_command ();
6722 Editor::split_region_at_transients ()
6724 AnalysisFeatureList positions;
6726 RegionSelection rs = get_regions_from_selection_and_entered ();
6728 if (!_session || rs.empty()) {
6732 begin_reversible_command (_("split regions"));
6734 for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ) {
6736 RegionSelection::iterator tmp;
6741 boost::shared_ptr<AudioRegion> ar = boost::dynamic_pointer_cast<AudioRegion> ((*i)->region());
6744 ar->transients (positions);
6745 split_region_at_points ((*i)->region(), positions, true);
6752 commit_reversible_command ();
6757 Editor::split_region_at_points (boost::shared_ptr<Region> r, AnalysisFeatureList& positions, bool can_ferret, bool select_new)
6759 bool use_rhythmic_rodent = false;
6761 boost::shared_ptr<Playlist> pl = r->playlist();
6763 list<boost::shared_ptr<Region> > new_regions;
6769 if (positions.empty()) {
6773 if (positions.size() > 20 && can_ferret) {
6774 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);
6775 MessageDialog msg (msgstr,
6778 Gtk::BUTTONS_OK_CANCEL);
6781 msg.add_button (_("Call for the Ferret!"), RESPONSE_APPLY);
6782 msg.set_secondary_text (_("Press OK to continue with this split operation\nor ask the Ferret dialog to tune the analysis"));
6784 msg.set_secondary_text (_("Press OK to continue with this split operation"));
6787 msg.set_title (_("Excessive split?"));
6790 int response = msg.run();
6796 case RESPONSE_APPLY:
6797 use_rhythmic_rodent = true;
6804 if (use_rhythmic_rodent) {
6805 show_rhythm_ferret ();
6809 AnalysisFeatureList::const_iterator x;
6811 pl->clear_changes ();
6812 pl->clear_owned_changes ();
6814 x = positions.begin();
6816 if (x == positions.end()) {
6821 pl->remove_region (r);
6825 framepos_t rstart = r->first_frame ();
6826 framepos_t rend = r->last_frame ();
6828 while (x != positions.end()) {
6830 /* deal with positons that are out of scope of present region bounds */
6831 if (*x <= rstart || *x > rend) {
6836 /* file start = original start + how far we from the initial position ? */
6838 framepos_t file_start = r->start() + pos;
6840 /* length = next position - current position */
6842 framepos_t len = (*x) - pos - rstart;
6844 /* XXX we do we really want to allow even single-sample regions?
6845 * shouldn't we have some kind of lower limit on region size?
6854 if (RegionFactory::region_name (new_name, r->name())) {
6858 /* do NOT announce new regions 1 by one, just wait till they are all done */
6862 plist.add (ARDOUR::Properties::start, file_start);
6863 plist.add (ARDOUR::Properties::length, len);
6864 plist.add (ARDOUR::Properties::name, new_name);
6865 plist.add (ARDOUR::Properties::layer, 0);
6866 // TODO set transients_offset
6868 boost::shared_ptr<Region> nr = RegionFactory::create (r->sources(), plist, false);
6869 /* because we set annouce to false, manually add the new region to the
6872 RegionFactory::map_add (nr);
6874 pl->add_region (nr, rstart + pos);
6877 new_regions.push_front(nr);
6886 RegionFactory::region_name (new_name, r->name());
6888 /* Add the final region */
6891 plist.add (ARDOUR::Properties::start, r->start() + pos);
6892 plist.add (ARDOUR::Properties::length, r->last_frame() - (r->position() + pos) + 1);
6893 plist.add (ARDOUR::Properties::name, new_name);
6894 plist.add (ARDOUR::Properties::layer, 0);
6896 boost::shared_ptr<Region> nr = RegionFactory::create (r->sources(), plist, false);
6897 /* because we set annouce to false, manually add the new region to the
6900 RegionFactory::map_add (nr);
6901 pl->add_region (nr, r->position() + pos);
6904 new_regions.push_front(nr);
6909 /* We might have removed regions, which alters other regions' layering_index,
6910 so we need to do a recursive diff here.
6912 vector<Command*> cmds;
6914 _session->add_commands (cmds);
6916 _session->add_command (new StatefulDiffCommand (pl));
6920 for (list<boost::shared_ptr<Region> >::iterator i = new_regions.begin(); i != new_regions.end(); ++i){
6921 set_selected_regionview_from_region_list ((*i), Selection::Add);
6927 Editor::place_transient()
6933 RegionSelection rs = get_regions_from_selection_and_edit_point ();
6939 framepos_t where = get_preferred_edit_position();
6941 begin_reversible_command (_("place transient"));
6943 for (RegionSelection::iterator r = rs.begin(); r != rs.end(); ++r) {
6944 (*r)->region()->add_transient(where);
6947 commit_reversible_command ();
6951 Editor::remove_transient(ArdourCanvas::Item* item)
6957 ArdourCanvas::Line* _line = reinterpret_cast<ArdourCanvas::Line*> (item);
6960 AudioRegionView* _arv = reinterpret_cast<AudioRegionView*> (item->get_data ("regionview"));
6961 _arv->remove_transient (*(float*) _line->get_data ("position"));
6965 Editor::snap_regions_to_grid ()
6967 list <boost::shared_ptr<Playlist > > used_playlists;
6969 RegionSelection rs = get_regions_from_selection_and_entered ();
6971 if (!_session || rs.empty()) {
6975 begin_reversible_command (_("snap regions to grid"));
6977 for (RegionSelection::iterator r = rs.begin(); r != rs.end(); ++r) {
6979 boost::shared_ptr<Playlist> pl = (*r)->region()->playlist();
6981 if (!pl->frozen()) {
6982 /* we haven't seen this playlist before */
6984 /* remember used playlists so we can thaw them later */
6985 used_playlists.push_back(pl);
6989 framepos_t start_frame = (*r)->region()->first_frame ();
6990 snap_to (start_frame);
6991 (*r)->region()->set_position (start_frame);
6994 while (used_playlists.size() > 0) {
6995 list <boost::shared_ptr<Playlist > >::iterator i = used_playlists.begin();
6997 used_playlists.pop_front();
7000 commit_reversible_command ();
7004 Editor::close_region_gaps ()
7006 list <boost::shared_ptr<Playlist > > used_playlists;
7008 RegionSelection rs = get_regions_from_selection_and_entered ();
7010 if (!_session || rs.empty()) {
7014 Dialog dialog (_("Close Region Gaps"));
7017 table.set_spacings (12);
7018 table.set_border_width (12);
7019 Label* l = manage (left_aligned_label (_("Crossfade length")));
7020 table.attach (*l, 0, 1, 0, 1);
7022 SpinButton spin_crossfade (1, 0);
7023 spin_crossfade.set_range (0, 15);
7024 spin_crossfade.set_increments (1, 1);
7025 spin_crossfade.set_value (5);
7026 table.attach (spin_crossfade, 1, 2, 0, 1);
7028 table.attach (*manage (new Label (_("ms"))), 2, 3, 0, 1);
7030 l = manage (left_aligned_label (_("Pull-back length")));
7031 table.attach (*l, 0, 1, 1, 2);
7033 SpinButton spin_pullback (1, 0);
7034 spin_pullback.set_range (0, 100);
7035 spin_pullback.set_increments (1, 1);
7036 spin_pullback.set_value(30);
7037 table.attach (spin_pullback, 1, 2, 1, 2);
7039 table.attach (*manage (new Label (_("ms"))), 2, 3, 1, 2);
7041 dialog.get_vbox()->pack_start (table);
7042 dialog.add_button (Stock::CANCEL, RESPONSE_CANCEL);
7043 dialog.add_button (_("Ok"), RESPONSE_ACCEPT);
7046 if (dialog.run () == RESPONSE_CANCEL) {
7050 framepos_t crossfade_len = spin_crossfade.get_value();
7051 framepos_t pull_back_frames = spin_pullback.get_value();
7053 crossfade_len = lrintf (crossfade_len * _session->frame_rate()/1000);
7054 pull_back_frames = lrintf (pull_back_frames * _session->frame_rate()/1000);
7056 /* Iterate over the region list and make adjacent regions overlap by crossfade_len_ms */
7058 begin_reversible_command (_("close region gaps"));
7061 boost::shared_ptr<Region> last_region;
7063 rs.sort_by_position_and_track();
7065 for (RegionSelection::iterator r = rs.begin(); r != rs.end(); ++r) {
7067 boost::shared_ptr<Playlist> pl = (*r)->region()->playlist();
7069 if (!pl->frozen()) {
7070 /* we haven't seen this playlist before */
7072 /* remember used playlists so we can thaw them later */
7073 used_playlists.push_back(pl);
7077 framepos_t position = (*r)->region()->position();
7079 if (idx == 0 || position < last_region->position()){
7080 last_region = (*r)->region();
7085 (*r)->region()->trim_front( (position - pull_back_frames));
7086 last_region->trim_end( (position - pull_back_frames + crossfade_len));
7088 last_region = (*r)->region();
7093 while (used_playlists.size() > 0) {
7094 list <boost::shared_ptr<Playlist > >::iterator i = used_playlists.begin();
7096 used_playlists.pop_front();
7099 commit_reversible_command ();
7103 Editor::tab_to_transient (bool forward)
7105 AnalysisFeatureList positions;
7107 RegionSelection rs = get_regions_from_selection_and_entered ();
7113 framepos_t pos = _session->audible_frame ();
7115 if (!selection->tracks.empty()) {
7117 /* don't waste time searching for transients in duplicate playlists.
7120 TrackViewList ts = selection->tracks.filter_to_unique_playlists ();
7122 for (TrackViewList::iterator t = ts.begin(); t != ts.end(); ++t) {
7124 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (*t);
7127 boost::shared_ptr<Track> tr = rtv->track();
7129 boost::shared_ptr<Playlist> pl = tr->playlist ();
7131 framepos_t result = pl->find_next_transient (pos, forward ? 1 : -1);
7134 positions.push_back (result);
7147 for (RegionSelection::iterator r = rs.begin(); r != rs.end(); ++r) {
7148 (*r)->region()->get_transients (positions);
7152 TransientDetector::cleanup_transients (positions, _session->frame_rate(), 3.0);
7155 AnalysisFeatureList::iterator x;
7157 for (x = positions.begin(); x != positions.end(); ++x) {
7163 if (x != positions.end ()) {
7164 _session->request_locate (*x);
7168 AnalysisFeatureList::reverse_iterator x;
7170 for (x = positions.rbegin(); x != positions.rend(); ++x) {
7176 if (x != positions.rend ()) {
7177 _session->request_locate (*x);
7183 Editor::playhead_forward_to_grid ()
7189 framepos_t pos = playhead_cursor->current_frame ();
7190 if (pos < max_framepos - 1) {
7192 snap_to_internal (pos, RoundUpAlways, false);
7193 _session->request_locate (pos);
7199 Editor::playhead_backward_to_grid ()
7205 framepos_t pos = playhead_cursor->current_frame ();
7208 snap_to_internal (pos, RoundDownAlways, false);
7209 _session->request_locate (pos);
7214 Editor::set_track_height (Height h)
7216 TrackSelection& ts (selection->tracks);
7218 for (TrackSelection::iterator x = ts.begin(); x != ts.end(); ++x) {
7219 (*x)->set_height_enum (h);
7224 Editor::toggle_tracks_active ()
7226 TrackSelection& ts (selection->tracks);
7228 bool target = false;
7234 for (TrackSelection::iterator x = ts.begin(); x != ts.end(); ++x) {
7235 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*>(*x);
7239 target = !rtv->_route->active();
7242 rtv->_route->set_active (target, this);
7248 Editor::remove_tracks ()
7250 /* this will delete GUI objects that may be the subject of an event
7251 handler in which this method is called. Defer actual deletion to the
7252 next idle callback, when all event handling is finished.
7254 Glib::signal_idle().connect (sigc::mem_fun (*this, &Editor::idle_remove_tracks));
7258 Editor::idle_remove_tracks ()
7260 Session::StateProtector sp (_session);
7262 return false; /* do not call again */
7266 Editor::_remove_tracks ()
7268 TrackSelection& ts (selection->tracks);
7274 vector<string> choices;
7278 const char* trackstr;
7280 vector<boost::shared_ptr<Route> > routes;
7281 bool special_bus = false;
7283 for (TrackSelection::iterator x = ts.begin(); x != ts.end(); ++x) {
7284 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (*x);
7288 if (rtv->is_track()) {
7293 routes.push_back (rtv->_route);
7295 if (rtv->route()->is_master() || rtv->route()->is_monitor()) {
7300 if (special_bus && !Config->get_allow_special_bus_removal()) {
7301 MessageDialog msg (_("That would be bad news ...."),
7305 msg.set_secondary_text (string_compose (_(
7306 "Removing the master or monitor bus is such a bad idea\n\
7307 that %1 is not going to allow it.\n\
7309 If you really want to do this sort of thing\n\
7310 edit your ardour.rc file to set the\n\
7311 \"allow-special-bus-removal\" option to be \"yes\""), PROGRAM_NAME));
7318 if (ntracks + nbusses == 0) {
7322 trackstr = P_("track", "tracks", ntracks);
7323 busstr = P_("bus", "busses", nbusses);
7327 prompt = string_compose (_("Do you really want to remove %1 %2 and %3 %4?\n"
7328 "(You may also lose the playlists associated with the %2)\n\n"
7329 "This action cannot be undone, and the session file will be overwritten!"),
7330 ntracks, trackstr, nbusses, busstr);
7332 prompt = string_compose (_("Do you really want to remove %1 %2?\n"
7333 "(You may also lose the playlists associated with the %2)\n\n"
7334 "This action cannot be undone, and the session file will be overwritten!"),
7337 } else if (nbusses) {
7338 prompt = string_compose (_("Do you really want to remove %1 %2?\n\n"
7339 "This action cannot be undone, and the session file will be overwritten"),
7343 choices.push_back (_("No, do nothing."));
7344 if (ntracks + nbusses > 1) {
7345 choices.push_back (_("Yes, remove them."));
7347 choices.push_back (_("Yes, remove it."));
7352 title = string_compose (_("Remove %1"), trackstr);
7354 title = string_compose (_("Remove %1"), busstr);
7357 Choice prompter (title, prompt, choices);
7359 if (prompter.run () != 1) {
7364 DisplaySuspender ds;
7365 boost::shared_ptr<RouteList> rl (new RouteList);
7366 for (vector<boost::shared_ptr<Route> >::iterator x = routes.begin(); x != routes.end(); ++x) {
7369 _session->remove_routes (rl);
7371 /* TrackSelection and RouteList leave scope,
7372 * destructors are called,
7373 * diskstream drops references, save_state is called (again for every track)
7378 Editor::do_insert_time ()
7380 if (selection->tracks.empty()) {
7384 InsertRemoveTimeDialog d (*this);
7385 int response = d.run ();
7387 if (response != RESPONSE_OK) {
7391 if (d.distance() == 0) {
7396 get_preferred_edit_position (EDIT_IGNORE_MOUSE),
7398 d.intersected_region_action (),
7402 d.move_glued_markers(),
7403 d.move_locked_markers(),
7409 Editor::insert_time (
7410 framepos_t pos, framecnt_t frames, InsertTimeOption opt,
7411 bool all_playlists, bool ignore_music_glue, bool markers_too, bool glued_markers_too, bool locked_markers_too, bool tempo_too
7415 if (Config->get_edit_mode() == Lock) {
7418 bool in_command = false;
7420 TrackViewList ts = selection->tracks.filter_to_unique_playlists ();
7422 for (TrackViewList::iterator x = ts.begin(); x != ts.end(); ++x) {
7426 /* don't operate on any playlist more than once, which could
7427 * happen if "all playlists" is enabled, but there is more
7428 * than 1 track using playlists "from" a given track.
7431 set<boost::shared_ptr<Playlist> > pl;
7433 if (all_playlists) {
7434 RouteTimeAxisView* rtav = dynamic_cast<RouteTimeAxisView*> (*x);
7435 if (rtav && rtav->track ()) {
7436 vector<boost::shared_ptr<Playlist> > all = _session->playlists->playlists_for_track (rtav->track ());
7437 for (vector<boost::shared_ptr<Playlist> >::iterator p = all.begin(); p != all.end(); ++p) {
7442 if ((*x)->playlist ()) {
7443 pl.insert ((*x)->playlist ());
7447 for (set<boost::shared_ptr<Playlist> >::iterator i = pl.begin(); i != pl.end(); ++i) {
7449 (*i)->clear_changes ();
7450 (*i)->clear_owned_changes ();
7452 if (opt == SplitIntersected) {
7453 /* non musical split */
7454 (*i)->split (pos, 0);
7457 (*i)->shift (pos, frames, (opt == MoveIntersected), ignore_music_glue);
7460 begin_reversible_command (_("insert time"));
7463 vector<Command*> cmds;
7465 _session->add_commands (cmds);
7467 _session->add_command (new StatefulDiffCommand (*i));
7471 RouteTimeAxisView* rtav = dynamic_cast<RouteTimeAxisView*> (*x);
7474 begin_reversible_command (_("insert time"));
7477 rtav->route ()->shift (pos, frames);
7484 XMLNode& before (_session->locations()->get_state());
7485 Locations::LocationList copy (_session->locations()->list());
7487 for (Locations::LocationList::iterator i = copy.begin(); i != copy.end(); ++i) {
7489 Locations::LocationList::const_iterator tmp;
7491 if ((*i)->position_lock_style() == AudioTime || glued_markers_too) {
7492 bool const was_locked = (*i)->locked ();
7493 if (locked_markers_too) {
7497 if ((*i)->start() >= pos) {
7498 // move end first, in case we're moving by more than the length of the range
7499 if (!(*i)->is_mark()) {
7500 (*i)->set_end ((*i)->end() + frames);
7502 (*i)->set_start ((*i)->start() + frames);
7514 begin_reversible_command (_("insert time"));
7517 XMLNode& after (_session->locations()->get_state());
7518 _session->add_command (new MementoCommand<Locations>(*_session->locations(), &before, &after));
7524 begin_reversible_command (_("insert time"));
7527 XMLNode& before (_session->tempo_map().get_state());
7528 _session->tempo_map().insert_time (pos, frames);
7529 XMLNode& after (_session->tempo_map().get_state());
7530 _session->add_command (new MementoCommand<TempoMap>(_session->tempo_map(), &before, &after));
7534 commit_reversible_command ();
7539 Editor::do_remove_time ()
7541 if (selection->tracks.empty()) {
7545 framepos_t pos = get_preferred_edit_position (EDIT_IGNORE_MOUSE);
7546 InsertRemoveTimeDialog d (*this, true);
7548 int response = d.run ();
7550 if (response != RESPONSE_OK) {
7554 framecnt_t distance = d.distance();
7556 if (distance == 0) {
7566 d.move_glued_markers(),
7567 d.move_locked_markers(),
7573 Editor::remove_time (framepos_t pos, framecnt_t frames, InsertTimeOption opt,
7574 bool ignore_music_glue, bool markers_too, bool glued_markers_too, bool locked_markers_too, bool tempo_too)
7576 if (Config->get_edit_mode() == Lock) {
7577 error << (_("Cannot insert or delete time when in Lock edit.")) << endmsg;
7580 bool in_command = false;
7582 for (TrackSelection::iterator x = selection->tracks.begin(); x != selection->tracks.end(); ++x) {
7584 boost::shared_ptr<Playlist> pl = (*x)->playlist();
7588 XMLNode &before = pl->get_state();
7590 std::list<AudioRange> rl;
7591 AudioRange ar(pos, pos+frames, 0);
7594 pl->shift (pos, -frames, true, ignore_music_glue);
7597 begin_reversible_command (_("remove time"));
7600 XMLNode &after = pl->get_state();
7602 _session->add_command (new MementoCommand<Playlist> (*pl, &before, &after));
7606 RouteTimeAxisView* rtav = dynamic_cast<RouteTimeAxisView*> (*x);
7609 begin_reversible_command (_("remove time"));
7612 rtav->route ()->shift (pos, -frames);
7616 std::list<Location*> loc_kill_list;
7621 XMLNode& before (_session->locations()->get_state());
7622 Locations::LocationList copy (_session->locations()->list());
7624 for (Locations::LocationList::iterator i = copy.begin(); i != copy.end(); ++i) {
7625 if ((*i)->position_lock_style() == AudioTime || glued_markers_too) {
7627 bool const was_locked = (*i)->locked ();
7628 if (locked_markers_too) {
7632 if (!(*i)->is_mark()) { // it's a range; have to handle both start and end
7633 if ((*i)->end() >= pos
7634 && (*i)->end() < pos+frames
7635 && (*i)->start() >= pos
7636 && (*i)->end() < pos+frames) { // range is completely enclosed; kill it
7638 loc_kill_list.push_back(*i);
7639 } else { // only start or end is included, try to do the right thing
7640 // move start before moving end, to avoid trying to move the end to before the start
7641 // if we're removing more time than the length of the range
7642 if ((*i)->start() >= pos && (*i)->start() < pos+frames) {
7643 // start is within cut
7644 (*i)->set_start (pos); // bring the start marker to the beginning of the cut
7646 } else if ((*i)->start() >= pos+frames) {
7647 // start (and thus entire range) lies beyond end of cut
7648 (*i)->set_start ((*i)->start() - frames); // slip the start marker back
7651 if ((*i)->end() >= pos && (*i)->end() < pos+frames) {
7652 // end is inside cut
7653 (*i)->set_end (pos); // bring the end to the cut
7655 } else if ((*i)->end() >= pos+frames) {
7656 // end is beyond end of cut
7657 (*i)->set_end ((*i)->end() - frames); // slip the end marker back
7662 } else if ((*i)->start() >= pos && (*i)->start() < pos+frames ) {
7663 loc_kill_list.push_back(*i);
7665 } else if ((*i)->start() >= pos) {
7666 (*i)->set_start ((*i)->start() -frames);
7676 for (list<Location*>::iterator i = loc_kill_list.begin(); i != loc_kill_list.end(); ++i) {
7677 _session->locations()->remove( *i );
7682 begin_reversible_command (_("remove time"));
7685 XMLNode& after (_session->locations()->get_state());
7686 _session->add_command (new MementoCommand<Locations>(*_session->locations(), &before, &after));
7691 XMLNode& before (_session->tempo_map().get_state());
7693 if (_session->tempo_map().remove_time (pos, frames) ) {
7695 begin_reversible_command (_("remove time"));
7698 XMLNode& after (_session->tempo_map().get_state());
7699 _session->add_command (new MementoCommand<TempoMap>(_session->tempo_map(), &before, &after));
7704 commit_reversible_command ();
7709 Editor::fit_selection ()
7711 if (!selection->tracks.empty()) {
7712 fit_tracks (selection->tracks);
7716 /* no selected tracks - use tracks with selected regions */
7718 if (!selection->regions.empty()) {
7719 for (RegionSelection::iterator r = selection->regions.begin(); r != selection->regions.end(); ++r) {
7720 tvl.push_back (&(*r)->get_time_axis_view ());
7726 } else if (internal_editing()) {
7727 /* no selected tracks, or regions, but in internal edit mode, so follow the mouse and use
7730 if (entered_track) {
7731 tvl.push_back (entered_track);
7740 Editor::fit_tracks (TrackViewList & tracks)
7742 if (tracks.empty()) {
7746 uint32_t child_heights = 0;
7747 int visible_tracks = 0;
7749 for (TrackSelection::iterator t = tracks.begin(); t != tracks.end(); ++t) {
7751 if (!(*t)->marked_for_display()) {
7755 child_heights += (*t)->effective_height() - (*t)->current_height();
7759 /* compute the per-track height from:
7761 total canvas visible height -
7762 height that will be taken by visible children of selected
7763 tracks - height of the ruler/hscroll area
7765 uint32_t h = (uint32_t) floor ((trackviews_height() - child_heights) / visible_tracks);
7766 double first_y_pos = DBL_MAX;
7768 if (h < TimeAxisView::preset_height (HeightSmall)) {
7769 MessageDialog msg (_("There are too many tracks to fit in the current window"));
7770 /* too small to be displayed */
7774 undo_visual_stack.push_back (current_visual_state (true));
7775 PBD::Unwinder<bool> nsv (no_save_visual, true);
7777 /* build a list of all tracks, including children */
7780 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
7782 TimeAxisView::Children c = (*i)->get_child_list ();
7783 for (TimeAxisView::Children::iterator j = c.begin(); j != c.end(); ++j) {
7784 all.push_back (j->get());
7789 // find selection range.
7790 // if someone knows how to user TrackViewList::iterator for this
7792 int selected_top = -1;
7793 int selected_bottom = -1;
7795 for (TrackViewList::iterator t = all.begin(); t != all.end(); ++t, ++i) {
7796 if ((*t)->marked_for_display ()) {
7797 if (tracks.contains(*t)) {
7798 if (selected_top == -1) {
7801 selected_bottom = i;
7807 for (TrackViewList::iterator t = all.begin(); t != all.end(); ++t, ++i) {
7808 if ((*t)->marked_for_display ()) {
7809 if (tracks.contains(*t)) {
7810 (*t)->set_height (h);
7811 first_y_pos = std::min ((*t)->y_position (), first_y_pos);
7813 if (i > selected_top && i < selected_bottom) {
7814 hide_track_in_display (*t);
7821 set the controls_layout height now, because waiting for its size
7822 request signal handler will cause the vertical adjustment setting to fail
7825 controls_layout.property_height () = _full_canvas_height;
7826 vertical_adjustment.set_value (first_y_pos);
7828 redo_visual_stack.push_back (current_visual_state (true));
7830 visible_tracks_selector.set_text (_("Sel"));
7834 Editor::save_visual_state (uint32_t n)
7836 while (visual_states.size() <= n) {
7837 visual_states.push_back (0);
7840 if (visual_states[n] != 0) {
7841 delete visual_states[n];
7844 visual_states[n] = current_visual_state (true);
7849 Editor::goto_visual_state (uint32_t n)
7851 if (visual_states.size() <= n) {
7855 if (visual_states[n] == 0) {
7859 use_visual_state (*visual_states[n]);
7863 Editor::start_visual_state_op (uint32_t n)
7865 save_visual_state (n);
7867 PopUp* pup = new PopUp (WIN_POS_MOUSE, 1000, true);
7869 snprintf (buf, sizeof (buf), _("Saved view %u"), n+1);
7870 pup->set_text (buf);
7875 Editor::cancel_visual_state_op (uint32_t n)
7877 goto_visual_state (n);
7881 Editor::toggle_region_mute ()
7883 if (_ignore_region_action) {
7887 RegionSelection rs = get_regions_from_selection_and_entered ();
7893 if (rs.size() > 1) {
7894 begin_reversible_command (_("mute regions"));
7896 begin_reversible_command (_("mute region"));
7899 for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
7901 (*i)->region()->playlist()->clear_changes ();
7902 (*i)->region()->set_muted (!(*i)->region()->muted ());
7903 _session->add_command (new StatefulDiffCommand ((*i)->region()));
7907 commit_reversible_command ();
7911 Editor::combine_regions ()
7913 /* foreach track with selected regions, take all selected regions
7914 and join them into a new region containing the subregions (as a
7918 typedef set<RouteTimeAxisView*> RTVS;
7921 if (selection->regions.empty()) {
7925 for (RegionSelection::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
7926 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*>(&(*i)->get_time_axis_view());
7929 tracks.insert (rtv);
7933 begin_reversible_command (_("combine regions"));
7935 vector<RegionView*> new_selection;
7937 for (RTVS::iterator i = tracks.begin(); i != tracks.end(); ++i) {
7940 if ((rv = (*i)->combine_regions ()) != 0) {
7941 new_selection.push_back (rv);
7945 selection->clear_regions ();
7946 for (vector<RegionView*>::iterator i = new_selection.begin(); i != new_selection.end(); ++i) {
7947 selection->add (*i);
7950 commit_reversible_command ();
7954 Editor::uncombine_regions ()
7956 typedef set<RouteTimeAxisView*> RTVS;
7959 if (selection->regions.empty()) {
7963 for (RegionSelection::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
7964 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*>(&(*i)->get_time_axis_view());
7967 tracks.insert (rtv);
7971 begin_reversible_command (_("uncombine regions"));
7973 for (RTVS::iterator i = tracks.begin(); i != tracks.end(); ++i) {
7974 (*i)->uncombine_regions ();
7977 commit_reversible_command ();
7981 Editor::toggle_midi_input_active (bool flip_others)
7984 boost::shared_ptr<RouteList> rl (new RouteList);
7986 for (TrackSelection::iterator i = selection->tracks.begin(); i != selection->tracks.end(); ++i) {
7987 RouteTimeAxisView *rtav = dynamic_cast<RouteTimeAxisView *>(*i);
7993 boost::shared_ptr<MidiTrack> mt = rtav->midi_track();
7996 rl->push_back (rtav->route());
7997 onoff = !mt->input_active();
8001 _session->set_exclusive_input_active (rl, onoff, flip_others);
8004 static bool ok_fine (GdkEventAny*) { return true; }
8010 lock_dialog = new Gtk::Dialog (string_compose (_("%1: Locked"), PROGRAM_NAME), true);
8012 Gtk::Image* padlock = manage (new Gtk::Image (ARDOUR_UI_UTILS::get_icon ("padlock_closed")));
8013 lock_dialog->get_vbox()->pack_start (*padlock);
8014 lock_dialog->signal_delete_event ().connect (sigc::ptr_fun (ok_fine));
8016 ArdourButton* b = manage (new ArdourButton);
8017 b->set_name ("lock button");
8018 b->set_text (_("Click to unlock"));
8019 b->signal_clicked.connect (sigc::mem_fun (*this, &Editor::unlock));
8020 lock_dialog->get_vbox()->pack_start (*b);
8022 lock_dialog->get_vbox()->show_all ();
8023 lock_dialog->set_size_request (200, 200);
8026 delete _main_menu_disabler;
8027 _main_menu_disabler = new MainMenuDisabler;
8029 lock_dialog->present ();
8031 lock_dialog->get_window()->set_decorations (Gdk::WMDecoration (0));
8037 lock_dialog->hide ();
8039 delete _main_menu_disabler;
8040 _main_menu_disabler = 0;
8042 if (UIConfiguration::instance().get_lock_gui_after_seconds()) {
8043 start_lock_event_timing ();
8048 Editor::bring_in_callback (Gtk::Label* label, uint32_t n, uint32_t total, string name)
8050 Gtkmm2ext::UI::instance()->call_slot (invalidator (*this), boost::bind (&Editor::update_bring_in_message, this, label, n, total, name));
8054 Editor::update_bring_in_message (Gtk::Label* label, uint32_t n, uint32_t total, string name)
8056 Timers::TimerSuspender t;
8057 label->set_text (string_compose ("Copying %1, %2 of %3", name, n, total));
8058 Gtkmm2ext::UI::instance()->flush_pending (1);
8062 Editor::bring_all_sources_into_session ()
8069 ArdourDialog w (_("Moving embedded files into session folder"));
8070 w.get_vbox()->pack_start (msg);
8073 /* flush all pending GUI events because we're about to start copying
8077 Timers::TimerSuspender t;
8078 Gtkmm2ext::UI::instance()->flush_pending (3);
8082 _session->bring_all_sources_into_session (boost::bind (&Editor::bring_in_callback, this, &msg, _1, _2, _3));