2 Copyright (C) 2000-2004 Paul Davis
4 This program is free software; you can redistribute it and/or modify
5 it under the terms of the GNU General Public License as published by
6 the Free Software Foundation; either version 2 of the License, or
7 (at your option) any later version.
9 This program is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 GNU General Public License for more details.
14 You should have received a copy of the GNU General Public License
15 along with this program; if not, write to the Free Software
16 Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
20 /* Note: public Editor methods are documented in public_editor.h */
31 #include "pbd/error.h"
32 #include "pbd/basename.h"
33 #include "pbd/pthread_utils.h"
34 #include "pbd/memento_command.h"
35 #include "pbd/unwind.h"
36 #include "pbd/whitespace.h"
37 #include "pbd/stateful_diff_command.h"
39 #include <gtkmm2ext/utils.h>
40 #include <gtkmm2ext/choice.h>
41 #include <gtkmm2ext/popup.h>
43 #include "ardour/audio_track.h"
44 #include "ardour/audioregion.h"
45 #include "ardour/boost_debug.h"
46 #include "ardour/dB.h"
47 #include "ardour/location.h"
48 #include "ardour/midi_region.h"
49 #include "ardour/midi_track.h"
50 #include "ardour/operations.h"
51 #include "ardour/playlist_factory.h"
52 #include "ardour/profile.h"
53 #include "ardour/quantize.h"
54 #include "ardour/legatize.h"
55 #include "ardour/region_factory.h"
56 #include "ardour/reverse.h"
57 #include "ardour/session.h"
58 #include "ardour/session_playlists.h"
59 #include "ardour/strip_silence.h"
60 #include "ardour/transient_detector.h"
61 #include "ardour/transpose.h"
63 #include "canvas/canvas.h"
66 #include "audio_region_view.h"
67 #include "audio_streamview.h"
68 #include "audio_time_axis.h"
69 #include "automation_region_view.h"
70 #include "automation_time_axis.h"
71 #include "control_point.h"
75 #include "editor_cursors.h"
76 #include "editor_drag.h"
77 #include "editor_regions.h"
78 #include "editor_routes.h"
79 #include "gui_thread.h"
80 #include "insert_remove_time_dialog.h"
81 #include "interthread_progress_window.h"
82 #include "item_counts.h"
84 #include "midi_region_view.h"
86 #include "mixer_strip.h"
87 #include "mouse_cursors.h"
88 #include "normalize_dialog.h"
90 #include "paste_context.h"
91 #include "patch_change_dialog.h"
92 #include "quantize_dialog.h"
93 #include "region_gain_line.h"
94 #include "rgb_macros.h"
95 #include "route_time_axis.h"
96 #include "selection.h"
97 #include "selection_templates.h"
98 #include "streamview.h"
99 #include "strip_silence_dialog.h"
100 #include "time_axis_view.h"
102 #include "transpose_dialog.h"
103 #include "transform_dialog.h"
104 #include "ui_config.h"
106 #include "pbd/i18n.h"
109 using namespace ARDOUR;
112 using namespace Gtkmm2ext;
113 using namespace Editing;
114 using Gtkmm2ext::Keyboard;
116 /***********************************************************************
118 ***********************************************************************/
121 Editor::undo (uint32_t n)
123 if (_session && _session->actively_recording()) {
124 /* no undo allowed while recording. Session will check also,
125 but we don't even want to get to that.
130 if (_drags->active ()) {
136 if (_session->undo_depth() == 0) {
137 undo_action->set_sensitive(false);
139 redo_action->set_sensitive(true);
140 begin_selection_op_history ();
145 Editor::redo (uint32_t n)
147 if (_session && _session->actively_recording()) {
148 /* no redo allowed while recording. Session will check also,
149 but we don't even want to get to that.
154 if (_drags->active ()) {
160 if (_session->redo_depth() == 0) {
161 redo_action->set_sensitive(false);
163 undo_action->set_sensitive(true);
164 begin_selection_op_history ();
169 Editor::split_regions_at (MusicFrame where, RegionSelection& regions, bool snap_frame)
173 RegionSelection pre_selected_regions = selection->regions;
174 bool working_on_selection = !pre_selected_regions.empty();
176 list<boost::shared_ptr<Playlist> > used_playlists;
177 list<RouteTimeAxisView*> used_trackviews;
179 if (regions.empty()) {
183 begin_reversible_command (_("split"));
185 // if splitting a single region, and snap-to is using
186 // region boundaries, don't pay attention to them
188 if (regions.size() == 1) {
189 switch (_snap_type) {
190 case SnapToRegionStart:
191 case SnapToRegionSync:
192 case SnapToRegionEnd:
205 EditorFreeze(); /* Emit Signal */
208 for (RegionSelection::iterator a = regions.begin(); a != regions.end(); ) {
210 RegionSelection::iterator tmp;
212 /* XXX this test needs to be more complicated, to make sure we really
213 have something to split.
216 if (!(*a)->region()->covers (where.frame)) {
224 boost::shared_ptr<Playlist> pl = (*a)->region()->playlist();
232 /* we haven't seen this playlist before */
234 /* remember used playlists so we can thaw them later */
235 used_playlists.push_back(pl);
237 TimeAxisView& tv = (*a)->get_time_axis_view();
238 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (&tv);
240 used_trackviews.push_back (rtv);
247 pl->clear_changes ();
248 pl->split_region ((*a)->region(), where);
249 _session->add_command (new StatefulDiffCommand (pl));
255 latest_regionviews.clear ();
257 vector<sigc::connection> region_added_connections;
259 for (list<RouteTimeAxisView*>::iterator i = used_trackviews.begin(); i != used_trackviews.end(); ++i) {
260 region_added_connections.push_back ((*i)->view()->RegionViewAdded.connect (sigc::mem_fun(*this, &Editor::collect_new_region_view)));
263 while (used_playlists.size() > 0) {
264 list <boost::shared_ptr<Playlist > >::iterator i = used_playlists.begin();
266 used_playlists.pop_front();
269 for (vector<sigc::connection>::iterator c = region_added_connections.begin(); c != region_added_connections.end(); ++c) {
274 EditorThaw(); /* Emit Signal */
277 if (working_on_selection) {
278 // IFF we were working on selected regions, try to reinstate the other region selections that existed before the freeze/thaw.
280 RegionSelectionAfterSplit rsas = Config->get_region_selection_after_split();
281 /* There are three classes of regions that we might want selected after
282 splitting selected regions:
283 - regions selected before the split operation, and unaffected by it
284 - newly-created regions before the split
285 - newly-created regions after the split
288 if (rsas & Existing) {
289 // region selections that existed before the split.
290 selection->add ( pre_selected_regions );
293 for (RegionSelection::iterator ri = latest_regionviews.begin(); ri != latest_regionviews.end(); ri++) {
294 if ((*ri)->region()->position() < where.frame) {
295 // new regions created before the split
296 if (rsas & NewlyCreatedLeft) {
297 selection->add (*ri);
300 // new regions created after the split
301 if (rsas & NewlyCreatedRight) {
302 selection->add (*ri);
307 if( working_on_selection ) {
308 selection->add (latest_regionviews); //these are the new regions created after the split
312 commit_reversible_command ();
315 /** Move one extreme of the current range selection. If more than one range is selected,
316 * the start of the earliest range or the end of the latest range is moved.
318 * @param move_end true to move the end of the current range selection, false to move
320 * @param next true to move the extreme to the next region boundary, false to move to
324 Editor::move_range_selection_start_or_end_to_region_boundary (bool move_end, bool next)
326 if (selection->time.start() == selection->time.end_frame()) {
330 framepos_t start = selection->time.start ();
331 framepos_t end = selection->time.end_frame ();
333 /* the position of the thing we may move */
334 framepos_t pos = move_end ? end : start;
335 int dir = next ? 1 : -1;
337 /* so we don't find the current region again */
338 if (dir > 0 || pos > 0) {
342 framepos_t const target = get_region_boundary (pos, dir, true, false);
357 begin_reversible_selection_op (_("alter selection"));
358 selection->set_preserving_all_ranges (start, end);
359 commit_reversible_selection_op ();
363 Editor::nudge_forward_release (GdkEventButton* ev)
365 if (ev->state & Keyboard::PrimaryModifier) {
366 nudge_forward (false, true);
368 nudge_forward (false, false);
374 Editor::nudge_backward_release (GdkEventButton* ev)
376 if (ev->state & Keyboard::PrimaryModifier) {
377 nudge_backward (false, true);
379 nudge_backward (false, false);
386 Editor::nudge_forward (bool next, bool force_playhead)
389 framepos_t next_distance;
395 RegionSelection rs = get_regions_from_selection_and_entered ();
397 if (!force_playhead && !rs.empty()) {
399 begin_reversible_command (_("nudge regions forward"));
401 for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
402 boost::shared_ptr<Region> r ((*i)->region());
404 distance = get_nudge_distance (r->position(), next_distance);
407 distance = next_distance;
411 r->set_position (r->position() + distance);
412 _session->add_command (new StatefulDiffCommand (r));
415 commit_reversible_command ();
418 } else if (!force_playhead && !selection->markers.empty()) {
421 bool in_command = false;
422 const int32_t divisions = get_grid_music_divisions (0);
424 for (MarkerSelection::iterator i = selection->markers.begin(); i != selection->markers.end(); ++i) {
426 Location* loc = find_location_from_marker ((*i), is_start);
430 XMLNode& before (loc->get_state());
433 distance = get_nudge_distance (loc->start(), next_distance);
435 distance = next_distance;
437 if (max_framepos - distance > loc->start() + loc->length()) {
438 loc->set_start (loc->start() + distance, false, true, divisions);
440 loc->set_start (max_framepos - loc->length(), false, true, divisions);
443 distance = get_nudge_distance (loc->end(), next_distance);
445 distance = next_distance;
447 if (max_framepos - distance > loc->end()) {
448 loc->set_end (loc->end() + distance, false, true, divisions);
450 loc->set_end (max_framepos, false, true, divisions);
452 if (loc->is_session_range()) {
453 _session->set_end_is_free (false);
457 begin_reversible_command (_("nudge location forward"));
460 XMLNode& after (loc->get_state());
461 _session->add_command (new MementoCommand<Location>(*loc, &before, &after));
466 commit_reversible_command ();
469 distance = get_nudge_distance (playhead_cursor->current_frame (), next_distance);
470 _session->request_locate (playhead_cursor->current_frame () + distance);
475 Editor::nudge_backward (bool next, bool force_playhead)
478 framepos_t next_distance;
484 RegionSelection rs = get_regions_from_selection_and_entered ();
486 if (!force_playhead && !rs.empty()) {
488 begin_reversible_command (_("nudge regions backward"));
490 for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
491 boost::shared_ptr<Region> r ((*i)->region());
493 distance = get_nudge_distance (r->position(), next_distance);
496 distance = next_distance;
501 if (r->position() > distance) {
502 r->set_position (r->position() - distance);
506 _session->add_command (new StatefulDiffCommand (r));
509 commit_reversible_command ();
511 } else if (!force_playhead && !selection->markers.empty()) {
514 bool in_command = false;
516 for (MarkerSelection::iterator i = selection->markers.begin(); i != selection->markers.end(); ++i) {
518 Location* loc = find_location_from_marker ((*i), is_start);
522 XMLNode& before (loc->get_state());
525 distance = get_nudge_distance (loc->start(), next_distance);
527 distance = next_distance;
529 if (distance < loc->start()) {
530 loc->set_start (loc->start() - distance, false, true, get_grid_music_divisions(0));
532 loc->set_start (0, false, true, get_grid_music_divisions(0));
535 distance = get_nudge_distance (loc->end(), next_distance);
538 distance = next_distance;
541 if (distance < loc->end() - loc->length()) {
542 loc->set_end (loc->end() - distance, false, true, get_grid_music_divisions(0));
544 loc->set_end (loc->length(), false, true, get_grid_music_divisions(0));
546 if (loc->is_session_range()) {
547 _session->set_end_is_free (false);
551 begin_reversible_command (_("nudge location forward"));
554 XMLNode& after (loc->get_state());
555 _session->add_command (new MementoCommand<Location>(*loc, &before, &after));
559 commit_reversible_command ();
564 distance = get_nudge_distance (playhead_cursor->current_frame (), next_distance);
566 if (playhead_cursor->current_frame () > distance) {
567 _session->request_locate (playhead_cursor->current_frame () - distance);
569 _session->goto_start();
575 Editor::nudge_forward_capture_offset ()
577 RegionSelection rs = get_regions_from_selection_and_entered ();
579 if (!_session || rs.empty()) {
583 begin_reversible_command (_("nudge forward"));
585 framepos_t const distance = _session->worst_output_latency();
587 for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
588 boost::shared_ptr<Region> r ((*i)->region());
591 r->set_position (r->position() + distance);
592 _session->add_command(new StatefulDiffCommand (r));
595 commit_reversible_command ();
599 Editor::nudge_backward_capture_offset ()
601 RegionSelection rs = get_regions_from_selection_and_entered ();
603 if (!_session || rs.empty()) {
607 begin_reversible_command (_("nudge backward"));
609 framepos_t const distance = _session->worst_output_latency();
611 for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
612 boost::shared_ptr<Region> r ((*i)->region());
616 if (r->position() > distance) {
617 r->set_position (r->position() - distance);
621 _session->add_command(new StatefulDiffCommand (r));
624 commit_reversible_command ();
627 struct RegionSelectionPositionSorter {
628 bool operator() (RegionView* a, RegionView* b) {
629 return a->region()->position() < b->region()->position();
634 Editor::sequence_regions ()
637 framepos_t r_end_prev;
645 RegionSelection rs = get_regions_from_selection_and_entered ();
646 rs.sort(RegionSelectionPositionSorter());
650 bool in_command = false;
652 for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
653 boost::shared_ptr<Region> r ((*i)->region());
661 if(r->position_locked())
668 r->set_position(r_end_prev);
672 begin_reversible_command (_("sequence regions"));
675 _session->add_command (new StatefulDiffCommand (r));
677 r_end=r->position() + r->length();
683 commit_reversible_command ();
692 Editor::move_to_start ()
694 _session->goto_start ();
698 Editor::move_to_end ()
701 _session->request_locate (_session->current_end_frame());
705 Editor::build_region_boundary_cache ()
708 vector<RegionPoint> interesting_points;
709 boost::shared_ptr<Region> r;
710 TrackViewList tracks;
713 region_boundary_cache.clear ();
719 switch (_snap_type) {
720 case SnapToRegionStart:
721 interesting_points.push_back (Start);
723 case SnapToRegionEnd:
724 interesting_points.push_back (End);
726 case SnapToRegionSync:
727 interesting_points.push_back (SyncPoint);
729 case SnapToRegionBoundary:
730 interesting_points.push_back (Start);
731 interesting_points.push_back (End);
734 fatal << string_compose (_("build_region_boundary_cache called with snap_type = %1"), _snap_type) << endmsg;
735 abort(); /*NOTREACHED*/
739 TimeAxisView *ontrack = 0;
742 if (!selection->tracks.empty()) {
743 tlist = selection->tracks.filter_to_unique_playlists ();
745 tlist = track_views.filter_to_unique_playlists ();
748 while (pos < _session->current_end_frame() && !at_end) {
751 framepos_t lpos = max_framepos;
753 for (vector<RegionPoint>::iterator p = interesting_points.begin(); p != interesting_points.end(); ++p) {
755 if ((r = find_next_region (pos, *p, 1, tlist, &ontrack)) == 0) {
756 if (*p == interesting_points.back()) {
759 /* move to next point type */
765 rpos = r->first_frame();
769 rpos = r->last_frame();
773 rpos = r->sync_position ();
781 RouteTimeAxisView *rtav;
783 if (ontrack != 0 && (rtav = dynamic_cast<RouteTimeAxisView*>(ontrack)) != 0 ) {
784 if (rtav->track() != 0) {
785 speed = rtav->track()->speed();
789 rpos = track_frame_to_session_frame (rpos, speed);
795 /* prevent duplicates, but we don't use set<> because we want to be able
799 vector<framepos_t>::iterator ri;
801 for (ri = region_boundary_cache.begin(); ri != region_boundary_cache.end(); ++ri) {
807 if (ri == region_boundary_cache.end()) {
808 region_boundary_cache.push_back (rpos);
815 /* finally sort to be sure that the order is correct */
817 sort (region_boundary_cache.begin(), region_boundary_cache.end());
820 boost::shared_ptr<Region>
821 Editor::find_next_region (framepos_t frame, RegionPoint point, int32_t dir, TrackViewList& tracks, TimeAxisView **ontrack)
823 TrackViewList::iterator i;
824 framepos_t closest = max_framepos;
825 boost::shared_ptr<Region> ret;
829 framepos_t track_frame;
830 RouteTimeAxisView *rtav;
832 for (i = tracks.begin(); i != tracks.end(); ++i) {
835 boost::shared_ptr<Region> r;
838 if ( (rtav = dynamic_cast<RouteTimeAxisView*>(*i)) != 0 ) {
839 if (rtav->track()!=0)
840 track_speed = rtav->track()->speed();
843 track_frame = session_frame_to_track_frame(frame, track_speed);
845 if ((r = (*i)->find_next_region (track_frame, point, dir)) == 0) {
851 rpos = r->first_frame ();
855 rpos = r->last_frame ();
859 rpos = r->sync_position ();
863 // rpos is a "track frame", converting it to "_session frame"
864 rpos = track_frame_to_session_frame(rpos, track_speed);
867 distance = rpos - frame;
869 distance = frame - rpos;
872 if (distance < closest) {
884 Editor::find_next_region_boundary (framepos_t pos, int32_t dir, const TrackViewList& tracks)
886 framecnt_t distance = max_framepos;
887 framepos_t current_nearest = -1;
889 for (TrackViewList::const_iterator i = tracks.begin(); i != tracks.end(); ++i) {
890 framepos_t contender;
893 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (*i);
899 if ((contender = rtv->find_next_region_boundary (pos, dir)) < 0) {
903 d = ::llabs (pos - contender);
906 current_nearest = contender;
911 return current_nearest;
915 Editor::get_region_boundary (framepos_t pos, int32_t dir, bool with_selection, bool only_onscreen)
920 if (with_selection && Config->get_region_boundaries_from_selected_tracks()) {
922 if (!selection->tracks.empty()) {
924 target = find_next_region_boundary (pos, dir, selection->tracks);
928 if (only_onscreen || Config->get_region_boundaries_from_onscreen_tracks()) {
929 get_onscreen_tracks (tvl);
930 target = find_next_region_boundary (pos, dir, tvl);
932 target = find_next_region_boundary (pos, dir, track_views);
938 if (only_onscreen || Config->get_region_boundaries_from_onscreen_tracks()) {
939 get_onscreen_tracks (tvl);
940 target = find_next_region_boundary (pos, dir, tvl);
942 target = find_next_region_boundary (pos, dir, track_views);
950 Editor::cursor_to_region_boundary (bool with_selection, int32_t dir)
952 framepos_t pos = playhead_cursor->current_frame ();
959 // so we don't find the current region again..
960 if (dir > 0 || pos > 0) {
964 if ((target = get_region_boundary (pos, dir, with_selection, false)) < 0) {
968 _session->request_locate (target);
972 Editor::cursor_to_next_region_boundary (bool with_selection)
974 cursor_to_region_boundary (with_selection, 1);
978 Editor::cursor_to_previous_region_boundary (bool with_selection)
980 cursor_to_region_boundary (with_selection, -1);
984 Editor::cursor_to_region_point (EditorCursor* cursor, RegionPoint point, int32_t dir)
986 boost::shared_ptr<Region> r;
987 framepos_t pos = cursor->current_frame ();
993 TimeAxisView *ontrack = 0;
995 // so we don't find the current region again..
999 if (!selection->tracks.empty()) {
1001 r = find_next_region (pos, point, dir, selection->tracks, &ontrack);
1003 } else if (clicked_axisview) {
1006 t.push_back (clicked_axisview);
1008 r = find_next_region (pos, point, dir, t, &ontrack);
1012 r = find_next_region (pos, point, dir, track_views, &ontrack);
1021 pos = r->first_frame ();
1025 pos = r->last_frame ();
1029 pos = r->sync_position ();
1034 RouteTimeAxisView *rtav;
1036 if ( ontrack != 0 && (rtav = dynamic_cast<RouteTimeAxisView*>(ontrack)) != 0 ) {
1037 if (rtav->track() != 0) {
1038 speed = rtav->track()->speed();
1042 pos = track_frame_to_session_frame(pos, speed);
1044 if (cursor == playhead_cursor) {
1045 _session->request_locate (pos);
1047 cursor->set_position (pos);
1052 Editor::cursor_to_next_region_point (EditorCursor* cursor, RegionPoint point)
1054 cursor_to_region_point (cursor, point, 1);
1058 Editor::cursor_to_previous_region_point (EditorCursor* cursor, RegionPoint point)
1060 cursor_to_region_point (cursor, point, -1);
1064 Editor::cursor_to_selection_start (EditorCursor *cursor)
1068 switch (mouse_mode) {
1070 if (!selection->regions.empty()) {
1071 pos = selection->regions.start();
1076 if (!selection->time.empty()) {
1077 pos = selection->time.start ();
1085 if (cursor == playhead_cursor) {
1086 _session->request_locate (pos);
1088 cursor->set_position (pos);
1093 Editor::cursor_to_selection_end (EditorCursor *cursor)
1097 switch (mouse_mode) {
1099 if (!selection->regions.empty()) {
1100 pos = selection->regions.end_frame();
1105 if (!selection->time.empty()) {
1106 pos = selection->time.end_frame ();
1114 if (cursor == playhead_cursor) {
1115 _session->request_locate (pos);
1117 cursor->set_position (pos);
1122 Editor::selected_marker_to_region_boundary (bool with_selection, int32_t dir)
1132 if (selection->markers.empty()) {
1136 if (!mouse_frame (mouse, ignored)) {
1140 add_location_mark (mouse);
1143 if ((loc = find_location_from_marker (selection->markers.front(), ignored)) == 0) {
1147 framepos_t pos = loc->start();
1149 // so we don't find the current region again..
1150 if (dir > 0 || pos > 0) {
1154 if ((target = get_region_boundary (pos, dir, with_selection, false)) < 0) {
1158 loc->move_to (target, 0);
1162 Editor::selected_marker_to_next_region_boundary (bool with_selection)
1164 selected_marker_to_region_boundary (with_selection, 1);
1168 Editor::selected_marker_to_previous_region_boundary (bool with_selection)
1170 selected_marker_to_region_boundary (with_selection, -1);
1174 Editor::selected_marker_to_region_point (RegionPoint point, int32_t dir)
1176 boost::shared_ptr<Region> r;
1181 if (!_session || selection->markers.empty()) {
1185 if ((loc = find_location_from_marker (selection->markers.front(), ignored)) == 0) {
1189 TimeAxisView *ontrack = 0;
1193 // so we don't find the current region again..
1197 if (!selection->tracks.empty()) {
1199 r = find_next_region (pos, point, dir, selection->tracks, &ontrack);
1203 r = find_next_region (pos, point, dir, track_views, &ontrack);
1212 pos = r->first_frame ();
1216 pos = r->last_frame ();
1220 pos = r->adjust_to_sync (r->first_frame());
1225 RouteTimeAxisView *rtav;
1227 if (ontrack != 0 && (rtav = dynamic_cast<RouteTimeAxisView*>(ontrack)) != 0) {
1228 if (rtav->track() != 0) {
1229 speed = rtav->track()->speed();
1233 pos = track_frame_to_session_frame(pos, speed);
1235 loc->move_to (pos, 0);
1239 Editor::selected_marker_to_next_region_point (RegionPoint point)
1241 selected_marker_to_region_point (point, 1);
1245 Editor::selected_marker_to_previous_region_point (RegionPoint point)
1247 selected_marker_to_region_point (point, -1);
1251 Editor::selected_marker_to_selection_start ()
1257 if (!_session || selection->markers.empty()) {
1261 if ((loc = find_location_from_marker (selection->markers.front(), ignored)) == 0) {
1265 switch (mouse_mode) {
1267 if (!selection->regions.empty()) {
1268 pos = selection->regions.start();
1273 if (!selection->time.empty()) {
1274 pos = selection->time.start ();
1282 loc->move_to (pos, 0);
1286 Editor::selected_marker_to_selection_end ()
1292 if (!_session || selection->markers.empty()) {
1296 if ((loc = find_location_from_marker (selection->markers.front(), ignored)) == 0) {
1300 switch (mouse_mode) {
1302 if (!selection->regions.empty()) {
1303 pos = selection->regions.end_frame();
1308 if (!selection->time.empty()) {
1309 pos = selection->time.end_frame ();
1317 loc->move_to (pos, 0);
1321 Editor::scroll_playhead (bool forward)
1323 framepos_t pos = playhead_cursor->current_frame ();
1324 framecnt_t delta = (framecnt_t) floor (current_page_samples() / 0.8);
1327 if (pos == max_framepos) {
1331 if (pos < max_framepos - delta) {
1350 _session->request_locate (pos);
1354 Editor::cursor_align (bool playhead_to_edit)
1360 if (playhead_to_edit) {
1362 if (selection->markers.empty()) {
1366 _session->request_locate (selection->markers.front()->position(), _session->transport_rolling());
1369 const int32_t divisions = get_grid_music_divisions (0);
1370 /* move selected markers to playhead */
1372 for (MarkerSelection::iterator i = selection->markers.begin(); i != selection->markers.end(); ++i) {
1375 Location* loc = find_location_from_marker (*i, ignored);
1377 if (loc->is_mark()) {
1378 loc->set_start (playhead_cursor->current_frame (), false, true, divisions);
1380 loc->set (playhead_cursor->current_frame (),
1381 playhead_cursor->current_frame () + loc->length(), true, divisions);
1388 Editor::scroll_backward (float pages)
1390 framepos_t const one_page = (framepos_t) rint (_visible_canvas_width * samples_per_pixel);
1391 framepos_t const cnt = (framepos_t) floor (pages * one_page);
1394 if (leftmost_frame < cnt) {
1397 frame = leftmost_frame - cnt;
1400 reset_x_origin (frame);
1404 Editor::scroll_forward (float pages)
1406 framepos_t const one_page = (framepos_t) rint (_visible_canvas_width * samples_per_pixel);
1407 framepos_t const cnt = (framepos_t) floor (pages * one_page);
1410 if (max_framepos - cnt < leftmost_frame) {
1411 frame = max_framepos - cnt;
1413 frame = leftmost_frame + cnt;
1416 reset_x_origin (frame);
1420 Editor::scroll_tracks_down ()
1422 double vert_value = vertical_adjustment.get_value() + vertical_adjustment.get_page_size();
1423 if (vert_value > vertical_adjustment.get_upper() - _visible_canvas_height) {
1424 vert_value = vertical_adjustment.get_upper() - _visible_canvas_height;
1427 vertical_adjustment.set_value (vert_value);
1431 Editor::scroll_tracks_up ()
1433 vertical_adjustment.set_value (vertical_adjustment.get_value() - vertical_adjustment.get_page_size());
1437 Editor::scroll_tracks_down_line ()
1439 double vert_value = vertical_adjustment.get_value() + 60;
1441 if (vert_value > vertical_adjustment.get_upper() - _visible_canvas_height) {
1442 vert_value = vertical_adjustment.get_upper() - _visible_canvas_height;
1445 vertical_adjustment.set_value (vert_value);
1449 Editor::scroll_tracks_up_line ()
1451 reset_y_origin (vertical_adjustment.get_value() - 60);
1455 Editor::scroll_down_one_track (bool skip_child_views)
1457 TrackViewList::reverse_iterator next = track_views.rend();
1458 const double top_of_trackviews = vertical_adjustment.get_value();
1460 for (TrackViewList::reverse_iterator t = track_views.rbegin(); t != track_views.rend(); ++t) {
1461 if ((*t)->hidden()) {
1465 /* If this is the upper-most visible trackview, we want to display
1466 * the one above it (next)
1468 * Note that covers_y_position() is recursive and includes child views
1470 std::pair<TimeAxisView*,double> res = (*t)->covers_y_position (top_of_trackviews);
1473 if (skip_child_views) {
1476 /* automation lane (one level, non-recursive)
1478 * - if no automation lane exists -> move to next tack
1479 * - if the first (here: bottom-most) matches -> move to next tack
1480 * - if no y-axis match is found -> the current track is at the top
1481 * -> move to last (here: top-most) automation lane
1483 TimeAxisView::Children kids = (*t)->get_child_list();
1484 TimeAxisView::Children::reverse_iterator nkid = kids.rend();
1486 for (TimeAxisView::Children::reverse_iterator ci = kids.rbegin(); ci != kids.rend(); ++ci) {
1487 if ((*ci)->hidden()) {
1491 std::pair<TimeAxisView*,double> dev;
1492 dev = (*ci)->covers_y_position (top_of_trackviews);
1494 /* some automation lane is currently at the top */
1495 if (ci == kids.rbegin()) {
1496 /* first (bottom-most) autmation lane is at the top.
1497 * -> move to next track
1506 if (nkid != kids.rend()) {
1507 ensure_time_axis_view_is_visible (**nkid, true);
1515 /* move to the track below the first one that covers the */
1517 if (next != track_views.rend()) {
1518 ensure_time_axis_view_is_visible (**next, true);
1526 Editor::scroll_up_one_track (bool skip_child_views)
1528 TrackViewList::iterator prev = track_views.end();
1529 double top_of_trackviews = vertical_adjustment.get_value ();
1531 for (TrackViewList::iterator t = track_views.begin(); t != track_views.end(); ++t) {
1533 if ((*t)->hidden()) {
1537 /* find the trackview at the top of the trackview group
1539 * Note that covers_y_position() is recursive and includes child views
1541 std::pair<TimeAxisView*,double> res = (*t)->covers_y_position (top_of_trackviews);
1544 if (skip_child_views) {
1547 /* automation lane (one level, non-recursive)
1549 * - if no automation lane exists -> move to prev tack
1550 * - if no y-axis match is found -> the current track is at the top -> move to prev track
1551 * (actually last automation lane of previous track, see below)
1552 * - if first (top-most) lane is at the top -> move to this track
1553 * - else move up one lane
1555 TimeAxisView::Children kids = (*t)->get_child_list();
1556 TimeAxisView::Children::iterator pkid = kids.end();
1558 for (TimeAxisView::Children::iterator ci = kids.begin(); ci != kids.end(); ++ci) {
1559 if ((*ci)->hidden()) {
1563 std::pair<TimeAxisView*,double> dev;
1564 dev = (*ci)->covers_y_position (top_of_trackviews);
1566 /* some automation lane is currently at the top */
1567 if (ci == kids.begin()) {
1568 /* first (top-most) autmation lane is at the top.
1569 * jump directly to this track's top
1571 ensure_time_axis_view_is_visible (**t, true);
1574 else if (pkid != kids.end()) {
1575 /* some other automation lane is at the top.
1576 * move up to prev automation lane.
1578 ensure_time_axis_view_is_visible (**pkid, true);
1581 assert(0); // not reached
1592 if (prev != track_views.end()) {
1593 // move to bottom-most automation-lane of the previous track
1594 TimeAxisView::Children kids = (*prev)->get_child_list();
1595 TimeAxisView::Children::reverse_iterator pkid = kids.rend();
1596 if (!skip_child_views) {
1597 // find the last visible lane
1598 for (TimeAxisView::Children::reverse_iterator ci = kids.rbegin(); ci != kids.rend(); ++ci) {
1599 if (!(*ci)->hidden()) {
1605 if (pkid != kids.rend()) {
1606 ensure_time_axis_view_is_visible (**pkid, true);
1608 ensure_time_axis_view_is_visible (**prev, true);
1617 Editor::scroll_left_step ()
1619 framepos_t xdelta = (current_page_samples() / 8);
1621 if (leftmost_frame > xdelta) {
1622 reset_x_origin (leftmost_frame - xdelta);
1630 Editor::scroll_right_step ()
1632 framepos_t xdelta = (current_page_samples() / 8);
1634 if (max_framepos - xdelta > leftmost_frame) {
1635 reset_x_origin (leftmost_frame + xdelta);
1637 reset_x_origin (max_framepos - current_page_samples());
1642 Editor::scroll_left_half_page ()
1644 framepos_t xdelta = (current_page_samples() / 2);
1645 if (leftmost_frame > xdelta) {
1646 reset_x_origin (leftmost_frame - xdelta);
1653 Editor::scroll_right_half_page ()
1655 framepos_t xdelta = (current_page_samples() / 2);
1656 if (max_framepos - xdelta > leftmost_frame) {
1657 reset_x_origin (leftmost_frame + xdelta);
1659 reset_x_origin (max_framepos - current_page_samples());
1666 Editor::tav_zoom_step (bool coarser)
1668 DisplaySuspender ds;
1672 if (selection->tracks.empty()) {
1675 ts = &selection->tracks;
1678 for (TrackViewList::iterator i = ts->begin(); i != ts->end(); ++i) {
1679 TimeAxisView *tv = (static_cast<TimeAxisView*>(*i));
1680 tv->step_height (coarser);
1685 Editor::tav_zoom_smooth (bool coarser, bool force_all)
1687 DisplaySuspender ds;
1691 if (selection->tracks.empty() || force_all) {
1694 ts = &selection->tracks;
1697 for (TrackViewList::iterator i = ts->begin(); i != ts->end(); ++i) {
1698 TimeAxisView *tv = (static_cast<TimeAxisView*>(*i));
1699 uint32_t h = tv->current_height ();
1704 if (h >= TimeAxisView::preset_height (HeightSmall)) {
1709 tv->set_height (h + 5);
1715 Editor::temporal_zoom_step_mouse_focus_scale (bool zoom_out, double scale)
1717 Editing::ZoomFocus temp_focus = zoom_focus;
1718 zoom_focus = Editing::ZoomFocusMouse;
1719 temporal_zoom_step_scale (zoom_out, scale);
1720 zoom_focus = temp_focus;
1724 Editor::temporal_zoom_step_mouse_focus (bool zoom_out)
1726 temporal_zoom_step_mouse_focus_scale (zoom_out, 2.0);
1730 Editor::temporal_zoom_step (bool zoom_out)
1732 temporal_zoom_step_scale (zoom_out, 2.0);
1736 Editor::temporal_zoom_step_scale (bool zoom_out, double scale)
1738 ENSURE_GUI_THREAD (*this, &Editor::temporal_zoom_step, zoom_out, scale)
1740 framecnt_t nspp = samples_per_pixel;
1744 if (nspp == samples_per_pixel) {
1749 if (nspp == samples_per_pixel) {
1754 temporal_zoom (nspp);
1758 Editor::temporal_zoom (framecnt_t fpp)
1764 framepos_t current_page = current_page_samples();
1765 framepos_t current_leftmost = leftmost_frame;
1766 framepos_t current_rightmost;
1767 framepos_t current_center;
1768 framepos_t new_page_size;
1769 framepos_t half_page_size;
1770 framepos_t leftmost_after_zoom = 0;
1772 bool in_track_canvas;
1773 bool use_mouse_frame = true;
1777 if (fpp == samples_per_pixel) {
1781 // Imposing an arbitrary limit to zoom out as too much zoom out produces
1782 // segfaults for lack of memory. If somebody decides this is not high enough I
1783 // believe it can be raisen to higher values but some limit must be in place.
1785 // This constant represents 1 day @ 48kHz on a 1600 pixel wide display
1786 // all of which is used for the editor track displays. The whole day
1787 // would be 4147200000 samples, so 2592000 samples per pixel.
1789 nfpp = min (fpp, (framecnt_t) 2592000);
1790 nfpp = max ((framecnt_t) 1, nfpp);
1792 new_page_size = (framepos_t) floor (_visible_canvas_width * nfpp);
1793 half_page_size = new_page_size / 2;
1795 switch (zoom_focus) {
1797 leftmost_after_zoom = current_leftmost;
1800 case ZoomFocusRight:
1801 current_rightmost = leftmost_frame + current_page;
1802 if (current_rightmost < new_page_size) {
1803 leftmost_after_zoom = 0;
1805 leftmost_after_zoom = current_rightmost - new_page_size;
1809 case ZoomFocusCenter:
1810 current_center = current_leftmost + (current_page/2);
1811 if (current_center < half_page_size) {
1812 leftmost_after_zoom = 0;
1814 leftmost_after_zoom = current_center - half_page_size;
1818 case ZoomFocusPlayhead:
1819 /* centre playhead */
1820 l = playhead_cursor->current_frame () - (new_page_size * 0.5);
1823 leftmost_after_zoom = 0;
1824 } else if (l > max_framepos) {
1825 leftmost_after_zoom = max_framepos - new_page_size;
1827 leftmost_after_zoom = (framepos_t) l;
1831 case ZoomFocusMouse:
1832 /* try to keep the mouse over the same point in the display */
1834 if (_drags->active()) {
1835 where = _drags->current_pointer_frame ();
1836 } else if (!mouse_frame (where, in_track_canvas)) {
1837 use_mouse_frame = false;
1840 if (use_mouse_frame) {
1841 l = - ((new_page_size * ((where - current_leftmost)/(double)current_page)) - where);
1844 leftmost_after_zoom = 0;
1845 } else if (l > max_framepos) {
1846 leftmost_after_zoom = max_framepos - new_page_size;
1848 leftmost_after_zoom = (framepos_t) l;
1851 /* use playhead instead */
1852 where = playhead_cursor->current_frame ();
1854 if (where < half_page_size) {
1855 leftmost_after_zoom = 0;
1857 leftmost_after_zoom = where - half_page_size;
1863 /* try to keep the edit point in the same place */
1864 where = get_preferred_edit_position ();
1868 double l = - ((new_page_size * ((where - current_leftmost)/(double)current_page)) - where);
1871 leftmost_after_zoom = 0;
1872 } else if (l > max_framepos) {
1873 leftmost_after_zoom = max_framepos - new_page_size;
1875 leftmost_after_zoom = (framepos_t) l;
1879 /* edit point not defined */
1886 // leftmost_after_zoom = min (leftmost_after_zoom, _session->current_end_frame());
1888 reposition_and_zoom (leftmost_after_zoom, nfpp);
1892 Editor::calc_extra_zoom_edges(framepos_t &start, framepos_t &end)
1894 /* this func helps make sure we leave a little space
1895 at each end of the editor so that the zoom doesn't fit the region
1896 precisely to the screen.
1899 GdkScreen* screen = gdk_screen_get_default ();
1900 const gint pixwidth = gdk_screen_get_width (screen);
1901 const gint mmwidth = gdk_screen_get_width_mm (screen);
1902 const double pix_per_mm = (double) pixwidth/ (double) mmwidth;
1903 const double one_centimeter_in_pixels = pix_per_mm * 10.0;
1905 const framepos_t range = end - start;
1906 const framecnt_t new_fpp = (framecnt_t) ceil ((double) range / (double) _visible_canvas_width);
1907 const framepos_t extra_samples = (framepos_t) floor (one_centimeter_in_pixels * new_fpp);
1909 if (start > extra_samples) {
1910 start -= extra_samples;
1915 if (max_framepos - extra_samples > end) {
1916 end += extra_samples;
1923 Editor::get_selection_extents (framepos_t &start, framepos_t &end) const
1925 start = max_framepos;
1929 //ToDo: if notes are selected, set extents to that selection
1931 //ToDo: if control points are selected, set extents to that selection
1933 if ( !selection->regions.empty() ) {
1934 RegionSelection rs = get_regions_from_selection_and_entered ();
1936 for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
1938 if ((*i)->region()->position() < start) {
1939 start = (*i)->region()->position();
1942 if ((*i)->region()->last_frame() + 1 > end) {
1943 end = (*i)->region()->last_frame() + 1;
1947 } else if (!selection->time.empty()) {
1948 start = selection->time.start();
1949 end = selection->time.end_frame();
1951 ret = false; //no selection found
1954 if ((start == 0 && end == 0) || end < start) {
1963 Editor::temporal_zoom_selection (Editing::ZoomAxis axes)
1965 if (!selection) return;
1967 //ToDo: if notes are selected, zoom to that
1969 //ToDo: if control points are selected, zoom to that
1971 if (axes == Horizontal || axes == Both) {
1973 framepos_t start, end;
1974 if (get_selection_extents (start, end)) {
1975 calc_extra_zoom_edges (start, end);
1976 temporal_zoom_by_frame (start, end);
1980 if (axes == Vertical || axes == Both) {
1986 Editor::temporal_zoom_session ()
1988 ENSURE_GUI_THREAD (*this, &Editor::temporal_zoom_session)
1991 framecnt_t start = _session->current_start_frame();
1992 framecnt_t end = _session->current_end_frame();
1994 if (_session->actively_recording () ) {
1995 framepos_t cur = playhead_cursor->current_frame ();
1997 /* recording beyond the end marker; zoom out
1998 * by 5 seconds more so that if 'follow
1999 * playhead' is active we don't immediately
2002 end = cur + _session->frame_rate() * 5;
2006 if ((start == 0 && end == 0) || end < start) {
2010 calc_extra_zoom_edges(start, end);
2012 temporal_zoom_by_frame (start, end);
2017 Editor::temporal_zoom_by_frame (framepos_t start, framepos_t end)
2019 if (!_session) return;
2021 if ((start == 0 && end == 0) || end < start) {
2025 framepos_t range = end - start;
2027 const framecnt_t new_fpp = (framecnt_t) ceil ((double) range / (double) _visible_canvas_width);
2029 framepos_t new_page = range;
2030 framepos_t middle = (framepos_t) floor ((double) start + ((double) range / 2.0f));
2031 framepos_t new_leftmost = (framepos_t) floor ((double) middle - ((double) new_page / 2.0f));
2033 if (new_leftmost > middle) {
2037 if (new_leftmost < 0) {
2041 reposition_and_zoom (new_leftmost, new_fpp);
2045 Editor::temporal_zoom_to_frame (bool coarser, framepos_t frame)
2051 framecnt_t range_before = frame - leftmost_frame;
2055 if (samples_per_pixel <= 1) {
2058 new_spp = samples_per_pixel + (samples_per_pixel/2);
2060 range_before += range_before/2;
2062 if (samples_per_pixel >= 1) {
2063 new_spp = samples_per_pixel - (samples_per_pixel/2);
2065 /* could bail out here since we cannot zoom any finer,
2066 but leave that to the equality test below
2068 new_spp = samples_per_pixel;
2071 range_before -= range_before/2;
2074 if (new_spp == samples_per_pixel) {
2078 /* zoom focus is automatically taken as @param frame when this
2082 framepos_t new_leftmost = frame - (framepos_t)range_before;
2084 if (new_leftmost > frame) {
2088 if (new_leftmost < 0) {
2092 reposition_and_zoom (new_leftmost, new_spp);
2097 Editor::choose_new_marker_name(string &name) {
2099 if (!UIConfiguration::instance().get_name_new_markers()) {
2100 /* don't prompt user for a new name */
2104 ArdourPrompter dialog (true);
2106 dialog.set_prompt (_("New Name:"));
2108 dialog.set_title (_("New Location Marker"));
2110 dialog.set_name ("MarkNameWindow");
2111 dialog.set_size_request (250, -1);
2112 dialog.set_position (Gtk::WIN_POS_MOUSE);
2114 dialog.add_button (Stock::OK, RESPONSE_ACCEPT);
2115 dialog.set_initial_text (name);
2119 switch (dialog.run ()) {
2120 case RESPONSE_ACCEPT:
2126 dialog.get_result(name);
2133 Editor::add_location_from_selection ()
2137 if (selection->time.empty()) {
2141 if (_session == 0 || clicked_axisview == 0) {
2145 framepos_t start = selection->time[clicked_selection].start;
2146 framepos_t end = selection->time[clicked_selection].end;
2148 _session->locations()->next_available_name(rangename,"selection");
2149 Location *location = new Location (*_session, start, end, rangename, Location::IsRangeMarker, get_grid_music_divisions(0));
2151 begin_reversible_command (_("add marker"));
2153 XMLNode &before = _session->locations()->get_state();
2154 _session->locations()->add (location, true);
2155 XMLNode &after = _session->locations()->get_state();
2156 _session->add_command(new MementoCommand<Locations>(*(_session->locations()), &before, &after));
2158 commit_reversible_command ();
2162 Editor::add_location_mark (framepos_t where)
2166 select_new_marker = true;
2168 _session->locations()->next_available_name(markername,"mark");
2169 if (!choose_new_marker_name(markername)) {
2172 Location *location = new Location (*_session, where, where, markername, Location::IsMark, get_grid_music_divisions (0));
2173 begin_reversible_command (_("add marker"));
2175 XMLNode &before = _session->locations()->get_state();
2176 _session->locations()->add (location, true);
2177 XMLNode &after = _session->locations()->get_state();
2178 _session->add_command(new MementoCommand<Locations>(*(_session->locations()), &before, &after));
2180 commit_reversible_command ();
2184 Editor::set_session_start_from_playhead ()
2190 if ((loc = _session->locations()->session_range_location()) == 0) { //should never happen
2191 _session->set_session_extents ( _session->audible_frame(), _session->audible_frame() );
2193 XMLNode &before = loc->get_state();
2195 _session->set_session_extents ( _session->audible_frame(), loc->end() );
2197 XMLNode &after = loc->get_state();
2199 begin_reversible_command (_("Set session start"));
2201 _session->add_command (new MementoCommand<Location>(*loc, &before, &after));
2203 commit_reversible_command ();
2208 Editor::set_session_end_from_playhead ()
2214 if ((loc = _session->locations()->session_range_location()) == 0) { //should never happen
2215 _session->set_session_extents ( _session->audible_frame(), _session->audible_frame() );
2217 XMLNode &before = loc->get_state();
2219 _session->set_session_extents ( loc->start(), _session->audible_frame() );
2221 XMLNode &after = loc->get_state();
2223 begin_reversible_command (_("Set session start"));
2225 _session->add_command (new MementoCommand<Location>(*loc, &before, &after));
2227 commit_reversible_command ();
2230 _session->set_end_is_free (false);
2235 Editor::toggle_location_at_playhead_cursor ()
2237 if (!do_remove_location_at_playhead_cursor())
2239 add_location_from_playhead_cursor();
2244 Editor::add_location_from_playhead_cursor ()
2246 add_location_mark (_session->audible_frame());
2250 Editor::do_remove_location_at_playhead_cursor ()
2252 bool removed = false;
2255 XMLNode &before = _session->locations()->get_state();
2257 //find location(s) at this time
2258 Locations::LocationList locs;
2259 _session->locations()->find_all_between (_session->audible_frame(), _session->audible_frame()+1, locs, Location::Flags(0));
2260 for (Locations::LocationList::iterator i = locs.begin(); i != locs.end(); ++i) {
2261 if ((*i)->is_mark()) {
2262 _session->locations()->remove (*i);
2269 begin_reversible_command (_("remove marker"));
2270 XMLNode &after = _session->locations()->get_state();
2271 _session->add_command(new MementoCommand<Locations>(*(_session->locations()), &before, &after));
2272 commit_reversible_command ();
2279 Editor::remove_location_at_playhead_cursor ()
2281 do_remove_location_at_playhead_cursor ();
2284 /** Add a range marker around each selected region */
2286 Editor::add_locations_from_region ()
2288 RegionSelection rs = get_regions_from_selection_and_entered ();
2293 bool commit = false;
2295 XMLNode &before = _session->locations()->get_state();
2297 for (RegionSelection::iterator i = rs.begin (); i != rs.end (); ++i) {
2299 boost::shared_ptr<Region> region = (*i)->region ();
2301 Location *location = new Location (*_session, region->position(), region->last_frame(), region->name(), Location::IsRangeMarker, 0);
2303 _session->locations()->add (location, true);
2308 begin_reversible_command (selection->regions.size () > 1 ? _("add markers") : _("add marker"));
2309 XMLNode &after = _session->locations()->get_state();
2310 _session->add_command (new MementoCommand<Locations>(*(_session->locations()), &before, &after));
2311 commit_reversible_command ();
2315 /** Add a single range marker around all selected regions */
2317 Editor::add_location_from_region ()
2319 RegionSelection rs = get_regions_from_selection_and_entered ();
2325 XMLNode &before = _session->locations()->get_state();
2329 if (rs.size() > 1) {
2330 _session->locations()->next_available_name(markername, "regions");
2332 RegionView* rv = *(rs.begin());
2333 boost::shared_ptr<Region> region = rv->region();
2334 markername = region->name();
2337 if (!choose_new_marker_name(markername)) {
2341 // single range spanning all selected
2342 Location *location = new Location (*_session, selection->regions.start(), selection->regions.end_frame(), markername, Location::IsRangeMarker, 0);
2343 _session->locations()->add (location, true);
2345 begin_reversible_command (_("add marker"));
2346 XMLNode &after = _session->locations()->get_state();
2347 _session->add_command (new MementoCommand<Locations>(*(_session->locations()), &before, &after));
2348 commit_reversible_command ();
2354 Editor::jump_forward_to_mark ()
2360 framepos_t pos = _session->locations()->first_mark_after (playhead_cursor->current_frame());
2366 _session->request_locate (pos, _session->transport_rolling());
2370 Editor::jump_backward_to_mark ()
2376 framepos_t pos = _session->locations()->first_mark_before (playhead_cursor->current_frame());
2378 //handle the case where we are rolling, and we're less than one-half second past the mark, we want to go to the prior mark...
2379 if ( _session->transport_rolling() ) {
2380 if ( (playhead_cursor->current_frame() - pos) < _session->frame_rate()/2 ) {
2381 framepos_t prior = _session->locations()->first_mark_before ( pos );
2390 _session->request_locate (pos, _session->transport_rolling());
2396 framepos_t const pos = _session->audible_frame ();
2399 _session->locations()->next_available_name (markername, "mark");
2401 if (!choose_new_marker_name (markername)) {
2405 _session->locations()->add (new Location (*_session, pos, 0, markername, Location::IsMark, 0), true);
2409 Editor::clear_markers ()
2412 begin_reversible_command (_("clear markers"));
2414 XMLNode &before = _session->locations()->get_state();
2415 _session->locations()->clear_markers ();
2416 XMLNode &after = _session->locations()->get_state();
2417 _session->add_command(new MementoCommand<Locations>(*(_session->locations()), &before, &after));
2419 commit_reversible_command ();
2424 Editor::clear_ranges ()
2427 begin_reversible_command (_("clear ranges"));
2429 XMLNode &before = _session->locations()->get_state();
2431 _session->locations()->clear_ranges ();
2433 XMLNode &after = _session->locations()->get_state();
2434 _session->add_command(new MementoCommand<Locations>(*(_session->locations()), &before, &after));
2436 commit_reversible_command ();
2441 Editor::clear_locations ()
2443 begin_reversible_command (_("clear locations"));
2445 XMLNode &before = _session->locations()->get_state();
2446 _session->locations()->clear ();
2447 XMLNode &after = _session->locations()->get_state();
2448 _session->add_command(new MementoCommand<Locations>(*(_session->locations()), &before, &after));
2450 commit_reversible_command ();
2454 Editor::unhide_markers ()
2456 for (LocationMarkerMap::iterator i = location_markers.begin(); i != location_markers.end(); ++i) {
2457 Location *l = (*i).first;
2458 if (l->is_hidden() && l->is_mark()) {
2459 l->set_hidden(false, this);
2465 Editor::unhide_ranges ()
2467 for (LocationMarkerMap::iterator i = location_markers.begin(); i != location_markers.end(); ++i) {
2468 Location *l = (*i).first;
2469 if (l->is_hidden() && l->is_range_marker()) {
2470 l->set_hidden(false, this);
2475 /* INSERT/REPLACE */
2478 Editor::insert_region_list_selection (float times)
2480 RouteTimeAxisView *tv = 0;
2481 boost::shared_ptr<Playlist> playlist;
2483 if (clicked_routeview != 0) {
2484 tv = clicked_routeview;
2485 } else if (!selection->tracks.empty()) {
2486 if ((tv = dynamic_cast<RouteTimeAxisView*>(selection->tracks.front())) == 0) {
2489 } else if (entered_track != 0) {
2490 if ((tv = dynamic_cast<RouteTimeAxisView*>(entered_track)) == 0) {
2497 if ((playlist = tv->playlist()) == 0) {
2501 boost::shared_ptr<Region> region = _regions->get_single_selection ();
2506 begin_reversible_command (_("insert region"));
2507 playlist->clear_changes ();
2508 playlist->add_region ((RegionFactory::create (region, true)), get_preferred_edit_position(), times);
2509 if (Config->get_edit_mode() == Ripple)
2510 playlist->ripple (get_preferred_edit_position(), region->length() * times, boost::shared_ptr<Region>());
2512 _session->add_command(new StatefulDiffCommand (playlist));
2513 commit_reversible_command ();
2516 /* BUILT-IN EFFECTS */
2519 Editor::reverse_selection ()
2524 /* GAIN ENVELOPE EDITING */
2527 Editor::edit_envelope ()
2534 Editor::transition_to_rolling (bool fwd)
2540 if (_session->config.get_external_sync()) {
2541 switch (Config->get_sync_source()) {
2545 /* transport controlled by the master */
2550 if (_session->is_auditioning()) {
2551 _session->cancel_audition ();
2555 _session->request_transport_speed (fwd ? 1.0f : -1.0f);
2559 Editor::play_from_start ()
2561 _session->request_locate (_session->current_start_frame(), true);
2565 Editor::play_from_edit_point ()
2567 _session->request_locate (get_preferred_edit_position(), true);
2571 Editor::play_from_edit_point_and_return ()
2573 framepos_t start_frame;
2574 framepos_t return_frame;
2576 start_frame = get_preferred_edit_position ( EDIT_IGNORE_PHEAD );
2578 if (_session->transport_rolling()) {
2579 _session->request_locate (start_frame, false);
2583 /* don't reset the return frame if its already set */
2585 if ((return_frame = _session->requested_return_frame()) < 0) {
2586 return_frame = _session->audible_frame();
2589 if (start_frame >= 0) {
2590 _session->request_roll_at_and_return (start_frame, return_frame);
2595 Editor::play_selection ()
2597 framepos_t start, end;
2598 if (!get_selection_extents ( start, end))
2601 AudioRange ar (start, end, 0);
2602 list<AudioRange> lar;
2605 _session->request_play_range (&lar, true);
2610 Editor::maybe_locate_with_edit_preroll (framepos_t location)
2612 if ( _session->transport_rolling() || !UIConfiguration::instance().get_follow_edits() || _session->config.get_external_sync() )
2615 location -= _session->preroll_samples (location);
2617 //don't try to locate before the beginning of time
2622 //if follow_playhead is on, keep the playhead on the screen
2623 if ( _follow_playhead )
2624 if ( location < leftmost_frame )
2625 location = leftmost_frame;
2627 _session->request_locate( location );
2631 Editor::play_with_preroll ()
2633 framepos_t start, end;
2634 if ( UIConfiguration::instance().get_follow_edits() && get_selection_extents ( start, end) ) {
2635 const framepos_t preroll = _session->preroll_samples (start);
2637 framepos_t ret = start;
2639 if (start > preroll) {
2640 start = start - preroll;
2643 end = end + preroll; //"post-roll"
2645 AudioRange ar (start, end, 0);
2646 list<AudioRange> lar;
2649 _session->request_play_range (&lar, true);
2650 _session->set_requested_return_frame (ret); //force auto-return to return to range start, without the preroll
2652 framepos_t ph = playhead_cursor->current_frame ();
2653 const framepos_t preroll = _session->preroll_samples (ph);
2656 start = ph - preroll;
2660 _session->request_locate (start, true);
2661 _session->set_requested_return_frame (ph); //force auto-return to return to playhead location, without the preroll
2666 Editor::rec_with_preroll ()
2668 framepos_t ph = playhead_cursor->current_frame ();
2669 framepos_t preroll = _session->preroll_samples (ph);
2670 _session->request_preroll_record_trim (ph, preroll);
2675 Editor::play_location (Location& location)
2677 if (location.start() <= location.end()) {
2681 _session->request_bounded_roll (location.start(), location.end());
2685 Editor::loop_location (Location& location)
2687 if (location.start() <= location.end()) {
2693 if ((tll = transport_loop_location()) != 0) {
2694 tll->set (location.start(), location.end());
2696 // enable looping, reposition and start rolling
2697 _session->request_locate (tll->start(), true);
2698 _session->request_play_loop (true);
2703 Editor::do_layer_operation (LayerOperation op)
2705 if (selection->regions.empty ()) {
2709 bool const multiple = selection->regions.size() > 1;
2713 begin_reversible_command (_("raise regions"));
2715 begin_reversible_command (_("raise region"));
2721 begin_reversible_command (_("raise regions to top"));
2723 begin_reversible_command (_("raise region to top"));
2729 begin_reversible_command (_("lower regions"));
2731 begin_reversible_command (_("lower region"));
2737 begin_reversible_command (_("lower regions to bottom"));
2739 begin_reversible_command (_("lower region"));
2744 set<boost::shared_ptr<Playlist> > playlists = selection->regions.playlists ();
2745 for (set<boost::shared_ptr<Playlist> >::iterator i = playlists.begin(); i != playlists.end(); ++i) {
2746 (*i)->clear_owned_changes ();
2749 for (RegionSelection::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
2750 boost::shared_ptr<Region> r = (*i)->region ();
2762 r->lower_to_bottom ();
2766 for (set<boost::shared_ptr<Playlist> >::iterator i = playlists.begin(); i != playlists.end(); ++i) {
2767 vector<Command*> cmds;
2769 _session->add_commands (cmds);
2772 commit_reversible_command ();
2776 Editor::raise_region ()
2778 do_layer_operation (Raise);
2782 Editor::raise_region_to_top ()
2784 do_layer_operation (RaiseToTop);
2788 Editor::lower_region ()
2790 do_layer_operation (Lower);
2794 Editor::lower_region_to_bottom ()
2796 do_layer_operation (LowerToBottom);
2799 /** Show the region editor for the selected regions */
2801 Editor::show_region_properties ()
2803 selection->foreach_regionview (&RegionView::show_region_editor);
2806 /** Show the midi list editor for the selected MIDI regions */
2808 Editor::show_midi_list_editor ()
2810 selection->foreach_midi_regionview (&MidiRegionView::show_list_editor);
2814 Editor::rename_region ()
2816 RegionSelection rs = get_regions_from_selection_and_entered ();
2822 ArdourDialog d (_("Rename Region"), true, false);
2824 Label label (_("New name:"));
2827 hbox.set_spacing (6);
2828 hbox.pack_start (label, false, false);
2829 hbox.pack_start (entry, true, true);
2831 d.get_vbox()->set_border_width (12);
2832 d.get_vbox()->pack_start (hbox, false, false);
2834 d.add_button(Gtk::Stock::CANCEL, Gtk::RESPONSE_CANCEL);
2835 d.add_button(Gtk::Stock::OK, Gtk::RESPONSE_OK);
2837 d.set_size_request (300, -1);
2839 entry.set_text (rs.front()->region()->name());
2840 entry.select_region (0, -1);
2842 entry.signal_activate().connect (sigc::bind (sigc::mem_fun (d, &Dialog::response), RESPONSE_OK));
2848 int const ret = d.run();
2852 if (ret != RESPONSE_OK) {
2856 std::string str = entry.get_text();
2857 strip_whitespace_edges (str);
2859 rs.front()->region()->set_name (str);
2860 _regions->redisplay ();
2864 /** Start an audition of the first selected region */
2866 Editor::play_edit_range ()
2868 framepos_t start, end;
2870 if (get_edit_op_range (start, end)) {
2871 _session->request_bounded_roll (start, end);
2876 Editor::play_selected_region ()
2878 framepos_t start = max_framepos;
2881 RegionSelection rs = get_regions_from_selection_and_entered ();
2887 for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
2888 if ((*i)->region()->position() < start) {
2889 start = (*i)->region()->position();
2891 if ((*i)->region()->last_frame() + 1 > end) {
2892 end = (*i)->region()->last_frame() + 1;
2896 _session->request_bounded_roll (start, end);
2900 Editor::audition_playlist_region_standalone (boost::shared_ptr<Region> region)
2902 _session->audition_region (region);
2906 Editor::region_from_selection ()
2908 if (clicked_axisview == 0) {
2912 if (selection->time.empty()) {
2916 framepos_t start = selection->time[clicked_selection].start;
2917 framepos_t end = selection->time[clicked_selection].end;
2919 TrackViewList tracks = get_tracks_for_range_action ();
2921 framepos_t selection_cnt = end - start + 1;
2923 for (TrackSelection::iterator i = tracks.begin(); i != tracks.end(); ++i) {
2924 boost::shared_ptr<Region> current;
2925 boost::shared_ptr<Playlist> pl;
2926 framepos_t internal_start;
2929 if ((pl = (*i)->playlist()) == 0) {
2933 if ((current = pl->top_region_at (start)) == 0) {
2937 internal_start = start - current->position();
2938 RegionFactory::region_name (new_name, current->name(), true);
2942 plist.add (ARDOUR::Properties::start, current->start() + internal_start);
2943 plist.add (ARDOUR::Properties::length, selection_cnt);
2944 plist.add (ARDOUR::Properties::name, new_name);
2945 plist.add (ARDOUR::Properties::layer, 0);
2947 boost::shared_ptr<Region> region (RegionFactory::create (current, plist));
2952 Editor::create_region_from_selection (vector<boost::shared_ptr<Region> >& new_regions)
2954 if (selection->time.empty() || selection->tracks.empty()) {
2958 framepos_t start, end;
2959 if (clicked_selection) {
2960 start = selection->time[clicked_selection].start;
2961 end = selection->time[clicked_selection].end;
2963 start = selection->time.start();
2964 end = selection->time.end_frame();
2967 TrackViewList ts = selection->tracks.filter_to_unique_playlists ();
2968 sort_track_selection (ts);
2970 for (TrackSelection::iterator i = ts.begin(); i != ts.end(); ++i) {
2971 boost::shared_ptr<Region> current;
2972 boost::shared_ptr<Playlist> playlist;
2973 framepos_t internal_start;
2976 if ((playlist = (*i)->playlist()) == 0) {
2980 if ((current = playlist->top_region_at(start)) == 0) {
2984 internal_start = start - current->position();
2985 RegionFactory::region_name (new_name, current->name(), true);
2989 plist.add (ARDOUR::Properties::start, current->start() + internal_start);
2990 plist.add (ARDOUR::Properties::length, end - start + 1);
2991 plist.add (ARDOUR::Properties::name, new_name);
2993 new_regions.push_back (RegionFactory::create (current, plist));
2998 Editor::split_multichannel_region ()
3000 RegionSelection rs = get_regions_from_selection_and_entered ();
3006 vector< boost::shared_ptr<Region> > v;
3008 for (list<RegionView*>::iterator x = rs.begin(); x != rs.end(); ++x) {
3009 (*x)->region()->separate_by_channel (*_session, v);
3014 Editor::new_region_from_selection ()
3016 region_from_selection ();
3017 cancel_selection ();
3021 add_if_covered (RegionView* rv, const AudioRange* ar, RegionSelection* rs)
3023 switch (rv->region()->coverage (ar->start, ar->end - 1)) {
3024 // n.b. -1 because AudioRange::end is one past the end, but coverage expects inclusive ranges
3025 case Evoral::OverlapNone:
3033 * - selected tracks, or if there are none...
3034 * - tracks containing selected regions, or if there are none...
3039 Editor::get_tracks_for_range_action () const
3043 if (selection->tracks.empty()) {
3045 /* use tracks with selected regions */
3047 RegionSelection rs = selection->regions;
3049 for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
3050 TimeAxisView* tv = &(*i)->get_time_axis_view();
3052 if (!t.contains (tv)) {
3058 /* no regions and no tracks: use all tracks */
3064 t = selection->tracks;
3067 return t.filter_to_unique_playlists();
3071 Editor::separate_regions_between (const TimeSelection& ts)
3073 bool in_command = false;
3074 boost::shared_ptr<Playlist> playlist;
3075 RegionSelection new_selection;
3077 TrackViewList tmptracks = get_tracks_for_range_action ();
3078 sort_track_selection (tmptracks);
3080 for (TrackSelection::iterator i = tmptracks.begin(); i != tmptracks.end(); ++i) {
3082 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> ((*i));
3088 if (!rtv->is_track()) {
3092 /* no edits to destructive tracks */
3094 if (rtv->track()->destructive()) {
3098 if ((playlist = rtv->playlist()) != 0) {
3100 playlist->clear_changes ();
3102 /* XXX need to consider musical time selections here at some point */
3104 double speed = rtv->track()->speed();
3106 for (list<AudioRange>::const_iterator t = ts.begin(); t != ts.end(); ++t) {
3108 sigc::connection c = rtv->view()->RegionViewAdded.connect (
3109 sigc::mem_fun(*this, &Editor::collect_new_region_view));
3111 latest_regionviews.clear ();
3113 playlist->partition ((framepos_t)((*t).start * speed),
3114 (framepos_t)((*t).end * speed), false);
3118 if (!latest_regionviews.empty()) {
3120 rtv->view()->foreach_regionview (sigc::bind (
3121 sigc::ptr_fun (add_if_covered),
3122 &(*t), &new_selection));
3125 begin_reversible_command (_("separate"));
3129 /* pick up changes to existing regions */
3131 vector<Command*> cmds;
3132 playlist->rdiff (cmds);
3133 _session->add_commands (cmds);
3135 /* pick up changes to the playlist itself (adds/removes)
3138 _session->add_command(new StatefulDiffCommand (playlist));
3145 // selection->set (new_selection);
3147 commit_reversible_command ();
3151 struct PlaylistState {
3152 boost::shared_ptr<Playlist> playlist;
3156 /** Take tracks from get_tracks_for_range_action and cut any regions
3157 * on those tracks so that the tracks are empty over the time
3161 Editor::separate_region_from_selection ()
3163 /* preferentially use *all* ranges in the time selection if we're in range mode
3164 to allow discontiguous operation, since get_edit_op_range() currently
3165 returns a single range.
3168 if (!selection->time.empty()) {
3170 separate_regions_between (selection->time);
3177 if (get_edit_op_range (start, end)) {
3179 AudioRange ar (start, end, 1);
3183 separate_regions_between (ts);
3189 Editor::separate_region_from_punch ()
3191 Location* loc = _session->locations()->auto_punch_location();
3193 separate_regions_using_location (*loc);
3198 Editor::separate_region_from_loop ()
3200 Location* loc = _session->locations()->auto_loop_location();
3202 separate_regions_using_location (*loc);
3207 Editor::separate_regions_using_location (Location& loc)
3209 if (loc.is_mark()) {
3213 AudioRange ar (loc.start(), loc.end(), 1);
3218 separate_regions_between (ts);
3221 /** Separate regions under the selected region */
3223 Editor::separate_under_selected_regions ()
3225 vector<PlaylistState> playlists;
3229 rs = get_regions_from_selection_and_entered();
3231 if (!_session || rs.empty()) {
3235 begin_reversible_command (_("separate region under"));
3237 list<boost::shared_ptr<Region> > regions_to_remove;
3239 for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
3240 // we can't just remove the region(s) in this loop because
3241 // this removes them from the RegionSelection, and they thus
3242 // disappear from underneath the iterator, and the ++i above
3243 // SEGVs in a puzzling fashion.
3245 // so, first iterate over the regions to be removed from rs and
3246 // add them to the regions_to_remove list, and then
3247 // iterate over the list to actually remove them.
3249 regions_to_remove.push_back ((*i)->region());
3252 for (list<boost::shared_ptr<Region> >::iterator rl = regions_to_remove.begin(); rl != regions_to_remove.end(); ++rl) {
3254 boost::shared_ptr<Playlist> playlist = (*rl)->playlist();
3257 // is this check necessary?
3261 vector<PlaylistState>::iterator i;
3263 //only take state if this is a new playlist.
3264 for (i = playlists.begin(); i != playlists.end(); ++i) {
3265 if ((*i).playlist == playlist) {
3270 if (i == playlists.end()) {
3272 PlaylistState before;
3273 before.playlist = playlist;
3274 before.before = &playlist->get_state();
3276 playlist->freeze ();
3277 playlists.push_back(before);
3280 //Partition on the region bounds
3281 playlist->partition ((*rl)->first_frame() - 1, (*rl)->last_frame() + 1, true);
3283 //Re-add region that was just removed due to the partition operation
3284 playlist->add_region( (*rl), (*rl)->first_frame() );
3287 vector<PlaylistState>::iterator pl;
3289 for (pl = playlists.begin(); pl != playlists.end(); ++pl) {
3290 (*pl).playlist->thaw ();
3291 _session->add_command(new MementoCommand<Playlist>(*(*pl).playlist, (*pl).before, &(*pl).playlist->get_state()));
3294 commit_reversible_command ();
3298 Editor::crop_region_to_selection ()
3300 if (!selection->time.empty()) {
3302 crop_region_to (selection->time.start(), selection->time.end_frame());
3309 if (get_edit_op_range (start, end)) {
3310 crop_region_to (start, end);
3317 Editor::crop_region_to (framepos_t start, framepos_t end)
3319 vector<boost::shared_ptr<Playlist> > playlists;
3320 boost::shared_ptr<Playlist> playlist;
3323 if (selection->tracks.empty()) {
3324 ts = track_views.filter_to_unique_playlists();
3326 ts = selection->tracks.filter_to_unique_playlists ();
3329 sort_track_selection (ts);
3331 for (TrackSelection::iterator i = ts.begin(); i != ts.end(); ++i) {
3333 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> ((*i));
3339 boost::shared_ptr<Track> t = rtv->track();
3341 if (t != 0 && ! t->destructive()) {
3343 if ((playlist = rtv->playlist()) != 0) {
3344 playlists.push_back (playlist);
3349 if (playlists.empty()) {
3354 framepos_t new_start;
3356 framecnt_t new_length;
3357 bool in_command = false;
3359 for (vector<boost::shared_ptr<Playlist> >::iterator i = playlists.begin(); i != playlists.end(); ++i) {
3361 /* Only the top regions at start and end have to be cropped */
3362 boost::shared_ptr<Region> region_at_start = (*i)->top_region_at(start);
3363 boost::shared_ptr<Region> region_at_end = (*i)->top_region_at(end);
3365 vector<boost::shared_ptr<Region> > regions;
3367 if (region_at_start != 0) {
3368 regions.push_back (region_at_start);
3370 if (region_at_end != 0) {
3371 regions.push_back (region_at_end);
3374 /* now adjust lengths */
3375 for (vector<boost::shared_ptr<Region> >::iterator i = regions.begin(); i != regions.end(); ++i) {
3377 pos = (*i)->position();
3378 new_start = max (start, pos);
3379 if (max_framepos - pos > (*i)->length()) {
3380 new_end = pos + (*i)->length() - 1;
3382 new_end = max_framepos;
3384 new_end = min (end, new_end);
3385 new_length = new_end - new_start + 1;
3388 begin_reversible_command (_("trim to selection"));
3391 (*i)->clear_changes ();
3392 (*i)->trim_to (new_start, new_length);
3393 _session->add_command (new StatefulDiffCommand (*i));
3398 commit_reversible_command ();
3403 Editor::region_fill_track ()
3405 boost::shared_ptr<Playlist> playlist;
3406 RegionSelection regions = get_regions_from_selection_and_entered ();
3407 RegionSelection foo;
3409 framepos_t const end = _session->current_end_frame ();
3411 if (regions.empty () || regions.end_frame () + 1 >= end) {
3415 framepos_t const start_frame = regions.start ();
3416 framepos_t const end_frame = regions.end_frame ();
3417 framecnt_t const gap = end_frame - start_frame + 1;
3419 begin_reversible_command (Operations::region_fill);
3421 selection->clear_regions ();
3423 for (RegionSelection::iterator i = regions.begin(); i != regions.end(); ++i) {
3425 boost::shared_ptr<Region> r ((*i)->region());
3427 TimeAxisView& tv = (*i)->get_time_axis_view();
3428 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (&tv);
3429 latest_regionviews.clear ();
3430 sigc::connection c = rtv->view()->RegionViewAdded.connect (sigc::mem_fun(*this, &Editor::collect_new_region_view));
3432 framepos_t const position = end_frame + (r->first_frame() - start_frame + 1);
3433 playlist = (*i)->region()->playlist();
3434 playlist->clear_changes ();
3435 playlist->duplicate_until (r, position, gap, end);
3436 _session->add_command(new StatefulDiffCommand (playlist));
3440 foo.insert (foo.end(), latest_regionviews.begin(), latest_regionviews.end());
3444 selection->set (foo);
3447 commit_reversible_command ();
3451 Editor::set_region_sync_position ()
3453 set_sync_point (get_preferred_edit_position (), get_regions_from_selection_and_edit_point ());
3457 Editor::set_sync_point (framepos_t where, const RegionSelection& rs)
3459 bool in_command = false;
3461 for (RegionSelection::const_iterator r = rs.begin(); r != rs.end(); ++r) {
3463 if (!(*r)->region()->covers (where)) {
3467 boost::shared_ptr<Region> region ((*r)->region());
3470 begin_reversible_command (_("set sync point"));
3474 region->clear_changes ();
3475 region->set_sync_position (where);
3476 _session->add_command(new StatefulDiffCommand (region));
3480 commit_reversible_command ();
3484 /** Remove the sync positions of the selection */
3486 Editor::remove_region_sync ()
3488 RegionSelection rs = get_regions_from_selection_and_entered ();
3494 begin_reversible_command (_("remove region sync"));
3496 for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
3498 (*i)->region()->clear_changes ();
3499 (*i)->region()->clear_sync_position ();
3500 _session->add_command(new StatefulDiffCommand ((*i)->region()));
3503 commit_reversible_command ();
3507 Editor::naturalize_region ()
3509 RegionSelection rs = get_regions_from_selection_and_entered ();
3515 if (rs.size() > 1) {
3516 begin_reversible_command (_("move regions to original position"));
3518 begin_reversible_command (_("move region to original position"));
3521 for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
3522 (*i)->region()->clear_changes ();
3523 (*i)->region()->move_to_natural_position ();
3524 _session->add_command (new StatefulDiffCommand ((*i)->region()));
3527 commit_reversible_command ();
3531 Editor::align_regions (RegionPoint what)
3533 RegionSelection const rs = get_regions_from_selection_and_edit_point ();
3539 begin_reversible_command (_("align selection"));
3541 framepos_t const position = get_preferred_edit_position ();
3543 for (RegionSelection::const_iterator i = rs.begin(); i != rs.end(); ++i) {
3544 align_region_internal ((*i)->region(), what, position);
3547 commit_reversible_command ();
3550 struct RegionSortByTime {
3551 bool operator() (const RegionView* a, const RegionView* b) {
3552 return a->region()->position() < b->region()->position();
3557 Editor::align_regions_relative (RegionPoint point)
3559 RegionSelection const rs = get_regions_from_selection_and_edit_point ();
3565 framepos_t const position = get_preferred_edit_position ();
3567 framepos_t distance = 0;
3571 list<RegionView*> sorted;
3572 rs.by_position (sorted);
3574 boost::shared_ptr<Region> r ((*sorted.begin())->region());
3579 if (position > r->position()) {
3580 distance = position - r->position();
3582 distance = r->position() - position;
3588 if (position > r->last_frame()) {
3589 distance = position - r->last_frame();
3590 pos = r->position() + distance;
3592 distance = r->last_frame() - position;
3593 pos = r->position() - distance;
3599 pos = r->adjust_to_sync (position);
3600 if (pos > r->position()) {
3601 distance = pos - r->position();
3603 distance = r->position() - pos;
3609 if (pos == r->position()) {
3613 begin_reversible_command (_("align selection (relative)"));
3615 /* move first one specially */
3617 r->clear_changes ();
3618 r->set_position (pos);
3619 _session->add_command(new StatefulDiffCommand (r));
3621 /* move rest by the same amount */
3625 for (list<RegionView*>::iterator i = sorted.begin(); i != sorted.end(); ++i) {
3627 boost::shared_ptr<Region> region ((*i)->region());
3629 region->clear_changes ();
3632 region->set_position (region->position() + distance);
3634 region->set_position (region->position() - distance);
3637 _session->add_command(new StatefulDiffCommand (region));
3641 commit_reversible_command ();
3645 Editor::align_region (boost::shared_ptr<Region> region, RegionPoint point, framepos_t position)
3647 begin_reversible_command (_("align region"));
3648 align_region_internal (region, point, position);
3649 commit_reversible_command ();
3653 Editor::align_region_internal (boost::shared_ptr<Region> region, RegionPoint point, framepos_t position)
3655 region->clear_changes ();
3659 region->set_position (region->adjust_to_sync (position));
3663 if (position > region->length()) {
3664 region->set_position (position - region->length());
3669 region->set_position (position);
3673 _session->add_command(new StatefulDiffCommand (region));
3677 Editor::trim_region_front ()
3683 Editor::trim_region_back ()
3685 trim_region (false);
3689 Editor::trim_region (bool front)
3691 framepos_t where = get_preferred_edit_position();
3692 RegionSelection rs = get_regions_from_selection_and_edit_point ();
3698 begin_reversible_command (front ? _("trim front") : _("trim back"));
3700 for (list<RegionView*>::const_iterator i = rs.by_layer().begin(); i != rs.by_layer().end(); ++i) {
3701 if (!(*i)->region()->locked()) {
3703 (*i)->region()->clear_changes ();
3706 (*i)->region()->trim_front (where);
3708 (*i)->region()->trim_end (where);
3711 _session->add_command (new StatefulDiffCommand ((*i)->region()));
3715 commit_reversible_command ();
3718 /** Trim the end of the selected regions to the position of the edit cursor */
3720 Editor::trim_region_to_loop ()
3722 Location* loc = _session->locations()->auto_loop_location();
3726 trim_region_to_location (*loc, _("trim to loop"));
3730 Editor::trim_region_to_punch ()
3732 Location* loc = _session->locations()->auto_punch_location();
3736 trim_region_to_location (*loc, _("trim to punch"));
3740 Editor::trim_region_to_location (const Location& loc, const char* str)
3742 RegionSelection rs = get_regions_from_selection_and_entered ();
3743 bool in_command = false;
3745 for (RegionSelection::iterator x = rs.begin(); x != rs.end(); ++x) {
3746 RegionView* rv = (*x);
3748 /* require region to span proposed trim */
3749 switch (rv->region()->coverage (loc.start(), loc.end())) {
3750 case Evoral::OverlapInternal:
3756 RouteTimeAxisView* tav = dynamic_cast<RouteTimeAxisView*> (&rv->get_time_axis_view());
3765 if (tav->track() != 0) {
3766 speed = tav->track()->speed();
3769 start = session_frame_to_track_frame (loc.start(), speed);
3770 end = session_frame_to_track_frame (loc.end(), speed);
3772 rv->region()->clear_changes ();
3773 rv->region()->trim_to (start, (end - start));
3776 begin_reversible_command (str);
3779 _session->add_command(new StatefulDiffCommand (rv->region()));
3783 commit_reversible_command ();
3788 Editor::trim_region_to_previous_region_end ()
3790 return trim_to_region(false);
3794 Editor::trim_region_to_next_region_start ()
3796 return trim_to_region(true);
3800 Editor::trim_to_region(bool forward)
3802 RegionSelection rs = get_regions_from_selection_and_entered ();
3803 bool in_command = false;
3805 boost::shared_ptr<Region> next_region;
3807 for (RegionSelection::iterator x = rs.begin(); x != rs.end(); ++x) {
3809 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (*x);
3815 AudioTimeAxisView* atav = dynamic_cast<AudioTimeAxisView*> (&arv->get_time_axis_view());
3823 if (atav->track() != 0) {
3824 speed = atav->track()->speed();
3828 boost::shared_ptr<Region> region = arv->region();
3829 boost::shared_ptr<Playlist> playlist (region->playlist());
3831 region->clear_changes ();
3835 next_region = playlist->find_next_region (region->first_frame(), Start, 1);
3841 region->trim_end((framepos_t) ( (next_region->first_frame() - 1) * speed));
3842 arv->region_changed (PropertyChange (ARDOUR::Properties::length));
3846 next_region = playlist->find_next_region (region->first_frame(), Start, 0);
3852 region->trim_front((framepos_t) ((next_region->last_frame() + 1) * speed));
3854 arv->region_changed (ARDOUR::bounds_change);
3858 begin_reversible_command (_("trim to region"));
3861 _session->add_command(new StatefulDiffCommand (region));
3865 commit_reversible_command ();
3870 Editor::unfreeze_route ()
3872 if (clicked_routeview == 0 || !clicked_routeview->is_track()) {
3876 clicked_routeview->track()->unfreeze ();
3880 Editor::_freeze_thread (void* arg)
3882 return static_cast<Editor*>(arg)->freeze_thread ();
3886 Editor::freeze_thread ()
3888 /* create event pool because we may need to talk to the session */
3889 SessionEvent::create_per_thread_pool ("freeze events", 64);
3890 /* create per-thread buffers for process() tree to use */
3891 clicked_routeview->audio_track()->freeze_me (*current_interthread_info);
3892 current_interthread_info->done = true;
3897 Editor::freeze_route ()
3903 /* stop transport before we start. this is important */
3905 _session->request_transport_speed (0.0);
3907 /* wait for just a little while, because the above call is asynchronous */
3909 Glib::usleep (250000);
3911 if (clicked_routeview == 0 || !clicked_routeview->is_audio_track()) {
3915 if (!clicked_routeview->track()->bounceable (clicked_routeview->track()->main_outs(), true)) {
3917 _("This track/bus cannot be frozen because the signal adds or loses channels before reaching the outputs.\n"
3918 "This is typically caused by plugins that generate stereo output from mono input or vice versa.")
3920 d.set_title (_("Cannot freeze"));
3925 if (clicked_routeview->track()->has_external_redirects()) {
3926 MessageDialog d (string_compose (_("<b>%1</b>\n\nThis track has at least one send/insert/return as part of its signal flow.\n\n"
3927 "Freezing will only process the signal as far as the first send/insert/return."),
3928 clicked_routeview->track()->name()), true, MESSAGE_INFO, BUTTONS_NONE, true);
3930 d.add_button (_("Freeze anyway"), Gtk::RESPONSE_OK);
3931 d.add_button (_("Don't freeze"), Gtk::RESPONSE_CANCEL);
3932 d.set_title (_("Freeze Limits"));
3934 int response = d.run ();
3937 case Gtk::RESPONSE_CANCEL:
3944 InterThreadInfo itt;
3945 current_interthread_info = &itt;
3947 InterthreadProgressWindow ipw (current_interthread_info, _("Freeze"), _("Cancel Freeze"));
3949 pthread_create_and_store (X_("freezer"), &itt.thread, _freeze_thread, this);
3951 CursorContext::Handle cursor_ctx = CursorContext::create(*this, _cursors->wait);
3953 while (!itt.done && !itt.cancel) {
3954 gtk_main_iteration ();
3957 pthread_join (itt.thread, 0);
3958 current_interthread_info = 0;
3962 Editor::bounce_range_selection (bool replace, bool enable_processing)
3964 if (selection->time.empty()) {
3968 TrackSelection views = selection->tracks;
3970 for (TrackViewList::iterator i = views.begin(); i != views.end(); ++i) {
3972 if (enable_processing) {
3974 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (*i);
3976 if (rtv && rtv->track() && replace && enable_processing && !rtv->track()->bounceable (rtv->track()->main_outs(), false)) {
3978 _("You can't perform this operation because the processing of the signal "
3979 "will cause one or more of the tracks to end up with a region with more channels than this track has inputs.\n\n"
3980 "You can do this without processing, which is a different operation.")
3982 d.set_title (_("Cannot bounce"));
3989 framepos_t start = selection->time[clicked_selection].start;
3990 framepos_t end = selection->time[clicked_selection].end;
3991 framepos_t cnt = end - start + 1;
3992 bool in_command = false;
3994 for (TrackViewList::iterator i = views.begin(); i != views.end(); ++i) {
3996 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (*i);
4002 boost::shared_ptr<Playlist> playlist;
4004 if ((playlist = rtv->playlist()) == 0) {
4008 InterThreadInfo itt;
4010 playlist->clear_changes ();
4011 playlist->clear_owned_changes ();
4013 boost::shared_ptr<Region> r;
4015 if (enable_processing) {
4016 r = rtv->track()->bounce_range (start, start+cnt, itt, rtv->track()->main_outs(), false);
4018 r = rtv->track()->bounce_range (start, start+cnt, itt, boost::shared_ptr<Processor>(), false);
4026 list<AudioRange> ranges;
4027 ranges.push_back (AudioRange (start, start+cnt, 0));
4028 playlist->cut (ranges); // discard result
4029 playlist->add_region (r, start);
4033 begin_reversible_command (_("bounce range"));
4036 vector<Command*> cmds;
4037 playlist->rdiff (cmds);
4038 _session->add_commands (cmds);
4040 _session->add_command (new StatefulDiffCommand (playlist));
4044 commit_reversible_command ();
4048 /** Delete selected regions, automation points or a time range */
4052 //special case: if the user is pointing in the editor/mixer strip, they may be trying to delete a plugin.
4053 //we need this because the editor-mixer strip is in the editor window, so it doesn't get the bindings from the mix window
4054 bool deleted = false;
4055 if ( current_mixer_strip && current_mixer_strip == MixerStrip::entered_mixer_strip() )
4056 deleted = current_mixer_strip->delete_processors ();
4062 /** Cut selected regions, automation points or a time range */
4069 /** Copy selected regions, automation points or a time range */
4077 /** @return true if a Cut, Copy or Clear is possible */
4079 Editor::can_cut_copy () const
4081 if (!selection->time.empty() || !selection->regions.empty() || !selection->points.empty())
4088 /** Cut, copy or clear selected regions, automation points or a time range.
4089 * @param op Operation (Delete, Cut, Copy or Clear)
4092 Editor::cut_copy (CutCopyOp op)
4094 /* only cancel selection if cut/copy is successful.*/
4100 opname = _("delete");
4109 opname = _("clear");
4113 /* if we're deleting something, and the mouse is still pressed,
4114 the thing we started a drag for will be gone when we release
4115 the mouse button(s). avoid this. see part 2 at the end of
4119 if (op == Delete || op == Cut || op == Clear) {
4120 if (_drags->active ()) {
4125 if ( op != Delete ) //"Delete" doesn't change copy/paste buf
4126 cut_buffer->clear ();
4128 if (entered_marker) {
4130 /* cut/delete op while pointing at a marker */
4133 Location* loc = find_location_from_marker (entered_marker, ignored);
4135 if (_session && loc) {
4136 entered_marker = NULL;
4137 Glib::signal_idle().connect (sigc::bind (sigc::mem_fun(*this, &Editor::really_remove_marker), loc));
4144 switch (mouse_mode) {
4147 begin_reversible_command (opname + ' ' + X_("MIDI"));
4149 commit_reversible_command ();
4155 bool did_edit = false;
4157 if (!selection->regions.empty() || !selection->points.empty()) {
4158 begin_reversible_command (opname + ' ' + _("objects"));
4161 if (!selection->regions.empty()) {
4162 cut_copy_regions (op, selection->regions);
4164 if (op == Cut || op == Delete) {
4165 selection->clear_regions ();
4169 if (!selection->points.empty()) {
4170 cut_copy_points (op);
4172 if (op == Cut || op == Delete) {
4173 selection->clear_points ();
4176 } else if (selection->time.empty()) {
4177 framepos_t start, end;
4178 /* no time selection, see if we can get an edit range
4181 if (get_edit_op_range (start, end)) {
4182 selection->set (start, end);
4184 } else if (!selection->time.empty()) {
4185 begin_reversible_command (opname + ' ' + _("range"));
4188 cut_copy_ranges (op);
4190 if (op == Cut || op == Delete) {
4191 selection->clear_time ();
4196 /* reset repeated paste state */
4199 commit_reversible_command ();
4202 if (op == Delete || op == Cut || op == Clear) {
4208 struct AutomationRecord {
4209 AutomationRecord () : state (0) , line(NULL) {}
4210 AutomationRecord (XMLNode* s, const AutomationLine* l) : state (s) , line (l) {}
4212 XMLNode* state; ///< state before any operation
4213 const AutomationLine* line; ///< line this came from
4214 boost::shared_ptr<Evoral::ControlList> copy; ///< copied events for the cut buffer
4216 struct PointsSelectionPositionSorter {
4217 bool operator() (ControlPoint* a, ControlPoint* b) {
4218 return (*(a->model()))->when < (*(b->model()))->when;
4221 /** Cut, copy or clear selected automation points.
4222 * @param op Operation (Cut, Copy or Clear)
4225 Editor::cut_copy_points (Editing::CutCopyOp op, Evoral::Beats earliest, bool midi)
4227 if (selection->points.empty ()) {
4231 /* XXX: not ideal, as there may be more than one track involved in the point selection */
4232 _last_cut_copy_source_track = &selection->points.front()->line().trackview;
4234 /* Keep a record of the AutomationLists that we end up using in this operation */
4235 typedef std::map<boost::shared_ptr<AutomationList>, AutomationRecord> Lists;
4238 /* user could select points in any order */
4239 selection->points.sort(PointsSelectionPositionSorter ());
4241 /* Go through all selected points, making an AutomationRecord for each distinct AutomationList */
4242 for (PointSelection::iterator sel_point = selection->points.begin(); sel_point != selection->points.end(); ++sel_point) {
4243 const AutomationLine& line = (*sel_point)->line();
4244 const boost::shared_ptr<AutomationList> al = line.the_list();
4245 if (lists.find (al) == lists.end ()) {
4246 /* We haven't seen this list yet, so make a record for it. This includes
4247 taking a copy of its current state, in case this is needed for undo later.
4249 lists[al] = AutomationRecord (&al->get_state (), &line);
4253 if (op == Cut || op == Copy) {
4254 /* This operation will involve putting things in the cut buffer, so create an empty
4255 ControlList for each of our source lists to put the cut buffer data in.
4257 for (Lists::iterator i = lists.begin(); i != lists.end(); ++i) {
4258 i->second.copy = i->first->create (i->first->parameter (), i->first->descriptor());
4261 /* Add all selected points to the relevant copy ControlLists */
4262 MusicFrame start (std::numeric_limits<framepos_t>::max(), 0);
4263 for (PointSelection::iterator sel_point = selection->points.begin(); sel_point != selection->points.end(); ++sel_point) {
4264 boost::shared_ptr<AutomationList> al = (*sel_point)->line().the_list();
4265 AutomationList::const_iterator ctrl_evt = (*sel_point)->model ();
4267 lists[al].copy->fast_simple_add ((*ctrl_evt)->when, (*ctrl_evt)->value);
4269 /* Update earliest MIDI start time in beats */
4270 earliest = std::min(earliest, Evoral::Beats((*ctrl_evt)->when));
4272 /* Update earliest session start time in frames */
4273 start.frame = std::min(start.frame, (*sel_point)->line().session_position(ctrl_evt));
4277 /* Snap start time backwards, so copy/paste is snap aligned. */
4279 if (earliest == Evoral::Beats::max()) {
4280 earliest = Evoral::Beats(); // Weird... don't offset
4282 earliest.round_down_to_beat();
4284 if (start.frame == std::numeric_limits<double>::max()) {
4285 start.frame = 0; // Weird... don't offset
4287 snap_to(start, RoundDownMaybe);
4290 const double line_offset = midi ? earliest.to_double() : start.frame;
4291 for (Lists::iterator i = lists.begin(); i != lists.end(); ++i) {
4292 /* Correct this copy list so that it is relative to the earliest
4293 start time, so relative ordering between points is preserved
4294 when copying from several lists and the paste starts at the
4295 earliest copied piece of data. */
4296 boost::shared_ptr<Evoral::ControlList> &al_cpy = i->second.copy;
4297 for (AutomationList::iterator ctrl_evt = al_cpy->begin(); ctrl_evt != al_cpy->end(); ++ctrl_evt) {
4298 (*ctrl_evt)->when -= line_offset;
4301 /* And add it to the cut buffer */
4302 cut_buffer->add (al_cpy);
4306 if (op == Delete || op == Cut) {
4307 /* This operation needs to remove things from the main AutomationList, so do that now */
4309 for (Lists::iterator i = lists.begin(); i != lists.end(); ++i) {
4310 i->first->freeze ();
4313 /* Remove each selected point from its AutomationList */
4314 for (PointSelection::iterator sel_point = selection->points.begin(); sel_point != selection->points.end(); ++sel_point) {
4315 AutomationLine& line = (*sel_point)->line ();
4316 boost::shared_ptr<AutomationList> al = line.the_list();
4320 if (dynamic_cast<AudioRegionGainLine*> (&line)) {
4321 /* removing of first and last gain point in region gain lines is prohibited*/
4322 if (line.is_last_point (*(*sel_point)) || line.is_first_point (*(*sel_point))) {
4328 al->erase ((*sel_point)->model ());
4332 /* Thaw the lists and add undo records for them */
4333 for (Lists::iterator i = lists.begin(); i != lists.end(); ++i) {
4334 boost::shared_ptr<AutomationList> al = i->first;
4336 _session->add_command (new MementoCommand<AutomationList> (*al.get(), i->second.state, &(al->get_state ())));
4341 /** Cut, copy or clear selected automation points.
4342 * @param op Operation (Cut, Copy or Clear)
4345 Editor::cut_copy_midi (CutCopyOp op)
4347 Evoral::Beats earliest = Evoral::Beats::max();
4348 for (MidiRegionSelection::iterator i = selection->midi_regions.begin(); i != selection->midi_regions.end(); ++i) {
4349 MidiRegionView* mrv = dynamic_cast<MidiRegionView*>(*i);
4351 if (!mrv->selection().empty()) {
4352 earliest = std::min(earliest, (*mrv->selection().begin())->note()->time());
4354 mrv->cut_copy_clear (op);
4356 /* XXX: not ideal, as there may be more than one track involved in the selection */
4357 _last_cut_copy_source_track = &mrv->get_time_axis_view();
4361 if (!selection->points.empty()) {
4362 cut_copy_points (op, earliest, true);
4363 if (op == Cut || op == Delete) {
4364 selection->clear_points ();
4369 struct lt_playlist {
4370 bool operator () (const PlaylistState& a, const PlaylistState& b) {
4371 return a.playlist < b.playlist;
4375 struct PlaylistMapping {
4377 boost::shared_ptr<Playlist> pl;
4379 PlaylistMapping (TimeAxisView* tvp) : tv (tvp) {}
4382 /** Remove `clicked_regionview' */
4384 Editor::remove_clicked_region ()
4386 if (clicked_routeview == 0 || clicked_regionview == 0) {
4390 begin_reversible_command (_("remove region"));
4392 boost::shared_ptr<Playlist> playlist = clicked_routeview->playlist();
4394 playlist->clear_changes ();
4395 playlist->clear_owned_changes ();
4396 playlist->remove_region (clicked_regionview->region());
4397 if (Config->get_edit_mode() == Ripple)
4398 playlist->ripple (clicked_regionview->region()->position(), -clicked_regionview->region()->length(), boost::shared_ptr<Region>());
4400 /* We might have removed regions, which alters other regions' layering_index,
4401 so we need to do a recursive diff here.
4403 vector<Command*> cmds;
4404 playlist->rdiff (cmds);
4405 _session->add_commands (cmds);
4407 _session->add_command(new StatefulDiffCommand (playlist));
4408 commit_reversible_command ();
4412 /** Remove the selected regions */
4414 Editor::remove_selected_regions ()
4416 RegionSelection rs = get_regions_from_selection_and_entered ();
4418 if (!_session || rs.empty()) {
4422 list<boost::shared_ptr<Region> > regions_to_remove;
4424 for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
4425 // we can't just remove the region(s) in this loop because
4426 // this removes them from the RegionSelection, and they thus
4427 // disappear from underneath the iterator, and the ++i above
4428 // SEGVs in a puzzling fashion.
4430 // so, first iterate over the regions to be removed from rs and
4431 // add them to the regions_to_remove list, and then
4432 // iterate over the list to actually remove them.
4434 regions_to_remove.push_back ((*i)->region());
4437 vector<boost::shared_ptr<Playlist> > playlists;
4439 for (list<boost::shared_ptr<Region> >::iterator rl = regions_to_remove.begin(); rl != regions_to_remove.end(); ++rl) {
4441 boost::shared_ptr<Playlist> playlist = (*rl)->playlist();
4444 // is this check necessary?
4448 /* get_regions_from_selection_and_entered() guarantees that
4449 the playlists involved are unique, so there is no need
4453 playlists.push_back (playlist);
4455 playlist->clear_changes ();
4456 playlist->clear_owned_changes ();
4457 playlist->freeze ();
4458 playlist->remove_region (*rl);
4459 if (Config->get_edit_mode() == Ripple)
4460 playlist->ripple ((*rl)->position(), -(*rl)->length(), boost::shared_ptr<Region>());
4464 vector<boost::shared_ptr<Playlist> >::iterator pl;
4465 bool in_command = false;
4467 for (pl = playlists.begin(); pl != playlists.end(); ++pl) {
4470 /* We might have removed regions, which alters other regions' layering_index,
4471 so we need to do a recursive diff here.
4475 begin_reversible_command (_("remove region"));
4478 vector<Command*> cmds;
4479 (*pl)->rdiff (cmds);
4480 _session->add_commands (cmds);
4482 _session->add_command(new StatefulDiffCommand (*pl));
4486 commit_reversible_command ();
4490 /** Cut, copy or clear selected regions.
4491 * @param op Operation (Cut, Copy or Clear)
4494 Editor::cut_copy_regions (CutCopyOp op, RegionSelection& rs)
4496 /* we can't use a std::map here because the ordering is important, and we can't trivially sort
4497 a map when we want ordered access to both elements. i think.
4500 vector<PlaylistMapping> pmap;
4502 framepos_t first_position = max_framepos;
4504 typedef set<boost::shared_ptr<Playlist> > FreezeList;
4505 FreezeList freezelist;
4507 /* get ordering correct before we cut/copy */
4509 rs.sort_by_position_and_track ();
4511 for (RegionSelection::iterator x = rs.begin(); x != rs.end(); ++x) {
4513 first_position = min ((framepos_t) (*x)->region()->position(), first_position);
4515 if (op == Cut || op == Clear || op == Delete) {
4516 boost::shared_ptr<Playlist> pl = (*x)->region()->playlist();
4519 FreezeList::iterator fl;
4521 // only take state if this is a new playlist.
4522 for (fl = freezelist.begin(); fl != freezelist.end(); ++fl) {
4528 if (fl == freezelist.end()) {
4529 pl->clear_changes();
4530 pl->clear_owned_changes ();
4532 freezelist.insert (pl);
4537 TimeAxisView* tv = &(*x)->get_time_axis_view();
4538 vector<PlaylistMapping>::iterator z;
4540 for (z = pmap.begin(); z != pmap.end(); ++z) {
4541 if ((*z).tv == tv) {
4546 if (z == pmap.end()) {
4547 pmap.push_back (PlaylistMapping (tv));
4551 for (RegionSelection::iterator x = rs.begin(); x != rs.end(); ) {
4553 boost::shared_ptr<Playlist> pl = (*x)->region()->playlist();
4556 /* region not yet associated with a playlist (e.g. unfinished
4563 TimeAxisView& tv = (*x)->get_time_axis_view();
4564 boost::shared_ptr<Playlist> npl;
4565 RegionSelection::iterator tmp;
4572 vector<PlaylistMapping>::iterator z;
4574 for (z = pmap.begin(); z != pmap.end(); ++z) {
4575 if ((*z).tv == &tv) {
4580 assert (z != pmap.end());
4583 npl = PlaylistFactory::create (pl->data_type(), *_session, "cutlist", true);
4591 boost::shared_ptr<Region> r = (*x)->region();
4592 boost::shared_ptr<Region> _xx;
4598 pl->remove_region (r);
4599 if (Config->get_edit_mode() == Ripple)
4600 pl->ripple (r->position(), -r->length(), boost::shared_ptr<Region>());
4604 _xx = RegionFactory::create (r);
4605 npl->add_region (_xx, r->position() - first_position);
4606 pl->remove_region (r);
4607 if (Config->get_edit_mode() == Ripple)
4608 pl->ripple (r->position(), -r->length(), boost::shared_ptr<Region>());
4612 /* copy region before adding, so we're not putting same object into two different playlists */
4613 npl->add_region (RegionFactory::create (r), r->position() - first_position);
4617 pl->remove_region (r);
4618 if (Config->get_edit_mode() == Ripple)
4619 pl->ripple (r->position(), -r->length(), boost::shared_ptr<Region>());
4628 list<boost::shared_ptr<Playlist> > foo;
4630 /* the pmap is in the same order as the tracks in which selected regions occurred */
4632 for (vector<PlaylistMapping>::iterator i = pmap.begin(); i != pmap.end(); ++i) {
4635 foo.push_back ((*i).pl);
4640 cut_buffer->set (foo);
4644 _last_cut_copy_source_track = 0;
4646 _last_cut_copy_source_track = pmap.front().tv;
4650 for (FreezeList::iterator pl = freezelist.begin(); pl != freezelist.end(); ++pl) {
4653 /* We might have removed regions, which alters other regions' layering_index,
4654 so we need to do a recursive diff here.
4656 vector<Command*> cmds;
4657 (*pl)->rdiff (cmds);
4658 _session->add_commands (cmds);
4660 _session->add_command (new StatefulDiffCommand (*pl));
4665 Editor::cut_copy_ranges (CutCopyOp op)
4667 TrackViewList ts = selection->tracks.filter_to_unique_playlists ();
4669 /* Sort the track selection now, so that it if is used, the playlists
4670 selected by the calls below to cut_copy_clear are in the order that
4671 their tracks appear in the editor. This makes things like paste
4672 of ranges work properly.
4675 sort_track_selection (ts);
4678 if (!entered_track) {
4681 ts.push_back (entered_track);
4684 for (TrackViewList::iterator i = ts.begin(); i != ts.end(); ++i) {
4685 (*i)->cut_copy_clear (*selection, op);
4690 Editor::paste (float times, bool from_context)
4692 DEBUG_TRACE (DEBUG::CutNPaste, "paste to preferred edit pos\n");
4693 MusicFrame where (get_preferred_edit_position (EDIT_IGNORE_NONE, from_context), 0);
4694 paste_internal (where.frame, times, 0);
4698 Editor::mouse_paste ()
4700 MusicFrame where (0, 0);
4702 if (!mouse_frame (where.frame, ignored)) {
4707 paste_internal (where.frame, 1, where.division);
4711 Editor::paste_internal (framepos_t position, float times, const int32_t sub_num)
4713 DEBUG_TRACE (DEBUG::CutNPaste, string_compose ("apparent paste position is %1\n", position));
4715 if (cut_buffer->empty(internal_editing())) {
4719 if (position == max_framepos) {
4720 position = get_preferred_edit_position();
4721 DEBUG_TRACE (DEBUG::CutNPaste, string_compose ("preferred edit position is %1\n", position));
4724 if (position == last_paste_pos) {
4725 /* repeated paste in the same position */
4728 /* paste in new location, reset repeated paste state */
4730 last_paste_pos = position;
4733 /* get everything in the correct order */
4736 if (!selection->tracks.empty()) {
4737 /* If there is a track selection, paste into exactly those tracks and
4738 only those tracks. This allows the user to be explicit and override
4739 the below "do the reasonable thing" logic. */
4740 ts = selection->tracks.filter_to_unique_playlists ();
4741 sort_track_selection (ts);
4743 /* Figure out which track to base the paste at. */
4744 TimeAxisView* base_track = NULL;
4745 if (_edit_point == Editing::EditAtMouse && entered_track) {
4746 /* With the mouse edit point, paste onto the track under the mouse. */
4747 base_track = entered_track;
4748 } else if (_edit_point == Editing::EditAtMouse && entered_regionview) {
4749 /* With the mouse edit point, paste onto the track of the region under the mouse. */
4750 base_track = &entered_regionview->get_time_axis_view();
4751 } else if (_last_cut_copy_source_track) {
4752 /* Paste to the track that the cut/copy came from (see mantis #333). */
4753 base_track = _last_cut_copy_source_track;
4755 /* This is "impossible" since we've copied... well, do nothing. */
4759 /* Walk up to parent if necessary, so base track is a route. */
4760 while (base_track->get_parent()) {
4761 base_track = base_track->get_parent();
4764 /* Add base track and all tracks below it. The paste logic will select
4765 the appropriate object types from the cut buffer in relative order. */
4766 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
4767 if ((*i)->order() >= base_track->order()) {
4772 /* Sort tracks so the nth track of type T will pick the nth object of type T. */
4773 sort_track_selection (ts);
4775 /* Add automation children of each track in order, for pasting several lines. */
4776 for (TrackViewList::iterator i = ts.begin(); i != ts.end();) {
4777 /* Add any automation children for pasting several lines */
4778 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*>(*i++);
4783 typedef RouteTimeAxisView::AutomationTracks ATracks;
4784 const ATracks& atracks = rtv->automation_tracks();
4785 for (ATracks::const_iterator a = atracks.begin(); a != atracks.end(); ++a) {
4786 i = ts.insert(i, a->second.get());
4791 /* We now have a list of trackviews starting at base_track, including
4792 automation children, in the order shown in the editor, e.g. R1,
4793 R1.A1, R1.A2, R2, R2.A1, ... */
4796 begin_reversible_command (Operations::paste);
4798 if (ts.size() == 1 && cut_buffer->lines.size() == 1 &&
4799 dynamic_cast<AutomationTimeAxisView*>(ts.front())) {
4800 /* Only one line copied, and one automation track selected. Do a
4801 "greedy" paste from one automation type to another. */
4803 PasteContext ctx(paste_count, times, ItemCounts(), true);
4804 ts.front()->paste (position, *cut_buffer, ctx, sub_num);
4808 /* Paste into tracks */
4810 PasteContext ctx(paste_count, times, ItemCounts(), false);
4811 for (TrackViewList::iterator i = ts.begin(); i != ts.end(); ++i) {
4812 (*i)->paste (position, *cut_buffer, ctx, sub_num);
4816 commit_reversible_command ();
4820 Editor::duplicate_regions (float times)
4822 RegionSelection rs (get_regions_from_selection_and_entered());
4823 duplicate_some_regions (rs, times);
4827 Editor::duplicate_some_regions (RegionSelection& regions, float times)
4829 if (regions.empty ()) {
4833 boost::shared_ptr<Playlist> playlist;
4834 RegionSelection sel = regions; // clear (below) may clear the argument list if its the current region selection
4835 RegionSelection foo;
4837 framepos_t const start_frame = regions.start ();
4838 framepos_t const end_frame = regions.end_frame ();
4839 framecnt_t const gap = end_frame - start_frame + 1;
4841 begin_reversible_command (Operations::duplicate_region);
4843 selection->clear_regions ();
4845 for (RegionSelection::iterator i = sel.begin(); i != sel.end(); ++i) {
4847 boost::shared_ptr<Region> r ((*i)->region());
4849 TimeAxisView& tv = (*i)->get_time_axis_view();
4850 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (&tv);
4851 latest_regionviews.clear ();
4852 sigc::connection c = rtv->view()->RegionViewAdded.connect (sigc::mem_fun(*this, &Editor::collect_new_region_view));
4854 framepos_t const position = end_frame + (r->first_frame() - start_frame + 1);
4855 playlist = (*i)->region()->playlist();
4856 playlist->clear_changes ();
4857 playlist->duplicate (r, position, gap, times);
4858 _session->add_command(new StatefulDiffCommand (playlist));
4862 foo.insert (foo.end(), latest_regionviews.begin(), latest_regionviews.end());
4866 selection->set (foo);
4869 commit_reversible_command ();
4873 Editor::duplicate_selection (float times)
4875 if (selection->time.empty() || selection->tracks.empty()) {
4879 boost::shared_ptr<Playlist> playlist;
4881 TrackViewList ts = selection->tracks.filter_to_unique_playlists ();
4883 bool in_command = false;
4885 for (TrackViewList::iterator i = ts.begin(); i != ts.end(); ++i) {
4886 if ((playlist = (*i)->playlist()) == 0) {
4889 playlist->clear_changes ();
4891 if (clicked_selection) {
4892 playlist->duplicate_range (selection->time[clicked_selection], times);
4894 playlist->duplicate_ranges (selection->time, times);
4898 begin_reversible_command (_("duplicate range selection"));
4901 _session->add_command (new StatefulDiffCommand (playlist));
4906 if (times == 1.0f) {
4907 // now "move" range selection to after the current range selection
4908 framecnt_t distance = 0;
4910 if (clicked_selection) {
4912 selection->time[clicked_selection].end - selection->time[clicked_selection].start;
4914 distance = selection->time.end_frame () - selection->time.start ();
4917 selection->move_time (distance);
4919 commit_reversible_command ();
4923 /** Reset all selected points to the relevant default value */
4925 Editor::reset_point_selection ()
4927 for (PointSelection::iterator i = selection->points.begin(); i != selection->points.end(); ++i) {
4928 ARDOUR::AutomationList::iterator j = (*i)->model ();
4929 (*j)->value = (*i)->line().the_list()->default_value ();
4934 Editor::center_playhead ()
4936 float const page = _visible_canvas_width * samples_per_pixel;
4937 center_screen_internal (playhead_cursor->current_frame (), page);
4941 Editor::center_edit_point ()
4943 float const page = _visible_canvas_width * samples_per_pixel;
4944 center_screen_internal (get_preferred_edit_position(), page);
4947 /** Caller must begin and commit a reversible command */
4949 Editor::clear_playlist (boost::shared_ptr<Playlist> playlist)
4951 playlist->clear_changes ();
4953 _session->add_command (new StatefulDiffCommand (playlist));
4957 Editor::nudge_track (bool use_edit, bool forwards)
4959 boost::shared_ptr<Playlist> playlist;
4960 framepos_t distance;
4961 framepos_t next_distance;
4965 start = get_preferred_edit_position();
4970 if ((distance = get_nudge_distance (start, next_distance)) == 0) {
4974 if (selection->tracks.empty()) {
4978 TrackViewList ts = selection->tracks.filter_to_unique_playlists ();
4979 bool in_command = false;
4981 for (TrackViewList::iterator i = ts.begin(); i != ts.end(); ++i) {
4983 if ((playlist = (*i)->playlist()) == 0) {
4987 playlist->clear_changes ();
4988 playlist->clear_owned_changes ();
4990 playlist->nudge_after (start, distance, forwards);
4993 begin_reversible_command (_("nudge track"));
4996 vector<Command*> cmds;
4998 playlist->rdiff (cmds);
4999 _session->add_commands (cmds);
5001 _session->add_command (new StatefulDiffCommand (playlist));
5005 commit_reversible_command ();
5010 Editor::remove_last_capture ()
5012 vector<string> choices;
5019 if (Config->get_verify_remove_last_capture()) {
5020 prompt = _("Do you really want to destroy the last capture?"
5021 "\n(This is destructive and cannot be undone)");
5023 choices.push_back (_("No, do nothing."));
5024 choices.push_back (_("Yes, destroy it."));
5026 Gtkmm2ext::Choice prompter (_("Destroy last capture"), prompt, choices);
5028 if (prompter.run () == 1) {
5029 _session->remove_last_capture ();
5030 _regions->redisplay ();
5034 _session->remove_last_capture();
5035 _regions->redisplay ();
5040 Editor::normalize_region ()
5046 RegionSelection rs = get_regions_from_selection_and_entered ();
5052 NormalizeDialog dialog (rs.size() > 1);
5054 if (dialog.run () != RESPONSE_ACCEPT) {
5058 CursorContext::Handle cursor_ctx = CursorContext::create(*this, _cursors->wait);
5061 /* XXX: should really only count audio regions here */
5062 int const regions = rs.size ();
5064 /* Make a list of the selected audio regions' maximum amplitudes, and also
5065 obtain the maximum amplitude of them all.
5067 list<double> max_amps;
5068 list<double> rms_vals;
5071 bool use_rms = dialog.constrain_rms ();
5073 for (RegionSelection::const_iterator i = rs.begin(); i != rs.end(); ++i) {
5074 AudioRegionView const * arv = dynamic_cast<AudioRegionView const *> (*i);
5078 dialog.descend (1.0 / regions);
5079 double const a = arv->audio_region()->maximum_amplitude (&dialog);
5081 double r = arv->audio_region()->rms (&dialog);
5082 max_rms = max (max_rms, r);
5083 rms_vals.push_back (r);
5087 /* the user cancelled the operation */
5091 max_amps.push_back (a);
5092 max_amp = max (max_amp, a);
5096 list<double>::const_iterator a = max_amps.begin ();
5097 list<double>::const_iterator l = rms_vals.begin ();
5098 bool in_command = false;
5100 for (RegionSelection::iterator r = rs.begin(); r != rs.end(); ++r) {
5101 AudioRegionView* const arv = dynamic_cast<AudioRegionView*> (*r);
5106 arv->region()->clear_changes ();
5108 double amp = dialog.normalize_individually() ? *a : max_amp;
5109 double target = dialog.target_peak (); // dB
5112 double const amp_rms = dialog.normalize_individually() ? *l : max_rms;
5113 const double t_rms = dialog.target_rms ();
5114 const gain_t c_peak = dB_to_coefficient (target);
5115 const gain_t c_rms = dB_to_coefficient (t_rms);
5116 if ((amp_rms / c_rms) > (amp / c_peak)) {
5122 arv->audio_region()->normalize (amp, target);
5125 begin_reversible_command (_("normalize"));
5128 _session->add_command (new StatefulDiffCommand (arv->region()));
5135 commit_reversible_command ();
5141 Editor::reset_region_scale_amplitude ()
5147 RegionSelection rs = get_regions_from_selection_and_entered ();
5153 bool in_command = false;
5155 for (RegionSelection::iterator r = rs.begin(); r != rs.end(); ++r) {
5156 AudioRegionView* const arv = dynamic_cast<AudioRegionView*>(*r);
5159 arv->region()->clear_changes ();
5160 arv->audio_region()->set_scale_amplitude (1.0f);
5163 begin_reversible_command ("reset gain");
5166 _session->add_command (new StatefulDiffCommand (arv->region()));
5170 commit_reversible_command ();
5175 Editor::adjust_region_gain (bool up)
5177 RegionSelection rs = get_regions_from_selection_and_entered ();
5179 if (!_session || rs.empty()) {
5183 bool in_command = false;
5185 for (RegionSelection::iterator r = rs.begin(); r != rs.end(); ++r) {
5186 AudioRegionView* const arv = dynamic_cast<AudioRegionView*>(*r);
5191 arv->region()->clear_changes ();
5193 double dB = accurate_coefficient_to_dB (arv->audio_region()->scale_amplitude ());
5201 arv->audio_region()->set_scale_amplitude (dB_to_coefficient (dB));
5204 begin_reversible_command ("adjust region gain");
5207 _session->add_command (new StatefulDiffCommand (arv->region()));
5211 commit_reversible_command ();
5217 Editor::reverse_region ()
5223 Reverse rev (*_session);
5224 apply_filter (rev, _("reverse regions"));
5228 Editor::strip_region_silence ()
5234 RegionSelection rs = get_regions_from_selection_and_entered ();
5240 std::list<RegionView*> audio_only;
5242 for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
5243 AudioRegionView* const arv = dynamic_cast<AudioRegionView*> (*i);
5245 audio_only.push_back (arv);
5249 assert (!audio_only.empty());
5251 StripSilenceDialog d (_session, audio_only);
5252 int const r = d.run ();
5256 if (r == Gtk::RESPONSE_OK) {
5257 ARDOUR::AudioIntervalMap silences;
5258 d.silences (silences);
5259 StripSilence s (*_session, silences, d.fade_length());
5261 apply_filter (s, _("strip silence"), &d);
5266 Editor::apply_midi_note_edit_op_to_region (MidiOperator& op, MidiRegionView& mrv)
5268 Evoral::Sequence<Evoral::Beats>::Notes selected;
5269 mrv.selection_as_notelist (selected, true);
5271 vector<Evoral::Sequence<Evoral::Beats>::Notes> v;
5272 v.push_back (selected);
5274 Evoral::Beats pos_beats = Evoral::Beats (mrv.midi_region()->beat()) - mrv.midi_region()->start_beats();
5276 return op (mrv.midi_region()->model(), pos_beats, v);
5280 Editor::apply_midi_note_edit_op (MidiOperator& op, const RegionSelection& rs)
5286 bool in_command = false;
5288 for (RegionSelection::const_iterator r = rs.begin(); r != rs.end(); ) {
5289 RegionSelection::const_iterator tmp = r;
5292 MidiRegionView* const mrv = dynamic_cast<MidiRegionView*> (*r);
5295 Command* cmd = apply_midi_note_edit_op_to_region (op, *mrv);
5298 begin_reversible_command (op.name ());
5302 _session->add_command (cmd);
5310 commit_reversible_command ();
5315 Editor::fork_region ()
5317 RegionSelection rs = get_regions_from_selection_and_entered ();
5323 CursorContext::Handle cursor_ctx = CursorContext::create(*this, _cursors->wait);
5324 bool in_command = false;
5328 for (RegionSelection::iterator r = rs.begin(); r != rs.end(); ) {
5329 RegionSelection::iterator tmp = r;
5332 MidiRegionView* const mrv = dynamic_cast<MidiRegionView*>(*r);
5336 boost::shared_ptr<Playlist> playlist = mrv->region()->playlist();
5337 boost::shared_ptr<MidiSource> new_source = _session->create_midi_source_by_stealing_name (mrv->midi_view()->track());
5338 boost::shared_ptr<MidiRegion> newregion = mrv->midi_region()->clone (new_source);
5341 begin_reversible_command (_("Fork Region(s)"));
5344 playlist->clear_changes ();
5345 playlist->replace_region (mrv->region(), newregion, mrv->region()->position());
5346 _session->add_command(new StatefulDiffCommand (playlist));
5348 error << string_compose (_("Could not unlink %1"), mrv->region()->name()) << endmsg;
5356 commit_reversible_command ();
5361 Editor::quantize_region ()
5364 quantize_regions(get_regions_from_selection_and_entered ());
5369 Editor::quantize_regions (const RegionSelection& rs)
5371 if (rs.n_midi_regions() == 0) {
5375 if (!quantize_dialog) {
5376 quantize_dialog = new QuantizeDialog (*this);
5379 if (quantize_dialog->is_mapped()) {
5380 /* in progress already */
5384 quantize_dialog->present ();
5385 const int r = quantize_dialog->run ();
5386 quantize_dialog->hide ();
5388 if (r == Gtk::RESPONSE_OK) {
5389 Quantize quant (quantize_dialog->snap_start(),
5390 quantize_dialog->snap_end(),
5391 quantize_dialog->start_grid_size(),
5392 quantize_dialog->end_grid_size(),
5393 quantize_dialog->strength(),
5394 quantize_dialog->swing(),
5395 quantize_dialog->threshold());
5397 apply_midi_note_edit_op (quant, rs);
5402 Editor::legatize_region (bool shrink_only)
5405 legatize_regions(get_regions_from_selection_and_entered (), shrink_only);
5410 Editor::legatize_regions (const RegionSelection& rs, bool shrink_only)
5412 if (rs.n_midi_regions() == 0) {
5416 Legatize legatize(shrink_only);
5417 apply_midi_note_edit_op (legatize, rs);
5421 Editor::transform_region ()
5424 transform_regions(get_regions_from_selection_and_entered ());
5429 Editor::transform_regions (const RegionSelection& rs)
5431 if (rs.n_midi_regions() == 0) {
5438 const int r = td.run();
5441 if (r == Gtk::RESPONSE_OK) {
5442 Transform transform(td.get());
5443 apply_midi_note_edit_op(transform, rs);
5448 Editor::transpose_region ()
5451 transpose_regions(get_regions_from_selection_and_entered ());
5456 Editor::transpose_regions (const RegionSelection& rs)
5458 if (rs.n_midi_regions() == 0) {
5463 int const r = d.run ();
5465 if (r == RESPONSE_ACCEPT) {
5466 Transpose transpose(d.semitones ());
5467 apply_midi_note_edit_op (transpose, rs);
5472 Editor::insert_patch_change (bool from_context)
5474 RegionSelection rs = get_regions_from_selection_and_entered ();
5480 const framepos_t p = get_preferred_edit_position (EDIT_IGNORE_NONE, from_context);
5482 /* XXX: bit of a hack; use the MIDNAM from the first selected region;
5483 there may be more than one, but the PatchChangeDialog can only offer
5484 one set of patch menus.
5486 MidiRegionView* first = dynamic_cast<MidiRegionView*> (rs.front ());
5488 Evoral::PatchChange<Evoral::Beats> empty (Evoral::Beats(), 0, 0, 0);
5489 PatchChangeDialog d (0, _session, empty, first->instrument_info(), Gtk::Stock::ADD);
5491 if (d.run() == RESPONSE_CANCEL) {
5495 for (RegionSelection::iterator i = rs.begin (); i != rs.end(); ++i) {
5496 MidiRegionView* const mrv = dynamic_cast<MidiRegionView*> (*i);
5498 if (p >= mrv->region()->first_frame() && p <= mrv->region()->last_frame()) {
5499 mrv->add_patch_change (p - mrv->region()->position(), d.patch ());
5506 Editor::apply_filter (Filter& filter, string command, ProgressReporter* progress)
5508 RegionSelection rs = get_regions_from_selection_and_entered ();
5514 CursorContext::Handle cursor_ctx = CursorContext::create(*this, _cursors->wait);
5515 bool in_command = false;
5520 int const N = rs.size ();
5522 for (RegionSelection::iterator r = rs.begin(); r != rs.end(); ) {
5523 RegionSelection::iterator tmp = r;
5526 AudioRegionView* const arv = dynamic_cast<AudioRegionView*>(*r);
5528 boost::shared_ptr<Playlist> playlist = arv->region()->playlist();
5531 progress->descend (1.0 / N);
5534 if (arv->audio_region()->apply (filter, progress) == 0) {
5536 playlist->clear_changes ();
5537 playlist->clear_owned_changes ();
5540 begin_reversible_command (command);
5544 if (filter.results.empty ()) {
5546 /* no regions returned; remove the old one */
5547 playlist->remove_region (arv->region ());
5551 std::vector<boost::shared_ptr<Region> >::iterator res = filter.results.begin ();
5553 /* first region replaces the old one */
5554 playlist->replace_region (arv->region(), *res, (*res)->position());
5558 while (res != filter.results.end()) {
5559 playlist->add_region (*res, (*res)->position());
5565 /* We might have removed regions, which alters other regions' layering_index,
5566 so we need to do a recursive diff here.
5568 vector<Command*> cmds;
5569 playlist->rdiff (cmds);
5570 _session->add_commands (cmds);
5572 _session->add_command(new StatefulDiffCommand (playlist));
5576 progress->ascend ();
5585 commit_reversible_command ();
5590 Editor::external_edit_region ()
5596 Editor::reset_region_gain_envelopes ()
5598 RegionSelection rs = get_regions_from_selection_and_entered ();
5600 if (!_session || rs.empty()) {
5604 bool in_command = false;
5606 for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
5607 AudioRegionView* const arv = dynamic_cast<AudioRegionView*>(*i);
5609 boost::shared_ptr<AutomationList> alist (arv->audio_region()->envelope());
5610 XMLNode& before (alist->get_state());
5612 arv->audio_region()->set_default_envelope ();
5615 begin_reversible_command (_("reset region gain"));
5618 _session->add_command (new MementoCommand<AutomationList>(*arv->audio_region()->envelope().get(), &before, &alist->get_state()));
5623 commit_reversible_command ();
5628 Editor::set_region_gain_visibility (RegionView* rv)
5630 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (rv);
5632 arv->update_envelope_visibility();
5637 Editor::set_gain_envelope_visibility ()
5643 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
5644 AudioTimeAxisView* v = dynamic_cast<AudioTimeAxisView*>(*i);
5646 v->audio_view()->foreach_regionview (sigc::mem_fun (this, &Editor::set_region_gain_visibility));
5652 Editor::toggle_gain_envelope_active ()
5654 if (_ignore_region_action) {
5658 RegionSelection rs = get_regions_from_selection_and_entered ();
5660 if (!_session || rs.empty()) {
5664 bool in_command = false;
5666 for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
5667 AudioRegionView* const arv = dynamic_cast<AudioRegionView*>(*i);
5669 arv->region()->clear_changes ();
5670 arv->audio_region()->set_envelope_active (!arv->audio_region()->envelope_active());
5673 begin_reversible_command (_("region gain envelope active"));
5676 _session->add_command (new StatefulDiffCommand (arv->region()));
5681 commit_reversible_command ();
5686 Editor::toggle_region_lock ()
5688 if (_ignore_region_action) {
5692 RegionSelection rs = get_regions_from_selection_and_entered ();
5694 if (!_session || rs.empty()) {
5698 begin_reversible_command (_("toggle region lock"));
5700 for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
5701 (*i)->region()->clear_changes ();
5702 (*i)->region()->set_locked (!(*i)->region()->locked());
5703 _session->add_command (new StatefulDiffCommand ((*i)->region()));
5706 commit_reversible_command ();
5710 Editor::toggle_region_video_lock ()
5712 if (_ignore_region_action) {
5716 RegionSelection rs = get_regions_from_selection_and_entered ();
5718 if (!_session || rs.empty()) {
5722 begin_reversible_command (_("Toggle Video Lock"));
5724 for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
5725 (*i)->region()->clear_changes ();
5726 (*i)->region()->set_video_locked (!(*i)->region()->video_locked());
5727 _session->add_command (new StatefulDiffCommand ((*i)->region()));
5730 commit_reversible_command ();
5734 Editor::toggle_region_lock_style ()
5736 if (_ignore_region_action) {
5740 RegionSelection rs = get_regions_from_selection_and_entered ();
5742 if (!_session || rs.empty()) {
5746 Glib::RefPtr<ToggleAction> a = Glib::RefPtr<ToggleAction>::cast_dynamic (_region_actions->get_action("toggle-region-lock-style"));
5747 vector<Widget*> proxies = a->get_proxies();
5748 Gtk::CheckMenuItem* cmi = dynamic_cast<Gtk::CheckMenuItem*> (proxies.front());
5752 begin_reversible_command (_("toggle region lock style"));
5754 for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
5755 (*i)->region()->clear_changes ();
5756 PositionLockStyle const ns = ((*i)->region()->position_lock_style() == AudioTime && !cmi->get_inconsistent()) ? MusicTime : AudioTime;
5757 (*i)->region()->set_position_lock_style (ns);
5758 _session->add_command (new StatefulDiffCommand ((*i)->region()));
5761 commit_reversible_command ();
5765 Editor::toggle_opaque_region ()
5767 if (_ignore_region_action) {
5771 RegionSelection rs = get_regions_from_selection_and_entered ();
5773 if (!_session || rs.empty()) {
5777 begin_reversible_command (_("change region opacity"));
5779 for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
5780 (*i)->region()->clear_changes ();
5781 (*i)->region()->set_opaque (!(*i)->region()->opaque());
5782 _session->add_command (new StatefulDiffCommand ((*i)->region()));
5785 commit_reversible_command ();
5789 Editor::toggle_record_enable ()
5791 bool new_state = false;
5793 for (TrackSelection::iterator i = selection->tracks.begin(); i != selection->tracks.end(); ++i) {
5794 RouteTimeAxisView *rtav = dynamic_cast<RouteTimeAxisView *>(*i);
5797 if (!rtav->is_track())
5801 new_state = !rtav->track()->rec_enable_control()->get_value();
5805 rtav->track()->rec_enable_control()->set_value (new_state, Controllable::UseGroup);
5810 Editor::toggle_solo ()
5812 bool new_state = false;
5814 boost::shared_ptr<ControlList> cl (new ControlList);
5816 for (TrackSelection::iterator i = selection->tracks.begin(); i != selection->tracks.end(); ++i) {
5817 RouteTimeAxisView *rtav = dynamic_cast<RouteTimeAxisView *>(*i);
5824 new_state = !rtav->route()->soloed ();
5828 cl->push_back (rtav->route()->solo_control());
5831 _session->set_controls (cl, new_state ? 1.0 : 0.0, Controllable::UseGroup);
5835 Editor::toggle_mute ()
5837 bool new_state = false;
5839 boost::shared_ptr<RouteList> rl (new RouteList);
5841 for (TrackSelection::iterator i = selection->tracks.begin(); i != selection->tracks.end(); ++i) {
5842 RouteTimeAxisView *rtav = dynamic_cast<RouteTimeAxisView *>(*i);
5849 new_state = !rtav->route()->muted();
5853 rl->push_back (rtav->route());
5856 _session->set_controls (route_list_to_control_list (rl, &Stripable::mute_control), new_state, Controllable::UseGroup);
5860 Editor::toggle_solo_isolate ()
5866 Editor::fade_range ()
5868 TrackViewList ts = selection->tracks.filter_to_unique_playlists ();
5870 begin_reversible_command (_("fade range"));
5872 for (TrackViewList::iterator i = ts.begin(); i != ts.end(); ++i) {
5873 (*i)->fade_range (selection->time);
5876 commit_reversible_command ();
5881 Editor::set_fade_length (bool in)
5883 RegionSelection rs = get_regions_from_selection_and_entered ();
5889 /* we need a region to measure the offset from the start */
5891 RegionView* rv = rs.front ();
5893 framepos_t pos = get_preferred_edit_position();
5897 if (pos > rv->region()->last_frame() || pos < rv->region()->first_frame()) {
5898 /* edit point is outside the relevant region */
5903 if (pos <= rv->region()->position()) {
5907 len = pos - rv->region()->position();
5908 cmd = _("set fade in length");
5910 if (pos >= rv->region()->last_frame()) {
5914 len = rv->region()->last_frame() - pos;
5915 cmd = _("set fade out length");
5918 bool in_command = false;
5920 for (RegionSelection::iterator x = rs.begin(); x != rs.end(); ++x) {
5921 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (*x);
5927 boost::shared_ptr<AutomationList> alist;
5929 alist = tmp->audio_region()->fade_in();
5931 alist = tmp->audio_region()->fade_out();
5934 XMLNode &before = alist->get_state();
5937 tmp->audio_region()->set_fade_in_length (len);
5938 tmp->audio_region()->set_fade_in_active (true);
5940 tmp->audio_region()->set_fade_out_length (len);
5941 tmp->audio_region()->set_fade_out_active (true);
5945 begin_reversible_command (cmd);
5948 XMLNode &after = alist->get_state();
5949 _session->add_command(new MementoCommand<AutomationList>(*alist, &before, &after));
5953 commit_reversible_command ();
5958 Editor::set_fade_in_shape (FadeShape shape)
5960 RegionSelection rs = get_regions_from_selection_and_entered ();
5965 bool in_command = false;
5967 for (RegionSelection::iterator x = rs.begin(); x != rs.end(); ++x) {
5968 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (*x);
5974 boost::shared_ptr<AutomationList> alist = tmp->audio_region()->fade_in();
5975 XMLNode &before = alist->get_state();
5977 tmp->audio_region()->set_fade_in_shape (shape);
5980 begin_reversible_command (_("set fade in shape"));
5983 XMLNode &after = alist->get_state();
5984 _session->add_command(new MementoCommand<AutomationList>(*alist.get(), &before, &after));
5988 commit_reversible_command ();
5993 Editor::set_fade_out_shape (FadeShape shape)
5995 RegionSelection rs = get_regions_from_selection_and_entered ();
6000 bool in_command = false;
6002 for (RegionSelection::iterator x = rs.begin(); x != rs.end(); ++x) {
6003 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (*x);
6009 boost::shared_ptr<AutomationList> alist = tmp->audio_region()->fade_out();
6010 XMLNode &before = alist->get_state();
6012 tmp->audio_region()->set_fade_out_shape (shape);
6015 begin_reversible_command (_("set fade out shape"));
6018 XMLNode &after = alist->get_state();
6019 _session->add_command(new MementoCommand<AutomationList>(*alist.get(), &before, &after));
6023 commit_reversible_command ();
6028 Editor::set_fade_in_active (bool yn)
6030 RegionSelection rs = get_regions_from_selection_and_entered ();
6035 bool in_command = false;
6037 for (RegionSelection::iterator x = rs.begin(); x != rs.end(); ++x) {
6038 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (*x);
6045 boost::shared_ptr<AudioRegion> ar (tmp->audio_region());
6047 ar->clear_changes ();
6048 ar->set_fade_in_active (yn);
6051 begin_reversible_command (_("set fade in active"));
6054 _session->add_command (new StatefulDiffCommand (ar));
6058 commit_reversible_command ();
6063 Editor::set_fade_out_active (bool yn)
6065 RegionSelection rs = get_regions_from_selection_and_entered ();
6070 bool in_command = false;
6072 for (RegionSelection::iterator x = rs.begin(); x != rs.end(); ++x) {
6073 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (*x);
6079 boost::shared_ptr<AudioRegion> ar (tmp->audio_region());
6081 ar->clear_changes ();
6082 ar->set_fade_out_active (yn);
6085 begin_reversible_command (_("set fade out active"));
6088 _session->add_command(new StatefulDiffCommand (ar));
6092 commit_reversible_command ();
6097 Editor::toggle_region_fades (int dir)
6099 if (_ignore_region_action) {
6103 boost::shared_ptr<AudioRegion> ar;
6106 RegionSelection rs = get_regions_from_selection_and_entered ();
6112 RegionSelection::iterator i;
6113 for (i = rs.begin(); i != rs.end(); ++i) {
6114 if ((ar = boost::dynamic_pointer_cast<AudioRegion>((*i)->region())) != 0) {
6116 yn = ar->fade_out_active ();
6118 yn = ar->fade_in_active ();
6124 if (i == rs.end()) {
6128 /* XXX should this undo-able? */
6129 bool in_command = false;
6131 for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
6132 if ((ar = boost::dynamic_pointer_cast<AudioRegion>((*i)->region())) == 0) {
6135 ar->clear_changes ();
6137 if (dir == 1 || dir == 0) {
6138 ar->set_fade_in_active (!yn);
6141 if (dir == -1 || dir == 0) {
6142 ar->set_fade_out_active (!yn);
6145 begin_reversible_command (_("toggle fade active"));
6148 _session->add_command(new StatefulDiffCommand (ar));
6152 commit_reversible_command ();
6157 /** Update region fade visibility after its configuration has been changed */
6159 Editor::update_region_fade_visibility ()
6161 bool _fade_visibility = _session->config.get_show_region_fades ();
6163 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
6164 AudioTimeAxisView* v = dynamic_cast<AudioTimeAxisView*>(*i);
6166 if (_fade_visibility) {
6167 v->audio_view()->show_all_fades ();
6169 v->audio_view()->hide_all_fades ();
6176 Editor::set_edit_point ()
6179 MusicFrame where (0, 0);
6181 if (!mouse_frame (where.frame, ignored)) {
6187 if (selection->markers.empty()) {
6189 mouse_add_new_marker (where.frame);
6194 Location* loc = find_location_from_marker (selection->markers.front(), ignored);
6197 loc->move_to (where.frame, where.division);
6203 Editor::set_playhead_cursor ()
6205 if (entered_marker) {
6206 _session->request_locate (entered_marker->position(), _session->transport_rolling());
6208 MusicFrame where (0, 0);
6211 if (!mouse_frame (where.frame, ignored)) {
6218 _session->request_locate (where.frame, _session->transport_rolling());
6222 //not sure what this was for; remove it for now.
6223 // if (UIConfiguration::instance().get_follow_edits() && (!_session || !_session->config.get_external_sync())) {
6224 // cancel_time_selection();
6230 Editor::split_region ()
6232 if (_drags->active ()) {
6236 //if a range is selected, separate it
6237 if ( !selection->time.empty()) {
6238 separate_regions_between (selection->time);
6242 //if no range was selected, try to find some regions to split
6243 if (current_mouse_mode() == MouseObject) { //don't try this for Internal Edit, Stretch, Draw, etc.
6245 RegionSelection rs = get_regions_from_selection_and_edit_point ();
6246 const framepos_t pos = get_preferred_edit_position();
6247 const int32_t division = get_grid_music_divisions (0);
6248 MusicFrame where (pos, division);
6254 split_regions_at (where, rs);
6260 Editor::select_next_route()
6262 if (selection->tracks.empty()) {
6263 selection->set (track_views.front());
6267 TimeAxisView* current = selection->tracks.front();
6271 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
6273 if (*i == current) {
6275 if (i != track_views.end()) {
6278 current = (*(track_views.begin()));
6279 //selection->set (*(track_views.begin()));
6285 rui = dynamic_cast<RouteUI *>(current);
6287 } while (current->hidden() || (rui == NULL) || !rui->route()->active());
6289 selection->set (current);
6291 ensure_time_axis_view_is_visible (*current, false);
6295 Editor::select_prev_route()
6297 if (selection->tracks.empty()) {
6298 selection->set (track_views.front());
6302 TimeAxisView* current = selection->tracks.front();
6306 for (TrackViewList::reverse_iterator i = track_views.rbegin(); i != track_views.rend(); ++i) {
6308 if (*i == current) {
6310 if (i != track_views.rend()) {
6313 current = *(track_views.rbegin());
6318 rui = dynamic_cast<RouteUI *>(current);
6320 } while (current->hidden() || (rui == NULL) || !rui->route()->active());
6322 selection->set (current);
6324 ensure_time_axis_view_is_visible (*current, false);
6328 Editor::set_loop_from_selection (bool play)
6330 if (_session == 0) {
6334 framepos_t start, end;
6335 if (!get_selection_extents ( start, end))
6338 set_loop_range (start, end, _("set loop range from selection"));
6341 _session->request_play_loop (true, true);
6346 Editor::set_loop_from_region (bool play)
6348 framepos_t start, end;
6349 if (!get_selection_extents ( start, end))
6352 set_loop_range (start, end, _("set loop range from region"));
6355 _session->request_locate (start, true);
6356 _session->request_play_loop (true);
6361 Editor::set_punch_from_selection ()
6363 if (_session == 0) {
6367 framepos_t start, end;
6368 if (!get_selection_extents ( start, end))
6371 set_punch_range (start, end, _("set punch range from selection"));
6375 Editor::set_auto_punch_range ()
6377 // auto punch in/out button from a single button
6378 // If Punch In is unset, set punch range from playhead to end, enable punch in
6379 // If Punch In is set, the next punch sets Punch Out, unless the playhead has been
6380 // rewound beyond the Punch In marker, in which case that marker will be moved back
6381 // to the current playhead position.
6382 // If punch out is set, it clears the punch range and Punch In/Out buttons
6384 if (_session == 0) {
6388 Location* tpl = transport_punch_location();
6389 framepos_t now = playhead_cursor->current_frame();
6390 framepos_t begin = now;
6391 framepos_t end = _session->current_end_frame();
6393 if (!_session->config.get_punch_in()) {
6394 // First Press - set punch in and create range from here to eternity
6395 set_punch_range (begin, end, _("Auto Punch In"));
6396 _session->config.set_punch_in(true);
6397 } else if (tpl && !_session->config.get_punch_out()) {
6398 // Second press - update end range marker and set punch_out
6399 if (now < tpl->start()) {
6400 // playhead has been rewound - move start back and pretend nothing happened
6402 set_punch_range (begin, end, _("Auto Punch In/Out"));
6404 // normal case for 2nd press - set the punch out
6405 end = playhead_cursor->current_frame ();
6406 set_punch_range (tpl->start(), now, _("Auto Punch In/Out"));
6407 _session->config.set_punch_out(true);
6410 if (_session->config.get_punch_out()) {
6411 _session->config.set_punch_out(false);
6414 if (_session->config.get_punch_in()) {
6415 _session->config.set_punch_in(false);
6420 // third press - unset punch in/out and remove range
6421 _session->locations()->remove(tpl);
6428 Editor::set_session_extents_from_selection ()
6430 if (_session == 0) {
6434 framepos_t start, end;
6435 if (!get_selection_extents ( start, end))
6439 if ((loc = _session->locations()->session_range_location()) == 0) {
6440 _session->set_session_extents (start, end); // this will create a new session range; no need for UNDO
6442 XMLNode &before = loc->get_state();
6444 _session->set_session_extents (start, end);
6446 XMLNode &after = loc->get_state();
6448 begin_reversible_command (_("set session start/end from selection"));
6450 _session->add_command (new MementoCommand<Location>(*loc, &before, &after));
6452 commit_reversible_command ();
6455 _session->set_end_is_free (false);
6459 Editor::set_punch_start_from_edit_point ()
6463 MusicFrame start (0, 0);
6464 framepos_t end = max_framepos;
6466 //use the existing punch end, if any
6467 Location* tpl = transport_punch_location();
6472 if ((_edit_point == EditAtPlayhead) && _session->transport_rolling()) {
6473 start.frame = _session->audible_frame();
6475 start.frame = get_preferred_edit_position();
6478 //snap the selection start/end
6481 //if there's not already a sensible selection endpoint, go "forever"
6482 if (start.frame > end ) {
6486 set_punch_range (start.frame, end, _("set punch start from EP"));
6492 Editor::set_punch_end_from_edit_point ()
6496 framepos_t start = 0;
6497 MusicFrame end (max_framepos, 0);
6499 //use the existing punch start, if any
6500 Location* tpl = transport_punch_location();
6502 start = tpl->start();
6505 if ((_edit_point == EditAtPlayhead) && _session->transport_rolling()) {
6506 end.frame = _session->audible_frame();
6508 end.frame = get_preferred_edit_position();
6511 //snap the selection start/end
6514 set_punch_range (start, end.frame, _("set punch end from EP"));
6520 Editor::set_loop_start_from_edit_point ()
6524 MusicFrame start (0, 0);
6525 framepos_t end = max_framepos;
6527 //use the existing loop end, if any
6528 Location* tpl = transport_loop_location();
6533 if ((_edit_point == EditAtPlayhead) && _session->transport_rolling()) {
6534 start.frame = _session->audible_frame();
6536 start.frame = get_preferred_edit_position();
6539 //snap the selection start/end
6542 //if there's not already a sensible selection endpoint, go "forever"
6543 if (start.frame > end ) {
6547 set_loop_range (start.frame, end, _("set loop start from EP"));
6553 Editor::set_loop_end_from_edit_point ()
6557 framepos_t start = 0;
6558 MusicFrame end (max_framepos, 0);
6560 //use the existing loop start, if any
6561 Location* tpl = transport_loop_location();
6563 start = tpl->start();
6566 if ((_edit_point == EditAtPlayhead) && _session->transport_rolling()) {
6567 end.frame = _session->audible_frame();
6569 end.frame = get_preferred_edit_position();
6572 //snap the selection start/end
6575 set_loop_range (start, end.frame, _("set loop end from EP"));
6580 Editor::set_punch_from_region ()
6582 framepos_t start, end;
6583 if (!get_selection_extents ( start, end))
6586 set_punch_range (start, end, _("set punch range from region"));
6590 Editor::pitch_shift_region ()
6592 RegionSelection rs = get_regions_from_selection_and_entered ();
6594 RegionSelection audio_rs;
6595 for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
6596 if (dynamic_cast<AudioRegionView*> (*i)) {
6597 audio_rs.push_back (*i);
6601 if (audio_rs.empty()) {
6605 pitch_shift (audio_rs, 1.2);
6609 Editor::set_tempo_from_region ()
6611 RegionSelection rs = get_regions_from_selection_and_entered ();
6613 if (!_session || rs.empty()) {
6617 RegionView* rv = rs.front();
6619 define_one_bar (rv->region()->position(), rv->region()->last_frame() + 1);
6623 Editor::use_range_as_bar ()
6625 framepos_t start, end;
6626 if (get_edit_op_range (start, end)) {
6627 define_one_bar (start, end);
6632 Editor::define_one_bar (framepos_t start, framepos_t end)
6634 framepos_t length = end - start;
6636 const Meter& m (_session->tempo_map().meter_at_frame (start));
6638 /* length = 1 bar */
6640 /* We're going to deliver a constant tempo here,
6641 so we can use frames per beat to determine length.
6642 now we want frames per beat.
6643 we have frames per bar, and beats per bar, so ...
6646 /* XXXX METER MATH */
6648 double frames_per_beat = length / m.divisions_per_bar();
6650 /* beats per minute = */
6652 double beats_per_minute = (_session->frame_rate() * 60.0) / frames_per_beat;
6654 /* now decide whether to:
6656 (a) set global tempo
6657 (b) add a new tempo marker
6661 const TempoSection& t (_session->tempo_map().tempo_section_at_frame (start));
6663 bool do_global = false;
6665 if ((_session->tempo_map().n_tempos() == 1) && (_session->tempo_map().n_meters() == 1)) {
6667 /* only 1 tempo & 1 meter: ask if the user wants to set the tempo
6668 at the start, or create a new marker
6671 vector<string> options;
6672 options.push_back (_("Cancel"));
6673 options.push_back (_("Add new marker"));
6674 options.push_back (_("Set global tempo"));
6677 _("Define one bar"),
6678 _("Do you want to set the global tempo or add a new tempo marker?"),
6682 c.set_default_response (2);
6698 /* more than 1 tempo and/or meter section already, go ahead do the "usual":
6699 if the marker is at the region starter, change it, otherwise add
6704 begin_reversible_command (_("set tempo from region"));
6705 XMLNode& before (_session->tempo_map().get_state());
6708 _session->tempo_map().change_initial_tempo (beats_per_minute, t.note_type());
6709 } else if (t.frame() == start) {
6710 _session->tempo_map().change_existing_tempo_at (start, beats_per_minute, t.note_type());
6712 const Tempo tempo (beats_per_minute, t.note_type());
6713 _session->tempo_map().add_tempo (tempo, 0.0, start, TempoSection::Constant, AudioTime);
6716 XMLNode& after (_session->tempo_map().get_state());
6718 _session->add_command (new MementoCommand<TempoMap>(_session->tempo_map(), &before, &after));
6719 commit_reversible_command ();
6723 Editor::split_region_at_transients ()
6725 AnalysisFeatureList positions;
6727 RegionSelection rs = get_regions_from_selection_and_entered ();
6729 if (!_session || rs.empty()) {
6733 begin_reversible_command (_("split regions"));
6735 for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ) {
6737 RegionSelection::iterator tmp;
6742 boost::shared_ptr<AudioRegion> ar = boost::dynamic_pointer_cast<AudioRegion> ((*i)->region());
6745 ar->transients (positions);
6746 split_region_at_points ((*i)->region(), positions, true);
6753 commit_reversible_command ();
6758 Editor::split_region_at_points (boost::shared_ptr<Region> r, AnalysisFeatureList& positions, bool can_ferret, bool select_new)
6760 bool use_rhythmic_rodent = false;
6762 boost::shared_ptr<Playlist> pl = r->playlist();
6764 list<boost::shared_ptr<Region> > new_regions;
6770 if (positions.empty()) {
6774 if (positions.size() > 20 && can_ferret) {
6775 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);
6776 MessageDialog msg (msgstr,
6779 Gtk::BUTTONS_OK_CANCEL);
6782 msg.add_button (_("Call for the Ferret!"), RESPONSE_APPLY);
6783 msg.set_secondary_text (_("Press OK to continue with this split operation\nor ask the Ferret dialog to tune the analysis"));
6785 msg.set_secondary_text (_("Press OK to continue with this split operation"));
6788 msg.set_title (_("Excessive split?"));
6791 int response = msg.run();
6797 case RESPONSE_APPLY:
6798 use_rhythmic_rodent = true;
6805 if (use_rhythmic_rodent) {
6806 show_rhythm_ferret ();
6810 AnalysisFeatureList::const_iterator x;
6812 pl->clear_changes ();
6813 pl->clear_owned_changes ();
6815 x = positions.begin();
6817 if (x == positions.end()) {
6822 pl->remove_region (r);
6826 framepos_t rstart = r->first_frame ();
6827 framepos_t rend = r->last_frame ();
6829 while (x != positions.end()) {
6831 /* deal with positons that are out of scope of present region bounds */
6832 if (*x <= rstart || *x > rend) {
6837 /* file start = original start + how far we from the initial position ? */
6839 framepos_t file_start = r->start() + pos;
6841 /* length = next position - current position */
6843 framepos_t len = (*x) - pos - rstart;
6845 /* XXX we do we really want to allow even single-sample regions?
6846 * shouldn't we have some kind of lower limit on region size?
6855 if (RegionFactory::region_name (new_name, r->name())) {
6859 /* do NOT announce new regions 1 by one, just wait till they are all done */
6863 plist.add (ARDOUR::Properties::start, file_start);
6864 plist.add (ARDOUR::Properties::length, len);
6865 plist.add (ARDOUR::Properties::name, new_name);
6866 plist.add (ARDOUR::Properties::layer, 0);
6867 // TODO set transients_offset
6869 boost::shared_ptr<Region> nr = RegionFactory::create (r->sources(), plist, false);
6870 /* because we set annouce to false, manually add the new region to the
6873 RegionFactory::map_add (nr);
6875 pl->add_region (nr, rstart + pos);
6878 new_regions.push_front(nr);
6887 RegionFactory::region_name (new_name, r->name());
6889 /* Add the final region */
6892 plist.add (ARDOUR::Properties::start, r->start() + pos);
6893 plist.add (ARDOUR::Properties::length, r->last_frame() - (r->position() + pos) + 1);
6894 plist.add (ARDOUR::Properties::name, new_name);
6895 plist.add (ARDOUR::Properties::layer, 0);
6897 boost::shared_ptr<Region> nr = RegionFactory::create (r->sources(), plist, false);
6898 /* because we set annouce to false, manually add the new region to the
6901 RegionFactory::map_add (nr);
6902 pl->add_region (nr, r->position() + pos);
6905 new_regions.push_front(nr);
6910 /* We might have removed regions, which alters other regions' layering_index,
6911 so we need to do a recursive diff here.
6913 vector<Command*> cmds;
6915 _session->add_commands (cmds);
6917 _session->add_command (new StatefulDiffCommand (pl));
6921 for (list<boost::shared_ptr<Region> >::iterator i = new_regions.begin(); i != new_regions.end(); ++i){
6922 set_selected_regionview_from_region_list ((*i), Selection::Add);
6928 Editor::place_transient()
6934 RegionSelection rs = get_regions_from_selection_and_edit_point ();
6940 framepos_t where = get_preferred_edit_position();
6942 begin_reversible_command (_("place transient"));
6944 for (RegionSelection::iterator r = rs.begin(); r != rs.end(); ++r) {
6945 (*r)->region()->add_transient(where);
6948 commit_reversible_command ();
6952 Editor::remove_transient(ArdourCanvas::Item* item)
6958 ArdourCanvas::Line* _line = reinterpret_cast<ArdourCanvas::Line*> (item);
6961 AudioRegionView* _arv = reinterpret_cast<AudioRegionView*> (item->get_data ("regionview"));
6962 _arv->remove_transient (*(float*) _line->get_data ("position"));
6966 Editor::snap_regions_to_grid ()
6968 list <boost::shared_ptr<Playlist > > used_playlists;
6970 RegionSelection rs = get_regions_from_selection_and_entered ();
6972 if (!_session || rs.empty()) {
6976 begin_reversible_command (_("snap regions to grid"));
6978 for (RegionSelection::iterator r = rs.begin(); r != rs.end(); ++r) {
6980 boost::shared_ptr<Playlist> pl = (*r)->region()->playlist();
6982 if (!pl->frozen()) {
6983 /* we haven't seen this playlist before */
6985 /* remember used playlists so we can thaw them later */
6986 used_playlists.push_back(pl);
6990 MusicFrame start ((*r)->region()->first_frame (), 0);
6992 (*r)->region()->set_position (start.frame, start.division);
6995 while (used_playlists.size() > 0) {
6996 list <boost::shared_ptr<Playlist > >::iterator i = used_playlists.begin();
6998 used_playlists.pop_front();
7001 commit_reversible_command ();
7005 Editor::close_region_gaps ()
7007 list <boost::shared_ptr<Playlist > > used_playlists;
7009 RegionSelection rs = get_regions_from_selection_and_entered ();
7011 if (!_session || rs.empty()) {
7015 Dialog dialog (_("Close Region Gaps"));
7018 table.set_spacings (12);
7019 table.set_border_width (12);
7020 Label* l = manage (left_aligned_label (_("Crossfade length")));
7021 table.attach (*l, 0, 1, 0, 1);
7023 SpinButton spin_crossfade (1, 0);
7024 spin_crossfade.set_range (0, 15);
7025 spin_crossfade.set_increments (1, 1);
7026 spin_crossfade.set_value (5);
7027 table.attach (spin_crossfade, 1, 2, 0, 1);
7029 table.attach (*manage (new Label (_("ms"))), 2, 3, 0, 1);
7031 l = manage (left_aligned_label (_("Pull-back length")));
7032 table.attach (*l, 0, 1, 1, 2);
7034 SpinButton spin_pullback (1, 0);
7035 spin_pullback.set_range (0, 100);
7036 spin_pullback.set_increments (1, 1);
7037 spin_pullback.set_value(30);
7038 table.attach (spin_pullback, 1, 2, 1, 2);
7040 table.attach (*manage (new Label (_("ms"))), 2, 3, 1, 2);
7042 dialog.get_vbox()->pack_start (table);
7043 dialog.add_button (Stock::CANCEL, RESPONSE_CANCEL);
7044 dialog.add_button (_("Ok"), RESPONSE_ACCEPT);
7047 if (dialog.run () == RESPONSE_CANCEL) {
7051 framepos_t crossfade_len = spin_crossfade.get_value();
7052 framepos_t pull_back_frames = spin_pullback.get_value();
7054 crossfade_len = lrintf (crossfade_len * _session->frame_rate()/1000);
7055 pull_back_frames = lrintf (pull_back_frames * _session->frame_rate()/1000);
7057 /* Iterate over the region list and make adjacent regions overlap by crossfade_len_ms */
7059 begin_reversible_command (_("close region gaps"));
7062 boost::shared_ptr<Region> last_region;
7064 rs.sort_by_position_and_track();
7066 for (RegionSelection::iterator r = rs.begin(); r != rs.end(); ++r) {
7068 boost::shared_ptr<Playlist> pl = (*r)->region()->playlist();
7070 if (!pl->frozen()) {
7071 /* we haven't seen this playlist before */
7073 /* remember used playlists so we can thaw them later */
7074 used_playlists.push_back(pl);
7078 framepos_t position = (*r)->region()->position();
7080 if (idx == 0 || position < last_region->position()){
7081 last_region = (*r)->region();
7086 (*r)->region()->clear_changes ();
7087 (*r)->region()->trim_front( (position - pull_back_frames));
7088 last_region->trim_end( (position - pull_back_frames + crossfade_len));
7090 _session->add_command (new StatefulDiffCommand ((*r)->region()));
7091 last_region = (*r)->region();
7096 while (used_playlists.size() > 0) {
7097 list <boost::shared_ptr<Playlist > >::iterator i = used_playlists.begin();
7099 used_playlists.pop_front();
7102 commit_reversible_command ();
7106 Editor::tab_to_transient (bool forward)
7108 AnalysisFeatureList positions;
7110 RegionSelection rs = get_regions_from_selection_and_entered ();
7116 framepos_t pos = _session->audible_frame ();
7118 if (!selection->tracks.empty()) {
7120 /* don't waste time searching for transients in duplicate playlists.
7123 TrackViewList ts = selection->tracks.filter_to_unique_playlists ();
7125 for (TrackViewList::iterator t = ts.begin(); t != ts.end(); ++t) {
7127 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (*t);
7130 boost::shared_ptr<Track> tr = rtv->track();
7132 boost::shared_ptr<Playlist> pl = tr->playlist ();
7134 framepos_t result = pl->find_next_transient (pos, forward ? 1 : -1);
7137 positions.push_back (result);
7150 for (RegionSelection::iterator r = rs.begin(); r != rs.end(); ++r) {
7151 (*r)->region()->get_transients (positions);
7155 TransientDetector::cleanup_transients (positions, _session->frame_rate(), 3.0);
7158 AnalysisFeatureList::iterator x;
7160 for (x = positions.begin(); x != positions.end(); ++x) {
7166 if (x != positions.end ()) {
7167 _session->request_locate (*x);
7171 AnalysisFeatureList::reverse_iterator x;
7173 for (x = positions.rbegin(); x != positions.rend(); ++x) {
7179 if (x != positions.rend ()) {
7180 _session->request_locate (*x);
7186 Editor::playhead_forward_to_grid ()
7192 MusicFrame pos (playhead_cursor->current_frame (), 0);
7194 if (pos.frame < max_framepos - 1) {
7196 snap_to_internal (pos, RoundUpAlways, false);
7197 _session->request_locate (pos.frame);
7203 Editor::playhead_backward_to_grid ()
7209 MusicFrame pos (playhead_cursor->current_frame (), 0);
7211 if (pos.frame > 2) {
7213 snap_to_internal (pos, RoundDownAlways, false);
7214 _session->request_locate (pos.frame);
7219 Editor::set_track_height (Height h)
7221 TrackSelection& ts (selection->tracks);
7223 for (TrackSelection::iterator x = ts.begin(); x != ts.end(); ++x) {
7224 (*x)->set_height_enum (h);
7229 Editor::toggle_tracks_active ()
7231 TrackSelection& ts (selection->tracks);
7233 bool target = false;
7239 for (TrackSelection::iterator x = ts.begin(); x != ts.end(); ++x) {
7240 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*>(*x);
7244 target = !rtv->_route->active();
7247 rtv->_route->set_active (target, this);
7253 Editor::remove_tracks ()
7255 /* this will delete GUI objects that may be the subject of an event
7256 handler in which this method is called. Defer actual deletion to the
7257 next idle callback, when all event handling is finished.
7259 Glib::signal_idle().connect (sigc::mem_fun (*this, &Editor::idle_remove_tracks));
7263 Editor::idle_remove_tracks ()
7265 Session::StateProtector sp (_session);
7267 return false; /* do not call again */
7271 Editor::_remove_tracks ()
7273 TrackSelection& ts (selection->tracks);
7279 vector<string> choices;
7283 const char* trackstr;
7285 vector<boost::shared_ptr<Route> > routes;
7286 bool special_bus = false;
7288 for (TrackSelection::iterator x = ts.begin(); x != ts.end(); ++x) {
7289 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (*x);
7293 if (rtv->is_track()) {
7298 routes.push_back (rtv->_route);
7300 if (rtv->route()->is_master() || rtv->route()->is_monitor()) {
7305 if (special_bus && !Config->get_allow_special_bus_removal()) {
7306 MessageDialog msg (_("That would be bad news ...."),
7310 msg.set_secondary_text (string_compose (_(
7311 "Removing the master or monitor bus is such a bad idea\n\
7312 that %1 is not going to allow it.\n\
7314 If you really want to do this sort of thing\n\
7315 edit your ardour.rc file to set the\n\
7316 \"allow-special-bus-removal\" option to be \"yes\""), PROGRAM_NAME));
7323 if (ntracks + nbusses == 0) {
7327 trackstr = P_("track", "tracks", ntracks);
7328 busstr = P_("bus", "busses", nbusses);
7332 prompt = string_compose (_("Do you really want to remove %1 %2 and %3 %4?\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!"),
7335 ntracks, trackstr, nbusses, busstr);
7337 prompt = string_compose (_("Do you really want to remove %1 %2?\n"
7338 "(You may also lose the playlists associated with the %2)\n\n"
7339 "This action cannot be undone, and the session file will be overwritten!"),
7342 } else if (nbusses) {
7343 prompt = string_compose (_("Do you really want to remove %1 %2?\n\n"
7344 "This action cannot be undone, and the session file will be overwritten"),
7348 choices.push_back (_("No, do nothing."));
7349 if (ntracks + nbusses > 1) {
7350 choices.push_back (_("Yes, remove them."));
7352 choices.push_back (_("Yes, remove it."));
7357 title = string_compose (_("Remove %1"), trackstr);
7359 title = string_compose (_("Remove %1"), busstr);
7362 Choice prompter (title, prompt, choices);
7364 if (prompter.run () != 1) {
7368 if (current_mixer_strip && routes.size () > 1 && std::find (routes.begin(), routes.end(), current_mixer_strip->route()) != routes.end ()) {
7369 /* Route deletion calls Editor::timeaxisview_deleted() iteratively (for each deleted
7370 * route). If the deleted route is currently displayed in the Editor-Mixer (highly
7371 * likely because deletion requires selection) this will call
7372 * Editor::set_selected_mixer_strip () which is expensive ( MixerStrip::set_route() ).
7373 * It's likewise likely that the route that has just been displayed in the
7374 * Editor-Mixer will be next in line for deletion.
7376 * So simply switch to the master-bus (if present)
7378 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
7379 if ((*i)->stripable ()->is_master ()) {
7380 set_selected_mixer_strip (*(*i));
7387 PresentationInfo::ChangeSuspender cs;
7388 DisplaySuspender ds;
7390 boost::shared_ptr<RouteList> rl (new RouteList);
7391 for (vector<boost::shared_ptr<Route> >::iterator x = routes.begin(); x != routes.end(); ++x) {
7394 _session->remove_routes (rl);
7396 /* TrackSelection and RouteList leave scope,
7397 * destructors are called,
7398 * diskstream drops references, save_state is called (again for every track)
7403 Editor::do_insert_time ()
7405 if (selection->tracks.empty()) {
7409 InsertRemoveTimeDialog d (*this);
7410 int response = d.run ();
7412 if (response != RESPONSE_OK) {
7416 if (d.distance() == 0) {
7423 d.intersected_region_action (),
7427 d.move_glued_markers(),
7428 d.move_locked_markers(),
7434 Editor::insert_time (
7435 framepos_t pos, framecnt_t frames, InsertTimeOption opt,
7436 bool all_playlists, bool ignore_music_glue, bool markers_too, bool glued_markers_too, bool locked_markers_too, bool tempo_too
7440 if (Config->get_edit_mode() == Lock) {
7443 bool in_command = false;
7445 TrackViewList ts = selection->tracks.filter_to_unique_playlists ();
7447 for (TrackViewList::iterator x = ts.begin(); x != ts.end(); ++x) {
7451 /* don't operate on any playlist more than once, which could
7452 * happen if "all playlists" is enabled, but there is more
7453 * than 1 track using playlists "from" a given track.
7456 set<boost::shared_ptr<Playlist> > pl;
7458 if (all_playlists) {
7459 RouteTimeAxisView* rtav = dynamic_cast<RouteTimeAxisView*> (*x);
7460 if (rtav && rtav->track ()) {
7461 vector<boost::shared_ptr<Playlist> > all = _session->playlists->playlists_for_track (rtav->track ());
7462 for (vector<boost::shared_ptr<Playlist> >::iterator p = all.begin(); p != all.end(); ++p) {
7467 if ((*x)->playlist ()) {
7468 pl.insert ((*x)->playlist ());
7472 for (set<boost::shared_ptr<Playlist> >::iterator i = pl.begin(); i != pl.end(); ++i) {
7474 (*i)->clear_changes ();
7475 (*i)->clear_owned_changes ();
7477 if (opt == SplitIntersected) {
7478 /* non musical split */
7479 (*i)->split (MusicFrame (pos, 0));
7482 (*i)->shift (pos, frames, (opt == MoveIntersected), ignore_music_glue);
7485 begin_reversible_command (_("insert time"));
7488 vector<Command*> cmds;
7490 _session->add_commands (cmds);
7492 _session->add_command (new StatefulDiffCommand (*i));
7496 RouteTimeAxisView* rtav = dynamic_cast<RouteTimeAxisView*> (*x);
7499 begin_reversible_command (_("insert time"));
7502 rtav->route ()->shift (pos, frames);
7509 const int32_t divisions = get_grid_music_divisions (0);
7510 XMLNode& before (_session->locations()->get_state());
7511 Locations::LocationList copy (_session->locations()->list());
7513 for (Locations::LocationList::iterator i = copy.begin(); i != copy.end(); ++i) {
7515 Locations::LocationList::const_iterator tmp;
7517 if ((*i)->position_lock_style() == AudioTime || glued_markers_too) {
7518 bool const was_locked = (*i)->locked ();
7519 if (locked_markers_too) {
7523 if ((*i)->start() >= pos) {
7524 // move end first, in case we're moving by more than the length of the range
7525 if (!(*i)->is_mark()) {
7526 (*i)->set_end ((*i)->end() + frames, false, true, divisions);
7528 (*i)->set_start ((*i)->start() + frames, false, true, divisions);
7540 begin_reversible_command (_("insert time"));
7543 XMLNode& after (_session->locations()->get_state());
7544 _session->add_command (new MementoCommand<Locations>(*_session->locations(), &before, &after));
7550 begin_reversible_command (_("insert time"));
7553 XMLNode& before (_session->tempo_map().get_state());
7554 _session->tempo_map().insert_time (pos, frames);
7555 XMLNode& after (_session->tempo_map().get_state());
7556 _session->add_command (new MementoCommand<TempoMap>(_session->tempo_map(), &before, &after));
7560 commit_reversible_command ();
7565 Editor::do_remove_time ()
7567 if (selection->tracks.empty()) {
7571 InsertRemoveTimeDialog d (*this, true);
7573 int response = d.run ();
7575 if (response != RESPONSE_OK) {
7579 framecnt_t distance = d.distance();
7581 if (distance == 0) {
7591 d.move_glued_markers(),
7592 d.move_locked_markers(),
7598 Editor::remove_time (framepos_t pos, framecnt_t frames, InsertTimeOption opt,
7599 bool ignore_music_glue, bool markers_too, bool glued_markers_too, bool locked_markers_too, bool tempo_too)
7601 if (Config->get_edit_mode() == Lock) {
7602 error << (_("Cannot insert or delete time when in Lock edit.")) << endmsg;
7605 bool in_command = false;
7607 for (TrackSelection::iterator x = selection->tracks.begin(); x != selection->tracks.end(); ++x) {
7609 boost::shared_ptr<Playlist> pl = (*x)->playlist();
7613 XMLNode &before = pl->get_state();
7615 std::list<AudioRange> rl;
7616 AudioRange ar(pos, pos+frames, 0);
7619 pl->shift (pos, -frames, true, ignore_music_glue);
7622 begin_reversible_command (_("remove time"));
7625 XMLNode &after = pl->get_state();
7627 _session->add_command (new MementoCommand<Playlist> (*pl, &before, &after));
7631 RouteTimeAxisView* rtav = dynamic_cast<RouteTimeAxisView*> (*x);
7634 begin_reversible_command (_("remove time"));
7637 rtav->route ()->shift (pos, -frames);
7641 const int32_t divisions = get_grid_music_divisions (0);
7642 std::list<Location*> loc_kill_list;
7647 XMLNode& before (_session->locations()->get_state());
7648 Locations::LocationList copy (_session->locations()->list());
7650 for (Locations::LocationList::iterator i = copy.begin(); i != copy.end(); ++i) {
7651 if ((*i)->position_lock_style() == AudioTime || glued_markers_too) {
7653 bool const was_locked = (*i)->locked ();
7654 if (locked_markers_too) {
7658 if (!(*i)->is_mark()) { // it's a range; have to handle both start and end
7659 if ((*i)->end() >= pos
7660 && (*i)->end() < pos+frames
7661 && (*i)->start() >= pos
7662 && (*i)->end() < pos+frames) { // range is completely enclosed; kill it
7664 loc_kill_list.push_back(*i);
7665 } else { // only start or end is included, try to do the right thing
7666 // move start before moving end, to avoid trying to move the end to before the start
7667 // if we're removing more time than the length of the range
7668 if ((*i)->start() >= pos && (*i)->start() < pos+frames) {
7669 // start is within cut
7670 (*i)->set_start (pos, false, true,divisions); // bring the start marker to the beginning of the cut
7672 } else if ((*i)->start() >= pos+frames) {
7673 // start (and thus entire range) lies beyond end of cut
7674 (*i)->set_start ((*i)->start() - frames, false, true, divisions); // slip the start marker back
7677 if ((*i)->end() >= pos && (*i)->end() < pos+frames) {
7678 // end is inside cut
7679 (*i)->set_end (pos, false, true, divisions); // bring the end to the cut
7681 } else if ((*i)->end() >= pos+frames) {
7682 // end is beyond end of cut
7683 (*i)->set_end ((*i)->end() - frames, false, true, divisions); // slip the end marker back
7688 } else if ((*i)->start() >= pos && (*i)->start() < pos+frames ) {
7689 loc_kill_list.push_back(*i);
7691 } else if ((*i)->start() >= pos) {
7692 (*i)->set_start ((*i)->start() -frames, false, true, divisions);
7702 for (list<Location*>::iterator i = loc_kill_list.begin(); i != loc_kill_list.end(); ++i) {
7703 _session->locations()->remove( *i );
7708 begin_reversible_command (_("remove time"));
7711 XMLNode& after (_session->locations()->get_state());
7712 _session->add_command (new MementoCommand<Locations>(*_session->locations(), &before, &after));
7717 XMLNode& before (_session->tempo_map().get_state());
7719 if (_session->tempo_map().remove_time (pos, frames) ) {
7721 begin_reversible_command (_("remove time"));
7724 XMLNode& after (_session->tempo_map().get_state());
7725 _session->add_command (new MementoCommand<TempoMap>(_session->tempo_map(), &before, &after));
7730 commit_reversible_command ();
7735 Editor::fit_selection ()
7737 if (!selection->tracks.empty()) {
7738 fit_tracks (selection->tracks);
7742 /* no selected tracks - use tracks with selected regions */
7744 if (!selection->regions.empty()) {
7745 for (RegionSelection::iterator r = selection->regions.begin(); r != selection->regions.end(); ++r) {
7746 tvl.push_back (&(*r)->get_time_axis_view ());
7752 } else if (internal_editing()) {
7753 /* no selected tracks, or regions, but in internal edit mode, so follow the mouse and use
7756 if (entered_track) {
7757 tvl.push_back (entered_track);
7766 Editor::fit_tracks (TrackViewList & tracks)
7768 if (tracks.empty()) {
7772 uint32_t child_heights = 0;
7773 int visible_tracks = 0;
7775 for (TrackSelection::iterator t = tracks.begin(); t != tracks.end(); ++t) {
7777 if (!(*t)->marked_for_display()) {
7781 child_heights += (*t)->effective_height() - (*t)->current_height();
7785 /* compute the per-track height from:
7787 total canvas visible height -
7788 height that will be taken by visible children of selected
7789 tracks - height of the ruler/hscroll area
7791 uint32_t h = (uint32_t) floor ((trackviews_height() - child_heights) / visible_tracks);
7792 double first_y_pos = DBL_MAX;
7794 if (h < TimeAxisView::preset_height (HeightSmall)) {
7795 MessageDialog msg (_("There are too many tracks to fit in the current window"));
7796 /* too small to be displayed */
7800 undo_visual_stack.push_back (current_visual_state (true));
7801 PBD::Unwinder<bool> nsv (no_save_visual, true);
7803 /* build a list of all tracks, including children */
7806 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
7808 TimeAxisView::Children c = (*i)->get_child_list ();
7809 for (TimeAxisView::Children::iterator j = c.begin(); j != c.end(); ++j) {
7810 all.push_back (j->get());
7815 // find selection range.
7816 // if someone knows how to user TrackViewList::iterator for this
7818 int selected_top = -1;
7819 int selected_bottom = -1;
7821 for (TrackViewList::iterator t = all.begin(); t != all.end(); ++t, ++i) {
7822 if ((*t)->marked_for_display ()) {
7823 if (tracks.contains(*t)) {
7824 if (selected_top == -1) {
7827 selected_bottom = i;
7833 for (TrackViewList::iterator t = all.begin(); t != all.end(); ++t, ++i) {
7834 if ((*t)->marked_for_display ()) {
7835 if (tracks.contains(*t)) {
7836 (*t)->set_height (h);
7837 first_y_pos = std::min ((*t)->y_position (), first_y_pos);
7839 if (i > selected_top && i < selected_bottom) {
7840 hide_track_in_display (*t);
7847 set the controls_layout height now, because waiting for its size
7848 request signal handler will cause the vertical adjustment setting to fail
7851 controls_layout.property_height () = _full_canvas_height;
7852 vertical_adjustment.set_value (first_y_pos);
7854 redo_visual_stack.push_back (current_visual_state (true));
7856 visible_tracks_selector.set_text (_("Sel"));
7860 Editor::save_visual_state (uint32_t n)
7862 while (visual_states.size() <= n) {
7863 visual_states.push_back (0);
7866 if (visual_states[n] != 0) {
7867 delete visual_states[n];
7870 visual_states[n] = current_visual_state (true);
7875 Editor::goto_visual_state (uint32_t n)
7877 if (visual_states.size() <= n) {
7881 if (visual_states[n] == 0) {
7885 use_visual_state (*visual_states[n]);
7889 Editor::start_visual_state_op (uint32_t n)
7891 save_visual_state (n);
7893 PopUp* pup = new PopUp (WIN_POS_MOUSE, 1000, true);
7895 snprintf (buf, sizeof (buf), _("Saved view %u"), n+1);
7896 pup->set_text (buf);
7901 Editor::cancel_visual_state_op (uint32_t n)
7903 goto_visual_state (n);
7907 Editor::toggle_region_mute ()
7909 if (_ignore_region_action) {
7913 RegionSelection rs = get_regions_from_selection_and_entered ();
7919 if (rs.size() > 1) {
7920 begin_reversible_command (_("mute regions"));
7922 begin_reversible_command (_("mute region"));
7925 for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
7927 (*i)->region()->playlist()->clear_changes ();
7928 (*i)->region()->set_muted (!(*i)->region()->muted ());
7929 _session->add_command (new StatefulDiffCommand ((*i)->region()));
7933 commit_reversible_command ();
7937 Editor::combine_regions ()
7939 /* foreach track with selected regions, take all selected regions
7940 and join them into a new region containing the subregions (as a
7944 typedef set<RouteTimeAxisView*> RTVS;
7947 if (selection->regions.empty()) {
7951 for (RegionSelection::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
7952 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*>(&(*i)->get_time_axis_view());
7955 tracks.insert (rtv);
7959 begin_reversible_command (_("combine regions"));
7961 vector<RegionView*> new_selection;
7963 for (RTVS::iterator i = tracks.begin(); i != tracks.end(); ++i) {
7966 if ((rv = (*i)->combine_regions ()) != 0) {
7967 new_selection.push_back (rv);
7971 selection->clear_regions ();
7972 for (vector<RegionView*>::iterator i = new_selection.begin(); i != new_selection.end(); ++i) {
7973 selection->add (*i);
7976 commit_reversible_command ();
7980 Editor::uncombine_regions ()
7982 typedef set<RouteTimeAxisView*> RTVS;
7985 if (selection->regions.empty()) {
7989 for (RegionSelection::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
7990 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*>(&(*i)->get_time_axis_view());
7993 tracks.insert (rtv);
7997 begin_reversible_command (_("uncombine regions"));
7999 for (RTVS::iterator i = tracks.begin(); i != tracks.end(); ++i) {
8000 (*i)->uncombine_regions ();
8003 commit_reversible_command ();
8007 Editor::toggle_midi_input_active (bool flip_others)
8010 boost::shared_ptr<RouteList> rl (new RouteList);
8012 for (TrackSelection::iterator i = selection->tracks.begin(); i != selection->tracks.end(); ++i) {
8013 RouteTimeAxisView *rtav = dynamic_cast<RouteTimeAxisView *>(*i);
8019 boost::shared_ptr<MidiTrack> mt = rtav->midi_track();
8022 rl->push_back (rtav->route());
8023 onoff = !mt->input_active();
8027 _session->set_exclusive_input_active (rl, onoff, flip_others);
8030 static bool ok_fine (GdkEventAny*) { return true; }
8036 lock_dialog = new Gtk::Dialog (string_compose (_("%1: Locked"), PROGRAM_NAME), true);
8038 Gtk::Image* padlock = manage (new Gtk::Image (ARDOUR_UI_UTILS::get_icon ("padlock_closed")));
8039 lock_dialog->get_vbox()->pack_start (*padlock);
8040 lock_dialog->signal_delete_event ().connect (sigc::ptr_fun (ok_fine));
8042 ArdourButton* b = manage (new ArdourButton);
8043 b->set_name ("lock button");
8044 b->set_text (_("Click to unlock"));
8045 b->signal_clicked.connect (sigc::mem_fun (*this, &Editor::unlock));
8046 lock_dialog->get_vbox()->pack_start (*b);
8048 lock_dialog->get_vbox()->show_all ();
8049 lock_dialog->set_size_request (200, 200);
8052 delete _main_menu_disabler;
8053 _main_menu_disabler = new MainMenuDisabler;
8055 lock_dialog->present ();
8057 lock_dialog->get_window()->set_decorations (Gdk::WMDecoration (0));
8063 lock_dialog->hide ();
8065 delete _main_menu_disabler;
8066 _main_menu_disabler = 0;
8068 if (UIConfiguration::instance().get_lock_gui_after_seconds()) {
8069 start_lock_event_timing ();
8074 Editor::bring_in_callback (Gtk::Label* label, uint32_t n, uint32_t total, string name)
8076 Gtkmm2ext::UI::instance()->call_slot (invalidator (*this), boost::bind (&Editor::update_bring_in_message, this, label, n, total, name));
8080 Editor::update_bring_in_message (Gtk::Label* label, uint32_t n, uint32_t total, string name)
8082 Timers::TimerSuspender t;
8083 label->set_text (string_compose ("Copying %1, %2 of %3", name, n, total));
8084 Gtkmm2ext::UI::instance()->flush_pending (1);
8088 Editor::bring_all_sources_into_session ()
8095 ArdourDialog w (_("Moving embedded files into session folder"));
8096 w.get_vbox()->pack_start (msg);
8099 /* flush all pending GUI events because we're about to start copying
8103 Timers::TimerSuspender t;
8104 Gtkmm2ext::UI::instance()->flush_pending (3);
8108 _session->bring_all_sources_into_session (boost::bind (&Editor::bring_in_callback, this, &msg, _1, _2, _3));