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::select_topmost_track ()
1457 const double top_of_trackviews = vertical_adjustment.get_value();
1458 for (TrackViewList::iterator t = track_views.begin(); t != track_views.end(); ++t) {
1459 if ((*t)->hidden()) {
1462 std::pair<TimeAxisView*,double> res = (*t)->covers_y_position (top_of_trackviews);
1464 selection->set (*t);
1471 Editor::scroll_down_one_track (bool skip_child_views)
1473 TrackViewList::reverse_iterator next = track_views.rend();
1474 const double top_of_trackviews = vertical_adjustment.get_value();
1476 for (TrackViewList::reverse_iterator t = track_views.rbegin(); t != track_views.rend(); ++t) {
1477 if ((*t)->hidden()) {
1481 /* If this is the upper-most visible trackview, we want to display
1482 * the one above it (next)
1484 * Note that covers_y_position() is recursive and includes child views
1486 std::pair<TimeAxisView*,double> res = (*t)->covers_y_position (top_of_trackviews);
1489 if (skip_child_views) {
1492 /* automation lane (one level, non-recursive)
1494 * - if no automation lane exists -> move to next tack
1495 * - if the first (here: bottom-most) matches -> move to next tack
1496 * - if no y-axis match is found -> the current track is at the top
1497 * -> move to last (here: top-most) automation lane
1499 TimeAxisView::Children kids = (*t)->get_child_list();
1500 TimeAxisView::Children::reverse_iterator nkid = kids.rend();
1502 for (TimeAxisView::Children::reverse_iterator ci = kids.rbegin(); ci != kids.rend(); ++ci) {
1503 if ((*ci)->hidden()) {
1507 std::pair<TimeAxisView*,double> dev;
1508 dev = (*ci)->covers_y_position (top_of_trackviews);
1510 /* some automation lane is currently at the top */
1511 if (ci == kids.rbegin()) {
1512 /* first (bottom-most) autmation lane is at the top.
1513 * -> move to next track
1522 if (nkid != kids.rend()) {
1523 ensure_time_axis_view_is_visible (**nkid, true);
1531 /* move to the track below the first one that covers the */
1533 if (next != track_views.rend()) {
1534 ensure_time_axis_view_is_visible (**next, true);
1542 Editor::scroll_up_one_track (bool skip_child_views)
1544 TrackViewList::iterator prev = track_views.end();
1545 double top_of_trackviews = vertical_adjustment.get_value ();
1547 for (TrackViewList::iterator t = track_views.begin(); t != track_views.end(); ++t) {
1549 if ((*t)->hidden()) {
1553 /* find the trackview at the top of the trackview group
1555 * Note that covers_y_position() is recursive and includes child views
1557 std::pair<TimeAxisView*,double> res = (*t)->covers_y_position (top_of_trackviews);
1560 if (skip_child_views) {
1563 /* automation lane (one level, non-recursive)
1565 * - if no automation lane exists -> move to prev tack
1566 * - if no y-axis match is found -> the current track is at the top -> move to prev track
1567 * (actually last automation lane of previous track, see below)
1568 * - if first (top-most) lane is at the top -> move to this track
1569 * - else move up one lane
1571 TimeAxisView::Children kids = (*t)->get_child_list();
1572 TimeAxisView::Children::iterator pkid = kids.end();
1574 for (TimeAxisView::Children::iterator ci = kids.begin(); ci != kids.end(); ++ci) {
1575 if ((*ci)->hidden()) {
1579 std::pair<TimeAxisView*,double> dev;
1580 dev = (*ci)->covers_y_position (top_of_trackviews);
1582 /* some automation lane is currently at the top */
1583 if (ci == kids.begin()) {
1584 /* first (top-most) autmation lane is at the top.
1585 * jump directly to this track's top
1587 ensure_time_axis_view_is_visible (**t, true);
1590 else if (pkid != kids.end()) {
1591 /* some other automation lane is at the top.
1592 * move up to prev automation lane.
1594 ensure_time_axis_view_is_visible (**pkid, true);
1597 assert(0); // not reached
1608 if (prev != track_views.end()) {
1609 // move to bottom-most automation-lane of the previous track
1610 TimeAxisView::Children kids = (*prev)->get_child_list();
1611 TimeAxisView::Children::reverse_iterator pkid = kids.rend();
1612 if (!skip_child_views) {
1613 // find the last visible lane
1614 for (TimeAxisView::Children::reverse_iterator ci = kids.rbegin(); ci != kids.rend(); ++ci) {
1615 if (!(*ci)->hidden()) {
1621 if (pkid != kids.rend()) {
1622 ensure_time_axis_view_is_visible (**pkid, true);
1624 ensure_time_axis_view_is_visible (**prev, true);
1633 Editor::scroll_left_step ()
1635 framepos_t xdelta = (current_page_samples() / 8);
1637 if (leftmost_frame > xdelta) {
1638 reset_x_origin (leftmost_frame - xdelta);
1646 Editor::scroll_right_step ()
1648 framepos_t xdelta = (current_page_samples() / 8);
1650 if (max_framepos - xdelta > leftmost_frame) {
1651 reset_x_origin (leftmost_frame + xdelta);
1653 reset_x_origin (max_framepos - current_page_samples());
1658 Editor::scroll_left_half_page ()
1660 framepos_t xdelta = (current_page_samples() / 2);
1661 if (leftmost_frame > xdelta) {
1662 reset_x_origin (leftmost_frame - xdelta);
1669 Editor::scroll_right_half_page ()
1671 framepos_t xdelta = (current_page_samples() / 2);
1672 if (max_framepos - xdelta > leftmost_frame) {
1673 reset_x_origin (leftmost_frame + xdelta);
1675 reset_x_origin (max_framepos - current_page_samples());
1682 Editor::tav_zoom_step (bool coarser)
1684 DisplaySuspender ds;
1688 if (selection->tracks.empty()) {
1691 ts = &selection->tracks;
1694 for (TrackViewList::iterator i = ts->begin(); i != ts->end(); ++i) {
1695 TimeAxisView *tv = (static_cast<TimeAxisView*>(*i));
1696 tv->step_height (coarser);
1701 Editor::tav_zoom_smooth (bool coarser, bool force_all)
1703 DisplaySuspender ds;
1707 if (selection->tracks.empty() || force_all) {
1710 ts = &selection->tracks;
1713 for (TrackViewList::iterator i = ts->begin(); i != ts->end(); ++i) {
1714 TimeAxisView *tv = (static_cast<TimeAxisView*>(*i));
1715 uint32_t h = tv->current_height ();
1720 if (h >= TimeAxisView::preset_height (HeightSmall)) {
1725 tv->set_height (h + 5);
1731 Editor::temporal_zoom_step_mouse_focus_scale (bool zoom_out, double scale)
1733 Editing::ZoomFocus temp_focus = zoom_focus;
1734 zoom_focus = Editing::ZoomFocusMouse;
1735 temporal_zoom_step_scale (zoom_out, scale);
1736 zoom_focus = temp_focus;
1740 Editor::temporal_zoom_step_mouse_focus (bool zoom_out)
1742 temporal_zoom_step_mouse_focus_scale (zoom_out, 2.0);
1746 Editor::temporal_zoom_step (bool zoom_out)
1748 temporal_zoom_step_scale (zoom_out, 2.0);
1752 Editor::temporal_zoom_step_scale (bool zoom_out, double scale)
1754 ENSURE_GUI_THREAD (*this, &Editor::temporal_zoom_step, zoom_out, scale)
1756 framecnt_t nspp = samples_per_pixel;
1760 if (nspp == samples_per_pixel) {
1765 if (nspp == samples_per_pixel) {
1770 temporal_zoom (nspp);
1774 Editor::temporal_zoom (framecnt_t fpp)
1780 framepos_t current_page = current_page_samples();
1781 framepos_t current_leftmost = leftmost_frame;
1782 framepos_t current_rightmost;
1783 framepos_t current_center;
1784 framepos_t new_page_size;
1785 framepos_t half_page_size;
1786 framepos_t leftmost_after_zoom = 0;
1788 bool in_track_canvas;
1789 bool use_mouse_frame = true;
1793 if (fpp == samples_per_pixel) {
1797 // Imposing an arbitrary limit to zoom out as too much zoom out produces
1798 // segfaults for lack of memory. If somebody decides this is not high enough I
1799 // believe it can be raisen to higher values but some limit must be in place.
1801 // This constant represents 1 day @ 48kHz on a 1600 pixel wide display
1802 // all of which is used for the editor track displays. The whole day
1803 // would be 4147200000 samples, so 2592000 samples per pixel.
1805 nfpp = min (fpp, (framecnt_t) 2592000);
1806 nfpp = max ((framecnt_t) 1, nfpp);
1808 new_page_size = (framepos_t) floor (_visible_canvas_width * nfpp);
1809 half_page_size = new_page_size / 2;
1811 switch (zoom_focus) {
1813 leftmost_after_zoom = current_leftmost;
1816 case ZoomFocusRight:
1817 current_rightmost = leftmost_frame + current_page;
1818 if (current_rightmost < new_page_size) {
1819 leftmost_after_zoom = 0;
1821 leftmost_after_zoom = current_rightmost - new_page_size;
1825 case ZoomFocusCenter:
1826 current_center = current_leftmost + (current_page/2);
1827 if (current_center < half_page_size) {
1828 leftmost_after_zoom = 0;
1830 leftmost_after_zoom = current_center - half_page_size;
1834 case ZoomFocusPlayhead:
1835 /* centre playhead */
1836 l = playhead_cursor->current_frame () - (new_page_size * 0.5);
1839 leftmost_after_zoom = 0;
1840 } else if (l > max_framepos) {
1841 leftmost_after_zoom = max_framepos - new_page_size;
1843 leftmost_after_zoom = (framepos_t) l;
1847 case ZoomFocusMouse:
1848 /* try to keep the mouse over the same point in the display */
1850 if (_drags->active()) {
1851 where = _drags->current_pointer_frame ();
1852 } else if (!mouse_frame (where, in_track_canvas)) {
1853 use_mouse_frame = false;
1856 if (use_mouse_frame) {
1857 l = - ((new_page_size * ((where - current_leftmost)/(double)current_page)) - where);
1860 leftmost_after_zoom = 0;
1861 } else if (l > max_framepos) {
1862 leftmost_after_zoom = max_framepos - new_page_size;
1864 leftmost_after_zoom = (framepos_t) l;
1867 /* use playhead instead */
1868 where = playhead_cursor->current_frame ();
1870 if (where < half_page_size) {
1871 leftmost_after_zoom = 0;
1873 leftmost_after_zoom = where - half_page_size;
1879 /* try to keep the edit point in the same place */
1880 where = get_preferred_edit_position ();
1884 double l = - ((new_page_size * ((where - current_leftmost)/(double)current_page)) - where);
1887 leftmost_after_zoom = 0;
1888 } else if (l > max_framepos) {
1889 leftmost_after_zoom = max_framepos - new_page_size;
1891 leftmost_after_zoom = (framepos_t) l;
1895 /* edit point not defined */
1902 // leftmost_after_zoom = min (leftmost_after_zoom, _session->current_end_frame());
1904 reposition_and_zoom (leftmost_after_zoom, nfpp);
1908 Editor::calc_extra_zoom_edges(framepos_t &start, framepos_t &end)
1910 /* this func helps make sure we leave a little space
1911 at each end of the editor so that the zoom doesn't fit the region
1912 precisely to the screen.
1915 GdkScreen* screen = gdk_screen_get_default ();
1916 const gint pixwidth = gdk_screen_get_width (screen);
1917 const gint mmwidth = gdk_screen_get_width_mm (screen);
1918 const double pix_per_mm = (double) pixwidth/ (double) mmwidth;
1919 const double one_centimeter_in_pixels = pix_per_mm * 10.0;
1921 const framepos_t range = end - start;
1922 const framecnt_t new_fpp = (framecnt_t) ceil ((double) range / (double) _visible_canvas_width);
1923 const framepos_t extra_samples = (framepos_t) floor (one_centimeter_in_pixels * new_fpp);
1925 if (start > extra_samples) {
1926 start -= extra_samples;
1931 if (max_framepos - extra_samples > end) {
1932 end += extra_samples;
1939 Editor::get_selection_extents (framepos_t &start, framepos_t &end) const
1941 start = max_framepos;
1945 //ToDo: if notes are selected, set extents to that selection
1947 //ToDo: if control points are selected, set extents to that selection
1949 if ( !selection->regions.empty() ) {
1950 RegionSelection rs = get_regions_from_selection_and_entered ();
1952 for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
1954 if ((*i)->region()->position() < start) {
1955 start = (*i)->region()->position();
1958 if ((*i)->region()->last_frame() + 1 > end) {
1959 end = (*i)->region()->last_frame() + 1;
1963 } else if (!selection->time.empty()) {
1964 start = selection->time.start();
1965 end = selection->time.end_frame();
1967 ret = false; //no selection found
1970 if ((start == 0 && end == 0) || end < start) {
1979 Editor::temporal_zoom_selection (Editing::ZoomAxis axes)
1981 if (!selection) return;
1983 //ToDo: if notes are selected, zoom to that
1985 //ToDo: if control points are selected, zoom to that
1987 if (axes == Horizontal || axes == Both) {
1989 framepos_t start, end;
1990 if (get_selection_extents (start, end)) {
1991 calc_extra_zoom_edges (start, end);
1992 temporal_zoom_by_frame (start, end);
1996 if (axes == Vertical || axes == Both) {
2002 Editor::temporal_zoom_session ()
2004 ENSURE_GUI_THREAD (*this, &Editor::temporal_zoom_session)
2007 framecnt_t start = _session->current_start_frame();
2008 framecnt_t end = _session->current_end_frame();
2010 if (_session->actively_recording () ) {
2011 framepos_t cur = playhead_cursor->current_frame ();
2013 /* recording beyond the end marker; zoom out
2014 * by 5 seconds more so that if 'follow
2015 * playhead' is active we don't immediately
2018 end = cur + _session->frame_rate() * 5;
2022 if ((start == 0 && end == 0) || end < start) {
2026 calc_extra_zoom_edges(start, end);
2028 temporal_zoom_by_frame (start, end);
2033 Editor::temporal_zoom_by_frame (framepos_t start, framepos_t end)
2035 if (!_session) return;
2037 if ((start == 0 && end == 0) || end < start) {
2041 framepos_t range = end - start;
2043 const framecnt_t new_fpp = (framecnt_t) ceil ((double) range / (double) _visible_canvas_width);
2045 framepos_t new_page = range;
2046 framepos_t middle = (framepos_t) floor ((double) start + ((double) range / 2.0f));
2047 framepos_t new_leftmost = (framepos_t) floor ((double) middle - ((double) new_page / 2.0f));
2049 if (new_leftmost > middle) {
2053 if (new_leftmost < 0) {
2057 reposition_and_zoom (new_leftmost, new_fpp);
2061 Editor::temporal_zoom_to_frame (bool coarser, framepos_t frame)
2067 framecnt_t range_before = frame - leftmost_frame;
2071 if (samples_per_pixel <= 1) {
2074 new_spp = samples_per_pixel + (samples_per_pixel/2);
2076 range_before += range_before/2;
2078 if (samples_per_pixel >= 1) {
2079 new_spp = samples_per_pixel - (samples_per_pixel/2);
2081 /* could bail out here since we cannot zoom any finer,
2082 but leave that to the equality test below
2084 new_spp = samples_per_pixel;
2087 range_before -= range_before/2;
2090 if (new_spp == samples_per_pixel) {
2094 /* zoom focus is automatically taken as @param frame when this
2098 framepos_t new_leftmost = frame - (framepos_t)range_before;
2100 if (new_leftmost > frame) {
2104 if (new_leftmost < 0) {
2108 reposition_and_zoom (new_leftmost, new_spp);
2113 Editor::choose_new_marker_name(string &name) {
2115 if (!UIConfiguration::instance().get_name_new_markers()) {
2116 /* don't prompt user for a new name */
2120 ArdourPrompter dialog (true);
2122 dialog.set_prompt (_("New Name:"));
2124 dialog.set_title (_("New Location Marker"));
2126 dialog.set_name ("MarkNameWindow");
2127 dialog.set_size_request (250, -1);
2128 dialog.set_position (Gtk::WIN_POS_MOUSE);
2130 dialog.add_button (Stock::OK, RESPONSE_ACCEPT);
2131 dialog.set_initial_text (name);
2135 switch (dialog.run ()) {
2136 case RESPONSE_ACCEPT:
2142 dialog.get_result(name);
2149 Editor::add_location_from_selection ()
2153 if (selection->time.empty()) {
2157 if (_session == 0 || clicked_axisview == 0) {
2161 framepos_t start = selection->time[clicked_selection].start;
2162 framepos_t end = selection->time[clicked_selection].end;
2164 _session->locations()->next_available_name(rangename,"selection");
2165 Location *location = new Location (*_session, start, end, rangename, Location::IsRangeMarker, get_grid_music_divisions(0));
2167 begin_reversible_command (_("add marker"));
2169 XMLNode &before = _session->locations()->get_state();
2170 _session->locations()->add (location, true);
2171 XMLNode &after = _session->locations()->get_state();
2172 _session->add_command(new MementoCommand<Locations>(*(_session->locations()), &before, &after));
2174 commit_reversible_command ();
2178 Editor::add_location_mark (framepos_t where)
2182 select_new_marker = true;
2184 _session->locations()->next_available_name(markername,"mark");
2185 if (!choose_new_marker_name(markername)) {
2188 Location *location = new Location (*_session, where, where, markername, Location::IsMark, get_grid_music_divisions (0));
2189 begin_reversible_command (_("add marker"));
2191 XMLNode &before = _session->locations()->get_state();
2192 _session->locations()->add (location, true);
2193 XMLNode &after = _session->locations()->get_state();
2194 _session->add_command(new MementoCommand<Locations>(*(_session->locations()), &before, &after));
2196 commit_reversible_command ();
2200 Editor::set_session_start_from_playhead ()
2206 if ((loc = _session->locations()->session_range_location()) == 0) { //should never happen
2207 _session->set_session_extents ( _session->audible_frame(), _session->audible_frame() );
2209 XMLNode &before = loc->get_state();
2211 _session->set_session_extents ( _session->audible_frame(), loc->end() );
2213 XMLNode &after = loc->get_state();
2215 begin_reversible_command (_("Set session start"));
2217 _session->add_command (new MementoCommand<Location>(*loc, &before, &after));
2219 commit_reversible_command ();
2224 Editor::set_session_end_from_playhead ()
2230 if ((loc = _session->locations()->session_range_location()) == 0) { //should never happen
2231 _session->set_session_extents ( _session->audible_frame(), _session->audible_frame() );
2233 XMLNode &before = loc->get_state();
2235 _session->set_session_extents ( loc->start(), _session->audible_frame() );
2237 XMLNode &after = loc->get_state();
2239 begin_reversible_command (_("Set session start"));
2241 _session->add_command (new MementoCommand<Location>(*loc, &before, &after));
2243 commit_reversible_command ();
2246 _session->set_end_is_free (false);
2251 Editor::toggle_location_at_playhead_cursor ()
2253 if (!do_remove_location_at_playhead_cursor())
2255 add_location_from_playhead_cursor();
2260 Editor::add_location_from_playhead_cursor ()
2262 add_location_mark (_session->audible_frame());
2266 Editor::do_remove_location_at_playhead_cursor ()
2268 bool removed = false;
2271 XMLNode &before = _session->locations()->get_state();
2273 //find location(s) at this time
2274 Locations::LocationList locs;
2275 _session->locations()->find_all_between (_session->audible_frame(), _session->audible_frame()+1, locs, Location::Flags(0));
2276 for (Locations::LocationList::iterator i = locs.begin(); i != locs.end(); ++i) {
2277 if ((*i)->is_mark()) {
2278 _session->locations()->remove (*i);
2285 begin_reversible_command (_("remove marker"));
2286 XMLNode &after = _session->locations()->get_state();
2287 _session->add_command(new MementoCommand<Locations>(*(_session->locations()), &before, &after));
2288 commit_reversible_command ();
2295 Editor::remove_location_at_playhead_cursor ()
2297 do_remove_location_at_playhead_cursor ();
2300 /** Add a range marker around each selected region */
2302 Editor::add_locations_from_region ()
2304 RegionSelection rs = get_regions_from_selection_and_entered ();
2309 bool commit = false;
2311 XMLNode &before = _session->locations()->get_state();
2313 for (RegionSelection::iterator i = rs.begin (); i != rs.end (); ++i) {
2315 boost::shared_ptr<Region> region = (*i)->region ();
2317 Location *location = new Location (*_session, region->position(), region->last_frame(), region->name(), Location::IsRangeMarker, 0);
2319 _session->locations()->add (location, true);
2324 begin_reversible_command (selection->regions.size () > 1 ? _("add markers") : _("add marker"));
2325 XMLNode &after = _session->locations()->get_state();
2326 _session->add_command (new MementoCommand<Locations>(*(_session->locations()), &before, &after));
2327 commit_reversible_command ();
2331 /** Add a single range marker around all selected regions */
2333 Editor::add_location_from_region ()
2335 RegionSelection rs = get_regions_from_selection_and_entered ();
2341 XMLNode &before = _session->locations()->get_state();
2345 if (rs.size() > 1) {
2346 _session->locations()->next_available_name(markername, "regions");
2348 RegionView* rv = *(rs.begin());
2349 boost::shared_ptr<Region> region = rv->region();
2350 markername = region->name();
2353 if (!choose_new_marker_name(markername)) {
2357 // single range spanning all selected
2358 Location *location = new Location (*_session, selection->regions.start(), selection->regions.end_frame(), markername, Location::IsRangeMarker, 0);
2359 _session->locations()->add (location, true);
2361 begin_reversible_command (_("add marker"));
2362 XMLNode &after = _session->locations()->get_state();
2363 _session->add_command (new MementoCommand<Locations>(*(_session->locations()), &before, &after));
2364 commit_reversible_command ();
2370 Editor::jump_forward_to_mark ()
2376 framepos_t pos = _session->locations()->first_mark_after (playhead_cursor->current_frame());
2382 _session->request_locate (pos, _session->transport_rolling());
2386 Editor::jump_backward_to_mark ()
2392 framepos_t pos = _session->locations()->first_mark_before (playhead_cursor->current_frame());
2394 //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...
2395 if ( _session->transport_rolling() ) {
2396 if ( (playhead_cursor->current_frame() - pos) < _session->frame_rate()/2 ) {
2397 framepos_t prior = _session->locations()->first_mark_before ( pos );
2406 _session->request_locate (pos, _session->transport_rolling());
2412 framepos_t const pos = _session->audible_frame ();
2415 _session->locations()->next_available_name (markername, "mark");
2417 if (!choose_new_marker_name (markername)) {
2421 _session->locations()->add (new Location (*_session, pos, 0, markername, Location::IsMark, 0), true);
2425 Editor::clear_markers ()
2428 begin_reversible_command (_("clear markers"));
2430 XMLNode &before = _session->locations()->get_state();
2431 _session->locations()->clear_markers ();
2432 XMLNode &after = _session->locations()->get_state();
2433 _session->add_command(new MementoCommand<Locations>(*(_session->locations()), &before, &after));
2435 commit_reversible_command ();
2440 Editor::clear_ranges ()
2443 begin_reversible_command (_("clear ranges"));
2445 XMLNode &before = _session->locations()->get_state();
2447 _session->locations()->clear_ranges ();
2449 XMLNode &after = _session->locations()->get_state();
2450 _session->add_command(new MementoCommand<Locations>(*(_session->locations()), &before, &after));
2452 commit_reversible_command ();
2457 Editor::clear_locations ()
2459 begin_reversible_command (_("clear locations"));
2461 XMLNode &before = _session->locations()->get_state();
2462 _session->locations()->clear ();
2463 XMLNode &after = _session->locations()->get_state();
2464 _session->add_command(new MementoCommand<Locations>(*(_session->locations()), &before, &after));
2466 commit_reversible_command ();
2470 Editor::unhide_markers ()
2472 for (LocationMarkerMap::iterator i = location_markers.begin(); i != location_markers.end(); ++i) {
2473 Location *l = (*i).first;
2474 if (l->is_hidden() && l->is_mark()) {
2475 l->set_hidden(false, this);
2481 Editor::unhide_ranges ()
2483 for (LocationMarkerMap::iterator i = location_markers.begin(); i != location_markers.end(); ++i) {
2484 Location *l = (*i).first;
2485 if (l->is_hidden() && l->is_range_marker()) {
2486 l->set_hidden(false, this);
2491 /* INSERT/REPLACE */
2494 Editor::insert_region_list_selection (float times)
2496 RouteTimeAxisView *tv = 0;
2497 boost::shared_ptr<Playlist> playlist;
2499 if (clicked_routeview != 0) {
2500 tv = clicked_routeview;
2501 } else if (!selection->tracks.empty()) {
2502 if ((tv = dynamic_cast<RouteTimeAxisView*>(selection->tracks.front())) == 0) {
2505 } else if (entered_track != 0) {
2506 if ((tv = dynamic_cast<RouteTimeAxisView*>(entered_track)) == 0) {
2513 if ((playlist = tv->playlist()) == 0) {
2517 boost::shared_ptr<Region> region = _regions->get_single_selection ();
2522 begin_reversible_command (_("insert region"));
2523 playlist->clear_changes ();
2524 playlist->add_region ((RegionFactory::create (region, true)), get_preferred_edit_position(), times);
2525 if (Config->get_edit_mode() == Ripple)
2526 playlist->ripple (get_preferred_edit_position(), region->length() * times, boost::shared_ptr<Region>());
2528 _session->add_command(new StatefulDiffCommand (playlist));
2529 commit_reversible_command ();
2532 /* BUILT-IN EFFECTS */
2535 Editor::reverse_selection ()
2540 /* GAIN ENVELOPE EDITING */
2543 Editor::edit_envelope ()
2550 Editor::transition_to_rolling (bool fwd)
2556 if (_session->config.get_external_sync()) {
2557 switch (Config->get_sync_source()) {
2561 /* transport controlled by the master */
2566 if (_session->is_auditioning()) {
2567 _session->cancel_audition ();
2571 _session->request_transport_speed (fwd ? 1.0f : -1.0f);
2575 Editor::play_from_start ()
2577 _session->request_locate (_session->current_start_frame(), true);
2581 Editor::play_from_edit_point ()
2583 _session->request_locate (get_preferred_edit_position(), true);
2587 Editor::play_from_edit_point_and_return ()
2589 framepos_t start_frame;
2590 framepos_t return_frame;
2592 start_frame = get_preferred_edit_position ( EDIT_IGNORE_PHEAD );
2594 if (_session->transport_rolling()) {
2595 _session->request_locate (start_frame, false);
2599 /* don't reset the return frame if its already set */
2601 if ((return_frame = _session->requested_return_frame()) < 0) {
2602 return_frame = _session->audible_frame();
2605 if (start_frame >= 0) {
2606 _session->request_roll_at_and_return (start_frame, return_frame);
2611 Editor::play_selection ()
2613 framepos_t start, end;
2614 if (!get_selection_extents ( start, end))
2617 AudioRange ar (start, end, 0);
2618 list<AudioRange> lar;
2621 _session->request_play_range (&lar, true);
2626 Editor::maybe_locate_with_edit_preroll (framepos_t location)
2628 if ( _session->transport_rolling() || !UIConfiguration::instance().get_follow_edits() || _session->config.get_external_sync() )
2631 location -= _session->preroll_samples (location);
2633 //don't try to locate before the beginning of time
2638 //if follow_playhead is on, keep the playhead on the screen
2639 if ( _follow_playhead )
2640 if ( location < leftmost_frame )
2641 location = leftmost_frame;
2643 _session->request_locate( location );
2647 Editor::play_with_preroll ()
2649 framepos_t start, end;
2650 if ( UIConfiguration::instance().get_follow_edits() && get_selection_extents ( start, end) ) {
2651 const framepos_t preroll = _session->preroll_samples (start);
2653 framepos_t ret = start;
2655 if (start > preroll) {
2656 start = start - preroll;
2659 end = end + preroll; //"post-roll"
2661 AudioRange ar (start, end, 0);
2662 list<AudioRange> lar;
2665 _session->request_play_range (&lar, true);
2666 _session->set_requested_return_frame (ret); //force auto-return to return to range start, without the preroll
2668 framepos_t ph = playhead_cursor->current_frame ();
2669 const framepos_t preroll = _session->preroll_samples (ph);
2672 start = ph - preroll;
2676 _session->request_locate (start, true);
2677 _session->set_requested_return_frame (ph); //force auto-return to return to playhead location, without the preroll
2682 Editor::rec_with_preroll ()
2684 framepos_t ph = playhead_cursor->current_frame ();
2685 framepos_t preroll = _session->preroll_samples (ph);
2686 _session->request_preroll_record_trim (ph, preroll);
2690 Editor::rec_with_count_in ()
2692 _session->request_count_in_record ();
2696 Editor::play_location (Location& location)
2698 if (location.start() <= location.end()) {
2702 _session->request_bounded_roll (location.start(), location.end());
2706 Editor::loop_location (Location& location)
2708 if (location.start() <= location.end()) {
2714 if ((tll = transport_loop_location()) != 0) {
2715 tll->set (location.start(), location.end());
2717 // enable looping, reposition and start rolling
2718 _session->request_locate (tll->start(), true);
2719 _session->request_play_loop (true);
2724 Editor::do_layer_operation (LayerOperation op)
2726 if (selection->regions.empty ()) {
2730 bool const multiple = selection->regions.size() > 1;
2734 begin_reversible_command (_("raise regions"));
2736 begin_reversible_command (_("raise region"));
2742 begin_reversible_command (_("raise regions to top"));
2744 begin_reversible_command (_("raise region to top"));
2750 begin_reversible_command (_("lower regions"));
2752 begin_reversible_command (_("lower region"));
2758 begin_reversible_command (_("lower regions to bottom"));
2760 begin_reversible_command (_("lower region"));
2765 set<boost::shared_ptr<Playlist> > playlists = selection->regions.playlists ();
2766 for (set<boost::shared_ptr<Playlist> >::iterator i = playlists.begin(); i != playlists.end(); ++i) {
2767 (*i)->clear_owned_changes ();
2770 for (RegionSelection::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
2771 boost::shared_ptr<Region> r = (*i)->region ();
2783 r->lower_to_bottom ();
2787 for (set<boost::shared_ptr<Playlist> >::iterator i = playlists.begin(); i != playlists.end(); ++i) {
2788 vector<Command*> cmds;
2790 _session->add_commands (cmds);
2793 commit_reversible_command ();
2797 Editor::raise_region ()
2799 do_layer_operation (Raise);
2803 Editor::raise_region_to_top ()
2805 do_layer_operation (RaiseToTop);
2809 Editor::lower_region ()
2811 do_layer_operation (Lower);
2815 Editor::lower_region_to_bottom ()
2817 do_layer_operation (LowerToBottom);
2820 /** Show the region editor for the selected regions */
2822 Editor::show_region_properties ()
2824 selection->foreach_regionview (&RegionView::show_region_editor);
2827 /** Show the midi list editor for the selected MIDI regions */
2829 Editor::show_midi_list_editor ()
2831 selection->foreach_midi_regionview (&MidiRegionView::show_list_editor);
2835 Editor::rename_region ()
2837 RegionSelection rs = get_regions_from_selection_and_entered ();
2843 ArdourDialog d (_("Rename Region"), true, false);
2845 Label label (_("New name:"));
2848 hbox.set_spacing (6);
2849 hbox.pack_start (label, false, false);
2850 hbox.pack_start (entry, true, true);
2852 d.get_vbox()->set_border_width (12);
2853 d.get_vbox()->pack_start (hbox, false, false);
2855 d.add_button(Gtk::Stock::CANCEL, Gtk::RESPONSE_CANCEL);
2856 d.add_button(Gtk::Stock::OK, Gtk::RESPONSE_OK);
2858 d.set_size_request (300, -1);
2860 entry.set_text (rs.front()->region()->name());
2861 entry.select_region (0, -1);
2863 entry.signal_activate().connect (sigc::bind (sigc::mem_fun (d, &Dialog::response), RESPONSE_OK));
2869 int const ret = d.run();
2873 if (ret != RESPONSE_OK) {
2877 std::string str = entry.get_text();
2878 strip_whitespace_edges (str);
2880 rs.front()->region()->set_name (str);
2881 _regions->redisplay ();
2885 /** Start an audition of the first selected region */
2887 Editor::play_edit_range ()
2889 framepos_t start, end;
2891 if (get_edit_op_range (start, end)) {
2892 _session->request_bounded_roll (start, end);
2897 Editor::play_selected_region ()
2899 framepos_t start = max_framepos;
2902 RegionSelection rs = get_regions_from_selection_and_entered ();
2908 for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
2909 if ((*i)->region()->position() < start) {
2910 start = (*i)->region()->position();
2912 if ((*i)->region()->last_frame() + 1 > end) {
2913 end = (*i)->region()->last_frame() + 1;
2917 _session->request_bounded_roll (start, end);
2921 Editor::audition_playlist_region_standalone (boost::shared_ptr<Region> region)
2923 _session->audition_region (region);
2927 Editor::region_from_selection ()
2929 if (clicked_axisview == 0) {
2933 if (selection->time.empty()) {
2937 framepos_t start = selection->time[clicked_selection].start;
2938 framepos_t end = selection->time[clicked_selection].end;
2940 TrackViewList tracks = get_tracks_for_range_action ();
2942 framepos_t selection_cnt = end - start + 1;
2944 for (TrackSelection::iterator i = tracks.begin(); i != tracks.end(); ++i) {
2945 boost::shared_ptr<Region> current;
2946 boost::shared_ptr<Playlist> pl;
2947 framepos_t internal_start;
2950 if ((pl = (*i)->playlist()) == 0) {
2954 if ((current = pl->top_region_at (start)) == 0) {
2958 internal_start = start - current->position();
2959 RegionFactory::region_name (new_name, current->name(), true);
2963 plist.add (ARDOUR::Properties::start, current->start() + internal_start);
2964 plist.add (ARDOUR::Properties::length, selection_cnt);
2965 plist.add (ARDOUR::Properties::name, new_name);
2966 plist.add (ARDOUR::Properties::layer, 0);
2968 boost::shared_ptr<Region> region (RegionFactory::create (current, plist));
2973 Editor::create_region_from_selection (vector<boost::shared_ptr<Region> >& new_regions)
2975 if (selection->time.empty() || selection->tracks.empty()) {
2979 framepos_t start, end;
2980 if (clicked_selection) {
2981 start = selection->time[clicked_selection].start;
2982 end = selection->time[clicked_selection].end;
2984 start = selection->time.start();
2985 end = selection->time.end_frame();
2988 TrackViewList ts = selection->tracks.filter_to_unique_playlists ();
2989 sort_track_selection (ts);
2991 for (TrackSelection::iterator i = ts.begin(); i != ts.end(); ++i) {
2992 boost::shared_ptr<Region> current;
2993 boost::shared_ptr<Playlist> playlist;
2994 framepos_t internal_start;
2997 if ((playlist = (*i)->playlist()) == 0) {
3001 if ((current = playlist->top_region_at(start)) == 0) {
3005 internal_start = start - current->position();
3006 RegionFactory::region_name (new_name, current->name(), true);
3010 plist.add (ARDOUR::Properties::start, current->start() + internal_start);
3011 plist.add (ARDOUR::Properties::length, end - start + 1);
3012 plist.add (ARDOUR::Properties::name, new_name);
3014 new_regions.push_back (RegionFactory::create (current, plist));
3019 Editor::split_multichannel_region ()
3021 RegionSelection rs = get_regions_from_selection_and_entered ();
3027 vector< boost::shared_ptr<Region> > v;
3029 for (list<RegionView*>::iterator x = rs.begin(); x != rs.end(); ++x) {
3030 (*x)->region()->separate_by_channel (*_session, v);
3035 Editor::new_region_from_selection ()
3037 region_from_selection ();
3038 cancel_selection ();
3042 add_if_covered (RegionView* rv, const AudioRange* ar, RegionSelection* rs)
3044 switch (rv->region()->coverage (ar->start, ar->end - 1)) {
3045 // n.b. -1 because AudioRange::end is one past the end, but coverage expects inclusive ranges
3046 case Evoral::OverlapNone:
3054 * - selected tracks, or if there are none...
3055 * - tracks containing selected regions, or if there are none...
3060 Editor::get_tracks_for_range_action () const
3064 if (selection->tracks.empty()) {
3066 /* use tracks with selected regions */
3068 RegionSelection rs = selection->regions;
3070 for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
3071 TimeAxisView* tv = &(*i)->get_time_axis_view();
3073 if (!t.contains (tv)) {
3079 /* no regions and no tracks: use all tracks */
3085 t = selection->tracks;
3088 return t.filter_to_unique_playlists();
3092 Editor::separate_regions_between (const TimeSelection& ts)
3094 bool in_command = false;
3095 boost::shared_ptr<Playlist> playlist;
3096 RegionSelection new_selection;
3098 TrackViewList tmptracks = get_tracks_for_range_action ();
3099 sort_track_selection (tmptracks);
3101 for (TrackSelection::iterator i = tmptracks.begin(); i != tmptracks.end(); ++i) {
3103 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> ((*i));
3109 if (!rtv->is_track()) {
3113 /* no edits to destructive tracks */
3115 if (rtv->track()->destructive()) {
3119 if ((playlist = rtv->playlist()) != 0) {
3121 playlist->clear_changes ();
3123 /* XXX need to consider musical time selections here at some point */
3125 double speed = rtv->track()->speed();
3127 for (list<AudioRange>::const_iterator t = ts.begin(); t != ts.end(); ++t) {
3129 sigc::connection c = rtv->view()->RegionViewAdded.connect (
3130 sigc::mem_fun(*this, &Editor::collect_new_region_view));
3132 latest_regionviews.clear ();
3134 playlist->partition ((framepos_t)((*t).start * speed),
3135 (framepos_t)((*t).end * speed), false);
3139 if (!latest_regionviews.empty()) {
3141 rtv->view()->foreach_regionview (sigc::bind (
3142 sigc::ptr_fun (add_if_covered),
3143 &(*t), &new_selection));
3146 begin_reversible_command (_("separate"));
3150 /* pick up changes to existing regions */
3152 vector<Command*> cmds;
3153 playlist->rdiff (cmds);
3154 _session->add_commands (cmds);
3156 /* pick up changes to the playlist itself (adds/removes)
3159 _session->add_command(new StatefulDiffCommand (playlist));
3166 // selection->set (new_selection);
3168 commit_reversible_command ();
3172 struct PlaylistState {
3173 boost::shared_ptr<Playlist> playlist;
3177 /** Take tracks from get_tracks_for_range_action and cut any regions
3178 * on those tracks so that the tracks are empty over the time
3182 Editor::separate_region_from_selection ()
3184 /* preferentially use *all* ranges in the time selection if we're in range mode
3185 to allow discontiguous operation, since get_edit_op_range() currently
3186 returns a single range.
3189 if (!selection->time.empty()) {
3191 separate_regions_between (selection->time);
3198 if (get_edit_op_range (start, end)) {
3200 AudioRange ar (start, end, 1);
3204 separate_regions_between (ts);
3210 Editor::separate_region_from_punch ()
3212 Location* loc = _session->locations()->auto_punch_location();
3214 separate_regions_using_location (*loc);
3219 Editor::separate_region_from_loop ()
3221 Location* loc = _session->locations()->auto_loop_location();
3223 separate_regions_using_location (*loc);
3228 Editor::separate_regions_using_location (Location& loc)
3230 if (loc.is_mark()) {
3234 AudioRange ar (loc.start(), loc.end(), 1);
3239 separate_regions_between (ts);
3242 /** Separate regions under the selected region */
3244 Editor::separate_under_selected_regions ()
3246 vector<PlaylistState> playlists;
3250 rs = get_regions_from_selection_and_entered();
3252 if (!_session || rs.empty()) {
3256 begin_reversible_command (_("separate region under"));
3258 list<boost::shared_ptr<Region> > regions_to_remove;
3260 for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
3261 // we can't just remove the region(s) in this loop because
3262 // this removes them from the RegionSelection, and they thus
3263 // disappear from underneath the iterator, and the ++i above
3264 // SEGVs in a puzzling fashion.
3266 // so, first iterate over the regions to be removed from rs and
3267 // add them to the regions_to_remove list, and then
3268 // iterate over the list to actually remove them.
3270 regions_to_remove.push_back ((*i)->region());
3273 for (list<boost::shared_ptr<Region> >::iterator rl = regions_to_remove.begin(); rl != regions_to_remove.end(); ++rl) {
3275 boost::shared_ptr<Playlist> playlist = (*rl)->playlist();
3278 // is this check necessary?
3282 vector<PlaylistState>::iterator i;
3284 //only take state if this is a new playlist.
3285 for (i = playlists.begin(); i != playlists.end(); ++i) {
3286 if ((*i).playlist == playlist) {
3291 if (i == playlists.end()) {
3293 PlaylistState before;
3294 before.playlist = playlist;
3295 before.before = &playlist->get_state();
3297 playlist->freeze ();
3298 playlists.push_back(before);
3301 //Partition on the region bounds
3302 playlist->partition ((*rl)->first_frame() - 1, (*rl)->last_frame() + 1, true);
3304 //Re-add region that was just removed due to the partition operation
3305 playlist->add_region( (*rl), (*rl)->first_frame() );
3308 vector<PlaylistState>::iterator pl;
3310 for (pl = playlists.begin(); pl != playlists.end(); ++pl) {
3311 (*pl).playlist->thaw ();
3312 _session->add_command(new MementoCommand<Playlist>(*(*pl).playlist, (*pl).before, &(*pl).playlist->get_state()));
3315 commit_reversible_command ();
3319 Editor::crop_region_to_selection ()
3321 if (!selection->time.empty()) {
3323 crop_region_to (selection->time.start(), selection->time.end_frame());
3330 if (get_edit_op_range (start, end)) {
3331 crop_region_to (start, end);
3338 Editor::crop_region_to (framepos_t start, framepos_t end)
3340 vector<boost::shared_ptr<Playlist> > playlists;
3341 boost::shared_ptr<Playlist> playlist;
3344 if (selection->tracks.empty()) {
3345 ts = track_views.filter_to_unique_playlists();
3347 ts = selection->tracks.filter_to_unique_playlists ();
3350 sort_track_selection (ts);
3352 for (TrackSelection::iterator i = ts.begin(); i != ts.end(); ++i) {
3354 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> ((*i));
3360 boost::shared_ptr<Track> t = rtv->track();
3362 if (t != 0 && ! t->destructive()) {
3364 if ((playlist = rtv->playlist()) != 0) {
3365 playlists.push_back (playlist);
3370 if (playlists.empty()) {
3375 framepos_t new_start;
3377 framecnt_t new_length;
3378 bool in_command = false;
3380 for (vector<boost::shared_ptr<Playlist> >::iterator i = playlists.begin(); i != playlists.end(); ++i) {
3382 /* Only the top regions at start and end have to be cropped */
3383 boost::shared_ptr<Region> region_at_start = (*i)->top_region_at(start);
3384 boost::shared_ptr<Region> region_at_end = (*i)->top_region_at(end);
3386 vector<boost::shared_ptr<Region> > regions;
3388 if (region_at_start != 0) {
3389 regions.push_back (region_at_start);
3391 if (region_at_end != 0) {
3392 regions.push_back (region_at_end);
3395 /* now adjust lengths */
3396 for (vector<boost::shared_ptr<Region> >::iterator i = regions.begin(); i != regions.end(); ++i) {
3398 pos = (*i)->position();
3399 new_start = max (start, pos);
3400 if (max_framepos - pos > (*i)->length()) {
3401 new_end = pos + (*i)->length() - 1;
3403 new_end = max_framepos;
3405 new_end = min (end, new_end);
3406 new_length = new_end - new_start + 1;
3409 begin_reversible_command (_("trim to selection"));
3412 (*i)->clear_changes ();
3413 (*i)->trim_to (new_start, new_length);
3414 _session->add_command (new StatefulDiffCommand (*i));
3419 commit_reversible_command ();
3424 Editor::region_fill_track ()
3426 boost::shared_ptr<Playlist> playlist;
3427 RegionSelection regions = get_regions_from_selection_and_entered ();
3428 RegionSelection foo;
3430 framepos_t const end = _session->current_end_frame ();
3432 if (regions.empty () || regions.end_frame () + 1 >= end) {
3436 framepos_t const start_frame = regions.start ();
3437 framepos_t const end_frame = regions.end_frame ();
3438 framecnt_t const gap = end_frame - start_frame + 1;
3440 begin_reversible_command (Operations::region_fill);
3442 selection->clear_regions ();
3444 for (RegionSelection::iterator i = regions.begin(); i != regions.end(); ++i) {
3446 boost::shared_ptr<Region> r ((*i)->region());
3448 TimeAxisView& tv = (*i)->get_time_axis_view();
3449 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (&tv);
3450 latest_regionviews.clear ();
3451 sigc::connection c = rtv->view()->RegionViewAdded.connect (sigc::mem_fun(*this, &Editor::collect_new_region_view));
3453 framepos_t const position = end_frame + (r->first_frame() - start_frame + 1);
3454 playlist = (*i)->region()->playlist();
3455 playlist->clear_changes ();
3456 playlist->duplicate_until (r, position, gap, end);
3457 _session->add_command(new StatefulDiffCommand (playlist));
3461 foo.insert (foo.end(), latest_regionviews.begin(), latest_regionviews.end());
3465 selection->set (foo);
3468 commit_reversible_command ();
3472 Editor::set_region_sync_position ()
3474 set_sync_point (get_preferred_edit_position (), get_regions_from_selection_and_edit_point ());
3478 Editor::set_sync_point (framepos_t where, const RegionSelection& rs)
3480 bool in_command = false;
3482 for (RegionSelection::const_iterator r = rs.begin(); r != rs.end(); ++r) {
3484 if (!(*r)->region()->covers (where)) {
3488 boost::shared_ptr<Region> region ((*r)->region());
3491 begin_reversible_command (_("set sync point"));
3495 region->clear_changes ();
3496 region->set_sync_position (where);
3497 _session->add_command(new StatefulDiffCommand (region));
3501 commit_reversible_command ();
3505 /** Remove the sync positions of the selection */
3507 Editor::remove_region_sync ()
3509 RegionSelection rs = get_regions_from_selection_and_entered ();
3515 begin_reversible_command (_("remove region sync"));
3517 for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
3519 (*i)->region()->clear_changes ();
3520 (*i)->region()->clear_sync_position ();
3521 _session->add_command(new StatefulDiffCommand ((*i)->region()));
3524 commit_reversible_command ();
3528 Editor::naturalize_region ()
3530 RegionSelection rs = get_regions_from_selection_and_entered ();
3536 if (rs.size() > 1) {
3537 begin_reversible_command (_("move regions to original position"));
3539 begin_reversible_command (_("move region to original position"));
3542 for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
3543 (*i)->region()->clear_changes ();
3544 (*i)->region()->move_to_natural_position ();
3545 _session->add_command (new StatefulDiffCommand ((*i)->region()));
3548 commit_reversible_command ();
3552 Editor::align_regions (RegionPoint what)
3554 RegionSelection const rs = get_regions_from_selection_and_edit_point ();
3560 begin_reversible_command (_("align selection"));
3562 framepos_t const position = get_preferred_edit_position ();
3564 for (RegionSelection::const_iterator i = rs.begin(); i != rs.end(); ++i) {
3565 align_region_internal ((*i)->region(), what, position);
3568 commit_reversible_command ();
3571 struct RegionSortByTime {
3572 bool operator() (const RegionView* a, const RegionView* b) {
3573 return a->region()->position() < b->region()->position();
3578 Editor::align_regions_relative (RegionPoint point)
3580 RegionSelection const rs = get_regions_from_selection_and_edit_point ();
3586 framepos_t const position = get_preferred_edit_position ();
3588 framepos_t distance = 0;
3592 list<RegionView*> sorted;
3593 rs.by_position (sorted);
3595 boost::shared_ptr<Region> r ((*sorted.begin())->region());
3600 if (position > r->position()) {
3601 distance = position - r->position();
3603 distance = r->position() - position;
3609 if (position > r->last_frame()) {
3610 distance = position - r->last_frame();
3611 pos = r->position() + distance;
3613 distance = r->last_frame() - position;
3614 pos = r->position() - distance;
3620 pos = r->adjust_to_sync (position);
3621 if (pos > r->position()) {
3622 distance = pos - r->position();
3624 distance = r->position() - pos;
3630 if (pos == r->position()) {
3634 begin_reversible_command (_("align selection (relative)"));
3636 /* move first one specially */
3638 r->clear_changes ();
3639 r->set_position (pos);
3640 _session->add_command(new StatefulDiffCommand (r));
3642 /* move rest by the same amount */
3646 for (list<RegionView*>::iterator i = sorted.begin(); i != sorted.end(); ++i) {
3648 boost::shared_ptr<Region> region ((*i)->region());
3650 region->clear_changes ();
3653 region->set_position (region->position() + distance);
3655 region->set_position (region->position() - distance);
3658 _session->add_command(new StatefulDiffCommand (region));
3662 commit_reversible_command ();
3666 Editor::align_region (boost::shared_ptr<Region> region, RegionPoint point, framepos_t position)
3668 begin_reversible_command (_("align region"));
3669 align_region_internal (region, point, position);
3670 commit_reversible_command ();
3674 Editor::align_region_internal (boost::shared_ptr<Region> region, RegionPoint point, framepos_t position)
3676 region->clear_changes ();
3680 region->set_position (region->adjust_to_sync (position));
3684 if (position > region->length()) {
3685 region->set_position (position - region->length());
3690 region->set_position (position);
3694 _session->add_command(new StatefulDiffCommand (region));
3698 Editor::trim_region_front ()
3704 Editor::trim_region_back ()
3706 trim_region (false);
3710 Editor::trim_region (bool front)
3712 framepos_t where = get_preferred_edit_position();
3713 RegionSelection rs = get_regions_from_selection_and_edit_point ();
3719 begin_reversible_command (front ? _("trim front") : _("trim back"));
3721 for (list<RegionView*>::const_iterator i = rs.by_layer().begin(); i != rs.by_layer().end(); ++i) {
3722 if (!(*i)->region()->locked()) {
3724 (*i)->region()->clear_changes ();
3727 (*i)->region()->trim_front (where);
3729 (*i)->region()->trim_end (where);
3732 _session->add_command (new StatefulDiffCommand ((*i)->region()));
3736 commit_reversible_command ();
3739 /** Trim the end of the selected regions to the position of the edit cursor */
3741 Editor::trim_region_to_loop ()
3743 Location* loc = _session->locations()->auto_loop_location();
3747 trim_region_to_location (*loc, _("trim to loop"));
3751 Editor::trim_region_to_punch ()
3753 Location* loc = _session->locations()->auto_punch_location();
3757 trim_region_to_location (*loc, _("trim to punch"));
3761 Editor::trim_region_to_location (const Location& loc, const char* str)
3763 RegionSelection rs = get_regions_from_selection_and_entered ();
3764 bool in_command = false;
3766 for (RegionSelection::iterator x = rs.begin(); x != rs.end(); ++x) {
3767 RegionView* rv = (*x);
3769 /* require region to span proposed trim */
3770 switch (rv->region()->coverage (loc.start(), loc.end())) {
3771 case Evoral::OverlapInternal:
3777 RouteTimeAxisView* tav = dynamic_cast<RouteTimeAxisView*> (&rv->get_time_axis_view());
3786 if (tav->track() != 0) {
3787 speed = tav->track()->speed();
3790 start = session_frame_to_track_frame (loc.start(), speed);
3791 end = session_frame_to_track_frame (loc.end(), speed);
3793 rv->region()->clear_changes ();
3794 rv->region()->trim_to (start, (end - start));
3797 begin_reversible_command (str);
3800 _session->add_command(new StatefulDiffCommand (rv->region()));
3804 commit_reversible_command ();
3809 Editor::trim_region_to_previous_region_end ()
3811 return trim_to_region(false);
3815 Editor::trim_region_to_next_region_start ()
3817 return trim_to_region(true);
3821 Editor::trim_to_region(bool forward)
3823 RegionSelection rs = get_regions_from_selection_and_entered ();
3824 bool in_command = false;
3826 boost::shared_ptr<Region> next_region;
3828 for (RegionSelection::iterator x = rs.begin(); x != rs.end(); ++x) {
3830 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (*x);
3836 AudioTimeAxisView* atav = dynamic_cast<AudioTimeAxisView*> (&arv->get_time_axis_view());
3844 if (atav->track() != 0) {
3845 speed = atav->track()->speed();
3849 boost::shared_ptr<Region> region = arv->region();
3850 boost::shared_ptr<Playlist> playlist (region->playlist());
3852 region->clear_changes ();
3856 next_region = playlist->find_next_region (region->first_frame(), Start, 1);
3862 region->trim_end((framepos_t) ( (next_region->first_frame() - 1) * speed));
3863 arv->region_changed (PropertyChange (ARDOUR::Properties::length));
3867 next_region = playlist->find_next_region (region->first_frame(), Start, 0);
3873 region->trim_front((framepos_t) ((next_region->last_frame() + 1) * speed));
3875 arv->region_changed (ARDOUR::bounds_change);
3879 begin_reversible_command (_("trim to region"));
3882 _session->add_command(new StatefulDiffCommand (region));
3886 commit_reversible_command ();
3891 Editor::unfreeze_route ()
3893 if (clicked_routeview == 0 || !clicked_routeview->is_track()) {
3897 clicked_routeview->track()->unfreeze ();
3901 Editor::_freeze_thread (void* arg)
3903 return static_cast<Editor*>(arg)->freeze_thread ();
3907 Editor::freeze_thread ()
3909 /* create event pool because we may need to talk to the session */
3910 SessionEvent::create_per_thread_pool ("freeze events", 64);
3911 /* create per-thread buffers for process() tree to use */
3912 clicked_routeview->audio_track()->freeze_me (*current_interthread_info);
3913 current_interthread_info->done = true;
3918 Editor::freeze_route ()
3924 /* stop transport before we start. this is important */
3926 _session->request_transport_speed (0.0);
3928 /* wait for just a little while, because the above call is asynchronous */
3930 Glib::usleep (250000);
3932 if (clicked_routeview == 0 || !clicked_routeview->is_audio_track()) {
3936 if (!clicked_routeview->track()->bounceable (clicked_routeview->track()->main_outs(), true)) {
3938 _("This track/bus cannot be frozen because the signal adds or loses channels before reaching the outputs.\n"
3939 "This is typically caused by plugins that generate stereo output from mono input or vice versa.")
3941 d.set_title (_("Cannot freeze"));
3946 if (clicked_routeview->track()->has_external_redirects()) {
3947 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"
3948 "Freezing will only process the signal as far as the first send/insert/return."),
3949 clicked_routeview->track()->name()), true, MESSAGE_INFO, BUTTONS_NONE, true);
3951 d.add_button (_("Freeze anyway"), Gtk::RESPONSE_OK);
3952 d.add_button (_("Don't freeze"), Gtk::RESPONSE_CANCEL);
3953 d.set_title (_("Freeze Limits"));
3955 int response = d.run ();
3958 case Gtk::RESPONSE_CANCEL:
3965 InterThreadInfo itt;
3966 current_interthread_info = &itt;
3968 InterthreadProgressWindow ipw (current_interthread_info, _("Freeze"), _("Cancel Freeze"));
3970 pthread_create_and_store (X_("freezer"), &itt.thread, _freeze_thread, this);
3972 CursorContext::Handle cursor_ctx = CursorContext::create(*this, _cursors->wait);
3974 while (!itt.done && !itt.cancel) {
3975 gtk_main_iteration ();
3978 pthread_join (itt.thread, 0);
3979 current_interthread_info = 0;
3983 Editor::bounce_range_selection (bool replace, bool enable_processing)
3985 if (selection->time.empty()) {
3989 TrackSelection views = selection->tracks;
3991 for (TrackViewList::iterator i = views.begin(); i != views.end(); ++i) {
3993 if (enable_processing) {
3995 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (*i);
3997 if (rtv && rtv->track() && replace && enable_processing && !rtv->track()->bounceable (rtv->track()->main_outs(), false)) {
3999 _("You can't perform this operation because the processing of the signal "
4000 "will cause one or more of the tracks to end up with a region with more channels than this track has inputs.\n\n"
4001 "You can do this without processing, which is a different operation.")
4003 d.set_title (_("Cannot bounce"));
4010 framepos_t start = selection->time[clicked_selection].start;
4011 framepos_t end = selection->time[clicked_selection].end;
4012 framepos_t cnt = end - start + 1;
4013 bool in_command = false;
4015 for (TrackViewList::iterator i = views.begin(); i != views.end(); ++i) {
4017 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (*i);
4023 boost::shared_ptr<Playlist> playlist;
4025 if ((playlist = rtv->playlist()) == 0) {
4029 InterThreadInfo itt;
4031 playlist->clear_changes ();
4032 playlist->clear_owned_changes ();
4034 boost::shared_ptr<Region> r;
4036 if (enable_processing) {
4037 r = rtv->track()->bounce_range (start, start+cnt, itt, rtv->track()->main_outs(), false);
4039 r = rtv->track()->bounce_range (start, start+cnt, itt, boost::shared_ptr<Processor>(), false);
4047 list<AudioRange> ranges;
4048 ranges.push_back (AudioRange (start, start+cnt, 0));
4049 playlist->cut (ranges); // discard result
4050 playlist->add_region (r, start);
4054 begin_reversible_command (_("bounce range"));
4057 vector<Command*> cmds;
4058 playlist->rdiff (cmds);
4059 _session->add_commands (cmds);
4061 _session->add_command (new StatefulDiffCommand (playlist));
4065 commit_reversible_command ();
4069 /** Delete selected regions, automation points or a time range */
4073 //special case: if the user is pointing in the editor/mixer strip, they may be trying to delete a plugin.
4074 //we need this because the editor-mixer strip is in the editor window, so it doesn't get the bindings from the mix window
4075 bool deleted = false;
4076 if ( current_mixer_strip && current_mixer_strip == MixerStrip::entered_mixer_strip() )
4077 deleted = current_mixer_strip->delete_processors ();
4083 /** Cut selected regions, automation points or a time range */
4090 /** Copy selected regions, automation points or a time range */
4098 /** @return true if a Cut, Copy or Clear is possible */
4100 Editor::can_cut_copy () const
4102 if (!selection->time.empty() || !selection->regions.empty() || !selection->points.empty())
4109 /** Cut, copy or clear selected regions, automation points or a time range.
4110 * @param op Operation (Delete, Cut, Copy or Clear)
4113 Editor::cut_copy (CutCopyOp op)
4115 /* only cancel selection if cut/copy is successful.*/
4121 opname = _("delete");
4130 opname = _("clear");
4134 /* if we're deleting something, and the mouse is still pressed,
4135 the thing we started a drag for will be gone when we release
4136 the mouse button(s). avoid this. see part 2 at the end of
4140 if (op == Delete || op == Cut || op == Clear) {
4141 if (_drags->active ()) {
4146 if ( op != Delete ) //"Delete" doesn't change copy/paste buf
4147 cut_buffer->clear ();
4149 if (entered_marker) {
4151 /* cut/delete op while pointing at a marker */
4154 Location* loc = find_location_from_marker (entered_marker, ignored);
4156 if (_session && loc) {
4157 entered_marker = NULL;
4158 Glib::signal_idle().connect (sigc::bind (sigc::mem_fun(*this, &Editor::really_remove_marker), loc));
4165 switch (mouse_mode) {
4168 begin_reversible_command (opname + ' ' + X_("MIDI"));
4170 commit_reversible_command ();
4176 bool did_edit = false;
4178 if (!selection->regions.empty() || !selection->points.empty()) {
4179 begin_reversible_command (opname + ' ' + _("objects"));
4182 if (!selection->regions.empty()) {
4183 cut_copy_regions (op, selection->regions);
4185 if (op == Cut || op == Delete) {
4186 selection->clear_regions ();
4190 if (!selection->points.empty()) {
4191 cut_copy_points (op);
4193 if (op == Cut || op == Delete) {
4194 selection->clear_points ();
4197 } else if (selection->time.empty()) {
4198 framepos_t start, end;
4199 /* no time selection, see if we can get an edit range
4202 if (get_edit_op_range (start, end)) {
4203 selection->set (start, end);
4205 } else if (!selection->time.empty()) {
4206 begin_reversible_command (opname + ' ' + _("range"));
4209 cut_copy_ranges (op);
4211 if (op == Cut || op == Delete) {
4212 selection->clear_time ();
4217 /* reset repeated paste state */
4220 commit_reversible_command ();
4223 if (op == Delete || op == Cut || op == Clear) {
4229 struct AutomationRecord {
4230 AutomationRecord () : state (0) , line(NULL) {}
4231 AutomationRecord (XMLNode* s, const AutomationLine* l) : state (s) , line (l) {}
4233 XMLNode* state; ///< state before any operation
4234 const AutomationLine* line; ///< line this came from
4235 boost::shared_ptr<Evoral::ControlList> copy; ///< copied events for the cut buffer
4237 struct PointsSelectionPositionSorter {
4238 bool operator() (ControlPoint* a, ControlPoint* b) {
4239 return (*(a->model()))->when < (*(b->model()))->when;
4242 /** Cut, copy or clear selected automation points.
4243 * @param op Operation (Cut, Copy or Clear)
4246 Editor::cut_copy_points (Editing::CutCopyOp op, Evoral::Beats earliest, bool midi)
4248 if (selection->points.empty ()) {
4252 /* XXX: not ideal, as there may be more than one track involved in the point selection */
4253 _last_cut_copy_source_track = &selection->points.front()->line().trackview;
4255 /* Keep a record of the AutomationLists that we end up using in this operation */
4256 typedef std::map<boost::shared_ptr<AutomationList>, AutomationRecord> Lists;
4259 /* user could select points in any order */
4260 selection->points.sort(PointsSelectionPositionSorter ());
4262 /* Go through all selected points, making an AutomationRecord for each distinct AutomationList */
4263 for (PointSelection::iterator sel_point = selection->points.begin(); sel_point != selection->points.end(); ++sel_point) {
4264 const AutomationLine& line = (*sel_point)->line();
4265 const boost::shared_ptr<AutomationList> al = line.the_list();
4266 if (lists.find (al) == lists.end ()) {
4267 /* We haven't seen this list yet, so make a record for it. This includes
4268 taking a copy of its current state, in case this is needed for undo later.
4270 lists[al] = AutomationRecord (&al->get_state (), &line);
4274 if (op == Cut || op == Copy) {
4275 /* This operation will involve putting things in the cut buffer, so create an empty
4276 ControlList for each of our source lists to put the cut buffer data in.
4278 for (Lists::iterator i = lists.begin(); i != lists.end(); ++i) {
4279 i->second.copy = i->first->create (i->first->parameter (), i->first->descriptor());
4282 /* Add all selected points to the relevant copy ControlLists */
4283 MusicFrame start (std::numeric_limits<framepos_t>::max(), 0);
4284 for (PointSelection::iterator sel_point = selection->points.begin(); sel_point != selection->points.end(); ++sel_point) {
4285 boost::shared_ptr<AutomationList> al = (*sel_point)->line().the_list();
4286 AutomationList::const_iterator ctrl_evt = (*sel_point)->model ();
4288 lists[al].copy->fast_simple_add ((*ctrl_evt)->when, (*ctrl_evt)->value);
4290 /* Update earliest MIDI start time in beats */
4291 earliest = std::min(earliest, Evoral::Beats((*ctrl_evt)->when));
4293 /* Update earliest session start time in frames */
4294 start.frame = std::min(start.frame, (*sel_point)->line().session_position(ctrl_evt));
4298 /* Snap start time backwards, so copy/paste is snap aligned. */
4300 if (earliest == Evoral::Beats::max()) {
4301 earliest = Evoral::Beats(); // Weird... don't offset
4303 earliest.round_down_to_beat();
4305 if (start.frame == std::numeric_limits<double>::max()) {
4306 start.frame = 0; // Weird... don't offset
4308 snap_to(start, RoundDownMaybe);
4311 const double line_offset = midi ? earliest.to_double() : start.frame;
4312 for (Lists::iterator i = lists.begin(); i != lists.end(); ++i) {
4313 /* Correct this copy list so that it is relative to the earliest
4314 start time, so relative ordering between points is preserved
4315 when copying from several lists and the paste starts at the
4316 earliest copied piece of data. */
4317 boost::shared_ptr<Evoral::ControlList> &al_cpy = i->second.copy;
4318 for (AutomationList::iterator ctrl_evt = al_cpy->begin(); ctrl_evt != al_cpy->end(); ++ctrl_evt) {
4319 (*ctrl_evt)->when -= line_offset;
4322 /* And add it to the cut buffer */
4323 cut_buffer->add (al_cpy);
4327 if (op == Delete || op == Cut) {
4328 /* This operation needs to remove things from the main AutomationList, so do that now */
4330 for (Lists::iterator i = lists.begin(); i != lists.end(); ++i) {
4331 i->first->freeze ();
4334 /* Remove each selected point from its AutomationList */
4335 for (PointSelection::iterator sel_point = selection->points.begin(); sel_point != selection->points.end(); ++sel_point) {
4336 AutomationLine& line = (*sel_point)->line ();
4337 boost::shared_ptr<AutomationList> al = line.the_list();
4341 if (dynamic_cast<AudioRegionGainLine*> (&line)) {
4342 /* removing of first and last gain point in region gain lines is prohibited*/
4343 if (line.is_last_point (*(*sel_point)) || line.is_first_point (*(*sel_point))) {
4349 al->erase ((*sel_point)->model ());
4353 /* Thaw the lists and add undo records for them */
4354 for (Lists::iterator i = lists.begin(); i != lists.end(); ++i) {
4355 boost::shared_ptr<AutomationList> al = i->first;
4357 _session->add_command (new MementoCommand<AutomationList> (*al.get(), i->second.state, &(al->get_state ())));
4362 /** Cut, copy or clear selected automation points.
4363 * @param op Operation (Cut, Copy or Clear)
4366 Editor::cut_copy_midi (CutCopyOp op)
4368 Evoral::Beats earliest = Evoral::Beats::max();
4369 for (MidiRegionSelection::iterator i = selection->midi_regions.begin(); i != selection->midi_regions.end(); ++i) {
4370 MidiRegionView* mrv = dynamic_cast<MidiRegionView*>(*i);
4372 if (!mrv->selection().empty()) {
4373 earliest = std::min(earliest, (*mrv->selection().begin())->note()->time());
4375 mrv->cut_copy_clear (op);
4377 /* XXX: not ideal, as there may be more than one track involved in the selection */
4378 _last_cut_copy_source_track = &mrv->get_time_axis_view();
4382 if (!selection->points.empty()) {
4383 cut_copy_points (op, earliest, true);
4384 if (op == Cut || op == Delete) {
4385 selection->clear_points ();
4390 struct lt_playlist {
4391 bool operator () (const PlaylistState& a, const PlaylistState& b) {
4392 return a.playlist < b.playlist;
4396 struct PlaylistMapping {
4398 boost::shared_ptr<Playlist> pl;
4400 PlaylistMapping (TimeAxisView* tvp) : tv (tvp) {}
4403 /** Remove `clicked_regionview' */
4405 Editor::remove_clicked_region ()
4407 if (clicked_routeview == 0 || clicked_regionview == 0) {
4411 begin_reversible_command (_("remove region"));
4413 boost::shared_ptr<Playlist> playlist = clicked_routeview->playlist();
4415 playlist->clear_changes ();
4416 playlist->clear_owned_changes ();
4417 playlist->remove_region (clicked_regionview->region());
4418 if (Config->get_edit_mode() == Ripple)
4419 playlist->ripple (clicked_regionview->region()->position(), -clicked_regionview->region()->length(), boost::shared_ptr<Region>());
4421 /* We might have removed regions, which alters other regions' layering_index,
4422 so we need to do a recursive diff here.
4424 vector<Command*> cmds;
4425 playlist->rdiff (cmds);
4426 _session->add_commands (cmds);
4428 _session->add_command(new StatefulDiffCommand (playlist));
4429 commit_reversible_command ();
4433 /** Remove the selected regions */
4435 Editor::remove_selected_regions ()
4437 RegionSelection rs = get_regions_from_selection_and_entered ();
4439 if (!_session || rs.empty()) {
4443 list<boost::shared_ptr<Region> > regions_to_remove;
4445 for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
4446 // we can't just remove the region(s) in this loop because
4447 // this removes them from the RegionSelection, and they thus
4448 // disappear from underneath the iterator, and the ++i above
4449 // SEGVs in a puzzling fashion.
4451 // so, first iterate over the regions to be removed from rs and
4452 // add them to the regions_to_remove list, and then
4453 // iterate over the list to actually remove them.
4455 regions_to_remove.push_back ((*i)->region());
4458 vector<boost::shared_ptr<Playlist> > playlists;
4460 for (list<boost::shared_ptr<Region> >::iterator rl = regions_to_remove.begin(); rl != regions_to_remove.end(); ++rl) {
4462 boost::shared_ptr<Playlist> playlist = (*rl)->playlist();
4465 // is this check necessary?
4469 /* get_regions_from_selection_and_entered() guarantees that
4470 the playlists involved are unique, so there is no need
4474 playlists.push_back (playlist);
4476 playlist->clear_changes ();
4477 playlist->clear_owned_changes ();
4478 playlist->freeze ();
4479 playlist->remove_region (*rl);
4480 if (Config->get_edit_mode() == Ripple)
4481 playlist->ripple ((*rl)->position(), -(*rl)->length(), boost::shared_ptr<Region>());
4485 vector<boost::shared_ptr<Playlist> >::iterator pl;
4486 bool in_command = false;
4488 for (pl = playlists.begin(); pl != playlists.end(); ++pl) {
4491 /* We might have removed regions, which alters other regions' layering_index,
4492 so we need to do a recursive diff here.
4496 begin_reversible_command (_("remove region"));
4499 vector<Command*> cmds;
4500 (*pl)->rdiff (cmds);
4501 _session->add_commands (cmds);
4503 _session->add_command(new StatefulDiffCommand (*pl));
4507 commit_reversible_command ();
4511 /** Cut, copy or clear selected regions.
4512 * @param op Operation (Cut, Copy or Clear)
4515 Editor::cut_copy_regions (CutCopyOp op, RegionSelection& rs)
4517 /* we can't use a std::map here because the ordering is important, and we can't trivially sort
4518 a map when we want ordered access to both elements. i think.
4521 vector<PlaylistMapping> pmap;
4523 framepos_t first_position = max_framepos;
4525 typedef set<boost::shared_ptr<Playlist> > FreezeList;
4526 FreezeList freezelist;
4528 /* get ordering correct before we cut/copy */
4530 rs.sort_by_position_and_track ();
4532 for (RegionSelection::iterator x = rs.begin(); x != rs.end(); ++x) {
4534 first_position = min ((framepos_t) (*x)->region()->position(), first_position);
4536 if (op == Cut || op == Clear || op == Delete) {
4537 boost::shared_ptr<Playlist> pl = (*x)->region()->playlist();
4540 FreezeList::iterator fl;
4542 // only take state if this is a new playlist.
4543 for (fl = freezelist.begin(); fl != freezelist.end(); ++fl) {
4549 if (fl == freezelist.end()) {
4550 pl->clear_changes();
4551 pl->clear_owned_changes ();
4553 freezelist.insert (pl);
4558 TimeAxisView* tv = &(*x)->get_time_axis_view();
4559 vector<PlaylistMapping>::iterator z;
4561 for (z = pmap.begin(); z != pmap.end(); ++z) {
4562 if ((*z).tv == tv) {
4567 if (z == pmap.end()) {
4568 pmap.push_back (PlaylistMapping (tv));
4572 for (RegionSelection::iterator x = rs.begin(); x != rs.end(); ) {
4574 boost::shared_ptr<Playlist> pl = (*x)->region()->playlist();
4577 /* region not yet associated with a playlist (e.g. unfinished
4584 TimeAxisView& tv = (*x)->get_time_axis_view();
4585 boost::shared_ptr<Playlist> npl;
4586 RegionSelection::iterator tmp;
4593 vector<PlaylistMapping>::iterator z;
4595 for (z = pmap.begin(); z != pmap.end(); ++z) {
4596 if ((*z).tv == &tv) {
4601 assert (z != pmap.end());
4604 npl = PlaylistFactory::create (pl->data_type(), *_session, "cutlist", true);
4612 boost::shared_ptr<Region> r = (*x)->region();
4613 boost::shared_ptr<Region> _xx;
4619 pl->remove_region (r);
4620 if (Config->get_edit_mode() == Ripple)
4621 pl->ripple (r->position(), -r->length(), boost::shared_ptr<Region>());
4625 _xx = RegionFactory::create (r);
4626 npl->add_region (_xx, r->position() - first_position);
4627 pl->remove_region (r);
4628 if (Config->get_edit_mode() == Ripple)
4629 pl->ripple (r->position(), -r->length(), boost::shared_ptr<Region>());
4633 /* copy region before adding, so we're not putting same object into two different playlists */
4634 npl->add_region (RegionFactory::create (r), r->position() - first_position);
4638 pl->remove_region (r);
4639 if (Config->get_edit_mode() == Ripple)
4640 pl->ripple (r->position(), -r->length(), boost::shared_ptr<Region>());
4649 list<boost::shared_ptr<Playlist> > foo;
4651 /* the pmap is in the same order as the tracks in which selected regions occurred */
4653 for (vector<PlaylistMapping>::iterator i = pmap.begin(); i != pmap.end(); ++i) {
4656 foo.push_back ((*i).pl);
4661 cut_buffer->set (foo);
4665 _last_cut_copy_source_track = 0;
4667 _last_cut_copy_source_track = pmap.front().tv;
4671 for (FreezeList::iterator pl = freezelist.begin(); pl != freezelist.end(); ++pl) {
4674 /* We might have removed regions, which alters other regions' layering_index,
4675 so we need to do a recursive diff here.
4677 vector<Command*> cmds;
4678 (*pl)->rdiff (cmds);
4679 _session->add_commands (cmds);
4681 _session->add_command (new StatefulDiffCommand (*pl));
4686 Editor::cut_copy_ranges (CutCopyOp op)
4688 TrackViewList ts = selection->tracks.filter_to_unique_playlists ();
4690 /* Sort the track selection now, so that it if is used, the playlists
4691 selected by the calls below to cut_copy_clear are in the order that
4692 their tracks appear in the editor. This makes things like paste
4693 of ranges work properly.
4696 sort_track_selection (ts);
4699 if (!entered_track) {
4702 ts.push_back (entered_track);
4705 for (TrackViewList::iterator i = ts.begin(); i != ts.end(); ++i) {
4706 (*i)->cut_copy_clear (*selection, op);
4711 Editor::paste (float times, bool from_context)
4713 DEBUG_TRACE (DEBUG::CutNPaste, "paste to preferred edit pos\n");
4714 MusicFrame where (get_preferred_edit_position (EDIT_IGNORE_NONE, from_context), 0);
4715 paste_internal (where.frame, times, 0);
4719 Editor::mouse_paste ()
4721 MusicFrame where (0, 0);
4723 if (!mouse_frame (where.frame, ignored)) {
4728 paste_internal (where.frame, 1, where.division);
4732 Editor::paste_internal (framepos_t position, float times, const int32_t sub_num)
4734 DEBUG_TRACE (DEBUG::CutNPaste, string_compose ("apparent paste position is %1\n", position));
4736 if (cut_buffer->empty(internal_editing())) {
4740 if (position == max_framepos) {
4741 position = get_preferred_edit_position();
4742 DEBUG_TRACE (DEBUG::CutNPaste, string_compose ("preferred edit position is %1\n", position));
4745 if (position == last_paste_pos) {
4746 /* repeated paste in the same position */
4749 /* paste in new location, reset repeated paste state */
4751 last_paste_pos = position;
4754 /* get everything in the correct order */
4757 if (!selection->tracks.empty()) {
4758 /* If there is a track selection, paste into exactly those tracks and
4759 only those tracks. This allows the user to be explicit and override
4760 the below "do the reasonable thing" logic. */
4761 ts = selection->tracks.filter_to_unique_playlists ();
4762 sort_track_selection (ts);
4764 /* Figure out which track to base the paste at. */
4765 TimeAxisView* base_track = NULL;
4766 if (_edit_point == Editing::EditAtMouse && entered_track) {
4767 /* With the mouse edit point, paste onto the track under the mouse. */
4768 base_track = entered_track;
4769 } else if (_edit_point == Editing::EditAtMouse && entered_regionview) {
4770 /* With the mouse edit point, paste onto the track of the region under the mouse. */
4771 base_track = &entered_regionview->get_time_axis_view();
4772 } else if (_last_cut_copy_source_track) {
4773 /* Paste to the track that the cut/copy came from (see mantis #333). */
4774 base_track = _last_cut_copy_source_track;
4776 /* This is "impossible" since we've copied... well, do nothing. */
4780 /* Walk up to parent if necessary, so base track is a route. */
4781 while (base_track->get_parent()) {
4782 base_track = base_track->get_parent();
4785 /* Add base track and all tracks below it. The paste logic will select
4786 the appropriate object types from the cut buffer in relative order. */
4787 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
4788 if ((*i)->order() >= base_track->order()) {
4793 /* Sort tracks so the nth track of type T will pick the nth object of type T. */
4794 sort_track_selection (ts);
4796 /* Add automation children of each track in order, for pasting several lines. */
4797 for (TrackViewList::iterator i = ts.begin(); i != ts.end();) {
4798 /* Add any automation children for pasting several lines */
4799 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*>(*i++);
4804 typedef RouteTimeAxisView::AutomationTracks ATracks;
4805 const ATracks& atracks = rtv->automation_tracks();
4806 for (ATracks::const_iterator a = atracks.begin(); a != atracks.end(); ++a) {
4807 i = ts.insert(i, a->second.get());
4812 /* We now have a list of trackviews starting at base_track, including
4813 automation children, in the order shown in the editor, e.g. R1,
4814 R1.A1, R1.A2, R2, R2.A1, ... */
4817 begin_reversible_command (Operations::paste);
4819 if (ts.size() == 1 && cut_buffer->lines.size() == 1 &&
4820 dynamic_cast<AutomationTimeAxisView*>(ts.front())) {
4821 /* Only one line copied, and one automation track selected. Do a
4822 "greedy" paste from one automation type to another. */
4824 PasteContext ctx(paste_count, times, ItemCounts(), true);
4825 ts.front()->paste (position, *cut_buffer, ctx, sub_num);
4829 /* Paste into tracks */
4831 PasteContext ctx(paste_count, times, ItemCounts(), false);
4832 for (TrackViewList::iterator i = ts.begin(); i != ts.end(); ++i) {
4833 (*i)->paste (position, *cut_buffer, ctx, sub_num);
4837 commit_reversible_command ();
4841 Editor::duplicate_regions (float times)
4843 RegionSelection rs (get_regions_from_selection_and_entered());
4844 duplicate_some_regions (rs, times);
4848 Editor::duplicate_some_regions (RegionSelection& regions, float times)
4850 if (regions.empty ()) {
4854 boost::shared_ptr<Playlist> playlist;
4855 RegionSelection sel = regions; // clear (below) may clear the argument list if its the current region selection
4856 RegionSelection foo;
4858 framepos_t const start_frame = regions.start ();
4859 framepos_t const end_frame = regions.end_frame ();
4860 framecnt_t const gap = end_frame - start_frame + 1;
4862 begin_reversible_command (Operations::duplicate_region);
4864 selection->clear_regions ();
4866 for (RegionSelection::iterator i = sel.begin(); i != sel.end(); ++i) {
4868 boost::shared_ptr<Region> r ((*i)->region());
4870 TimeAxisView& tv = (*i)->get_time_axis_view();
4871 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (&tv);
4872 latest_regionviews.clear ();
4873 sigc::connection c = rtv->view()->RegionViewAdded.connect (sigc::mem_fun(*this, &Editor::collect_new_region_view));
4875 framepos_t const position = end_frame + (r->first_frame() - start_frame + 1);
4876 playlist = (*i)->region()->playlist();
4877 playlist->clear_changes ();
4878 playlist->duplicate (r, position, gap, times);
4879 _session->add_command(new StatefulDiffCommand (playlist));
4883 foo.insert (foo.end(), latest_regionviews.begin(), latest_regionviews.end());
4887 selection->set (foo);
4890 commit_reversible_command ();
4894 Editor::duplicate_selection (float times)
4896 if (selection->time.empty() || selection->tracks.empty()) {
4900 boost::shared_ptr<Playlist> playlist;
4902 TrackViewList ts = selection->tracks.filter_to_unique_playlists ();
4904 bool in_command = false;
4906 for (TrackViewList::iterator i = ts.begin(); i != ts.end(); ++i) {
4907 if ((playlist = (*i)->playlist()) == 0) {
4910 playlist->clear_changes ();
4912 if (clicked_selection) {
4913 playlist->duplicate_range (selection->time[clicked_selection], times);
4915 playlist->duplicate_ranges (selection->time, times);
4919 begin_reversible_command (_("duplicate range selection"));
4922 _session->add_command (new StatefulDiffCommand (playlist));
4927 if (times == 1.0f) {
4928 // now "move" range selection to after the current range selection
4929 framecnt_t distance = 0;
4931 if (clicked_selection) {
4933 selection->time[clicked_selection].end - selection->time[clicked_selection].start;
4935 distance = selection->time.end_frame () - selection->time.start ();
4938 selection->move_time (distance);
4940 commit_reversible_command ();
4944 /** Reset all selected points to the relevant default value */
4946 Editor::reset_point_selection ()
4948 for (PointSelection::iterator i = selection->points.begin(); i != selection->points.end(); ++i) {
4949 ARDOUR::AutomationList::iterator j = (*i)->model ();
4950 (*j)->value = (*i)->line().the_list()->default_value ();
4955 Editor::center_playhead ()
4957 float const page = _visible_canvas_width * samples_per_pixel;
4958 center_screen_internal (playhead_cursor->current_frame (), page);
4962 Editor::center_edit_point ()
4964 float const page = _visible_canvas_width * samples_per_pixel;
4965 center_screen_internal (get_preferred_edit_position(), page);
4968 /** Caller must begin and commit a reversible command */
4970 Editor::clear_playlist (boost::shared_ptr<Playlist> playlist)
4972 playlist->clear_changes ();
4974 _session->add_command (new StatefulDiffCommand (playlist));
4978 Editor::nudge_track (bool use_edit, bool forwards)
4980 boost::shared_ptr<Playlist> playlist;
4981 framepos_t distance;
4982 framepos_t next_distance;
4986 start = get_preferred_edit_position();
4991 if ((distance = get_nudge_distance (start, next_distance)) == 0) {
4995 if (selection->tracks.empty()) {
4999 TrackViewList ts = selection->tracks.filter_to_unique_playlists ();
5000 bool in_command = false;
5002 for (TrackViewList::iterator i = ts.begin(); i != ts.end(); ++i) {
5004 if ((playlist = (*i)->playlist()) == 0) {
5008 playlist->clear_changes ();
5009 playlist->clear_owned_changes ();
5011 playlist->nudge_after (start, distance, forwards);
5014 begin_reversible_command (_("nudge track"));
5017 vector<Command*> cmds;
5019 playlist->rdiff (cmds);
5020 _session->add_commands (cmds);
5022 _session->add_command (new StatefulDiffCommand (playlist));
5026 commit_reversible_command ();
5031 Editor::remove_last_capture ()
5033 vector<string> choices;
5040 if (Config->get_verify_remove_last_capture()) {
5041 prompt = _("Do you really want to destroy the last capture?"
5042 "\n(This is destructive and cannot be undone)");
5044 choices.push_back (_("No, do nothing."));
5045 choices.push_back (_("Yes, destroy it."));
5047 Gtkmm2ext::Choice prompter (_("Destroy last capture"), prompt, choices);
5049 if (prompter.run () == 1) {
5050 _session->remove_last_capture ();
5051 _regions->redisplay ();
5055 _session->remove_last_capture();
5056 _regions->redisplay ();
5061 Editor::normalize_region ()
5067 RegionSelection rs = get_regions_from_selection_and_entered ();
5073 NormalizeDialog dialog (rs.size() > 1);
5075 if (dialog.run () != RESPONSE_ACCEPT) {
5079 CursorContext::Handle cursor_ctx = CursorContext::create(*this, _cursors->wait);
5082 /* XXX: should really only count audio regions here */
5083 int const regions = rs.size ();
5085 /* Make a list of the selected audio regions' maximum amplitudes, and also
5086 obtain the maximum amplitude of them all.
5088 list<double> max_amps;
5089 list<double> rms_vals;
5092 bool use_rms = dialog.constrain_rms ();
5094 for (RegionSelection::const_iterator i = rs.begin(); i != rs.end(); ++i) {
5095 AudioRegionView const * arv = dynamic_cast<AudioRegionView const *> (*i);
5099 dialog.descend (1.0 / regions);
5100 double const a = arv->audio_region()->maximum_amplitude (&dialog);
5102 double r = arv->audio_region()->rms (&dialog);
5103 max_rms = max (max_rms, r);
5104 rms_vals.push_back (r);
5108 /* the user cancelled the operation */
5112 max_amps.push_back (a);
5113 max_amp = max (max_amp, a);
5117 list<double>::const_iterator a = max_amps.begin ();
5118 list<double>::const_iterator l = rms_vals.begin ();
5119 bool in_command = false;
5121 for (RegionSelection::iterator r = rs.begin(); r != rs.end(); ++r) {
5122 AudioRegionView* const arv = dynamic_cast<AudioRegionView*> (*r);
5127 arv->region()->clear_changes ();
5129 double amp = dialog.normalize_individually() ? *a : max_amp;
5130 double target = dialog.target_peak (); // dB
5133 double const amp_rms = dialog.normalize_individually() ? *l : max_rms;
5134 const double t_rms = dialog.target_rms ();
5135 const gain_t c_peak = dB_to_coefficient (target);
5136 const gain_t c_rms = dB_to_coefficient (t_rms);
5137 if ((amp_rms / c_rms) > (amp / c_peak)) {
5143 arv->audio_region()->normalize (amp, target);
5146 begin_reversible_command (_("normalize"));
5149 _session->add_command (new StatefulDiffCommand (arv->region()));
5156 commit_reversible_command ();
5162 Editor::reset_region_scale_amplitude ()
5168 RegionSelection rs = get_regions_from_selection_and_entered ();
5174 bool in_command = false;
5176 for (RegionSelection::iterator r = rs.begin(); r != rs.end(); ++r) {
5177 AudioRegionView* const arv = dynamic_cast<AudioRegionView*>(*r);
5180 arv->region()->clear_changes ();
5181 arv->audio_region()->set_scale_amplitude (1.0f);
5184 begin_reversible_command ("reset gain");
5187 _session->add_command (new StatefulDiffCommand (arv->region()));
5191 commit_reversible_command ();
5196 Editor::adjust_region_gain (bool up)
5198 RegionSelection rs = get_regions_from_selection_and_entered ();
5200 if (!_session || rs.empty()) {
5204 bool in_command = false;
5206 for (RegionSelection::iterator r = rs.begin(); r != rs.end(); ++r) {
5207 AudioRegionView* const arv = dynamic_cast<AudioRegionView*>(*r);
5212 arv->region()->clear_changes ();
5214 double dB = accurate_coefficient_to_dB (arv->audio_region()->scale_amplitude ());
5222 arv->audio_region()->set_scale_amplitude (dB_to_coefficient (dB));
5225 begin_reversible_command ("adjust region gain");
5228 _session->add_command (new StatefulDiffCommand (arv->region()));
5232 commit_reversible_command ();
5237 Editor::reset_region_gain ()
5239 RegionSelection rs = get_regions_from_selection_and_entered ();
5241 if (!_session || rs.empty()) {
5245 bool in_command = false;
5247 for (RegionSelection::iterator r = rs.begin(); r != rs.end(); ++r) {
5248 AudioRegionView* const arv = dynamic_cast<AudioRegionView*>(*r);
5253 arv->region()->clear_changes ();
5255 arv->audio_region()->set_scale_amplitude (1.0f);
5258 begin_reversible_command ("reset region gain");
5261 _session->add_command (new StatefulDiffCommand (arv->region()));
5265 commit_reversible_command ();
5270 Editor::reverse_region ()
5276 Reverse rev (*_session);
5277 apply_filter (rev, _("reverse regions"));
5281 Editor::strip_region_silence ()
5287 RegionSelection rs = get_regions_from_selection_and_entered ();
5293 std::list<RegionView*> audio_only;
5295 for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
5296 AudioRegionView* const arv = dynamic_cast<AudioRegionView*> (*i);
5298 audio_only.push_back (arv);
5302 assert (!audio_only.empty());
5304 StripSilenceDialog d (_session, audio_only);
5305 int const r = d.run ();
5309 if (r == Gtk::RESPONSE_OK) {
5310 ARDOUR::AudioIntervalMap silences;
5311 d.silences (silences);
5312 StripSilence s (*_session, silences, d.fade_length());
5314 apply_filter (s, _("strip silence"), &d);
5319 Editor::apply_midi_note_edit_op_to_region (MidiOperator& op, MidiRegionView& mrv)
5321 Evoral::Sequence<Evoral::Beats>::Notes selected;
5322 mrv.selection_as_notelist (selected, true);
5324 vector<Evoral::Sequence<Evoral::Beats>::Notes> v;
5325 v.push_back (selected);
5327 Evoral::Beats pos_beats = Evoral::Beats (mrv.midi_region()->beat()) - mrv.midi_region()->start_beats();
5329 return op (mrv.midi_region()->model(), pos_beats, v);
5333 Editor::apply_midi_note_edit_op (MidiOperator& op, const RegionSelection& rs)
5339 bool in_command = false;
5341 for (RegionSelection::const_iterator r = rs.begin(); r != rs.end(); ) {
5342 RegionSelection::const_iterator tmp = r;
5345 MidiRegionView* const mrv = dynamic_cast<MidiRegionView*> (*r);
5348 Command* cmd = apply_midi_note_edit_op_to_region (op, *mrv);
5351 begin_reversible_command (op.name ());
5355 _session->add_command (cmd);
5363 commit_reversible_command ();
5368 Editor::fork_region ()
5370 RegionSelection rs = get_regions_from_selection_and_entered ();
5376 CursorContext::Handle cursor_ctx = CursorContext::create(*this, _cursors->wait);
5377 bool in_command = false;
5381 for (RegionSelection::iterator r = rs.begin(); r != rs.end(); ) {
5382 RegionSelection::iterator tmp = r;
5385 MidiRegionView* const mrv = dynamic_cast<MidiRegionView*>(*r);
5389 boost::shared_ptr<Playlist> playlist = mrv->region()->playlist();
5390 boost::shared_ptr<MidiSource> new_source = _session->create_midi_source_by_stealing_name (mrv->midi_view()->track());
5391 boost::shared_ptr<MidiRegion> newregion = mrv->midi_region()->clone (new_source);
5394 begin_reversible_command (_("Fork Region(s)"));
5397 playlist->clear_changes ();
5398 playlist->replace_region (mrv->region(), newregion, mrv->region()->position());
5399 _session->add_command(new StatefulDiffCommand (playlist));
5401 error << string_compose (_("Could not unlink %1"), mrv->region()->name()) << endmsg;
5409 commit_reversible_command ();
5414 Editor::quantize_region ()
5417 quantize_regions(get_regions_from_selection_and_entered ());
5422 Editor::quantize_regions (const RegionSelection& rs)
5424 if (rs.n_midi_regions() == 0) {
5428 if (!quantize_dialog) {
5429 quantize_dialog = new QuantizeDialog (*this);
5432 if (quantize_dialog->is_mapped()) {
5433 /* in progress already */
5437 quantize_dialog->present ();
5438 const int r = quantize_dialog->run ();
5439 quantize_dialog->hide ();
5441 if (r == Gtk::RESPONSE_OK) {
5442 Quantize quant (quantize_dialog->snap_start(),
5443 quantize_dialog->snap_end(),
5444 quantize_dialog->start_grid_size(),
5445 quantize_dialog->end_grid_size(),
5446 quantize_dialog->strength(),
5447 quantize_dialog->swing(),
5448 quantize_dialog->threshold());
5450 apply_midi_note_edit_op (quant, rs);
5455 Editor::legatize_region (bool shrink_only)
5458 legatize_regions(get_regions_from_selection_and_entered (), shrink_only);
5463 Editor::legatize_regions (const RegionSelection& rs, bool shrink_only)
5465 if (rs.n_midi_regions() == 0) {
5469 Legatize legatize(shrink_only);
5470 apply_midi_note_edit_op (legatize, rs);
5474 Editor::transform_region ()
5477 transform_regions(get_regions_from_selection_and_entered ());
5482 Editor::transform_regions (const RegionSelection& rs)
5484 if (rs.n_midi_regions() == 0) {
5491 const int r = td.run();
5494 if (r == Gtk::RESPONSE_OK) {
5495 Transform transform(td.get());
5496 apply_midi_note_edit_op(transform, rs);
5501 Editor::transpose_region ()
5504 transpose_regions(get_regions_from_selection_and_entered ());
5509 Editor::transpose_regions (const RegionSelection& rs)
5511 if (rs.n_midi_regions() == 0) {
5516 int const r = d.run ();
5518 if (r == RESPONSE_ACCEPT) {
5519 Transpose transpose(d.semitones ());
5520 apply_midi_note_edit_op (transpose, rs);
5525 Editor::insert_patch_change (bool from_context)
5527 RegionSelection rs = get_regions_from_selection_and_entered ();
5533 const framepos_t p = get_preferred_edit_position (EDIT_IGNORE_NONE, from_context);
5535 /* XXX: bit of a hack; use the MIDNAM from the first selected region;
5536 there may be more than one, but the PatchChangeDialog can only offer
5537 one set of patch menus.
5539 MidiRegionView* first = dynamic_cast<MidiRegionView*> (rs.front ());
5541 Evoral::PatchChange<Evoral::Beats> empty (Evoral::Beats(), 0, 0, 0);
5542 PatchChangeDialog d (0, _session, empty, first->instrument_info(), Gtk::Stock::ADD);
5544 if (d.run() == RESPONSE_CANCEL) {
5548 for (RegionSelection::iterator i = rs.begin (); i != rs.end(); ++i) {
5549 MidiRegionView* const mrv = dynamic_cast<MidiRegionView*> (*i);
5551 if (p >= mrv->region()->first_frame() && p <= mrv->region()->last_frame()) {
5552 mrv->add_patch_change (p - mrv->region()->position(), d.patch ());
5559 Editor::apply_filter (Filter& filter, string command, ProgressReporter* progress)
5561 RegionSelection rs = get_regions_from_selection_and_entered ();
5567 CursorContext::Handle cursor_ctx = CursorContext::create(*this, _cursors->wait);
5568 bool in_command = false;
5573 int const N = rs.size ();
5575 for (RegionSelection::iterator r = rs.begin(); r != rs.end(); ) {
5576 RegionSelection::iterator tmp = r;
5579 AudioRegionView* const arv = dynamic_cast<AudioRegionView*>(*r);
5581 boost::shared_ptr<Playlist> playlist = arv->region()->playlist();
5584 progress->descend (1.0 / N);
5587 if (arv->audio_region()->apply (filter, progress) == 0) {
5589 playlist->clear_changes ();
5590 playlist->clear_owned_changes ();
5593 begin_reversible_command (command);
5597 if (filter.results.empty ()) {
5599 /* no regions returned; remove the old one */
5600 playlist->remove_region (arv->region ());
5604 std::vector<boost::shared_ptr<Region> >::iterator res = filter.results.begin ();
5606 /* first region replaces the old one */
5607 playlist->replace_region (arv->region(), *res, (*res)->position());
5611 while (res != filter.results.end()) {
5612 playlist->add_region (*res, (*res)->position());
5618 /* We might have removed regions, which alters other regions' layering_index,
5619 so we need to do a recursive diff here.
5621 vector<Command*> cmds;
5622 playlist->rdiff (cmds);
5623 _session->add_commands (cmds);
5625 _session->add_command(new StatefulDiffCommand (playlist));
5629 progress->ascend ();
5638 commit_reversible_command ();
5643 Editor::external_edit_region ()
5649 Editor::reset_region_gain_envelopes ()
5651 RegionSelection rs = get_regions_from_selection_and_entered ();
5653 if (!_session || rs.empty()) {
5657 bool in_command = false;
5659 for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
5660 AudioRegionView* const arv = dynamic_cast<AudioRegionView*>(*i);
5662 boost::shared_ptr<AutomationList> alist (arv->audio_region()->envelope());
5663 XMLNode& before (alist->get_state());
5665 arv->audio_region()->set_default_envelope ();
5668 begin_reversible_command (_("reset region gain"));
5671 _session->add_command (new MementoCommand<AutomationList>(*arv->audio_region()->envelope().get(), &before, &alist->get_state()));
5676 commit_reversible_command ();
5681 Editor::set_region_gain_visibility (RegionView* rv)
5683 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (rv);
5685 arv->update_envelope_visibility();
5690 Editor::set_gain_envelope_visibility ()
5696 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
5697 AudioTimeAxisView* v = dynamic_cast<AudioTimeAxisView*>(*i);
5699 v->audio_view()->foreach_regionview (sigc::mem_fun (this, &Editor::set_region_gain_visibility));
5705 Editor::toggle_gain_envelope_active ()
5707 if (_ignore_region_action) {
5711 RegionSelection rs = get_regions_from_selection_and_entered ();
5713 if (!_session || rs.empty()) {
5717 bool in_command = false;
5719 for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
5720 AudioRegionView* const arv = dynamic_cast<AudioRegionView*>(*i);
5722 arv->region()->clear_changes ();
5723 arv->audio_region()->set_envelope_active (!arv->audio_region()->envelope_active());
5726 begin_reversible_command (_("region gain envelope active"));
5729 _session->add_command (new StatefulDiffCommand (arv->region()));
5734 commit_reversible_command ();
5739 Editor::toggle_region_lock ()
5741 if (_ignore_region_action) {
5745 RegionSelection rs = get_regions_from_selection_and_entered ();
5747 if (!_session || rs.empty()) {
5751 begin_reversible_command (_("toggle region lock"));
5753 for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
5754 (*i)->region()->clear_changes ();
5755 (*i)->region()->set_locked (!(*i)->region()->locked());
5756 _session->add_command (new StatefulDiffCommand ((*i)->region()));
5759 commit_reversible_command ();
5763 Editor::toggle_region_video_lock ()
5765 if (_ignore_region_action) {
5769 RegionSelection rs = get_regions_from_selection_and_entered ();
5771 if (!_session || rs.empty()) {
5775 begin_reversible_command (_("Toggle Video Lock"));
5777 for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
5778 (*i)->region()->clear_changes ();
5779 (*i)->region()->set_video_locked (!(*i)->region()->video_locked());
5780 _session->add_command (new StatefulDiffCommand ((*i)->region()));
5783 commit_reversible_command ();
5787 Editor::toggle_region_lock_style ()
5789 if (_ignore_region_action) {
5793 RegionSelection rs = get_regions_from_selection_and_entered ();
5795 if (!_session || rs.empty()) {
5799 Glib::RefPtr<ToggleAction> a = Glib::RefPtr<ToggleAction>::cast_dynamic (_region_actions->get_action("toggle-region-lock-style"));
5800 vector<Widget*> proxies = a->get_proxies();
5801 Gtk::CheckMenuItem* cmi = dynamic_cast<Gtk::CheckMenuItem*> (proxies.front());
5805 begin_reversible_command (_("toggle region lock style"));
5807 for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
5808 (*i)->region()->clear_changes ();
5809 PositionLockStyle const ns = ((*i)->region()->position_lock_style() == AudioTime && !cmi->get_inconsistent()) ? MusicTime : AudioTime;
5810 (*i)->region()->set_position_lock_style (ns);
5811 _session->add_command (new StatefulDiffCommand ((*i)->region()));
5814 commit_reversible_command ();
5818 Editor::toggle_opaque_region ()
5820 if (_ignore_region_action) {
5824 RegionSelection rs = get_regions_from_selection_and_entered ();
5826 if (!_session || rs.empty()) {
5830 begin_reversible_command (_("change region opacity"));
5832 for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
5833 (*i)->region()->clear_changes ();
5834 (*i)->region()->set_opaque (!(*i)->region()->opaque());
5835 _session->add_command (new StatefulDiffCommand ((*i)->region()));
5838 commit_reversible_command ();
5842 Editor::toggle_record_enable ()
5844 bool new_state = false;
5846 for (TrackSelection::iterator i = selection->tracks.begin(); i != selection->tracks.end(); ++i) {
5847 RouteTimeAxisView *rtav = dynamic_cast<RouteTimeAxisView *>(*i);
5850 if (!rtav->is_track())
5854 new_state = !rtav->track()->rec_enable_control()->get_value();
5858 rtav->track()->rec_enable_control()->set_value (new_state, Controllable::UseGroup);
5863 Editor::toggle_solo ()
5865 bool new_state = false;
5867 boost::shared_ptr<ControlList> cl (new ControlList);
5869 for (TrackSelection::iterator i = selection->tracks.begin(); i != selection->tracks.end(); ++i) {
5870 RouteTimeAxisView *rtav = dynamic_cast<RouteTimeAxisView *>(*i);
5877 new_state = !rtav->route()->soloed ();
5881 cl->push_back (rtav->route()->solo_control());
5884 _session->set_controls (cl, new_state ? 1.0 : 0.0, Controllable::UseGroup);
5888 Editor::toggle_mute ()
5890 bool new_state = false;
5892 boost::shared_ptr<RouteList> rl (new RouteList);
5894 for (TrackSelection::iterator i = selection->tracks.begin(); i != selection->tracks.end(); ++i) {
5895 RouteTimeAxisView *rtav = dynamic_cast<RouteTimeAxisView *>(*i);
5902 new_state = !rtav->route()->muted();
5906 rl->push_back (rtav->route());
5909 _session->set_controls (route_list_to_control_list (rl, &Stripable::mute_control), new_state, Controllable::UseGroup);
5913 Editor::toggle_solo_isolate ()
5919 Editor::fade_range ()
5921 TrackViewList ts = selection->tracks.filter_to_unique_playlists ();
5923 begin_reversible_command (_("fade range"));
5925 for (TrackViewList::iterator i = ts.begin(); i != ts.end(); ++i) {
5926 (*i)->fade_range (selection->time);
5929 commit_reversible_command ();
5934 Editor::set_fade_length (bool in)
5936 RegionSelection rs = get_regions_from_selection_and_entered ();
5942 /* we need a region to measure the offset from the start */
5944 RegionView* rv = rs.front ();
5946 framepos_t pos = get_preferred_edit_position();
5950 if (pos > rv->region()->last_frame() || pos < rv->region()->first_frame()) {
5951 /* edit point is outside the relevant region */
5956 if (pos <= rv->region()->position()) {
5960 len = pos - rv->region()->position();
5961 cmd = _("set fade in length");
5963 if (pos >= rv->region()->last_frame()) {
5967 len = rv->region()->last_frame() - pos;
5968 cmd = _("set fade out length");
5971 bool in_command = false;
5973 for (RegionSelection::iterator x = rs.begin(); x != rs.end(); ++x) {
5974 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (*x);
5980 boost::shared_ptr<AutomationList> alist;
5982 alist = tmp->audio_region()->fade_in();
5984 alist = tmp->audio_region()->fade_out();
5987 XMLNode &before = alist->get_state();
5990 tmp->audio_region()->set_fade_in_length (len);
5991 tmp->audio_region()->set_fade_in_active (true);
5993 tmp->audio_region()->set_fade_out_length (len);
5994 tmp->audio_region()->set_fade_out_active (true);
5998 begin_reversible_command (cmd);
6001 XMLNode &after = alist->get_state();
6002 _session->add_command(new MementoCommand<AutomationList>(*alist, &before, &after));
6006 commit_reversible_command ();
6011 Editor::set_fade_in_shape (FadeShape shape)
6013 RegionSelection rs = get_regions_from_selection_and_entered ();
6018 bool in_command = false;
6020 for (RegionSelection::iterator x = rs.begin(); x != rs.end(); ++x) {
6021 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (*x);
6027 boost::shared_ptr<AutomationList> alist = tmp->audio_region()->fade_in();
6028 XMLNode &before = alist->get_state();
6030 tmp->audio_region()->set_fade_in_shape (shape);
6033 begin_reversible_command (_("set fade in shape"));
6036 XMLNode &after = alist->get_state();
6037 _session->add_command(new MementoCommand<AutomationList>(*alist.get(), &before, &after));
6041 commit_reversible_command ();
6046 Editor::set_fade_out_shape (FadeShape shape)
6048 RegionSelection rs = get_regions_from_selection_and_entered ();
6053 bool in_command = false;
6055 for (RegionSelection::iterator x = rs.begin(); x != rs.end(); ++x) {
6056 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (*x);
6062 boost::shared_ptr<AutomationList> alist = tmp->audio_region()->fade_out();
6063 XMLNode &before = alist->get_state();
6065 tmp->audio_region()->set_fade_out_shape (shape);
6068 begin_reversible_command (_("set fade out shape"));
6071 XMLNode &after = alist->get_state();
6072 _session->add_command(new MementoCommand<AutomationList>(*alist.get(), &before, &after));
6076 commit_reversible_command ();
6081 Editor::set_fade_in_active (bool yn)
6083 RegionSelection rs = get_regions_from_selection_and_entered ();
6088 bool in_command = false;
6090 for (RegionSelection::iterator x = rs.begin(); x != rs.end(); ++x) {
6091 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (*x);
6098 boost::shared_ptr<AudioRegion> ar (tmp->audio_region());
6100 ar->clear_changes ();
6101 ar->set_fade_in_active (yn);
6104 begin_reversible_command (_("set fade in active"));
6107 _session->add_command (new StatefulDiffCommand (ar));
6111 commit_reversible_command ();
6116 Editor::set_fade_out_active (bool yn)
6118 RegionSelection rs = get_regions_from_selection_and_entered ();
6123 bool in_command = false;
6125 for (RegionSelection::iterator x = rs.begin(); x != rs.end(); ++x) {
6126 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (*x);
6132 boost::shared_ptr<AudioRegion> ar (tmp->audio_region());
6134 ar->clear_changes ();
6135 ar->set_fade_out_active (yn);
6138 begin_reversible_command (_("set fade out active"));
6141 _session->add_command(new StatefulDiffCommand (ar));
6145 commit_reversible_command ();
6150 Editor::toggle_region_fades (int dir)
6152 if (_ignore_region_action) {
6156 boost::shared_ptr<AudioRegion> ar;
6159 RegionSelection rs = get_regions_from_selection_and_entered ();
6165 RegionSelection::iterator i;
6166 for (i = rs.begin(); i != rs.end(); ++i) {
6167 if ((ar = boost::dynamic_pointer_cast<AudioRegion>((*i)->region())) != 0) {
6169 yn = ar->fade_out_active ();
6171 yn = ar->fade_in_active ();
6177 if (i == rs.end()) {
6181 /* XXX should this undo-able? */
6182 bool in_command = false;
6184 for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
6185 if ((ar = boost::dynamic_pointer_cast<AudioRegion>((*i)->region())) == 0) {
6188 ar->clear_changes ();
6190 if (dir == 1 || dir == 0) {
6191 ar->set_fade_in_active (!yn);
6194 if (dir == -1 || dir == 0) {
6195 ar->set_fade_out_active (!yn);
6198 begin_reversible_command (_("toggle fade active"));
6201 _session->add_command(new StatefulDiffCommand (ar));
6205 commit_reversible_command ();
6210 /** Update region fade visibility after its configuration has been changed */
6212 Editor::update_region_fade_visibility ()
6214 bool _fade_visibility = _session->config.get_show_region_fades ();
6216 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
6217 AudioTimeAxisView* v = dynamic_cast<AudioTimeAxisView*>(*i);
6219 if (_fade_visibility) {
6220 v->audio_view()->show_all_fades ();
6222 v->audio_view()->hide_all_fades ();
6229 Editor::set_edit_point ()
6232 MusicFrame where (0, 0);
6234 if (!mouse_frame (where.frame, ignored)) {
6240 if (selection->markers.empty()) {
6242 mouse_add_new_marker (where.frame);
6247 Location* loc = find_location_from_marker (selection->markers.front(), ignored);
6250 loc->move_to (where.frame, where.division);
6256 Editor::set_playhead_cursor ()
6258 if (entered_marker) {
6259 _session->request_locate (entered_marker->position(), _session->transport_rolling());
6261 MusicFrame where (0, 0);
6264 if (!mouse_frame (where.frame, ignored)) {
6271 _session->request_locate (where.frame, _session->transport_rolling());
6275 //not sure what this was for; remove it for now.
6276 // if (UIConfiguration::instance().get_follow_edits() && (!_session || !_session->config.get_external_sync())) {
6277 // cancel_time_selection();
6283 Editor::split_region ()
6285 if (_drags->active ()) {
6289 //if a range is selected, separate it
6290 if ( !selection->time.empty()) {
6291 separate_regions_between (selection->time);
6295 //if no range was selected, try to find some regions to split
6296 if (current_mouse_mode() == MouseObject) { //don't try this for Internal Edit, Stretch, Draw, etc.
6298 RegionSelection rs = get_regions_from_selection_and_edit_point ();
6299 const framepos_t pos = get_preferred_edit_position();
6300 const int32_t division = get_grid_music_divisions (0);
6301 MusicFrame where (pos, division);
6307 split_regions_at (where, rs);
6313 Editor::select_next_route()
6315 if (selection->tracks.empty()) {
6316 selection->set (track_views.front());
6320 TimeAxisView* current = selection->tracks.front();
6324 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
6326 if (*i == current) {
6328 if (i != track_views.end()) {
6331 current = (*(track_views.begin()));
6332 //selection->set (*(track_views.begin()));
6338 rui = dynamic_cast<RouteUI *>(current);
6340 } while (current->hidden() || (rui == NULL) || !rui->route()->active());
6342 selection->set (current);
6344 ensure_time_axis_view_is_visible (*current, false);
6348 Editor::select_prev_route()
6350 if (selection->tracks.empty()) {
6351 selection->set (track_views.front());
6355 TimeAxisView* current = selection->tracks.front();
6359 for (TrackViewList::reverse_iterator i = track_views.rbegin(); i != track_views.rend(); ++i) {
6361 if (*i == current) {
6363 if (i != track_views.rend()) {
6366 current = *(track_views.rbegin());
6371 rui = dynamic_cast<RouteUI *>(current);
6373 } while (current->hidden() || (rui == NULL) || !rui->route()->active());
6375 selection->set (current);
6377 ensure_time_axis_view_is_visible (*current, false);
6381 Editor::set_loop_from_selection (bool play)
6383 if (_session == 0) {
6387 framepos_t start, end;
6388 if (!get_selection_extents ( start, end))
6391 set_loop_range (start, end, _("set loop range from selection"));
6394 _session->request_play_loop (true, true);
6399 Editor::set_loop_from_region (bool play)
6401 framepos_t start, end;
6402 if (!get_selection_extents ( start, end))
6405 set_loop_range (start, end, _("set loop range from region"));
6408 _session->request_locate (start, true);
6409 _session->request_play_loop (true);
6414 Editor::set_punch_from_selection ()
6416 if (_session == 0) {
6420 framepos_t start, end;
6421 if (!get_selection_extents ( start, end))
6424 set_punch_range (start, end, _("set punch range from selection"));
6428 Editor::set_auto_punch_range ()
6430 // auto punch in/out button from a single button
6431 // If Punch In is unset, set punch range from playhead to end, enable punch in
6432 // If Punch In is set, the next punch sets Punch Out, unless the playhead has been
6433 // rewound beyond the Punch In marker, in which case that marker will be moved back
6434 // to the current playhead position.
6435 // If punch out is set, it clears the punch range and Punch In/Out buttons
6437 if (_session == 0) {
6441 Location* tpl = transport_punch_location();
6442 framepos_t now = playhead_cursor->current_frame();
6443 framepos_t begin = now;
6444 framepos_t end = _session->current_end_frame();
6446 if (!_session->config.get_punch_in()) {
6447 // First Press - set punch in and create range from here to eternity
6448 set_punch_range (begin, end, _("Auto Punch In"));
6449 _session->config.set_punch_in(true);
6450 } else if (tpl && !_session->config.get_punch_out()) {
6451 // Second press - update end range marker and set punch_out
6452 if (now < tpl->start()) {
6453 // playhead has been rewound - move start back and pretend nothing happened
6455 set_punch_range (begin, end, _("Auto Punch In/Out"));
6457 // normal case for 2nd press - set the punch out
6458 end = playhead_cursor->current_frame ();
6459 set_punch_range (tpl->start(), now, _("Auto Punch In/Out"));
6460 _session->config.set_punch_out(true);
6463 if (_session->config.get_punch_out()) {
6464 _session->config.set_punch_out(false);
6467 if (_session->config.get_punch_in()) {
6468 _session->config.set_punch_in(false);
6473 // third press - unset punch in/out and remove range
6474 _session->locations()->remove(tpl);
6481 Editor::set_session_extents_from_selection ()
6483 if (_session == 0) {
6487 framepos_t start, end;
6488 if (!get_selection_extents ( start, end))
6492 if ((loc = _session->locations()->session_range_location()) == 0) {
6493 _session->set_session_extents (start, end); // this will create a new session range; no need for UNDO
6495 XMLNode &before = loc->get_state();
6497 _session->set_session_extents (start, end);
6499 XMLNode &after = loc->get_state();
6501 begin_reversible_command (_("set session start/end from selection"));
6503 _session->add_command (new MementoCommand<Location>(*loc, &before, &after));
6505 commit_reversible_command ();
6508 _session->set_end_is_free (false);
6512 Editor::set_punch_start_from_edit_point ()
6516 MusicFrame start (0, 0);
6517 framepos_t end = max_framepos;
6519 //use the existing punch end, if any
6520 Location* tpl = transport_punch_location();
6525 if ((_edit_point == EditAtPlayhead) && _session->transport_rolling()) {
6526 start.frame = _session->audible_frame();
6528 start.frame = get_preferred_edit_position();
6531 //snap the selection start/end
6534 //if there's not already a sensible selection endpoint, go "forever"
6535 if (start.frame > end ) {
6539 set_punch_range (start.frame, end, _("set punch start from EP"));
6545 Editor::set_punch_end_from_edit_point ()
6549 framepos_t start = 0;
6550 MusicFrame end (max_framepos, 0);
6552 //use the existing punch start, if any
6553 Location* tpl = transport_punch_location();
6555 start = tpl->start();
6558 if ((_edit_point == EditAtPlayhead) && _session->transport_rolling()) {
6559 end.frame = _session->audible_frame();
6561 end.frame = get_preferred_edit_position();
6564 //snap the selection start/end
6567 set_punch_range (start, end.frame, _("set punch end from EP"));
6573 Editor::set_loop_start_from_edit_point ()
6577 MusicFrame start (0, 0);
6578 framepos_t end = max_framepos;
6580 //use the existing loop end, if any
6581 Location* tpl = transport_loop_location();
6586 if ((_edit_point == EditAtPlayhead) && _session->transport_rolling()) {
6587 start.frame = _session->audible_frame();
6589 start.frame = get_preferred_edit_position();
6592 //snap the selection start/end
6595 //if there's not already a sensible selection endpoint, go "forever"
6596 if (start.frame > end ) {
6600 set_loop_range (start.frame, end, _("set loop start from EP"));
6606 Editor::set_loop_end_from_edit_point ()
6610 framepos_t start = 0;
6611 MusicFrame end (max_framepos, 0);
6613 //use the existing loop start, if any
6614 Location* tpl = transport_loop_location();
6616 start = tpl->start();
6619 if ((_edit_point == EditAtPlayhead) && _session->transport_rolling()) {
6620 end.frame = _session->audible_frame();
6622 end.frame = get_preferred_edit_position();
6625 //snap the selection start/end
6628 set_loop_range (start, end.frame, _("set loop end from EP"));
6633 Editor::set_punch_from_region ()
6635 framepos_t start, end;
6636 if (!get_selection_extents ( start, end))
6639 set_punch_range (start, end, _("set punch range from region"));
6643 Editor::pitch_shift_region ()
6645 RegionSelection rs = get_regions_from_selection_and_entered ();
6647 RegionSelection audio_rs;
6648 for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
6649 if (dynamic_cast<AudioRegionView*> (*i)) {
6650 audio_rs.push_back (*i);
6654 if (audio_rs.empty()) {
6658 pitch_shift (audio_rs, 1.2);
6662 Editor::set_tempo_from_region ()
6664 RegionSelection rs = get_regions_from_selection_and_entered ();
6666 if (!_session || rs.empty()) {
6670 RegionView* rv = rs.front();
6672 define_one_bar (rv->region()->position(), rv->region()->last_frame() + 1);
6676 Editor::use_range_as_bar ()
6678 framepos_t start, end;
6679 if (get_edit_op_range (start, end)) {
6680 define_one_bar (start, end);
6685 Editor::define_one_bar (framepos_t start, framepos_t end)
6687 framepos_t length = end - start;
6689 const Meter& m (_session->tempo_map().meter_at_frame (start));
6691 /* length = 1 bar */
6693 /* We're going to deliver a constant tempo here,
6694 so we can use frames per beat to determine length.
6695 now we want frames per beat.
6696 we have frames per bar, and beats per bar, so ...
6699 /* XXXX METER MATH */
6701 double frames_per_beat = length / m.divisions_per_bar();
6703 /* beats per minute = */
6705 double beats_per_minute = (_session->frame_rate() * 60.0) / frames_per_beat;
6707 /* now decide whether to:
6709 (a) set global tempo
6710 (b) add a new tempo marker
6714 const TempoSection& t (_session->tempo_map().tempo_section_at_frame (start));
6716 bool do_global = false;
6718 if ((_session->tempo_map().n_tempos() == 1) && (_session->tempo_map().n_meters() == 1)) {
6720 /* only 1 tempo & 1 meter: ask if the user wants to set the tempo
6721 at the start, or create a new marker
6724 vector<string> options;
6725 options.push_back (_("Cancel"));
6726 options.push_back (_("Add new marker"));
6727 options.push_back (_("Set global tempo"));
6730 _("Define one bar"),
6731 _("Do you want to set the global tempo or add a new tempo marker?"),
6735 c.set_default_response (2);
6751 /* more than 1 tempo and/or meter section already, go ahead do the "usual":
6752 if the marker is at the region starter, change it, otherwise add
6757 begin_reversible_command (_("set tempo from region"));
6758 XMLNode& before (_session->tempo_map().get_state());
6761 _session->tempo_map().change_initial_tempo (beats_per_minute, t.note_type(), t.end_note_types_per_minute());
6762 } else if (t.frame() == start) {
6763 _session->tempo_map().change_existing_tempo_at (start, beats_per_minute, t.note_type(), t.end_note_types_per_minute());
6765 /* constant tempo */
6766 const Tempo tempo (beats_per_minute, t.note_type());
6767 _session->tempo_map().add_tempo (tempo, 0.0, start, AudioTime);
6770 XMLNode& after (_session->tempo_map().get_state());
6772 _session->add_command (new MementoCommand<TempoMap>(_session->tempo_map(), &before, &after));
6773 commit_reversible_command ();
6777 Editor::split_region_at_transients ()
6779 AnalysisFeatureList positions;
6781 RegionSelection rs = get_regions_from_selection_and_entered ();
6783 if (!_session || rs.empty()) {
6787 begin_reversible_command (_("split regions"));
6789 for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ) {
6791 RegionSelection::iterator tmp;
6796 boost::shared_ptr<AudioRegion> ar = boost::dynamic_pointer_cast<AudioRegion> ((*i)->region());
6799 ar->transients (positions);
6800 split_region_at_points ((*i)->region(), positions, true);
6807 commit_reversible_command ();
6812 Editor::split_region_at_points (boost::shared_ptr<Region> r, AnalysisFeatureList& positions, bool can_ferret, bool select_new)
6814 bool use_rhythmic_rodent = false;
6816 boost::shared_ptr<Playlist> pl = r->playlist();
6818 list<boost::shared_ptr<Region> > new_regions;
6824 if (positions.empty()) {
6828 if (positions.size() > 20 && can_ferret) {
6829 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);
6830 MessageDialog msg (msgstr,
6833 Gtk::BUTTONS_OK_CANCEL);
6836 msg.add_button (_("Call for the Ferret!"), RESPONSE_APPLY);
6837 msg.set_secondary_text (_("Press OK to continue with this split operation\nor ask the Ferret dialog to tune the analysis"));
6839 msg.set_secondary_text (_("Press OK to continue with this split operation"));
6842 msg.set_title (_("Excessive split?"));
6845 int response = msg.run();
6851 case RESPONSE_APPLY:
6852 use_rhythmic_rodent = true;
6859 if (use_rhythmic_rodent) {
6860 show_rhythm_ferret ();
6864 AnalysisFeatureList::const_iterator x;
6866 pl->clear_changes ();
6867 pl->clear_owned_changes ();
6869 x = positions.begin();
6871 if (x == positions.end()) {
6876 pl->remove_region (r);
6880 framepos_t rstart = r->first_frame ();
6881 framepos_t rend = r->last_frame ();
6883 while (x != positions.end()) {
6885 /* deal with positons that are out of scope of present region bounds */
6886 if (*x <= rstart || *x > rend) {
6891 /* file start = original start + how far we from the initial position ? */
6893 framepos_t file_start = r->start() + pos;
6895 /* length = next position - current position */
6897 framepos_t len = (*x) - pos - rstart;
6899 /* XXX we do we really want to allow even single-sample regions?
6900 * shouldn't we have some kind of lower limit on region size?
6909 if (RegionFactory::region_name (new_name, r->name())) {
6913 /* do NOT announce new regions 1 by one, just wait till they are all done */
6917 plist.add (ARDOUR::Properties::start, file_start);
6918 plist.add (ARDOUR::Properties::length, len);
6919 plist.add (ARDOUR::Properties::name, new_name);
6920 plist.add (ARDOUR::Properties::layer, 0);
6921 // TODO set transients_offset
6923 boost::shared_ptr<Region> nr = RegionFactory::create (r->sources(), plist, false);
6924 /* because we set annouce to false, manually add the new region to the
6927 RegionFactory::map_add (nr);
6929 pl->add_region (nr, rstart + pos);
6932 new_regions.push_front(nr);
6941 RegionFactory::region_name (new_name, r->name());
6943 /* Add the final region */
6946 plist.add (ARDOUR::Properties::start, r->start() + pos);
6947 plist.add (ARDOUR::Properties::length, r->last_frame() - (r->position() + pos) + 1);
6948 plist.add (ARDOUR::Properties::name, new_name);
6949 plist.add (ARDOUR::Properties::layer, 0);
6951 boost::shared_ptr<Region> nr = RegionFactory::create (r->sources(), plist, false);
6952 /* because we set annouce to false, manually add the new region to the
6955 RegionFactory::map_add (nr);
6956 pl->add_region (nr, r->position() + pos);
6959 new_regions.push_front(nr);
6964 /* We might have removed regions, which alters other regions' layering_index,
6965 so we need to do a recursive diff here.
6967 vector<Command*> cmds;
6969 _session->add_commands (cmds);
6971 _session->add_command (new StatefulDiffCommand (pl));
6975 for (list<boost::shared_ptr<Region> >::iterator i = new_regions.begin(); i != new_regions.end(); ++i){
6976 set_selected_regionview_from_region_list ((*i), Selection::Add);
6982 Editor::place_transient()
6988 RegionSelection rs = get_regions_from_selection_and_edit_point ();
6994 framepos_t where = get_preferred_edit_position();
6996 begin_reversible_command (_("place transient"));
6998 for (RegionSelection::iterator r = rs.begin(); r != rs.end(); ++r) {
6999 (*r)->region()->add_transient(where);
7002 commit_reversible_command ();
7006 Editor::remove_transient(ArdourCanvas::Item* item)
7012 ArdourCanvas::Line* _line = reinterpret_cast<ArdourCanvas::Line*> (item);
7015 AudioRegionView* _arv = reinterpret_cast<AudioRegionView*> (item->get_data ("regionview"));
7016 _arv->remove_transient (*(float*) _line->get_data ("position"));
7020 Editor::snap_regions_to_grid ()
7022 list <boost::shared_ptr<Playlist > > used_playlists;
7024 RegionSelection rs = get_regions_from_selection_and_entered ();
7026 if (!_session || rs.empty()) {
7030 begin_reversible_command (_("snap regions to grid"));
7032 for (RegionSelection::iterator r = rs.begin(); r != rs.end(); ++r) {
7034 boost::shared_ptr<Playlist> pl = (*r)->region()->playlist();
7036 if (!pl->frozen()) {
7037 /* we haven't seen this playlist before */
7039 /* remember used playlists so we can thaw them later */
7040 used_playlists.push_back(pl);
7043 (*r)->region()->clear_changes ();
7045 MusicFrame start ((*r)->region()->first_frame (), 0);
7047 (*r)->region()->set_position (start.frame, start.division);
7048 _session->add_command(new StatefulDiffCommand ((*r)->region()));
7051 while (used_playlists.size() > 0) {
7052 list <boost::shared_ptr<Playlist > >::iterator i = used_playlists.begin();
7054 used_playlists.pop_front();
7057 commit_reversible_command ();
7061 Editor::close_region_gaps ()
7063 list <boost::shared_ptr<Playlist > > used_playlists;
7065 RegionSelection rs = get_regions_from_selection_and_entered ();
7067 if (!_session || rs.empty()) {
7071 Dialog dialog (_("Close Region Gaps"));
7074 table.set_spacings (12);
7075 table.set_border_width (12);
7076 Label* l = manage (left_aligned_label (_("Crossfade length")));
7077 table.attach (*l, 0, 1, 0, 1);
7079 SpinButton spin_crossfade (1, 0);
7080 spin_crossfade.set_range (0, 15);
7081 spin_crossfade.set_increments (1, 1);
7082 spin_crossfade.set_value (5);
7083 table.attach (spin_crossfade, 1, 2, 0, 1);
7085 table.attach (*manage (new Label (_("ms"))), 2, 3, 0, 1);
7087 l = manage (left_aligned_label (_("Pull-back length")));
7088 table.attach (*l, 0, 1, 1, 2);
7090 SpinButton spin_pullback (1, 0);
7091 spin_pullback.set_range (0, 100);
7092 spin_pullback.set_increments (1, 1);
7093 spin_pullback.set_value(30);
7094 table.attach (spin_pullback, 1, 2, 1, 2);
7096 table.attach (*manage (new Label (_("ms"))), 2, 3, 1, 2);
7098 dialog.get_vbox()->pack_start (table);
7099 dialog.add_button (Stock::CANCEL, RESPONSE_CANCEL);
7100 dialog.add_button (_("Ok"), RESPONSE_ACCEPT);
7103 if (dialog.run () == RESPONSE_CANCEL) {
7107 framepos_t crossfade_len = spin_crossfade.get_value();
7108 framepos_t pull_back_frames = spin_pullback.get_value();
7110 crossfade_len = lrintf (crossfade_len * _session->frame_rate()/1000);
7111 pull_back_frames = lrintf (pull_back_frames * _session->frame_rate()/1000);
7113 /* Iterate over the region list and make adjacent regions overlap by crossfade_len_ms */
7115 begin_reversible_command (_("close region gaps"));
7118 boost::shared_ptr<Region> last_region;
7120 rs.sort_by_position_and_track();
7122 for (RegionSelection::iterator r = rs.begin(); r != rs.end(); ++r) {
7124 boost::shared_ptr<Playlist> pl = (*r)->region()->playlist();
7126 if (!pl->frozen()) {
7127 /* we haven't seen this playlist before */
7129 /* remember used playlists so we can thaw them later */
7130 used_playlists.push_back(pl);
7134 framepos_t position = (*r)->region()->position();
7136 if (idx == 0 || position < last_region->position()){
7137 last_region = (*r)->region();
7142 (*r)->region()->clear_changes ();
7143 (*r)->region()->trim_front( (position - pull_back_frames));
7145 last_region->clear_changes ();
7146 last_region->trim_end( (position - pull_back_frames + crossfade_len));
7148 _session->add_command (new StatefulDiffCommand ((*r)->region()));
7149 _session->add_command (new StatefulDiffCommand (last_region));
7151 last_region = (*r)->region();
7155 while (used_playlists.size() > 0) {
7156 list <boost::shared_ptr<Playlist > >::iterator i = used_playlists.begin();
7158 used_playlists.pop_front();
7161 commit_reversible_command ();
7165 Editor::tab_to_transient (bool forward)
7167 AnalysisFeatureList positions;
7169 RegionSelection rs = get_regions_from_selection_and_entered ();
7175 framepos_t pos = _session->audible_frame ();
7177 if (!selection->tracks.empty()) {
7179 /* don't waste time searching for transients in duplicate playlists.
7182 TrackViewList ts = selection->tracks.filter_to_unique_playlists ();
7184 for (TrackViewList::iterator t = ts.begin(); t != ts.end(); ++t) {
7186 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (*t);
7189 boost::shared_ptr<Track> tr = rtv->track();
7191 boost::shared_ptr<Playlist> pl = tr->playlist ();
7193 framepos_t result = pl->find_next_transient (pos, forward ? 1 : -1);
7196 positions.push_back (result);
7209 for (RegionSelection::iterator r = rs.begin(); r != rs.end(); ++r) {
7210 (*r)->region()->get_transients (positions);
7214 TransientDetector::cleanup_transients (positions, _session->frame_rate(), 3.0);
7217 AnalysisFeatureList::iterator x;
7219 for (x = positions.begin(); x != positions.end(); ++x) {
7225 if (x != positions.end ()) {
7226 _session->request_locate (*x);
7230 AnalysisFeatureList::reverse_iterator x;
7232 for (x = positions.rbegin(); x != positions.rend(); ++x) {
7238 if (x != positions.rend ()) {
7239 _session->request_locate (*x);
7245 Editor::playhead_forward_to_grid ()
7251 MusicFrame pos (playhead_cursor->current_frame (), 0);
7253 if (pos.frame < max_framepos - 1) {
7255 snap_to_internal (pos, RoundUpAlways, false);
7256 _session->request_locate (pos.frame);
7262 Editor::playhead_backward_to_grid ()
7268 MusicFrame pos (playhead_cursor->current_frame (), 0);
7270 if (pos.frame > 2) {
7272 snap_to_internal (pos, RoundDownAlways, false);
7273 _session->request_locate (pos.frame);
7278 Editor::set_track_height (Height h)
7280 TrackSelection& ts (selection->tracks);
7282 for (TrackSelection::iterator x = ts.begin(); x != ts.end(); ++x) {
7283 (*x)->set_height_enum (h);
7288 Editor::toggle_tracks_active ()
7290 TrackSelection& ts (selection->tracks);
7292 bool target = false;
7298 for (TrackSelection::iterator x = ts.begin(); x != ts.end(); ++x) {
7299 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*>(*x);
7303 target = !rtv->_route->active();
7306 rtv->_route->set_active (target, this);
7312 Editor::remove_tracks ()
7314 /* this will delete GUI objects that may be the subject of an event
7315 handler in which this method is called. Defer actual deletion to the
7316 next idle callback, when all event handling is finished.
7318 Glib::signal_idle().connect (sigc::mem_fun (*this, &Editor::idle_remove_tracks));
7322 Editor::idle_remove_tracks ()
7324 Session::StateProtector sp (_session);
7326 return false; /* do not call again */
7330 Editor::_remove_tracks ()
7332 TrackSelection& ts (selection->tracks);
7338 vector<string> choices;
7342 const char* trackstr;
7344 vector<boost::shared_ptr<Route> > routes;
7345 bool special_bus = false;
7347 for (TrackSelection::iterator x = ts.begin(); x != ts.end(); ++x) {
7348 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (*x);
7352 if (rtv->is_track()) {
7357 routes.push_back (rtv->_route);
7359 if (rtv->route()->is_master() || rtv->route()->is_monitor()) {
7364 if (special_bus && !Config->get_allow_special_bus_removal()) {
7365 MessageDialog msg (_("That would be bad news ...."),
7369 msg.set_secondary_text (string_compose (_(
7370 "Removing the master or monitor bus is such a bad idea\n\
7371 that %1 is not going to allow it.\n\
7373 If you really want to do this sort of thing\n\
7374 edit your ardour.rc file to set the\n\
7375 \"allow-special-bus-removal\" option to be \"yes\""), PROGRAM_NAME));
7382 if (ntracks + nbusses == 0) {
7386 trackstr = P_("track", "tracks", ntracks);
7387 busstr = P_("bus", "busses", nbusses);
7391 prompt = string_compose (_("Do you really want to remove %1 %2 and %3 %4?\n"
7392 "(You may also lose the playlists associated with the %2)\n\n"
7393 "This action cannot be undone, and the session file will be overwritten!"),
7394 ntracks, trackstr, nbusses, busstr);
7396 prompt = string_compose (_("Do you really want to remove %1 %2?\n"
7397 "(You may also lose the playlists associated with the %2)\n\n"
7398 "This action cannot be undone, and the session file will be overwritten!"),
7401 } else if (nbusses) {
7402 prompt = string_compose (_("Do you really want to remove %1 %2?\n\n"
7403 "This action cannot be undone, and the session file will be overwritten"),
7407 choices.push_back (_("No, do nothing."));
7408 if (ntracks + nbusses > 1) {
7409 choices.push_back (_("Yes, remove them."));
7411 choices.push_back (_("Yes, remove it."));
7416 title = string_compose (_("Remove %1"), trackstr);
7418 title = string_compose (_("Remove %1"), busstr);
7421 Choice prompter (title, prompt, choices);
7423 if (prompter.run () != 1) {
7427 if (current_mixer_strip && routes.size () > 1 && std::find (routes.begin(), routes.end(), current_mixer_strip->route()) != routes.end ()) {
7428 /* Route deletion calls Editor::timeaxisview_deleted() iteratively (for each deleted
7429 * route). If the deleted route is currently displayed in the Editor-Mixer (highly
7430 * likely because deletion requires selection) this will call
7431 * Editor::set_selected_mixer_strip () which is expensive ( MixerStrip::set_route() ).
7432 * It's likewise likely that the route that has just been displayed in the
7433 * Editor-Mixer will be next in line for deletion.
7435 * So simply switch to the master-bus (if present)
7437 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
7438 if ((*i)->stripable ()->is_master ()) {
7439 set_selected_mixer_strip (*(*i));
7446 PresentationInfo::ChangeSuspender cs;
7447 DisplaySuspender ds;
7449 boost::shared_ptr<RouteList> rl (new RouteList);
7450 for (vector<boost::shared_ptr<Route> >::iterator x = routes.begin(); x != routes.end(); ++x) {
7453 _session->remove_routes (rl);
7455 /* TrackSelection and RouteList leave scope,
7456 * destructors are called,
7457 * diskstream drops references, save_state is called (again for every track)
7462 Editor::do_insert_time ()
7464 if (selection->tracks.empty()) {
7468 InsertRemoveTimeDialog d (*this);
7469 int response = d.run ();
7471 if (response != RESPONSE_OK) {
7475 if (d.distance() == 0) {
7482 d.intersected_region_action (),
7486 d.move_glued_markers(),
7487 d.move_locked_markers(),
7493 Editor::insert_time (
7494 framepos_t pos, framecnt_t frames, InsertTimeOption opt,
7495 bool all_playlists, bool ignore_music_glue, bool markers_too, bool glued_markers_too, bool locked_markers_too, bool tempo_too
7499 if (Config->get_edit_mode() == Lock) {
7502 bool in_command = false;
7504 TrackViewList ts = selection->tracks.filter_to_unique_playlists ();
7506 for (TrackViewList::iterator x = ts.begin(); x != ts.end(); ++x) {
7510 /* don't operate on any playlist more than once, which could
7511 * happen if "all playlists" is enabled, but there is more
7512 * than 1 track using playlists "from" a given track.
7515 set<boost::shared_ptr<Playlist> > pl;
7517 if (all_playlists) {
7518 RouteTimeAxisView* rtav = dynamic_cast<RouteTimeAxisView*> (*x);
7519 if (rtav && rtav->track ()) {
7520 vector<boost::shared_ptr<Playlist> > all = _session->playlists->playlists_for_track (rtav->track ());
7521 for (vector<boost::shared_ptr<Playlist> >::iterator p = all.begin(); p != all.end(); ++p) {
7526 if ((*x)->playlist ()) {
7527 pl.insert ((*x)->playlist ());
7531 for (set<boost::shared_ptr<Playlist> >::iterator i = pl.begin(); i != pl.end(); ++i) {
7533 (*i)->clear_changes ();
7534 (*i)->clear_owned_changes ();
7537 begin_reversible_command (_("insert time"));
7541 if (opt == SplitIntersected) {
7542 /* non musical split */
7543 (*i)->split (MusicFrame (pos, 0));
7546 (*i)->shift (pos, frames, (opt == MoveIntersected), ignore_music_glue);
7548 vector<Command*> cmds;
7550 _session->add_commands (cmds);
7552 _session->add_command (new StatefulDiffCommand (*i));
7556 RouteTimeAxisView* rtav = dynamic_cast<RouteTimeAxisView*> (*x);
7559 begin_reversible_command (_("insert time"));
7562 rtav->route ()->shift (pos, frames);
7569 const int32_t divisions = get_grid_music_divisions (0);
7570 XMLNode& before (_session->locations()->get_state());
7571 Locations::LocationList copy (_session->locations()->list());
7573 for (Locations::LocationList::iterator i = copy.begin(); i != copy.end(); ++i) {
7575 Locations::LocationList::const_iterator tmp;
7577 if ((*i)->position_lock_style() == AudioTime || glued_markers_too) {
7578 bool const was_locked = (*i)->locked ();
7579 if (locked_markers_too) {
7583 if ((*i)->start() >= pos) {
7584 // move end first, in case we're moving by more than the length of the range
7585 if (!(*i)->is_mark()) {
7586 (*i)->set_end ((*i)->end() + frames, false, true, divisions);
7588 (*i)->set_start ((*i)->start() + frames, false, true, divisions);
7600 begin_reversible_command (_("insert time"));
7603 XMLNode& after (_session->locations()->get_state());
7604 _session->add_command (new MementoCommand<Locations>(*_session->locations(), &before, &after));
7610 begin_reversible_command (_("insert time"));
7613 XMLNode& before (_session->tempo_map().get_state());
7614 _session->tempo_map().insert_time (pos, frames);
7615 XMLNode& after (_session->tempo_map().get_state());
7616 _session->add_command (new MementoCommand<TempoMap>(_session->tempo_map(), &before, &after));
7620 commit_reversible_command ();
7625 Editor::do_remove_time ()
7627 if (selection->tracks.empty()) {
7631 InsertRemoveTimeDialog d (*this, true);
7633 int response = d.run ();
7635 if (response != RESPONSE_OK) {
7639 framecnt_t distance = d.distance();
7641 if (distance == 0) {
7651 d.move_glued_markers(),
7652 d.move_locked_markers(),
7658 Editor::remove_time (framepos_t pos, framecnt_t frames, InsertTimeOption opt,
7659 bool ignore_music_glue, bool markers_too, bool glued_markers_too, bool locked_markers_too, bool tempo_too)
7661 if (Config->get_edit_mode() == Lock) {
7662 error << (_("Cannot insert or delete time when in Lock edit.")) << endmsg;
7665 bool in_command = false;
7667 for (TrackSelection::iterator x = selection->tracks.begin(); x != selection->tracks.end(); ++x) {
7669 boost::shared_ptr<Playlist> pl = (*x)->playlist();
7673 XMLNode &before = pl->get_state();
7676 begin_reversible_command (_("remove time"));
7680 std::list<AudioRange> rl;
7681 AudioRange ar(pos, pos+frames, 0);
7684 pl->shift (pos, -frames, true, ignore_music_glue);
7686 XMLNode &after = pl->get_state();
7688 _session->add_command (new MementoCommand<Playlist> (*pl, &before, &after));
7692 RouteTimeAxisView* rtav = dynamic_cast<RouteTimeAxisView*> (*x);
7695 begin_reversible_command (_("remove time"));
7698 rtav->route ()->shift (pos, -frames);
7702 const int32_t divisions = get_grid_music_divisions (0);
7703 std::list<Location*> loc_kill_list;
7708 XMLNode& before (_session->locations()->get_state());
7709 Locations::LocationList copy (_session->locations()->list());
7711 for (Locations::LocationList::iterator i = copy.begin(); i != copy.end(); ++i) {
7712 if ((*i)->position_lock_style() == AudioTime || glued_markers_too) {
7714 bool const was_locked = (*i)->locked ();
7715 if (locked_markers_too) {
7719 if (!(*i)->is_mark()) { // it's a range; have to handle both start and end
7720 if ((*i)->end() >= pos
7721 && (*i)->end() < pos+frames
7722 && (*i)->start() >= pos
7723 && (*i)->end() < pos+frames) { // range is completely enclosed; kill it
7725 loc_kill_list.push_back(*i);
7726 } else { // only start or end is included, try to do the right thing
7727 // move start before moving end, to avoid trying to move the end to before the start
7728 // if we're removing more time than the length of the range
7729 if ((*i)->start() >= pos && (*i)->start() < pos+frames) {
7730 // start is within cut
7731 (*i)->set_start (pos, false, true,divisions); // bring the start marker to the beginning of the cut
7733 } else if ((*i)->start() >= pos+frames) {
7734 // start (and thus entire range) lies beyond end of cut
7735 (*i)->set_start ((*i)->start() - frames, false, true, divisions); // slip the start marker back
7738 if ((*i)->end() >= pos && (*i)->end() < pos+frames) {
7739 // end is inside cut
7740 (*i)->set_end (pos, false, true, divisions); // bring the end to the cut
7742 } else if ((*i)->end() >= pos+frames) {
7743 // end is beyond end of cut
7744 (*i)->set_end ((*i)->end() - frames, false, true, divisions); // slip the end marker back
7749 } else if ((*i)->start() >= pos && (*i)->start() < pos+frames ) {
7750 loc_kill_list.push_back(*i);
7752 } else if ((*i)->start() >= pos) {
7753 (*i)->set_start ((*i)->start() -frames, false, true, divisions);
7763 for (list<Location*>::iterator i = loc_kill_list.begin(); i != loc_kill_list.end(); ++i) {
7764 _session->locations()->remove( *i );
7769 begin_reversible_command (_("remove time"));
7772 XMLNode& after (_session->locations()->get_state());
7773 _session->add_command (new MementoCommand<Locations>(*_session->locations(), &before, &after));
7778 XMLNode& before (_session->tempo_map().get_state());
7780 if (_session->tempo_map().remove_time (pos, frames) ) {
7782 begin_reversible_command (_("remove time"));
7785 XMLNode& after (_session->tempo_map().get_state());
7786 _session->add_command (new MementoCommand<TempoMap>(_session->tempo_map(), &before, &after));
7791 commit_reversible_command ();
7796 Editor::fit_selection ()
7798 if (!selection->tracks.empty()) {
7799 fit_tracks (selection->tracks);
7803 /* no selected tracks - use tracks with selected regions */
7805 if (!selection->regions.empty()) {
7806 for (RegionSelection::iterator r = selection->regions.begin(); r != selection->regions.end(); ++r) {
7807 tvl.push_back (&(*r)->get_time_axis_view ());
7813 } else if (internal_editing()) {
7814 /* no selected tracks, or regions, but in internal edit mode, so follow the mouse and use
7817 if (entered_track) {
7818 tvl.push_back (entered_track);
7827 Editor::fit_tracks (TrackViewList & tracks)
7829 if (tracks.empty()) {
7833 uint32_t child_heights = 0;
7834 int visible_tracks = 0;
7836 for (TrackSelection::iterator t = tracks.begin(); t != tracks.end(); ++t) {
7838 if (!(*t)->marked_for_display()) {
7842 child_heights += (*t)->effective_height() - (*t)->current_height();
7846 /* compute the per-track height from:
7848 total canvas visible height -
7849 height that will be taken by visible children of selected
7850 tracks - height of the ruler/hscroll area
7852 uint32_t h = (uint32_t) floor ((trackviews_height() - child_heights) / visible_tracks);
7853 double first_y_pos = DBL_MAX;
7855 if (h < TimeAxisView::preset_height (HeightSmall)) {
7856 MessageDialog msg (_("There are too many tracks to fit in the current window"));
7857 /* too small to be displayed */
7861 undo_visual_stack.push_back (current_visual_state (true));
7862 PBD::Unwinder<bool> nsv (no_save_visual, true);
7864 /* build a list of all tracks, including children */
7867 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
7869 TimeAxisView::Children c = (*i)->get_child_list ();
7870 for (TimeAxisView::Children::iterator j = c.begin(); j != c.end(); ++j) {
7871 all.push_back (j->get());
7876 // find selection range.
7877 // if someone knows how to user TrackViewList::iterator for this
7879 int selected_top = -1;
7880 int selected_bottom = -1;
7882 for (TrackViewList::iterator t = all.begin(); t != all.end(); ++t, ++i) {
7883 if ((*t)->marked_for_display ()) {
7884 if (tracks.contains(*t)) {
7885 if (selected_top == -1) {
7888 selected_bottom = i;
7894 for (TrackViewList::iterator t = all.begin(); t != all.end(); ++t, ++i) {
7895 if ((*t)->marked_for_display ()) {
7896 if (tracks.contains(*t)) {
7897 (*t)->set_height (h);
7898 first_y_pos = std::min ((*t)->y_position (), first_y_pos);
7900 if (i > selected_top && i < selected_bottom) {
7901 hide_track_in_display (*t);
7908 set the controls_layout height now, because waiting for its size
7909 request signal handler will cause the vertical adjustment setting to fail
7912 controls_layout.property_height () = _full_canvas_height;
7913 vertical_adjustment.set_value (first_y_pos);
7915 redo_visual_stack.push_back (current_visual_state (true));
7917 visible_tracks_selector.set_text (_("Sel"));
7921 Editor::save_visual_state (uint32_t n)
7923 while (visual_states.size() <= n) {
7924 visual_states.push_back (0);
7927 if (visual_states[n] != 0) {
7928 delete visual_states[n];
7931 visual_states[n] = current_visual_state (true);
7936 Editor::goto_visual_state (uint32_t n)
7938 if (visual_states.size() <= n) {
7942 if (visual_states[n] == 0) {
7946 use_visual_state (*visual_states[n]);
7950 Editor::start_visual_state_op (uint32_t n)
7952 save_visual_state (n);
7954 PopUp* pup = new PopUp (WIN_POS_MOUSE, 1000, true);
7956 snprintf (buf, sizeof (buf), _("Saved view %u"), n+1);
7957 pup->set_text (buf);
7962 Editor::cancel_visual_state_op (uint32_t n)
7964 goto_visual_state (n);
7968 Editor::toggle_region_mute ()
7970 if (_ignore_region_action) {
7974 RegionSelection rs = get_regions_from_selection_and_entered ();
7980 if (rs.size() > 1) {
7981 begin_reversible_command (_("mute regions"));
7983 begin_reversible_command (_("mute region"));
7986 for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
7988 (*i)->region()->playlist()->clear_changes ();
7989 (*i)->region()->set_muted (!(*i)->region()->muted ());
7990 _session->add_command (new StatefulDiffCommand ((*i)->region()));
7994 commit_reversible_command ();
7998 Editor::combine_regions ()
8000 /* foreach track with selected regions, take all selected regions
8001 and join them into a new region containing the subregions (as a
8005 typedef set<RouteTimeAxisView*> RTVS;
8008 if (selection->regions.empty()) {
8012 for (RegionSelection::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
8013 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*>(&(*i)->get_time_axis_view());
8016 tracks.insert (rtv);
8020 begin_reversible_command (_("combine regions"));
8022 vector<RegionView*> new_selection;
8024 for (RTVS::iterator i = tracks.begin(); i != tracks.end(); ++i) {
8027 if ((rv = (*i)->combine_regions ()) != 0) {
8028 new_selection.push_back (rv);
8032 selection->clear_regions ();
8033 for (vector<RegionView*>::iterator i = new_selection.begin(); i != new_selection.end(); ++i) {
8034 selection->add (*i);
8037 commit_reversible_command ();
8041 Editor::uncombine_regions ()
8043 typedef set<RouteTimeAxisView*> RTVS;
8046 if (selection->regions.empty()) {
8050 for (RegionSelection::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
8051 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*>(&(*i)->get_time_axis_view());
8054 tracks.insert (rtv);
8058 begin_reversible_command (_("uncombine regions"));
8060 for (RTVS::iterator i = tracks.begin(); i != tracks.end(); ++i) {
8061 (*i)->uncombine_regions ();
8064 commit_reversible_command ();
8068 Editor::toggle_midi_input_active (bool flip_others)
8071 boost::shared_ptr<RouteList> rl (new RouteList);
8073 for (TrackSelection::iterator i = selection->tracks.begin(); i != selection->tracks.end(); ++i) {
8074 RouteTimeAxisView *rtav = dynamic_cast<RouteTimeAxisView *>(*i);
8080 boost::shared_ptr<MidiTrack> mt = rtav->midi_track();
8083 rl->push_back (rtav->route());
8084 onoff = !mt->input_active();
8088 _session->set_exclusive_input_active (rl, onoff, flip_others);
8091 static bool ok_fine (GdkEventAny*) { return true; }
8097 lock_dialog = new Gtk::Dialog (string_compose (_("%1: Locked"), PROGRAM_NAME), true);
8099 Gtk::Image* padlock = manage (new Gtk::Image (ARDOUR_UI_UTILS::get_icon ("padlock_closed")));
8100 lock_dialog->get_vbox()->pack_start (*padlock);
8101 lock_dialog->signal_delete_event ().connect (sigc::ptr_fun (ok_fine));
8103 ArdourButton* b = manage (new ArdourButton);
8104 b->set_name ("lock button");
8105 b->set_text (_("Click to unlock"));
8106 b->signal_clicked.connect (sigc::mem_fun (*this, &Editor::unlock));
8107 lock_dialog->get_vbox()->pack_start (*b);
8109 lock_dialog->get_vbox()->show_all ();
8110 lock_dialog->set_size_request (200, 200);
8113 delete _main_menu_disabler;
8114 _main_menu_disabler = new MainMenuDisabler;
8116 lock_dialog->present ();
8118 lock_dialog->get_window()->set_decorations (Gdk::WMDecoration (0));
8124 lock_dialog->hide ();
8126 delete _main_menu_disabler;
8127 _main_menu_disabler = 0;
8129 if (UIConfiguration::instance().get_lock_gui_after_seconds()) {
8130 start_lock_event_timing ();
8135 Editor::bring_in_callback (Gtk::Label* label, uint32_t n, uint32_t total, string name)
8137 Gtkmm2ext::UI::instance()->call_slot (invalidator (*this), boost::bind (&Editor::update_bring_in_message, this, label, n, total, name));
8141 Editor::update_bring_in_message (Gtk::Label* label, uint32_t n, uint32_t total, string name)
8143 Timers::TimerSuspender t;
8144 label->set_text (string_compose ("Copying %1, %2 of %3", name, n, total));
8145 Gtkmm2ext::UI::instance()->flush_pending (1);
8149 Editor::bring_all_sources_into_session ()
8156 ArdourDialog w (_("Moving embedded files into session folder"));
8157 w.get_vbox()->pack_start (msg);
8160 /* flush all pending GUI events because we're about to start copying
8164 Timers::TimerSuspender t;
8165 Gtkmm2ext::UI::instance()->flush_pending (3);
8169 _session->bring_all_sources_into_session (boost::bind (&Editor::bring_in_callback, this, &msg, _1, _2, _3));