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 <gtkmm/messagedialog.h>
33 #include "pbd/error.h"
34 #include "pbd/basename.h"
35 #include "pbd/pthread_utils.h"
36 #include "pbd/memento_command.h"
37 #include "pbd/unwind.h"
38 #include "pbd/whitespace.h"
39 #include "pbd/stateful_diff_command.h"
41 #include "gtkmm2ext/utils.h"
43 #include "widgets/choice.h"
44 #include "widgets/popup.h"
45 #include "widgets/prompter.h"
47 #include "ardour/audio_track.h"
48 #include "ardour/audioregion.h"
49 #include "ardour/boost_debug.h"
50 #include "ardour/dB.h"
51 #include "ardour/location.h"
52 #include "ardour/midi_region.h"
53 #include "ardour/midi_track.h"
54 #include "ardour/operations.h"
55 #include "ardour/playlist_factory.h"
56 #include "ardour/profile.h"
57 #include "ardour/quantize.h"
58 #include "ardour/legatize.h"
59 #include "ardour/region_factory.h"
60 #include "ardour/reverse.h"
61 #include "ardour/session.h"
62 #include "ardour/session_playlists.h"
63 #include "ardour/strip_silence.h"
64 #include "ardour/transient_detector.h"
65 #include "ardour/transpose.h"
66 #include "ardour/vca_manager.h"
68 #include "canvas/canvas.h"
71 #include "audio_region_view.h"
72 #include "audio_streamview.h"
73 #include "audio_time_axis.h"
74 #include "automation_region_view.h"
75 #include "automation_time_axis.h"
76 #include "control_point.h"
80 #include "editor_cursors.h"
81 #include "editor_drag.h"
82 #include "editor_regions.h"
83 #include "editor_routes.h"
84 #include "gui_thread.h"
85 #include "insert_remove_time_dialog.h"
86 #include "interthread_progress_window.h"
87 #include "item_counts.h"
89 #include "midi_region_view.h"
91 #include "mixer_strip.h"
92 #include "mouse_cursors.h"
93 #include "normalize_dialog.h"
95 #include "paste_context.h"
96 #include "patch_change_dialog.h"
97 #include "quantize_dialog.h"
98 #include "region_gain_line.h"
99 #include "rgb_macros.h"
100 #include "route_time_axis.h"
101 #include "selection.h"
102 #include "selection_templates.h"
103 #include "streamview.h"
104 #include "strip_silence_dialog.h"
105 #include "time_axis_view.h"
107 #include "transpose_dialog.h"
108 #include "transform_dialog.h"
109 #include "ui_config.h"
110 #include "vca_time_axis.h"
112 #include "pbd/i18n.h"
115 using namespace ARDOUR;
118 using namespace Gtkmm2ext;
119 using namespace ArdourWidgets;
120 using namespace Editing;
121 using Gtkmm2ext::Keyboard;
123 /***********************************************************************
125 ***********************************************************************/
128 Editor::undo (uint32_t n)
130 if (_session && _session->actively_recording()) {
131 /* no undo allowed while recording. Session will check also,
132 but we don't even want to get to that.
137 if (_drags->active ()) {
143 if (_session->undo_depth() == 0) {
144 undo_action->set_sensitive(false);
146 redo_action->set_sensitive(true);
147 begin_selection_op_history ();
152 Editor::redo (uint32_t n)
154 if (_session && _session->actively_recording()) {
155 /* no redo allowed while recording. Session will check also,
156 but we don't even want to get to that.
161 if (_drags->active ()) {
167 if (_session->redo_depth() == 0) {
168 redo_action->set_sensitive(false);
170 undo_action->set_sensitive(true);
171 begin_selection_op_history ();
176 Editor::split_regions_at (MusicSample where, RegionSelection& regions, bool snap_sample)
180 RegionSelection pre_selected_regions = selection->regions;
181 bool working_on_selection = !pre_selected_regions.empty();
183 list<boost::shared_ptr<Playlist> > used_playlists;
184 list<RouteTimeAxisView*> used_trackviews;
186 if (regions.empty()) {
190 begin_reversible_command (_("split"));
192 // if splitting a single region, and snap-to is using
193 // region boundaries, don't pay attention to them
195 if (regions.size() == 1) {
196 // switch (_snap_type) { //ToDo !!!
197 // case SnapToRegionStart:
198 // case SnapToRegionSync:
199 // case SnapToRegionEnd:
202 // if (snap_sample) {
212 EditorFreeze(); /* Emit Signal */
215 for (RegionSelection::iterator a = regions.begin(); a != regions.end(); ) {
217 RegionSelection::iterator tmp;
219 /* XXX this test needs to be more complicated, to make sure we really
220 have something to split.
223 if (!(*a)->region()->covers (where.sample)) {
231 boost::shared_ptr<Playlist> pl = (*a)->region()->playlist();
239 /* we haven't seen this playlist before */
241 /* remember used playlists so we can thaw them later */
242 used_playlists.push_back(pl);
244 TimeAxisView& tv = (*a)->get_time_axis_view();
245 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (&tv);
247 used_trackviews.push_back (rtv);
254 pl->clear_changes ();
255 pl->split_region ((*a)->region(), where);
256 _session->add_command (new StatefulDiffCommand (pl));
262 latest_regionviews.clear ();
264 vector<sigc::connection> region_added_connections;
266 for (list<RouteTimeAxisView*>::iterator i = used_trackviews.begin(); i != used_trackviews.end(); ++i) {
267 region_added_connections.push_back ((*i)->view()->RegionViewAdded.connect (sigc::mem_fun(*this, &Editor::collect_new_region_view)));
270 while (used_playlists.size() > 0) {
271 list <boost::shared_ptr<Playlist > >::iterator i = used_playlists.begin();
273 used_playlists.pop_front();
276 for (vector<sigc::connection>::iterator c = region_added_connections.begin(); c != region_added_connections.end(); ++c) {
281 EditorThaw(); /* Emit Signal */
284 if (working_on_selection) {
285 // IFF we were working on selected regions, try to reinstate the other region selections that existed before the freeze/thaw.
287 RegionSelectionAfterSplit rsas = Config->get_region_selection_after_split();
288 /* There are three classes of regions that we might want selected after
289 splitting selected regions:
290 - regions selected before the split operation, and unaffected by it
291 - newly-created regions before the split
292 - newly-created regions after the split
295 if (rsas & Existing) {
296 // region selections that existed before the split.
297 selection->add (pre_selected_regions);
300 for (RegionSelection::iterator ri = latest_regionviews.begin(); ri != latest_regionviews.end(); ri++) {
301 if ((*ri)->region()->position() < where.sample) {
302 // new regions created before the split
303 if (rsas & NewlyCreatedLeft) {
304 selection->add (*ri);
307 // new regions created after the split
308 if (rsas & NewlyCreatedRight) {
309 selection->add (*ri);
315 commit_reversible_command ();
318 /** Move one extreme of the current range selection. If more than one range is selected,
319 * the start of the earliest range or the end of the latest range is moved.
321 * @param move_end true to move the end of the current range selection, false to move
323 * @param next true to move the extreme to the next region boundary, false to move to
327 Editor::move_range_selection_start_or_end_to_region_boundary (bool move_end, bool next)
329 if (selection->time.start() == selection->time.end_sample()) {
333 samplepos_t start = selection->time.start ();
334 samplepos_t end = selection->time.end_sample ();
336 /* the position of the thing we may move */
337 samplepos_t pos = move_end ? end : start;
338 int dir = next ? 1 : -1;
340 /* so we don't find the current region again */
341 if (dir > 0 || pos > 0) {
345 samplepos_t const target = get_region_boundary (pos, dir, true, false);
360 begin_reversible_selection_op (_("alter selection"));
361 selection->set_preserving_all_ranges (start, end);
362 commit_reversible_selection_op ();
366 Editor::nudge_forward_release (GdkEventButton* ev)
368 if (ev->state & Keyboard::PrimaryModifier) {
369 nudge_forward (false, true);
371 nudge_forward (false, false);
377 Editor::nudge_backward_release (GdkEventButton* ev)
379 if (ev->state & Keyboard::PrimaryModifier) {
380 nudge_backward (false, true);
382 nudge_backward (false, false);
389 Editor::nudge_forward (bool next, bool force_playhead)
391 samplepos_t distance;
392 samplepos_t next_distance;
398 RegionSelection rs = get_regions_from_selection_and_entered ();
400 if (!force_playhead && !rs.empty()) {
402 begin_reversible_command (_("nudge regions forward"));
404 for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
405 boost::shared_ptr<Region> r ((*i)->region());
407 distance = get_nudge_distance (r->position(), next_distance);
410 distance = next_distance;
414 r->set_position (r->position() + distance);
415 _session->add_command (new StatefulDiffCommand (r));
418 commit_reversible_command ();
421 } else if (!force_playhead && !selection->markers.empty()) {
424 bool in_command = false;
425 const int32_t divisions = get_grid_music_divisions (0);
427 for (MarkerSelection::iterator i = selection->markers.begin(); i != selection->markers.end(); ++i) {
429 Location* loc = find_location_from_marker ((*i), is_start);
433 XMLNode& before (loc->get_state());
436 distance = get_nudge_distance (loc->start(), next_distance);
438 distance = next_distance;
440 if (max_samplepos - distance > loc->start() + loc->length()) {
441 loc->set_start (loc->start() + distance, false, true, divisions);
443 loc->set_start (max_samplepos - loc->length(), false, true, divisions);
446 distance = get_nudge_distance (loc->end(), next_distance);
448 distance = next_distance;
450 if (max_samplepos - distance > loc->end()) {
451 loc->set_end (loc->end() + distance, false, true, divisions);
453 loc->set_end (max_samplepos, false, true, divisions);
455 if (loc->is_session_range()) {
456 _session->set_end_is_free (false);
460 begin_reversible_command (_("nudge location forward"));
463 XMLNode& after (loc->get_state());
464 _session->add_command (new MementoCommand<Location>(*loc, &before, &after));
469 commit_reversible_command ();
472 distance = get_nudge_distance (playhead_cursor->current_sample (), next_distance);
473 _session->request_locate (playhead_cursor->current_sample () + distance);
478 Editor::nudge_backward (bool next, bool force_playhead)
480 samplepos_t distance;
481 samplepos_t next_distance;
487 RegionSelection rs = get_regions_from_selection_and_entered ();
489 if (!force_playhead && !rs.empty()) {
491 begin_reversible_command (_("nudge regions backward"));
493 for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
494 boost::shared_ptr<Region> r ((*i)->region());
496 distance = get_nudge_distance (r->position(), next_distance);
499 distance = next_distance;
504 if (r->position() > distance) {
505 r->set_position (r->position() - distance);
509 _session->add_command (new StatefulDiffCommand (r));
512 commit_reversible_command ();
514 } else if (!force_playhead && !selection->markers.empty()) {
517 bool in_command = false;
519 for (MarkerSelection::iterator i = selection->markers.begin(); i != selection->markers.end(); ++i) {
521 Location* loc = find_location_from_marker ((*i), is_start);
525 XMLNode& before (loc->get_state());
528 distance = get_nudge_distance (loc->start(), next_distance);
530 distance = next_distance;
532 if (distance < loc->start()) {
533 loc->set_start (loc->start() - distance, false, true, get_grid_music_divisions(0));
535 loc->set_start (0, false, true, get_grid_music_divisions(0));
538 distance = get_nudge_distance (loc->end(), next_distance);
541 distance = next_distance;
544 if (distance < loc->end() - loc->length()) {
545 loc->set_end (loc->end() - distance, false, true, get_grid_music_divisions(0));
547 loc->set_end (loc->length(), false, true, get_grid_music_divisions(0));
549 if (loc->is_session_range()) {
550 _session->set_end_is_free (false);
554 begin_reversible_command (_("nudge location forward"));
557 XMLNode& after (loc->get_state());
558 _session->add_command (new MementoCommand<Location>(*loc, &before, &after));
562 commit_reversible_command ();
567 distance = get_nudge_distance (playhead_cursor->current_sample (), next_distance);
569 if (playhead_cursor->current_sample () > distance) {
570 _session->request_locate (playhead_cursor->current_sample () - distance);
572 _session->goto_start();
578 Editor::nudge_forward_capture_offset ()
580 RegionSelection rs = get_regions_from_selection_and_entered ();
582 if (!_session || rs.empty()) {
586 begin_reversible_command (_("nudge forward"));
588 samplepos_t const distance = _session->worst_output_latency();
590 for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
591 boost::shared_ptr<Region> r ((*i)->region());
594 r->set_position (r->position() + distance);
595 _session->add_command(new StatefulDiffCommand (r));
598 commit_reversible_command ();
602 Editor::nudge_backward_capture_offset ()
604 RegionSelection rs = get_regions_from_selection_and_entered ();
606 if (!_session || rs.empty()) {
610 begin_reversible_command (_("nudge backward"));
612 samplepos_t const distance = _session->worst_output_latency();
614 for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
615 boost::shared_ptr<Region> r ((*i)->region());
619 if (r->position() > distance) {
620 r->set_position (r->position() - distance);
624 _session->add_command(new StatefulDiffCommand (r));
627 commit_reversible_command ();
630 struct RegionSelectionPositionSorter {
631 bool operator() (RegionView* a, RegionView* b) {
632 return a->region()->position() < b->region()->position();
637 Editor::sequence_regions ()
640 samplepos_t r_end_prev;
648 RegionSelection rs = get_regions_from_selection_and_entered ();
649 rs.sort(RegionSelectionPositionSorter());
653 bool in_command = false;
655 for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
656 boost::shared_ptr<Region> r ((*i)->region());
664 if(r->position_locked())
671 r->set_position(r_end_prev);
675 begin_reversible_command (_("sequence regions"));
678 _session->add_command (new StatefulDiffCommand (r));
680 r_end=r->position() + r->length();
686 commit_reversible_command ();
695 Editor::move_to_start ()
697 _session->goto_start ();
701 Editor::move_to_end ()
704 _session->request_locate (_session->current_end_sample());
708 Editor::build_region_boundary_cache ()
711 //ToDo: maybe set a timer so we don't recalutate when lots of changes are coming in
712 //ToDo: maybe somehow defer this until session is fully loaded.
714 if ( !_region_boundary_cache_dirty )
718 vector<RegionPoint> interesting_points;
719 boost::shared_ptr<Region> r;
720 TrackViewList tracks;
723 region_boundary_cache.clear ();
729 bool maybe_first_sample = false;
731 if ( UIConfiguration::instance().get_snap_to_region_start() ) {
732 interesting_points.push_back (Start);
733 maybe_first_sample = true;
736 if ( UIConfiguration::instance().get_snap_to_region_end() ) {
737 interesting_points.push_back (End);
740 if ( UIConfiguration::instance().get_snap_to_region_sync() ) {
741 interesting_points.push_back (SyncPoint);
744 TimeAxisView *ontrack = 0;
747 //in the past, we used the track selection to limit snap. I think this is not desired.
748 //or if it is, it needs to be updated every time the track selection changes (so the snapped-cursor can show it)
749 // if (!selection->tracks.empty()) {
750 // tlist = selection->tracks.filter_to_unique_playlists ();
752 tlist = track_views.filter_to_unique_playlists ();
755 if (maybe_first_sample) {
756 TrackViewList::const_iterator i;
757 for (i = tlist.begin(); i != tlist.end(); ++i) {
758 boost::shared_ptr<Playlist> pl = (*i)->playlist();
759 if (pl && pl->count_regions_at (0)) {
760 region_boundary_cache.push_back (0);
766 while (pos < _session->current_end_sample() && !at_end) {
769 samplepos_t lpos = max_samplepos;
771 for (vector<RegionPoint>::iterator p = interesting_points.begin(); p != interesting_points.end(); ++p) {
773 if ((r = find_next_region (pos, *p, 1, tlist, &ontrack)) == 0) {
774 if (*p == interesting_points.back()) {
777 /* move to next point type */
783 rpos = r->first_sample();
787 rpos = r->last_sample();
791 rpos = r->sync_position ();
802 /* prevent duplicates, but we don't use set<> because we want to be able
806 vector<samplepos_t>::iterator ri;
808 for (ri = region_boundary_cache.begin(); ri != region_boundary_cache.end(); ++ri) {
814 if (ri == region_boundary_cache.end()) {
815 region_boundary_cache.push_back (rpos);
822 /* finally sort to be sure that the order is correct */
824 sort (region_boundary_cache.begin(), region_boundary_cache.end());
826 _region_boundary_cache_dirty = false;
829 boost::shared_ptr<Region>
830 Editor::find_next_region (samplepos_t sample, RegionPoint point, int32_t dir, TrackViewList& tracks, TimeAxisView **ontrack)
832 TrackViewList::iterator i;
833 samplepos_t closest = max_samplepos;
834 boost::shared_ptr<Region> ret;
835 samplepos_t rpos = 0;
837 samplepos_t track_sample;
839 for (i = tracks.begin(); i != tracks.end(); ++i) {
841 samplecnt_t distance;
842 boost::shared_ptr<Region> r;
844 track_sample = sample;
846 if ((r = (*i)->find_next_region (track_sample, point, dir)) == 0) {
852 rpos = r->first_sample ();
856 rpos = r->last_sample ();
860 rpos = r->sync_position ();
865 distance = rpos - sample;
867 distance = sample - rpos;
870 if (distance < closest) {
882 Editor::find_next_region_boundary (samplepos_t pos, int32_t dir, const TrackViewList& tracks)
884 samplecnt_t distance = max_samplepos;
885 samplepos_t current_nearest = -1;
887 for (TrackViewList::const_iterator i = tracks.begin(); i != tracks.end(); ++i) {
888 samplepos_t contender;
891 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (*i);
897 if ((contender = rtv->find_next_region_boundary (pos, dir)) < 0) {
901 d = ::llabs (pos - contender);
904 current_nearest = contender;
909 return current_nearest;
913 Editor::get_region_boundary (samplepos_t pos, int32_t dir, bool with_selection, bool only_onscreen)
918 if (with_selection && Config->get_region_boundaries_from_selected_tracks()) {
920 if (!selection->tracks.empty()) {
922 target = find_next_region_boundary (pos, dir, selection->tracks);
926 if (only_onscreen || Config->get_region_boundaries_from_onscreen_tracks()) {
927 get_onscreen_tracks (tvl);
928 target = find_next_region_boundary (pos, dir, tvl);
930 target = find_next_region_boundary (pos, dir, track_views);
936 if (only_onscreen || Config->get_region_boundaries_from_onscreen_tracks()) {
937 get_onscreen_tracks (tvl);
938 target = find_next_region_boundary (pos, dir, tvl);
940 target = find_next_region_boundary (pos, dir, track_views);
948 Editor::cursor_to_region_boundary (bool with_selection, int32_t dir)
950 samplepos_t pos = playhead_cursor->current_sample ();
957 // so we don't find the current region again..
958 if (dir > 0 || pos > 0) {
962 if ((target = get_region_boundary (pos, dir, with_selection, false)) < 0) {
966 _session->request_locate (target);
970 Editor::cursor_to_next_region_boundary (bool with_selection)
972 cursor_to_region_boundary (with_selection, 1);
976 Editor::cursor_to_previous_region_boundary (bool with_selection)
978 cursor_to_region_boundary (with_selection, -1);
982 Editor::cursor_to_region_point (EditorCursor* cursor, RegionPoint point, int32_t dir)
984 boost::shared_ptr<Region> r;
985 samplepos_t pos = cursor->current_sample ();
991 TimeAxisView *ontrack = 0;
993 // so we don't find the current region again..
997 if (!selection->tracks.empty()) {
999 r = find_next_region (pos, point, dir, selection->tracks, &ontrack);
1001 } else if (clicked_axisview) {
1004 t.push_back (clicked_axisview);
1006 r = find_next_region (pos, point, dir, t, &ontrack);
1010 r = find_next_region (pos, point, dir, track_views, &ontrack);
1019 pos = r->first_sample ();
1023 pos = r->last_sample ();
1027 pos = r->sync_position ();
1031 if (cursor == playhead_cursor) {
1032 _session->request_locate (pos);
1034 cursor->set_position (pos);
1039 Editor::cursor_to_next_region_point (EditorCursor* cursor, RegionPoint point)
1041 cursor_to_region_point (cursor, point, 1);
1045 Editor::cursor_to_previous_region_point (EditorCursor* cursor, RegionPoint point)
1047 cursor_to_region_point (cursor, point, -1);
1051 Editor::cursor_to_selection_start (EditorCursor *cursor)
1053 samplepos_t pos = 0;
1055 switch (mouse_mode) {
1057 if (!selection->regions.empty()) {
1058 pos = selection->regions.start();
1063 if (!selection->time.empty()) {
1064 pos = selection->time.start ();
1072 if (cursor == playhead_cursor) {
1073 _session->request_locate (pos);
1075 cursor->set_position (pos);
1080 Editor::cursor_to_selection_end (EditorCursor *cursor)
1082 samplepos_t pos = 0;
1084 switch (mouse_mode) {
1086 if (!selection->regions.empty()) {
1087 pos = selection->regions.end_sample();
1092 if (!selection->time.empty()) {
1093 pos = selection->time.end_sample ();
1101 if (cursor == playhead_cursor) {
1102 _session->request_locate (pos);
1104 cursor->set_position (pos);
1109 Editor::selected_marker_to_region_boundary (bool with_selection, int32_t dir)
1119 if (selection->markers.empty()) {
1123 if (!mouse_sample (mouse, ignored)) {
1127 add_location_mark (mouse);
1130 if ((loc = find_location_from_marker (selection->markers.front(), ignored)) == 0) {
1134 samplepos_t pos = loc->start();
1136 // so we don't find the current region again..
1137 if (dir > 0 || pos > 0) {
1141 if ((target = get_region_boundary (pos, dir, with_selection, false)) < 0) {
1145 loc->move_to (target, 0);
1149 Editor::selected_marker_to_next_region_boundary (bool with_selection)
1151 selected_marker_to_region_boundary (with_selection, 1);
1155 Editor::selected_marker_to_previous_region_boundary (bool with_selection)
1157 selected_marker_to_region_boundary (with_selection, -1);
1161 Editor::selected_marker_to_region_point (RegionPoint point, int32_t dir)
1163 boost::shared_ptr<Region> r;
1168 if (!_session || selection->markers.empty()) {
1172 if ((loc = find_location_from_marker (selection->markers.front(), ignored)) == 0) {
1176 TimeAxisView *ontrack = 0;
1180 // so we don't find the current region again..
1184 if (!selection->tracks.empty()) {
1186 r = find_next_region (pos, point, dir, selection->tracks, &ontrack);
1190 r = find_next_region (pos, point, dir, track_views, &ontrack);
1199 pos = r->first_sample ();
1203 pos = r->last_sample ();
1207 pos = r->adjust_to_sync (r->first_sample());
1211 loc->move_to (pos, 0);
1215 Editor::selected_marker_to_next_region_point (RegionPoint point)
1217 selected_marker_to_region_point (point, 1);
1221 Editor::selected_marker_to_previous_region_point (RegionPoint point)
1223 selected_marker_to_region_point (point, -1);
1227 Editor::selected_marker_to_selection_start ()
1229 samplepos_t pos = 0;
1233 if (!_session || selection->markers.empty()) {
1237 if ((loc = find_location_from_marker (selection->markers.front(), ignored)) == 0) {
1241 switch (mouse_mode) {
1243 if (!selection->regions.empty()) {
1244 pos = selection->regions.start();
1249 if (!selection->time.empty()) {
1250 pos = selection->time.start ();
1258 loc->move_to (pos, 0);
1262 Editor::selected_marker_to_selection_end ()
1264 samplepos_t pos = 0;
1268 if (!_session || selection->markers.empty()) {
1272 if ((loc = find_location_from_marker (selection->markers.front(), ignored)) == 0) {
1276 switch (mouse_mode) {
1278 if (!selection->regions.empty()) {
1279 pos = selection->regions.end_sample();
1284 if (!selection->time.empty()) {
1285 pos = selection->time.end_sample ();
1293 loc->move_to (pos, 0);
1297 Editor::scroll_playhead (bool forward)
1299 samplepos_t pos = playhead_cursor->current_sample ();
1300 samplecnt_t delta = (samplecnt_t) floor (current_page_samples() / 0.8);
1303 if (pos == max_samplepos) {
1307 if (pos < max_samplepos - delta) {
1310 pos = max_samplepos;
1326 _session->request_locate (pos);
1330 Editor::cursor_align (bool playhead_to_edit)
1336 if (playhead_to_edit) {
1338 if (selection->markers.empty()) {
1342 _session->request_locate (selection->markers.front()->position(), _session->transport_rolling());
1345 const int32_t divisions = get_grid_music_divisions (0);
1346 /* move selected markers to playhead */
1348 for (MarkerSelection::iterator i = selection->markers.begin(); i != selection->markers.end(); ++i) {
1351 Location* loc = find_location_from_marker (*i, ignored);
1353 if (loc->is_mark()) {
1354 loc->set_start (playhead_cursor->current_sample (), false, true, divisions);
1356 loc->set (playhead_cursor->current_sample (),
1357 playhead_cursor->current_sample () + loc->length(), true, divisions);
1364 Editor::scroll_backward (float pages)
1366 samplepos_t const one_page = (samplepos_t) rint (_visible_canvas_width * samples_per_pixel);
1367 samplepos_t const cnt = (samplepos_t) floor (pages * one_page);
1370 if (_leftmost_sample < cnt) {
1373 sample = _leftmost_sample - cnt;
1376 reset_x_origin (sample);
1380 Editor::scroll_forward (float pages)
1382 samplepos_t const one_page = (samplepos_t) rint (_visible_canvas_width * samples_per_pixel);
1383 samplepos_t const cnt = (samplepos_t) floor (pages * one_page);
1386 if (max_samplepos - cnt < _leftmost_sample) {
1387 sample = max_samplepos - cnt;
1389 sample = _leftmost_sample + cnt;
1392 reset_x_origin (sample);
1396 Editor::scroll_tracks_down ()
1398 double vert_value = vertical_adjustment.get_value() + vertical_adjustment.get_page_size();
1399 if (vert_value > vertical_adjustment.get_upper() - _visible_canvas_height) {
1400 vert_value = vertical_adjustment.get_upper() - _visible_canvas_height;
1403 vertical_adjustment.set_value (vert_value);
1407 Editor::scroll_tracks_up ()
1409 vertical_adjustment.set_value (vertical_adjustment.get_value() - vertical_adjustment.get_page_size());
1413 Editor::scroll_tracks_down_line ()
1415 double vert_value = vertical_adjustment.get_value() + 60;
1417 if (vert_value > vertical_adjustment.get_upper() - _visible_canvas_height) {
1418 vert_value = vertical_adjustment.get_upper() - _visible_canvas_height;
1421 vertical_adjustment.set_value (vert_value);
1425 Editor::scroll_tracks_up_line ()
1427 reset_y_origin (vertical_adjustment.get_value() - 60);
1431 Editor::select_topmost_track ()
1433 const double top_of_trackviews = vertical_adjustment.get_value();
1434 for (TrackViewList::iterator t = track_views.begin(); t != track_views.end(); ++t) {
1435 if ((*t)->hidden()) {
1438 std::pair<TimeAxisView*,double> res = (*t)->covers_y_position (top_of_trackviews);
1440 selection->set (*t);
1447 Editor::scroll_down_one_track (bool skip_child_views)
1449 TrackViewList::reverse_iterator next = track_views.rend();
1450 const double top_of_trackviews = vertical_adjustment.get_value();
1452 for (TrackViewList::reverse_iterator t = track_views.rbegin(); t != track_views.rend(); ++t) {
1453 if ((*t)->hidden()) {
1457 /* If this is the upper-most visible trackview, we want to display
1458 * the one above it (next)
1460 * Note that covers_y_position() is recursive and includes child views
1462 std::pair<TimeAxisView*,double> res = (*t)->covers_y_position (top_of_trackviews);
1465 if (skip_child_views) {
1468 /* automation lane (one level, non-recursive)
1470 * - if no automation lane exists -> move to next tack
1471 * - if the first (here: bottom-most) matches -> move to next tack
1472 * - if no y-axis match is found -> the current track is at the top
1473 * -> move to last (here: top-most) automation lane
1475 TimeAxisView::Children kids = (*t)->get_child_list();
1476 TimeAxisView::Children::reverse_iterator nkid = kids.rend();
1478 for (TimeAxisView::Children::reverse_iterator ci = kids.rbegin(); ci != kids.rend(); ++ci) {
1479 if ((*ci)->hidden()) {
1483 std::pair<TimeAxisView*,double> dev;
1484 dev = (*ci)->covers_y_position (top_of_trackviews);
1486 /* some automation lane is currently at the top */
1487 if (ci == kids.rbegin()) {
1488 /* first (bottom-most) autmation lane is at the top.
1489 * -> move to next track
1498 if (nkid != kids.rend()) {
1499 ensure_time_axis_view_is_visible (**nkid, true);
1507 /* move to the track below the first one that covers the */
1509 if (next != track_views.rend()) {
1510 ensure_time_axis_view_is_visible (**next, true);
1518 Editor::scroll_up_one_track (bool skip_child_views)
1520 TrackViewList::iterator prev = track_views.end();
1521 double top_of_trackviews = vertical_adjustment.get_value ();
1523 for (TrackViewList::iterator t = track_views.begin(); t != track_views.end(); ++t) {
1525 if ((*t)->hidden()) {
1529 /* find the trackview at the top of the trackview group
1531 * Note that covers_y_position() is recursive and includes child views
1533 std::pair<TimeAxisView*,double> res = (*t)->covers_y_position (top_of_trackviews);
1536 if (skip_child_views) {
1539 /* automation lane (one level, non-recursive)
1541 * - if no automation lane exists -> move to prev tack
1542 * - if no y-axis match is found -> the current track is at the top -> move to prev track
1543 * (actually last automation lane of previous track, see below)
1544 * - if first (top-most) lane is at the top -> move to this track
1545 * - else move up one lane
1547 TimeAxisView::Children kids = (*t)->get_child_list();
1548 TimeAxisView::Children::iterator pkid = kids.end();
1550 for (TimeAxisView::Children::iterator ci = kids.begin(); ci != kids.end(); ++ci) {
1551 if ((*ci)->hidden()) {
1555 std::pair<TimeAxisView*,double> dev;
1556 dev = (*ci)->covers_y_position (top_of_trackviews);
1558 /* some automation lane is currently at the top */
1559 if (ci == kids.begin()) {
1560 /* first (top-most) autmation lane is at the top.
1561 * jump directly to this track's top
1563 ensure_time_axis_view_is_visible (**t, true);
1566 else if (pkid != kids.end()) {
1567 /* some other automation lane is at the top.
1568 * move up to prev automation lane.
1570 ensure_time_axis_view_is_visible (**pkid, true);
1573 assert(0); // not reached
1584 if (prev != track_views.end()) {
1585 // move to bottom-most automation-lane of the previous track
1586 TimeAxisView::Children kids = (*prev)->get_child_list();
1587 TimeAxisView::Children::reverse_iterator pkid = kids.rend();
1588 if (!skip_child_views) {
1589 // find the last visible lane
1590 for (TimeAxisView::Children::reverse_iterator ci = kids.rbegin(); ci != kids.rend(); ++ci) {
1591 if (!(*ci)->hidden()) {
1597 if (pkid != kids.rend()) {
1598 ensure_time_axis_view_is_visible (**pkid, true);
1600 ensure_time_axis_view_is_visible (**prev, true);
1609 Editor::scroll_left_step ()
1611 samplepos_t xdelta = (current_page_samples() / 8);
1613 if (_leftmost_sample > xdelta) {
1614 reset_x_origin (_leftmost_sample - xdelta);
1622 Editor::scroll_right_step ()
1624 samplepos_t xdelta = (current_page_samples() / 8);
1626 if (max_samplepos - xdelta > _leftmost_sample) {
1627 reset_x_origin (_leftmost_sample + xdelta);
1629 reset_x_origin (max_samplepos - current_page_samples());
1634 Editor::scroll_left_half_page ()
1636 samplepos_t xdelta = (current_page_samples() / 2);
1637 if (_leftmost_sample > xdelta) {
1638 reset_x_origin (_leftmost_sample - xdelta);
1645 Editor::scroll_right_half_page ()
1647 samplepos_t xdelta = (current_page_samples() / 2);
1648 if (max_samplepos - xdelta > _leftmost_sample) {
1649 reset_x_origin (_leftmost_sample + xdelta);
1651 reset_x_origin (max_samplepos - current_page_samples());
1658 Editor::tav_zoom_step (bool coarser)
1660 DisplaySuspender ds;
1664 if (selection->tracks.empty()) {
1667 ts = &selection->tracks;
1670 for (TrackViewList::iterator i = ts->begin(); i != ts->end(); ++i) {
1671 TimeAxisView *tv = (static_cast<TimeAxisView*>(*i));
1672 tv->step_height (coarser);
1677 Editor::tav_zoom_smooth (bool coarser, bool force_all)
1679 DisplaySuspender ds;
1683 if (selection->tracks.empty() || force_all) {
1686 ts = &selection->tracks;
1689 for (TrackViewList::iterator i = ts->begin(); i != ts->end(); ++i) {
1690 TimeAxisView *tv = (static_cast<TimeAxisView*>(*i));
1691 uint32_t h = tv->current_height ();
1696 if (h >= TimeAxisView::preset_height (HeightSmall)) {
1701 tv->set_height (h + 5);
1707 Editor::temporal_zoom_step_mouse_focus_scale (bool zoom_out, double scale)
1709 Editing::ZoomFocus temp_focus = zoom_focus;
1710 zoom_focus = Editing::ZoomFocusMouse;
1711 temporal_zoom_step_scale (zoom_out, scale);
1712 zoom_focus = temp_focus;
1716 Editor::temporal_zoom_step_mouse_focus (bool zoom_out)
1718 temporal_zoom_step_mouse_focus_scale (zoom_out, 2.0);
1722 Editor::temporal_zoom_step (bool zoom_out)
1724 temporal_zoom_step_scale (zoom_out, 2.0);
1728 Editor::temporal_zoom_step_scale (bool zoom_out, double scale)
1730 ENSURE_GUI_THREAD (*this, &Editor::temporal_zoom_step, zoom_out, scale)
1732 samplecnt_t nspp = samples_per_pixel;
1736 if (nspp == samples_per_pixel) {
1741 if (nspp == samples_per_pixel) {
1746 //zoom-behavior-tweaks
1747 //limit our maximum zoom to the session gui extents value
1748 std::pair<samplepos_t, samplepos_t> ext = session_gui_extents();
1749 samplecnt_t session_extents_pp = (ext.second - ext.first) / _visible_canvas_width;
1750 if (nspp > session_extents_pp)
1751 nspp = session_extents_pp;
1753 temporal_zoom (nspp);
1757 Editor::temporal_zoom (samplecnt_t fpp)
1763 samplepos_t current_page = current_page_samples();
1764 samplepos_t current_leftmost = _leftmost_sample;
1765 samplepos_t current_rightmost;
1766 samplepos_t current_center;
1767 samplepos_t new_page_size;
1768 samplepos_t half_page_size;
1769 samplepos_t leftmost_after_zoom = 0;
1771 bool in_track_canvas;
1772 bool use_mouse_sample = true;
1776 if (fpp == samples_per_pixel) {
1780 // Imposing an arbitrary limit to zoom out as too much zoom out produces
1781 // segfaults for lack of memory. If somebody decides this is not high enough I
1782 // believe it can be raisen to higher values but some limit must be in place.
1784 // This constant represents 1 day @ 48kHz on a 1600 pixel wide display
1785 // all of which is used for the editor track displays. The whole day
1786 // would be 4147200000 samples, so 2592000 samples per pixel.
1788 nfpp = min (fpp, (samplecnt_t) 2592000);
1789 nfpp = max ((samplecnt_t) 1, nfpp);
1791 new_page_size = (samplepos_t) floor (_visible_canvas_width * nfpp);
1792 half_page_size = new_page_size / 2;
1794 switch (zoom_focus) {
1796 leftmost_after_zoom = current_leftmost;
1799 case ZoomFocusRight:
1800 current_rightmost = _leftmost_sample + current_page;
1801 if (current_rightmost < new_page_size) {
1802 leftmost_after_zoom = 0;
1804 leftmost_after_zoom = current_rightmost - new_page_size;
1808 case ZoomFocusCenter:
1809 current_center = current_leftmost + (current_page/2);
1810 if (current_center < half_page_size) {
1811 leftmost_after_zoom = 0;
1813 leftmost_after_zoom = current_center - half_page_size;
1817 case ZoomFocusPlayhead:
1818 /* centre playhead */
1819 l = playhead_cursor->current_sample () - (new_page_size * 0.5);
1822 leftmost_after_zoom = 0;
1823 } else if (l > max_samplepos) {
1824 leftmost_after_zoom = max_samplepos - new_page_size;
1826 leftmost_after_zoom = (samplepos_t) l;
1830 case ZoomFocusMouse:
1831 /* try to keep the mouse over the same point in the display */
1833 if (_drags->active()) {
1834 where = _drags->current_pointer_sample ();
1835 } else if (!mouse_sample (where, in_track_canvas)) {
1836 use_mouse_sample = false;
1839 if (use_mouse_sample) {
1840 l = - ((new_page_size * ((where - current_leftmost)/(double)current_page)) - where);
1843 leftmost_after_zoom = 0;
1844 } else if (l > max_samplepos) {
1845 leftmost_after_zoom = max_samplepos - new_page_size;
1847 leftmost_after_zoom = (samplepos_t) l;
1850 /* use playhead instead */
1851 where = playhead_cursor->current_sample ();
1853 if (where < half_page_size) {
1854 leftmost_after_zoom = 0;
1856 leftmost_after_zoom = where - half_page_size;
1862 /* try to keep the edit point in the same place */
1863 where = get_preferred_edit_position ();
1867 double l = - ((new_page_size * ((where - current_leftmost)/(double)current_page)) - where);
1870 leftmost_after_zoom = 0;
1871 } else if (l > max_samplepos) {
1872 leftmost_after_zoom = max_samplepos - new_page_size;
1874 leftmost_after_zoom = (samplepos_t) l;
1878 /* edit point not defined */
1885 // leftmost_after_zoom = min (leftmost_after_zoom, _session->current_end_sample());
1887 reposition_and_zoom (leftmost_after_zoom, nfpp);
1891 Editor::calc_extra_zoom_edges(samplepos_t &start, samplepos_t &end)
1893 /* this func helps make sure we leave a little space
1894 at each end of the editor so that the zoom doesn't fit the region
1895 precisely to the screen.
1898 GdkScreen* screen = gdk_screen_get_default ();
1899 const gint pixwidth = gdk_screen_get_width (screen);
1900 const gint mmwidth = gdk_screen_get_width_mm (screen);
1901 const double pix_per_mm = (double) pixwidth/ (double) mmwidth;
1902 const double one_centimeter_in_pixels = pix_per_mm * 10.0;
1904 const samplepos_t range = end - start;
1905 const samplecnt_t new_fpp = (samplecnt_t) ceil ((double) range / (double) _visible_canvas_width);
1906 const samplepos_t extra_samples = (samplepos_t) floor (one_centimeter_in_pixels * new_fpp);
1908 if (start > extra_samples) {
1909 start -= extra_samples;
1914 if (max_samplepos - extra_samples > end) {
1915 end += extra_samples;
1917 end = max_samplepos;
1922 Editor::get_selection_extents (samplepos_t &start, samplepos_t &end) const
1924 start = max_samplepos;
1928 //ToDo: if notes are selected, set extents to that selection
1930 //ToDo: if control points are selected, set extents to that selection
1932 if (!selection->regions.empty()) {
1933 RegionSelection rs = get_regions_from_selection_and_entered ();
1935 for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
1937 if ((*i)->region()->position() < start) {
1938 start = (*i)->region()->position();
1941 if ((*i)->region()->last_sample() + 1 > end) {
1942 end = (*i)->region()->last_sample() + 1;
1946 } else if (!selection->time.empty()) {
1947 start = selection->time.start();
1948 end = selection->time.end_sample();
1950 ret = false; //no selection found
1953 if ((start == 0 && end == 0) || end < start) {
1962 Editor::temporal_zoom_selection (Editing::ZoomAxis axes)
1964 if (!selection) return;
1966 if ( selection->regions.empty() && selection->time.empty() ) {
1967 if (axes == Horizontal || axes == Both) {
1968 temporal_zoom_step(true);
1970 if (axes == Vertical || axes == Both) {
1971 if ( !track_views.empty() ) {
1975 //implicit hack: by extending the top & bottom check outside the current view limits, we include the trackviews immediately above & below what is visible
1976 const double top = vertical_adjustment.get_value() - 10;
1977 const double btm = top + _visible_canvas_height + 10;
1979 for (TrackViewList::iterator iter = track_views.begin(); iter != track_views.end(); ++iter) {
1980 if ( (*iter)->covered_by_y_range (top, btm) ) {
1981 tvl.push_back(*iter);
1991 //ToDo: if notes are selected, zoom to that
1993 //ToDo: if control points are selected, zoom to that
1995 if (axes == Horizontal || axes == Both) {
1997 samplepos_t start, end;
1998 if (get_selection_extents (start, end)) {
1999 calc_extra_zoom_edges (start, end);
2000 temporal_zoom_by_sample (start, end);
2004 if (axes == Vertical || axes == Both) {
2008 //normally, we don't do anything "automatic" to the user's selection.
2009 //but in this case, we will clear the selection after a zoom-to-selection.
2014 Editor::temporal_zoom_session ()
2016 ENSURE_GUI_THREAD (*this, &Editor::temporal_zoom_session)
2019 samplecnt_t start = _session->current_start_sample();
2020 samplecnt_t end = _session->current_end_sample();
2022 if (_session->actively_recording ()) {
2023 samplepos_t cur = playhead_cursor->current_sample ();
2025 /* recording beyond the end marker; zoom out
2026 * by 5 seconds more so that if 'follow
2027 * playhead' is active we don't immediately
2030 end = cur + _session->sample_rate() * 5;
2034 if ((start == 0 && end == 0) || end < start) {
2038 calc_extra_zoom_edges(start, end);
2040 temporal_zoom_by_sample (start, end);
2045 Editor::temporal_zoom_extents ()
2047 ENSURE_GUI_THREAD (*this, &Editor::temporal_zoom_extents)
2050 std::pair<samplepos_t, samplepos_t> ext = session_gui_extents (false); //in this case we want to zoom to the extents explicitly; ignore the users prefs for extra padding
2052 samplecnt_t start = ext.first;
2053 samplecnt_t end = ext.second;
2055 if (_session->actively_recording ()) {
2056 samplepos_t cur = playhead_cursor->current_sample ();
2058 /* recording beyond the end marker; zoom out
2059 * by 5 seconds more so that if 'follow
2060 * playhead' is active we don't immediately
2063 end = cur + _session->sample_rate() * 5;
2067 if ((start == 0 && end == 0) || end < start) {
2071 calc_extra_zoom_edges(start, end);
2073 temporal_zoom_by_sample (start, end);
2078 Editor::temporal_zoom_by_sample (samplepos_t start, samplepos_t end)
2080 if (!_session) return;
2082 if ((start == 0 && end == 0) || end < start) {
2086 samplepos_t range = end - start;
2088 const samplecnt_t new_fpp = (samplecnt_t) ceil ((double) range / (double) _visible_canvas_width);
2090 samplepos_t new_page = range;
2091 samplepos_t middle = (samplepos_t) floor ((double) start + ((double) range / 2.0f));
2092 samplepos_t new_leftmost = (samplepos_t) floor ((double) middle - ((double) new_page / 2.0f));
2094 if (new_leftmost > middle) {
2098 if (new_leftmost < 0) {
2102 reposition_and_zoom (new_leftmost, new_fpp);
2106 Editor::temporal_zoom_to_sample (bool coarser, samplepos_t sample)
2112 samplecnt_t range_before = sample - _leftmost_sample;
2113 samplecnt_t new_spp;
2116 if (samples_per_pixel <= 1) {
2119 new_spp = samples_per_pixel + (samples_per_pixel/2);
2121 range_before += range_before/2;
2123 if (samples_per_pixel >= 1) {
2124 new_spp = samples_per_pixel - (samples_per_pixel/2);
2126 /* could bail out here since we cannot zoom any finer,
2127 but leave that to the equality test below
2129 new_spp = samples_per_pixel;
2132 range_before -= range_before/2;
2135 if (new_spp == samples_per_pixel) {
2139 /* zoom focus is automatically taken as @param sample when this
2143 samplepos_t new_leftmost = sample - (samplepos_t)range_before;
2145 if (new_leftmost > sample) {
2149 if (new_leftmost < 0) {
2153 reposition_and_zoom (new_leftmost, new_spp);
2158 Editor::choose_new_marker_name(string &name) {
2160 if (!UIConfiguration::instance().get_name_new_markers()) {
2161 /* don't prompt user for a new name */
2165 Prompter dialog (true);
2167 dialog.set_prompt (_("New Name:"));
2169 dialog.set_title (_("New Location Marker"));
2171 dialog.set_name ("MarkNameWindow");
2172 dialog.set_size_request (250, -1);
2173 dialog.set_position (Gtk::WIN_POS_MOUSE);
2175 dialog.add_button (Stock::OK, RESPONSE_ACCEPT);
2176 dialog.set_initial_text (name);
2180 switch (dialog.run ()) {
2181 case RESPONSE_ACCEPT:
2187 dialog.get_result(name);
2194 Editor::add_location_from_selection ()
2198 if (selection->time.empty()) {
2202 if (_session == 0 || clicked_axisview == 0) {
2206 samplepos_t start = selection->time[clicked_selection].start;
2207 samplepos_t end = selection->time[clicked_selection].end;
2209 _session->locations()->next_available_name(rangename,"selection");
2210 Location *location = new Location (*_session, start, end, rangename, Location::IsRangeMarker, get_grid_music_divisions(0));
2212 begin_reversible_command (_("add marker"));
2214 XMLNode &before = _session->locations()->get_state();
2215 _session->locations()->add (location, true);
2216 XMLNode &after = _session->locations()->get_state();
2217 _session->add_command(new MementoCommand<Locations>(*(_session->locations()), &before, &after));
2219 commit_reversible_command ();
2223 Editor::add_location_mark (samplepos_t where)
2227 select_new_marker = true;
2229 _session->locations()->next_available_name(markername,"mark");
2230 if (!choose_new_marker_name(markername)) {
2233 Location *location = new Location (*_session, where, where, markername, Location::IsMark, get_grid_music_divisions (0));
2234 begin_reversible_command (_("add marker"));
2236 XMLNode &before = _session->locations()->get_state();
2237 _session->locations()->add (location, true);
2238 XMLNode &after = _session->locations()->get_state();
2239 _session->add_command(new MementoCommand<Locations>(*(_session->locations()), &before, &after));
2241 commit_reversible_command ();
2245 Editor::set_session_start_from_playhead ()
2251 if ((loc = _session->locations()->session_range_location()) == 0) { //should never happen
2252 _session->set_session_extents (_session->audible_sample(), _session->audible_sample());
2254 XMLNode &before = loc->get_state();
2256 _session->set_session_extents (_session->audible_sample(), loc->end());
2258 XMLNode &after = loc->get_state();
2260 begin_reversible_command (_("Set session start"));
2262 _session->add_command (new MementoCommand<Location>(*loc, &before, &after));
2264 commit_reversible_command ();
2269 Editor::set_session_end_from_playhead ()
2275 if ((loc = _session->locations()->session_range_location()) == 0) { //should never happen
2276 _session->set_session_extents (_session->audible_sample(), _session->audible_sample());
2278 XMLNode &before = loc->get_state();
2280 _session->set_session_extents (loc->start(), _session->audible_sample());
2282 XMLNode &after = loc->get_state();
2284 begin_reversible_command (_("Set session start"));
2286 _session->add_command (new MementoCommand<Location>(*loc, &before, &after));
2288 commit_reversible_command ();
2291 _session->set_end_is_free (false);
2296 Editor::toggle_location_at_playhead_cursor ()
2298 if (!do_remove_location_at_playhead_cursor())
2300 add_location_from_playhead_cursor();
2305 Editor::add_location_from_playhead_cursor ()
2307 add_location_mark (_session->audible_sample());
2311 Editor::do_remove_location_at_playhead_cursor ()
2313 bool removed = false;
2316 XMLNode &before = _session->locations()->get_state();
2318 //find location(s) at this time
2319 Locations::LocationList locs;
2320 _session->locations()->find_all_between (_session->audible_sample(), _session->audible_sample()+1, locs, Location::Flags(0));
2321 for (Locations::LocationList::iterator i = locs.begin(); i != locs.end(); ++i) {
2322 if ((*i)->is_mark()) {
2323 _session->locations()->remove (*i);
2330 begin_reversible_command (_("remove marker"));
2331 XMLNode &after = _session->locations()->get_state();
2332 _session->add_command(new MementoCommand<Locations>(*(_session->locations()), &before, &after));
2333 commit_reversible_command ();
2340 Editor::remove_location_at_playhead_cursor ()
2342 do_remove_location_at_playhead_cursor ();
2345 /** Add a range marker around each selected region */
2347 Editor::add_locations_from_region ()
2349 RegionSelection rs = get_regions_from_selection_and_entered ();
2354 bool commit = false;
2356 XMLNode &before = _session->locations()->get_state();
2358 for (RegionSelection::iterator i = rs.begin (); i != rs.end (); ++i) {
2360 boost::shared_ptr<Region> region = (*i)->region ();
2362 Location *location = new Location (*_session, region->position(), region->last_sample(), region->name(), Location::IsRangeMarker, 0);
2364 _session->locations()->add (location, true);
2369 begin_reversible_command (selection->regions.size () > 1 ? _("add markers") : _("add marker"));
2370 XMLNode &after = _session->locations()->get_state();
2371 _session->add_command (new MementoCommand<Locations>(*(_session->locations()), &before, &after));
2372 commit_reversible_command ();
2376 /** Add a single range marker around all selected regions */
2378 Editor::add_location_from_region ()
2380 RegionSelection rs = get_regions_from_selection_and_entered ();
2386 XMLNode &before = _session->locations()->get_state();
2390 if (rs.size() > 1) {
2391 _session->locations()->next_available_name(markername, "regions");
2393 RegionView* rv = *(rs.begin());
2394 boost::shared_ptr<Region> region = rv->region();
2395 markername = region->name();
2398 if (!choose_new_marker_name(markername)) {
2402 // single range spanning all selected
2403 Location *location = new Location (*_session, selection->regions.start(), selection->regions.end_sample(), markername, Location::IsRangeMarker, 0);
2404 _session->locations()->add (location, true);
2406 begin_reversible_command (_("add marker"));
2407 XMLNode &after = _session->locations()->get_state();
2408 _session->add_command (new MementoCommand<Locations>(*(_session->locations()), &before, &after));
2409 commit_reversible_command ();
2415 Editor::jump_forward_to_mark ()
2421 samplepos_t pos = _session->locations()->first_mark_after (playhead_cursor->current_sample());
2427 _session->request_locate (pos, _session->transport_rolling());
2431 Editor::jump_backward_to_mark ()
2437 samplepos_t pos = _session->locations()->first_mark_before (playhead_cursor->current_sample());
2439 //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...
2440 if (_session->transport_rolling()) {
2441 if ((playhead_cursor->current_sample() - pos) < _session->sample_rate()/2) {
2442 samplepos_t prior = _session->locations()->first_mark_before (pos);
2451 _session->request_locate (pos, _session->transport_rolling());
2457 samplepos_t const pos = _session->audible_sample ();
2460 _session->locations()->next_available_name (markername, "mark");
2462 if (!choose_new_marker_name (markername)) {
2466 _session->locations()->add (new Location (*_session, pos, 0, markername, Location::IsMark, 0), true);
2470 Editor::clear_markers ()
2473 begin_reversible_command (_("clear markers"));
2475 XMLNode &before = _session->locations()->get_state();
2476 _session->locations()->clear_markers ();
2477 XMLNode &after = _session->locations()->get_state();
2478 _session->add_command(new MementoCommand<Locations>(*(_session->locations()), &before, &after));
2480 commit_reversible_command ();
2485 Editor::clear_ranges ()
2488 begin_reversible_command (_("clear ranges"));
2490 XMLNode &before = _session->locations()->get_state();
2492 _session->locations()->clear_ranges ();
2494 XMLNode &after = _session->locations()->get_state();
2495 _session->add_command(new MementoCommand<Locations>(*(_session->locations()), &before, &after));
2497 commit_reversible_command ();
2502 Editor::clear_locations ()
2504 begin_reversible_command (_("clear locations"));
2506 XMLNode &before = _session->locations()->get_state();
2507 _session->locations()->clear ();
2508 XMLNode &after = _session->locations()->get_state();
2509 _session->add_command(new MementoCommand<Locations>(*(_session->locations()), &before, &after));
2511 commit_reversible_command ();
2515 Editor::unhide_markers ()
2517 for (LocationMarkerMap::iterator i = location_markers.begin(); i != location_markers.end(); ++i) {
2518 Location *l = (*i).first;
2519 if (l->is_hidden() && l->is_mark()) {
2520 l->set_hidden(false, this);
2526 Editor::unhide_ranges ()
2528 for (LocationMarkerMap::iterator i = location_markers.begin(); i != location_markers.end(); ++i) {
2529 Location *l = (*i).first;
2530 if (l->is_hidden() && l->is_range_marker()) {
2531 l->set_hidden(false, this);
2536 /* INSERT/REPLACE */
2539 Editor::insert_region_list_selection (float times)
2541 RouteTimeAxisView *tv = 0;
2542 boost::shared_ptr<Playlist> playlist;
2544 if (clicked_routeview != 0) {
2545 tv = clicked_routeview;
2546 } else if (!selection->tracks.empty()) {
2547 if ((tv = dynamic_cast<RouteTimeAxisView*>(selection->tracks.front())) == 0) {
2550 } else if (entered_track != 0) {
2551 if ((tv = dynamic_cast<RouteTimeAxisView*>(entered_track)) == 0) {
2558 if ((playlist = tv->playlist()) == 0) {
2562 boost::shared_ptr<Region> region = _regions->get_single_selection ();
2567 begin_reversible_command (_("insert region"));
2568 playlist->clear_changes ();
2569 playlist->add_region ((RegionFactory::create (region, true)), get_preferred_edit_position(), times);
2570 if (Config->get_edit_mode() == Ripple)
2571 playlist->ripple (get_preferred_edit_position(), region->length() * times, boost::shared_ptr<Region>());
2573 _session->add_command(new StatefulDiffCommand (playlist));
2574 commit_reversible_command ();
2577 /* BUILT-IN EFFECTS */
2580 Editor::reverse_selection ()
2585 /* GAIN ENVELOPE EDITING */
2588 Editor::edit_envelope ()
2595 Editor::transition_to_rolling (bool fwd)
2601 if (_session->config.get_external_sync()) {
2602 switch (Config->get_sync_source()) {
2606 /* transport controlled by the master */
2611 if (_session->is_auditioning()) {
2612 _session->cancel_audition ();
2616 _session->request_transport_speed (fwd ? 1.0f : -1.0f);
2620 Editor::play_from_start ()
2622 _session->request_locate (_session->current_start_sample(), true);
2626 Editor::play_from_edit_point ()
2628 _session->request_locate (get_preferred_edit_position(), true);
2632 Editor::play_from_edit_point_and_return ()
2634 samplepos_t start_sample;
2635 samplepos_t return_sample;
2637 start_sample = get_preferred_edit_position (EDIT_IGNORE_PHEAD);
2639 if (_session->transport_rolling()) {
2640 _session->request_locate (start_sample, false);
2644 /* don't reset the return sample if its already set */
2646 if ((return_sample = _session->requested_return_sample()) < 0) {
2647 return_sample = _session->audible_sample();
2650 if (start_sample >= 0) {
2651 _session->request_roll_at_and_return (start_sample, return_sample);
2656 Editor::play_selection ()
2658 samplepos_t start, end;
2659 if (!get_selection_extents (start, end))
2662 AudioRange ar (start, end, 0);
2663 list<AudioRange> lar;
2666 _session->request_play_range (&lar, true);
2671 Editor::maybe_locate_with_edit_preroll (samplepos_t location)
2673 if (_session->transport_rolling() || !UIConfiguration::instance().get_follow_edits() || _session->config.get_external_sync())
2676 location -= _session->preroll_samples (location);
2678 //don't try to locate before the beginning of time
2683 //if follow_playhead is on, keep the playhead on the screen
2684 if (_follow_playhead)
2685 if (location < _leftmost_sample)
2686 location = _leftmost_sample;
2688 _session->request_locate (location);
2692 Editor::play_with_preroll ()
2694 samplepos_t start, end;
2695 if (UIConfiguration::instance().get_follow_edits() && get_selection_extents (start, end)) {
2696 const samplepos_t preroll = _session->preroll_samples (start);
2698 samplepos_t ret = start;
2700 if (start > preroll) {
2701 start = start - preroll;
2704 end = end + preroll; //"post-roll"
2706 AudioRange ar (start, end, 0);
2707 list<AudioRange> lar;
2710 _session->request_play_range (&lar, true);
2711 _session->set_requested_return_sample (ret); //force auto-return to return to range start, without the preroll
2713 samplepos_t ph = playhead_cursor->current_sample ();
2714 const samplepos_t preroll = _session->preroll_samples (ph);
2717 start = ph - preroll;
2721 _session->request_locate (start, true);
2722 _session->set_requested_return_sample (ph); //force auto-return to return to playhead location, without the preroll
2727 Editor::rec_with_preroll ()
2729 samplepos_t ph = playhead_cursor->current_sample ();
2730 samplepos_t preroll = _session->preroll_samples (ph);
2731 _session->request_preroll_record_trim (ph, preroll);
2735 Editor::rec_with_count_in ()
2737 _session->request_count_in_record ();
2741 Editor::play_location (Location& location)
2743 if (location.start() <= location.end()) {
2747 _session->request_bounded_roll (location.start(), location.end());
2751 Editor::loop_location (Location& location)
2753 if (location.start() <= location.end()) {
2759 if ((tll = transport_loop_location()) != 0) {
2760 tll->set (location.start(), location.end());
2762 // enable looping, reposition and start rolling
2763 _session->request_locate (tll->start(), true);
2764 _session->request_play_loop (true);
2769 Editor::do_layer_operation (LayerOperation op)
2771 if (selection->regions.empty ()) {
2775 bool const multiple = selection->regions.size() > 1;
2779 begin_reversible_command (_("raise regions"));
2781 begin_reversible_command (_("raise region"));
2787 begin_reversible_command (_("raise regions to top"));
2789 begin_reversible_command (_("raise region to top"));
2795 begin_reversible_command (_("lower regions"));
2797 begin_reversible_command (_("lower region"));
2803 begin_reversible_command (_("lower regions to bottom"));
2805 begin_reversible_command (_("lower region"));
2810 set<boost::shared_ptr<Playlist> > playlists = selection->regions.playlists ();
2811 for (set<boost::shared_ptr<Playlist> >::iterator i = playlists.begin(); i != playlists.end(); ++i) {
2812 (*i)->clear_owned_changes ();
2815 for (RegionSelection::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
2816 boost::shared_ptr<Region> r = (*i)->region ();
2828 r->lower_to_bottom ();
2832 for (set<boost::shared_ptr<Playlist> >::iterator i = playlists.begin(); i != playlists.end(); ++i) {
2833 vector<Command*> cmds;
2835 _session->add_commands (cmds);
2838 commit_reversible_command ();
2842 Editor::raise_region ()
2844 do_layer_operation (Raise);
2848 Editor::raise_region_to_top ()
2850 do_layer_operation (RaiseToTop);
2854 Editor::lower_region ()
2856 do_layer_operation (Lower);
2860 Editor::lower_region_to_bottom ()
2862 do_layer_operation (LowerToBottom);
2865 /** Show the region editor for the selected regions */
2867 Editor::show_region_properties ()
2869 selection->foreach_regionview (&RegionView::show_region_editor);
2872 /** Show the midi list editor for the selected MIDI regions */
2874 Editor::show_midi_list_editor ()
2876 selection->foreach_midi_regionview (&MidiRegionView::show_list_editor);
2880 Editor::rename_region ()
2882 RegionSelection rs = get_regions_from_selection_and_entered ();
2888 ArdourDialog d (_("Rename Region"), true, false);
2890 Label label (_("New name:"));
2893 hbox.set_spacing (6);
2894 hbox.pack_start (label, false, false);
2895 hbox.pack_start (entry, true, true);
2897 d.get_vbox()->set_border_width (12);
2898 d.get_vbox()->pack_start (hbox, false, false);
2900 d.add_button(Gtk::Stock::CANCEL, Gtk::RESPONSE_CANCEL);
2901 d.add_button(Gtk::Stock::OK, Gtk::RESPONSE_OK);
2903 d.set_size_request (300, -1);
2905 entry.set_text (rs.front()->region()->name());
2906 entry.select_region (0, -1);
2908 entry.signal_activate().connect (sigc::bind (sigc::mem_fun (d, &Dialog::response), RESPONSE_OK));
2914 int const ret = d.run();
2918 if (ret != RESPONSE_OK) {
2922 std::string str = entry.get_text();
2923 strip_whitespace_edges (str);
2925 rs.front()->region()->set_name (str);
2926 _regions->redisplay ();
2930 /** Start an audition of the first selected region */
2932 Editor::play_edit_range ()
2934 samplepos_t start, end;
2936 if (get_edit_op_range (start, end)) {
2937 _session->request_bounded_roll (start, end);
2942 Editor::play_selected_region ()
2944 samplepos_t start = max_samplepos;
2945 samplepos_t end = 0;
2947 RegionSelection rs = get_regions_from_selection_and_entered ();
2953 for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
2954 if ((*i)->region()->position() < start) {
2955 start = (*i)->region()->position();
2957 if ((*i)->region()->last_sample() + 1 > end) {
2958 end = (*i)->region()->last_sample() + 1;
2962 _session->request_bounded_roll (start, end);
2966 Editor::audition_playlist_region_standalone (boost::shared_ptr<Region> region)
2968 _session->audition_region (region);
2972 Editor::region_from_selection ()
2974 if (clicked_axisview == 0) {
2978 if (selection->time.empty()) {
2982 samplepos_t start = selection->time[clicked_selection].start;
2983 samplepos_t end = selection->time[clicked_selection].end;
2985 TrackViewList tracks = get_tracks_for_range_action ();
2987 samplepos_t selection_cnt = end - start + 1;
2989 for (TrackSelection::iterator i = tracks.begin(); i != tracks.end(); ++i) {
2990 boost::shared_ptr<Region> current;
2991 boost::shared_ptr<Playlist> pl;
2992 samplepos_t internal_start;
2995 if ((pl = (*i)->playlist()) == 0) {
2999 if ((current = pl->top_region_at (start)) == 0) {
3003 internal_start = start - current->position();
3004 RegionFactory::region_name (new_name, current->name(), true);
3008 plist.add (ARDOUR::Properties::start, current->start() + internal_start);
3009 plist.add (ARDOUR::Properties::length, selection_cnt);
3010 plist.add (ARDOUR::Properties::name, new_name);
3011 plist.add (ARDOUR::Properties::layer, 0);
3013 boost::shared_ptr<Region> region (RegionFactory::create (current, plist));
3018 Editor::create_region_from_selection (vector<boost::shared_ptr<Region> >& new_regions)
3020 if (selection->time.empty() || selection->tracks.empty()) {
3024 samplepos_t start, end;
3025 if (clicked_selection) {
3026 start = selection->time[clicked_selection].start;
3027 end = selection->time[clicked_selection].end;
3029 start = selection->time.start();
3030 end = selection->time.end_sample();
3033 TrackViewList ts = selection->tracks.filter_to_unique_playlists ();
3034 sort_track_selection (ts);
3036 for (TrackSelection::iterator i = ts.begin(); i != ts.end(); ++i) {
3037 boost::shared_ptr<Region> current;
3038 boost::shared_ptr<Playlist> playlist;
3039 samplepos_t internal_start;
3042 if ((playlist = (*i)->playlist()) == 0) {
3046 if ((current = playlist->top_region_at(start)) == 0) {
3050 internal_start = start - current->position();
3051 RegionFactory::region_name (new_name, current->name(), true);
3055 plist.add (ARDOUR::Properties::start, current->start() + internal_start);
3056 plist.add (ARDOUR::Properties::length, end - start + 1);
3057 plist.add (ARDOUR::Properties::name, new_name);
3059 new_regions.push_back (RegionFactory::create (current, plist));
3064 Editor::split_multichannel_region ()
3066 RegionSelection rs = get_regions_from_selection_and_entered ();
3072 vector< boost::shared_ptr<Region> > v;
3074 for (list<RegionView*>::iterator x = rs.begin(); x != rs.end(); ++x) {
3075 (*x)->region()->separate_by_channel (v);
3080 Editor::new_region_from_selection ()
3082 region_from_selection ();
3083 cancel_selection ();
3087 add_if_covered (RegionView* rv, const AudioRange* ar, RegionSelection* rs)
3089 switch (rv->region()->coverage (ar->start, ar->end - 1)) {
3090 // n.b. -1 because AudioRange::end is one past the end, but coverage expects inclusive ranges
3091 case Evoral::OverlapNone:
3099 * - selected tracks, or if there are none...
3100 * - tracks containing selected regions, or if there are none...
3105 Editor::get_tracks_for_range_action () const
3109 if (selection->tracks.empty()) {
3111 /* use tracks with selected regions */
3113 RegionSelection rs = selection->regions;
3115 for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
3116 TimeAxisView* tv = &(*i)->get_time_axis_view();
3118 if (!t.contains (tv)) {
3124 /* no regions and no tracks: use all tracks */
3130 t = selection->tracks;
3133 return t.filter_to_unique_playlists();
3137 Editor::separate_regions_between (const TimeSelection& ts)
3139 bool in_command = false;
3140 boost::shared_ptr<Playlist> playlist;
3141 RegionSelection new_selection;
3143 TrackViewList tmptracks = get_tracks_for_range_action ();
3144 sort_track_selection (tmptracks);
3146 for (TrackSelection::iterator i = tmptracks.begin(); i != tmptracks.end(); ++i) {
3148 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> ((*i));
3154 if (!rtv->is_track()) {
3158 /* no edits to destructive tracks */
3160 if (rtv->track()->destructive()) {
3164 if ((playlist = rtv->playlist()) != 0) {
3166 playlist->clear_changes ();
3168 /* XXX need to consider musical time selections here at some point */
3170 for (list<AudioRange>::const_iterator t = ts.begin(); t != ts.end(); ++t) {
3172 sigc::connection c = rtv->view()->RegionViewAdded.connect (
3173 sigc::mem_fun(*this, &Editor::collect_new_region_view));
3175 latest_regionviews.clear ();
3177 playlist->partition ((*t).start, (*t).end, false);
3181 if (!latest_regionviews.empty()) {
3183 rtv->view()->foreach_regionview (sigc::bind (
3184 sigc::ptr_fun (add_if_covered),
3185 &(*t), &new_selection));
3188 begin_reversible_command (_("separate"));
3192 /* pick up changes to existing regions */
3194 vector<Command*> cmds;
3195 playlist->rdiff (cmds);
3196 _session->add_commands (cmds);
3198 /* pick up changes to the playlist itself (adds/removes)
3201 _session->add_command(new StatefulDiffCommand (playlist));
3208 // selection->set (new_selection);
3210 commit_reversible_command ();
3214 struct PlaylistState {
3215 boost::shared_ptr<Playlist> playlist;
3219 /** Take tracks from get_tracks_for_range_action and cut any regions
3220 * on those tracks so that the tracks are empty over the time
3224 Editor::separate_region_from_selection ()
3226 /* preferentially use *all* ranges in the time selection if we're in range mode
3227 to allow discontiguous operation, since get_edit_op_range() currently
3228 returns a single range.
3231 if (!selection->time.empty()) {
3233 separate_regions_between (selection->time);
3240 if (get_edit_op_range (start, end)) {
3242 AudioRange ar (start, end, 1);
3246 separate_regions_between (ts);
3252 Editor::separate_region_from_punch ()
3254 Location* loc = _session->locations()->auto_punch_location();
3256 separate_regions_using_location (*loc);
3261 Editor::separate_region_from_loop ()
3263 Location* loc = _session->locations()->auto_loop_location();
3265 separate_regions_using_location (*loc);
3270 Editor::separate_regions_using_location (Location& loc)
3272 if (loc.is_mark()) {
3276 AudioRange ar (loc.start(), loc.end(), 1);
3281 separate_regions_between (ts);
3284 /** Separate regions under the selected region */
3286 Editor::separate_under_selected_regions ()
3288 vector<PlaylistState> playlists;
3292 rs = get_regions_from_selection_and_entered();
3294 if (!_session || rs.empty()) {
3298 begin_reversible_command (_("separate region under"));
3300 list<boost::shared_ptr<Region> > regions_to_remove;
3302 for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
3303 // we can't just remove the region(s) in this loop because
3304 // this removes them from the RegionSelection, and they thus
3305 // disappear from underneath the iterator, and the ++i above
3306 // SEGVs in a puzzling fashion.
3308 // so, first iterate over the regions to be removed from rs and
3309 // add them to the regions_to_remove list, and then
3310 // iterate over the list to actually remove them.
3312 regions_to_remove.push_back ((*i)->region());
3315 for (list<boost::shared_ptr<Region> >::iterator rl = regions_to_remove.begin(); rl != regions_to_remove.end(); ++rl) {
3317 boost::shared_ptr<Playlist> playlist = (*rl)->playlist();
3320 // is this check necessary?
3324 vector<PlaylistState>::iterator i;
3326 //only take state if this is a new playlist.
3327 for (i = playlists.begin(); i != playlists.end(); ++i) {
3328 if ((*i).playlist == playlist) {
3333 if (i == playlists.end()) {
3335 PlaylistState before;
3336 before.playlist = playlist;
3337 before.before = &playlist->get_state();
3338 playlist->clear_changes ();
3339 playlist->freeze ();
3340 playlists.push_back(before);
3343 //Partition on the region bounds
3344 playlist->partition ((*rl)->first_sample() - 1, (*rl)->last_sample() + 1, true);
3346 //Re-add region that was just removed due to the partition operation
3347 playlist->add_region ((*rl), (*rl)->first_sample());
3350 vector<PlaylistState>::iterator pl;
3352 for (pl = playlists.begin(); pl != playlists.end(); ++pl) {
3353 (*pl).playlist->thaw ();
3354 _session->add_command(new MementoCommand<Playlist>(*(*pl).playlist, (*pl).before, &(*pl).playlist->get_state()));
3357 commit_reversible_command ();
3361 Editor::crop_region_to_selection ()
3363 if (!selection->time.empty()) {
3365 begin_reversible_command (_("Crop Regions to Time Selection"));
3366 for (std::list<AudioRange>::iterator i = selection->time.begin(); i != selection->time.end(); ++i) {
3367 crop_region_to ((*i).start, (*i).end);
3369 commit_reversible_command();
3375 if (get_edit_op_range (start, end)) {
3376 begin_reversible_command (_("Crop Regions to Edit Range"));
3378 crop_region_to (start, end);
3380 commit_reversible_command();
3387 Editor::crop_region_to (samplepos_t start, samplepos_t end)
3389 vector<boost::shared_ptr<Playlist> > playlists;
3390 boost::shared_ptr<Playlist> playlist;
3393 if (selection->tracks.empty()) {
3394 ts = track_views.filter_to_unique_playlists();
3396 ts = selection->tracks.filter_to_unique_playlists ();
3399 sort_track_selection (ts);
3401 for (TrackSelection::iterator i = ts.begin(); i != ts.end(); ++i) {
3403 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> ((*i));
3409 boost::shared_ptr<Track> t = rtv->track();
3411 if (t != 0 && ! t->destructive()) {
3413 if ((playlist = rtv->playlist()) != 0) {
3414 playlists.push_back (playlist);
3419 if (playlists.empty()) {
3424 samplepos_t new_start;
3425 samplepos_t new_end;
3426 samplecnt_t new_length;
3428 for (vector<boost::shared_ptr<Playlist> >::iterator i = playlists.begin(); i != playlists.end(); ++i) {
3430 /* Only the top regions at start and end have to be cropped */
3431 boost::shared_ptr<Region> region_at_start = (*i)->top_region_at(start);
3432 boost::shared_ptr<Region> region_at_end = (*i)->top_region_at(end);
3434 vector<boost::shared_ptr<Region> > regions;
3436 if (region_at_start != 0) {
3437 regions.push_back (region_at_start);
3439 if (region_at_end != 0) {
3440 regions.push_back (region_at_end);
3443 /* now adjust lengths */
3444 for (vector<boost::shared_ptr<Region> >::iterator i = regions.begin(); i != regions.end(); ++i) {
3446 pos = (*i)->position();
3447 new_start = max (start, pos);
3448 if (max_samplepos - pos > (*i)->length()) {
3449 new_end = pos + (*i)->length() - 1;
3451 new_end = max_samplepos;
3453 new_end = min (end, new_end);
3454 new_length = new_end - new_start + 1;
3456 (*i)->clear_changes ();
3457 (*i)->trim_to (new_start, new_length);
3458 _session->add_command (new StatefulDiffCommand (*i));
3464 Editor::region_fill_track ()
3466 boost::shared_ptr<Playlist> playlist;
3467 RegionSelection regions = get_regions_from_selection_and_entered ();
3468 RegionSelection foo;
3470 samplepos_t const end = _session->current_end_sample ();
3472 if (regions.empty () || regions.end_sample () + 1 >= end) {
3476 samplepos_t const start_sample = regions.start ();
3477 samplepos_t const end_sample = regions.end_sample ();
3478 samplecnt_t const gap = end_sample - start_sample + 1;
3480 begin_reversible_command (Operations::region_fill);
3482 selection->clear_regions ();
3484 for (RegionSelection::iterator i = regions.begin(); i != regions.end(); ++i) {
3486 boost::shared_ptr<Region> r ((*i)->region());
3488 TimeAxisView& tv = (*i)->get_time_axis_view();
3489 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (&tv);
3490 latest_regionviews.clear ();
3491 sigc::connection c = rtv->view()->RegionViewAdded.connect (sigc::mem_fun(*this, &Editor::collect_new_region_view));
3493 samplepos_t const position = end_sample + (r->first_sample() - start_sample + 1);
3494 playlist = (*i)->region()->playlist();
3495 playlist->clear_changes ();
3496 playlist->duplicate_until (r, position, gap, end);
3497 _session->add_command(new StatefulDiffCommand (playlist));
3501 foo.insert (foo.end(), latest_regionviews.begin(), latest_regionviews.end());
3505 selection->set (foo);
3508 commit_reversible_command ();
3512 Editor::set_region_sync_position ()
3514 set_sync_point (get_preferred_edit_position (), get_regions_from_selection_and_edit_point ());
3518 Editor::set_sync_point (samplepos_t where, const RegionSelection& rs)
3520 bool in_command = false;
3522 for (RegionSelection::const_iterator r = rs.begin(); r != rs.end(); ++r) {
3524 if (!(*r)->region()->covers (where)) {
3528 boost::shared_ptr<Region> region ((*r)->region());
3531 begin_reversible_command (_("set sync point"));
3535 region->clear_changes ();
3536 region->set_sync_position (where);
3537 _session->add_command(new StatefulDiffCommand (region));
3541 commit_reversible_command ();
3545 /** Remove the sync positions of the selection */
3547 Editor::remove_region_sync ()
3549 RegionSelection rs = get_regions_from_selection_and_entered ();
3555 begin_reversible_command (_("remove region sync"));
3557 for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
3559 (*i)->region()->clear_changes ();
3560 (*i)->region()->clear_sync_position ();
3561 _session->add_command(new StatefulDiffCommand ((*i)->region()));
3564 commit_reversible_command ();
3568 Editor::naturalize_region ()
3570 RegionSelection rs = get_regions_from_selection_and_entered ();
3576 if (rs.size() > 1) {
3577 begin_reversible_command (_("move regions to original position"));
3579 begin_reversible_command (_("move region to original position"));
3582 for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
3583 (*i)->region()->clear_changes ();
3584 (*i)->region()->move_to_natural_position ();
3585 _session->add_command (new StatefulDiffCommand ((*i)->region()));
3588 commit_reversible_command ();
3592 Editor::align_regions (RegionPoint what)
3594 RegionSelection const rs = get_regions_from_selection_and_edit_point ();
3600 begin_reversible_command (_("align selection"));
3602 samplepos_t const position = get_preferred_edit_position ();
3604 for (RegionSelection::const_iterator i = rs.begin(); i != rs.end(); ++i) {
3605 align_region_internal ((*i)->region(), what, position);
3608 commit_reversible_command ();
3611 struct RegionSortByTime {
3612 bool operator() (const RegionView* a, const RegionView* b) {
3613 return a->region()->position() < b->region()->position();
3618 Editor::align_regions_relative (RegionPoint point)
3620 RegionSelection const rs = get_regions_from_selection_and_edit_point ();
3626 samplepos_t const position = get_preferred_edit_position ();
3628 samplepos_t distance = 0;
3629 samplepos_t pos = 0;
3632 list<RegionView*> sorted;
3633 rs.by_position (sorted);
3635 boost::shared_ptr<Region> r ((*sorted.begin())->region());
3640 if (position > r->position()) {
3641 distance = position - r->position();
3643 distance = r->position() - position;
3649 if (position > r->last_sample()) {
3650 distance = position - r->last_sample();
3651 pos = r->position() + distance;
3653 distance = r->last_sample() - position;
3654 pos = r->position() - distance;
3660 pos = r->adjust_to_sync (position);
3661 if (pos > r->position()) {
3662 distance = pos - r->position();
3664 distance = r->position() - pos;
3670 if (pos == r->position()) {
3674 begin_reversible_command (_("align selection (relative)"));
3676 /* move first one specially */
3678 r->clear_changes ();
3679 r->set_position (pos);
3680 _session->add_command(new StatefulDiffCommand (r));
3682 /* move rest by the same amount */
3686 for (list<RegionView*>::iterator i = sorted.begin(); i != sorted.end(); ++i) {
3688 boost::shared_ptr<Region> region ((*i)->region());
3690 region->clear_changes ();
3693 region->set_position (region->position() + distance);
3695 region->set_position (region->position() - distance);
3698 _session->add_command(new StatefulDiffCommand (region));
3702 commit_reversible_command ();
3706 Editor::align_region (boost::shared_ptr<Region> region, RegionPoint point, samplepos_t position)
3708 begin_reversible_command (_("align region"));
3709 align_region_internal (region, point, position);
3710 commit_reversible_command ();
3714 Editor::align_region_internal (boost::shared_ptr<Region> region, RegionPoint point, samplepos_t position)
3716 region->clear_changes ();
3720 region->set_position (region->adjust_to_sync (position));
3724 if (position > region->length()) {
3725 region->set_position (position - region->length());
3730 region->set_position (position);
3734 _session->add_command(new StatefulDiffCommand (region));
3738 Editor::trim_region_front ()
3744 Editor::trim_region_back ()
3746 trim_region (false);
3750 Editor::trim_region (bool front)
3752 samplepos_t where = get_preferred_edit_position();
3753 RegionSelection rs = get_regions_from_selection_and_edit_point ();
3759 begin_reversible_command (front ? _("trim front") : _("trim back"));
3761 for (list<RegionView*>::const_iterator i = rs.by_layer().begin(); i != rs.by_layer().end(); ++i) {
3762 if (!(*i)->region()->locked()) {
3764 (*i)->region()->clear_changes ();
3767 (*i)->region()->trim_front (where);
3769 (*i)->region()->trim_end (where);
3772 _session->add_command (new StatefulDiffCommand ((*i)->region()));
3776 commit_reversible_command ();
3779 /** Trim the end of the selected regions to the position of the edit cursor */
3781 Editor::trim_region_to_loop ()
3783 Location* loc = _session->locations()->auto_loop_location();
3787 trim_region_to_location (*loc, _("trim to loop"));
3791 Editor::trim_region_to_punch ()
3793 Location* loc = _session->locations()->auto_punch_location();
3797 trim_region_to_location (*loc, _("trim to punch"));
3801 Editor::trim_region_to_location (const Location& loc, const char* str)
3803 RegionSelection rs = get_regions_from_selection_and_entered ();
3804 bool in_command = false;
3806 for (RegionSelection::iterator x = rs.begin(); x != rs.end(); ++x) {
3807 RegionView* rv = (*x);
3809 /* require region to span proposed trim */
3810 switch (rv->region()->coverage (loc.start(), loc.end())) {
3811 case Evoral::OverlapInternal:
3817 RouteTimeAxisView* tav = dynamic_cast<RouteTimeAxisView*> (&rv->get_time_axis_view());
3825 start = loc.start();
3828 rv->region()->clear_changes ();
3829 rv->region()->trim_to (start, (end - start));
3832 begin_reversible_command (str);
3835 _session->add_command(new StatefulDiffCommand (rv->region()));
3839 commit_reversible_command ();
3844 Editor::trim_region_to_previous_region_end ()
3846 return trim_to_region(false);
3850 Editor::trim_region_to_next_region_start ()
3852 return trim_to_region(true);
3856 Editor::trim_to_region(bool forward)
3858 RegionSelection rs = get_regions_from_selection_and_entered ();
3859 bool in_command = false;
3861 boost::shared_ptr<Region> next_region;
3863 for (RegionSelection::iterator x = rs.begin(); x != rs.end(); ++x) {
3865 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (*x);
3871 AudioTimeAxisView* atav = dynamic_cast<AudioTimeAxisView*> (&arv->get_time_axis_view());
3877 boost::shared_ptr<Region> region = arv->region();
3878 boost::shared_ptr<Playlist> playlist (region->playlist());
3880 region->clear_changes ();
3884 next_region = playlist->find_next_region (region->first_sample(), Start, 1);
3890 region->trim_end (next_region->first_sample() - 1);
3891 arv->region_changed (PropertyChange (ARDOUR::Properties::length));
3895 next_region = playlist->find_next_region (region->first_sample(), Start, 0);
3901 region->trim_front (next_region->last_sample() + 1);
3902 arv->region_changed (ARDOUR::bounds_change);
3906 begin_reversible_command (_("trim to region"));
3909 _session->add_command(new StatefulDiffCommand (region));
3913 commit_reversible_command ();
3918 Editor::unfreeze_route ()
3920 if (clicked_routeview == 0 || !clicked_routeview->is_track()) {
3924 clicked_routeview->track()->unfreeze ();
3928 Editor::_freeze_thread (void* arg)
3930 return static_cast<Editor*>(arg)->freeze_thread ();
3934 Editor::freeze_thread ()
3936 /* create event pool because we may need to talk to the session */
3937 SessionEvent::create_per_thread_pool ("freeze events", 64);
3938 /* create per-thread buffers for process() tree to use */
3939 clicked_routeview->audio_track()->freeze_me (*current_interthread_info);
3940 current_interthread_info->done = true;
3945 Editor::freeze_route ()
3951 /* stop transport before we start. this is important */
3953 _session->request_transport_speed (0.0);
3955 /* wait for just a little while, because the above call is asynchronous */
3957 Glib::usleep (250000);
3959 if (clicked_routeview == 0 || !clicked_routeview->is_audio_track()) {
3963 if (!clicked_routeview->track()->bounceable (clicked_routeview->track()->main_outs(), true)) {
3965 _("This track/bus cannot be frozen because the signal adds or loses channels before reaching the outputs.\n"
3966 "This is typically caused by plugins that generate stereo output from mono input or vice versa.")
3968 d.set_title (_("Cannot freeze"));
3973 if (clicked_routeview->track()->has_external_redirects()) {
3974 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"
3975 "Freezing will only process the signal as far as the first send/insert/return."),
3976 clicked_routeview->track()->name()), true, MESSAGE_INFO, BUTTONS_NONE, true);
3978 d.add_button (_("Freeze anyway"), Gtk::RESPONSE_OK);
3979 d.add_button (_("Don't freeze"), Gtk::RESPONSE_CANCEL);
3980 d.set_title (_("Freeze Limits"));
3982 int response = d.run ();
3985 case Gtk::RESPONSE_CANCEL:
3992 InterThreadInfo itt;
3993 current_interthread_info = &itt;
3995 InterthreadProgressWindow ipw (current_interthread_info, _("Freeze"), _("Cancel Freeze"));
3997 pthread_create_and_store (X_("freezer"), &itt.thread, _freeze_thread, this);
3999 CursorContext::Handle cursor_ctx = CursorContext::create(*this, _cursors->wait);
4001 while (!itt.done && !itt.cancel) {
4002 gtk_main_iteration ();
4005 pthread_join (itt.thread, 0);
4006 current_interthread_info = 0;
4010 Editor::bounce_range_selection (bool replace, bool enable_processing)
4012 if (selection->time.empty()) {
4016 TrackSelection views = selection->tracks;
4018 for (TrackViewList::iterator i = views.begin(); i != views.end(); ++i) {
4020 if (enable_processing) {
4022 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (*i);
4024 if (rtv && rtv->track() && replace && enable_processing && !rtv->track()->bounceable (rtv->track()->main_outs(), false)) {
4026 _("You can't perform this operation because the processing of the signal "
4027 "will cause one or more of the tracks to end up with a region with more channels than this track has inputs.\n\n"
4028 "You can do this without processing, which is a different operation.")
4030 d.set_title (_("Cannot bounce"));
4037 samplepos_t start = selection->time[clicked_selection].start;
4038 samplepos_t end = selection->time[clicked_selection].end;
4039 samplepos_t cnt = end - start + 1;
4040 bool in_command = false;
4042 for (TrackViewList::iterator i = views.begin(); i != views.end(); ++i) {
4044 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (*i);
4050 boost::shared_ptr<Playlist> playlist;
4052 if ((playlist = rtv->playlist()) == 0) {
4056 InterThreadInfo itt;
4058 playlist->clear_changes ();
4059 playlist->clear_owned_changes ();
4061 boost::shared_ptr<Region> r;
4063 if (enable_processing) {
4064 r = rtv->track()->bounce_range (start, start+cnt, itt, rtv->track()->main_outs(), false);
4066 r = rtv->track()->bounce_range (start, start+cnt, itt, boost::shared_ptr<Processor>(), false);
4074 list<AudioRange> ranges;
4075 ranges.push_back (AudioRange (start, start+cnt, 0));
4076 playlist->cut (ranges); // discard result
4077 playlist->add_region (r, start);
4081 begin_reversible_command (_("bounce range"));
4084 vector<Command*> cmds;
4085 playlist->rdiff (cmds);
4086 _session->add_commands (cmds);
4088 _session->add_command (new StatefulDiffCommand (playlist));
4092 commit_reversible_command ();
4096 /** Delete selected regions, automation points or a time range */
4100 //special case: if the user is pointing in the editor/mixer strip, they may be trying to delete a plugin.
4101 //we need this because the editor-mixer strip is in the editor window, so it doesn't get the bindings from the mix window
4102 bool deleted = false;
4103 if (current_mixer_strip && current_mixer_strip == MixerStrip::entered_mixer_strip())
4104 deleted = current_mixer_strip->delete_processors ();
4110 /** Cut selected regions, automation points or a time range */
4117 /** Copy selected regions, automation points or a time range */
4125 /** @return true if a Cut, Copy or Clear is possible */
4127 Editor::can_cut_copy () const
4129 if (!selection->time.empty() || !selection->regions.empty() || !selection->points.empty())
4136 /** Cut, copy or clear selected regions, automation points or a time range.
4137 * @param op Operation (Delete, Cut, Copy or Clear)
4140 Editor::cut_copy (CutCopyOp op)
4142 /* only cancel selection if cut/copy is successful.*/
4148 opname = _("delete");
4157 opname = _("clear");
4161 /* if we're deleting something, and the mouse is still pressed,
4162 the thing we started a drag for will be gone when we release
4163 the mouse button(s). avoid this. see part 2 at the end of
4167 if (op == Delete || op == Cut || op == Clear) {
4168 if (_drags->active ()) {
4173 if (op != Delete) { //"Delete" doesn't change copy/paste buf
4174 cut_buffer->clear ();
4177 if (entered_marker) {
4179 /* cut/delete op while pointing at a marker */
4182 Location* loc = find_location_from_marker (entered_marker, ignored);
4184 if (_session && loc) {
4185 entered_marker = NULL;
4186 Glib::signal_idle().connect (sigc::bind (sigc::mem_fun(*this, &Editor::really_remove_marker), loc));
4193 switch (mouse_mode) {
4196 begin_reversible_command (opname + ' ' + X_("MIDI"));
4198 commit_reversible_command ();
4204 bool did_edit = false;
4206 if (!selection->regions.empty() || !selection->points.empty()) {
4207 begin_reversible_command (opname + ' ' + _("objects"));
4210 if (!selection->regions.empty()) {
4211 cut_copy_regions (op, selection->regions);
4213 if (op == Cut || op == Delete) {
4214 selection->clear_regions ();
4218 if (!selection->points.empty()) {
4219 cut_copy_points (op);
4221 if (op == Cut || op == Delete) {
4222 selection->clear_points ();
4225 } else if (selection->time.empty()) {
4226 samplepos_t start, end;
4227 /* no time selection, see if we can get an edit range
4230 if (get_edit_op_range (start, end)) {
4231 selection->set (start, end);
4233 } else if (!selection->time.empty()) {
4234 begin_reversible_command (opname + ' ' + _("range"));
4237 cut_copy_ranges (op);
4239 if (op == Cut || op == Delete) {
4240 selection->clear_time ();
4245 /* reset repeated paste state */
4247 last_paste_pos = -1;
4248 commit_reversible_command ();
4251 if (op == Delete || op == Cut || op == Clear) {
4257 struct AutomationRecord {
4258 AutomationRecord () : state (0) , line(NULL) {}
4259 AutomationRecord (XMLNode* s, const AutomationLine* l) : state (s) , line (l) {}
4261 XMLNode* state; ///< state before any operation
4262 const AutomationLine* line; ///< line this came from
4263 boost::shared_ptr<Evoral::ControlList> copy; ///< copied events for the cut buffer
4266 struct PointsSelectionPositionSorter {
4267 bool operator() (ControlPoint* a, ControlPoint* b) {
4268 return (*(a->model()))->when < (*(b->model()))->when;
4272 /** Cut, copy or clear selected automation points.
4273 * @param op Operation (Cut, Copy or Clear)
4276 Editor::cut_copy_points (Editing::CutCopyOp op, Temporal::Beats earliest, bool midi)
4278 if (selection->points.empty ()) {
4282 /* XXX: not ideal, as there may be more than one track involved in the point selection */
4283 _last_cut_copy_source_track = &selection->points.front()->line().trackview;
4285 /* Keep a record of the AutomationLists that we end up using in this operation */
4286 typedef std::map<boost::shared_ptr<AutomationList>, AutomationRecord> Lists;
4289 /* user could select points in any order */
4290 selection->points.sort(PointsSelectionPositionSorter ());
4292 /* Go through all selected points, making an AutomationRecord for each distinct AutomationList */
4293 for (PointSelection::iterator sel_point = selection->points.begin(); sel_point != selection->points.end(); ++sel_point) {
4294 const AutomationLine& line = (*sel_point)->line();
4295 const boost::shared_ptr<AutomationList> al = line.the_list();
4296 if (lists.find (al) == lists.end ()) {
4297 /* We haven't seen this list yet, so make a record for it. This includes
4298 taking a copy of its current state, in case this is needed for undo later.
4300 lists[al] = AutomationRecord (&al->get_state (), &line);
4304 if (op == Cut || op == Copy) {
4305 /* This operation will involve putting things in the cut buffer, so create an empty
4306 ControlList for each of our source lists to put the cut buffer data in.
4308 for (Lists::iterator i = lists.begin(); i != lists.end(); ++i) {
4309 i->second.copy = i->first->create (i->first->parameter (), i->first->descriptor());
4312 /* Add all selected points to the relevant copy ControlLists */
4313 MusicSample start (std::numeric_limits<samplepos_t>::max(), 0);
4314 for (PointSelection::iterator sel_point = selection->points.begin(); sel_point != selection->points.end(); ++sel_point) {
4315 boost::shared_ptr<AutomationList> al = (*sel_point)->line().the_list();
4316 AutomationList::const_iterator ctrl_evt = (*sel_point)->model ();
4318 lists[al].copy->fast_simple_add ((*ctrl_evt)->when, (*ctrl_evt)->value);
4320 /* Update earliest MIDI start time in beats */
4321 earliest = std::min(earliest, Temporal::Beats((*ctrl_evt)->when));
4323 /* Update earliest session start time in samples */
4324 start.sample = std::min(start.sample, (*sel_point)->line().session_position(ctrl_evt));
4328 /* Snap start time backwards, so copy/paste is snap aligned. */
4330 if (earliest == std::numeric_limits<Temporal::Beats>::max()) {
4331 earliest = Temporal::Beats(); // Weird... don't offset
4333 earliest.round_down_to_beat();
4335 if (start.sample == std::numeric_limits<double>::max()) {
4336 start.sample = 0; // Weird... don't offset
4338 snap_to(start, RoundDownMaybe);
4341 const double line_offset = midi ? earliest.to_double() : start.sample;
4342 for (Lists::iterator i = lists.begin(); i != lists.end(); ++i) {
4343 /* Correct this copy list so that it is relative to the earliest
4344 start time, so relative ordering between points is preserved
4345 when copying from several lists and the paste starts at the
4346 earliest copied piece of data. */
4347 boost::shared_ptr<Evoral::ControlList> &al_cpy = i->second.copy;
4348 for (AutomationList::iterator ctrl_evt = al_cpy->begin(); ctrl_evt != al_cpy->end(); ++ctrl_evt) {
4349 (*ctrl_evt)->when -= line_offset;
4352 /* And add it to the cut buffer */
4353 cut_buffer->add (al_cpy);
4357 if (op == Delete || op == Cut) {
4358 /* This operation needs to remove things from the main AutomationList, so do that now */
4360 for (Lists::iterator i = lists.begin(); i != lists.end(); ++i) {
4361 i->first->freeze ();
4364 /* Remove each selected point from its AutomationList */
4365 for (PointSelection::iterator sel_point = selection->points.begin(); sel_point != selection->points.end(); ++sel_point) {
4366 AutomationLine& line = (*sel_point)->line ();
4367 boost::shared_ptr<AutomationList> al = line.the_list();
4371 if (dynamic_cast<AudioRegionGainLine*> (&line)) {
4372 /* removing of first and last gain point in region gain lines is prohibited*/
4373 if (line.is_last_point (*(*sel_point)) || line.is_first_point (*(*sel_point))) {
4379 al->erase ((*sel_point)->model ());
4383 /* Thaw the lists and add undo records for them */
4384 for (Lists::iterator i = lists.begin(); i != lists.end(); ++i) {
4385 boost::shared_ptr<AutomationList> al = i->first;
4387 _session->add_command (new MementoCommand<AutomationList> (*al.get(), i->second.state, &(al->get_state ())));
4392 /** Cut, copy or clear selected automation points.
4393 * @param op Operation (Cut, Copy or Clear)
4396 Editor::cut_copy_midi (CutCopyOp op)
4398 Temporal::Beats earliest = std::numeric_limits<Temporal::Beats>::max();
4399 for (MidiRegionSelection::iterator i = selection->midi_regions.begin(); i != selection->midi_regions.end(); ++i) {
4400 MidiRegionView* mrv = dynamic_cast<MidiRegionView*>(*i);
4402 if (!mrv->selection().empty()) {
4403 earliest = std::min(earliest, (*mrv->selection().begin())->note()->time());
4405 mrv->cut_copy_clear (op);
4407 /* XXX: not ideal, as there may be more than one track involved in the selection */
4408 _last_cut_copy_source_track = &mrv->get_time_axis_view();
4412 if (!selection->points.empty()) {
4413 cut_copy_points (op, earliest, true);
4414 if (op == Cut || op == Delete) {
4415 selection->clear_points ();
4420 struct lt_playlist {
4421 bool operator () (const PlaylistState& a, const PlaylistState& b) {
4422 return a.playlist < b.playlist;
4426 struct PlaylistMapping {
4428 boost::shared_ptr<Playlist> pl;
4430 PlaylistMapping (TimeAxisView* tvp) : tv (tvp) {}
4433 /** Remove `clicked_regionview' */
4435 Editor::remove_clicked_region ()
4437 if (clicked_routeview == 0 || clicked_regionview == 0) {
4441 begin_reversible_command (_("remove region"));
4443 boost::shared_ptr<Playlist> playlist = clicked_routeview->playlist();
4445 playlist->clear_changes ();
4446 playlist->clear_owned_changes ();
4447 playlist->remove_region (clicked_regionview->region());
4448 if (Config->get_edit_mode() == Ripple)
4449 playlist->ripple (clicked_regionview->region()->position(), -clicked_regionview->region()->length(), boost::shared_ptr<Region>());
4451 /* We might have removed regions, which alters other regions' layering_index,
4452 so we need to do a recursive diff here.
4454 vector<Command*> cmds;
4455 playlist->rdiff (cmds);
4456 _session->add_commands (cmds);
4458 _session->add_command(new StatefulDiffCommand (playlist));
4459 commit_reversible_command ();
4463 /** Remove the selected regions */
4465 Editor::remove_selected_regions ()
4467 RegionSelection rs = get_regions_from_selection_and_entered ();
4469 if (!_session || rs.empty()) {
4473 list<boost::shared_ptr<Region> > regions_to_remove;
4475 for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
4476 // we can't just remove the region(s) in this loop because
4477 // this removes them from the RegionSelection, and they thus
4478 // disappear from underneath the iterator, and the ++i above
4479 // SEGVs in a puzzling fashion.
4481 // so, first iterate over the regions to be removed from rs and
4482 // add them to the regions_to_remove list, and then
4483 // iterate over the list to actually remove them.
4485 regions_to_remove.push_back ((*i)->region());
4488 vector<boost::shared_ptr<Playlist> > playlists;
4490 for (list<boost::shared_ptr<Region> >::iterator rl = regions_to_remove.begin(); rl != regions_to_remove.end(); ++rl) {
4492 boost::shared_ptr<Playlist> playlist = (*rl)->playlist();
4495 // is this check necessary?
4499 /* get_regions_from_selection_and_entered() guarantees that
4500 the playlists involved are unique, so there is no need
4504 playlists.push_back (playlist);
4506 playlist->clear_changes ();
4507 playlist->clear_owned_changes ();
4508 playlist->freeze ();
4509 playlist->remove_region (*rl);
4510 if (Config->get_edit_mode() == Ripple)
4511 playlist->ripple ((*rl)->position(), -(*rl)->length(), boost::shared_ptr<Region>());
4515 vector<boost::shared_ptr<Playlist> >::iterator pl;
4516 bool in_command = false;
4518 for (pl = playlists.begin(); pl != playlists.end(); ++pl) {
4521 /* We might have removed regions, which alters other regions' layering_index,
4522 so we need to do a recursive diff here.
4526 begin_reversible_command (_("remove region"));
4529 vector<Command*> cmds;
4530 (*pl)->rdiff (cmds);
4531 _session->add_commands (cmds);
4533 _session->add_command(new StatefulDiffCommand (*pl));
4537 commit_reversible_command ();
4541 /** Cut, copy or clear selected regions.
4542 * @param op Operation (Cut, Copy or Clear)
4545 Editor::cut_copy_regions (CutCopyOp op, RegionSelection& rs)
4547 /* we can't use a std::map here because the ordering is important, and we can't trivially sort
4548 a map when we want ordered access to both elements. i think.
4551 vector<PlaylistMapping> pmap;
4553 samplepos_t first_position = max_samplepos;
4555 typedef set<boost::shared_ptr<Playlist> > FreezeList;
4556 FreezeList freezelist;
4558 /* get ordering correct before we cut/copy */
4560 rs.sort_by_position_and_track ();
4562 for (RegionSelection::iterator x = rs.begin(); x != rs.end(); ++x) {
4564 first_position = min ((samplepos_t) (*x)->region()->position(), first_position);
4566 if (op == Cut || op == Clear || op == Delete) {
4567 boost::shared_ptr<Playlist> pl = (*x)->region()->playlist();
4570 FreezeList::iterator fl;
4572 // only take state if this is a new playlist.
4573 for (fl = freezelist.begin(); fl != freezelist.end(); ++fl) {
4579 if (fl == freezelist.end()) {
4580 pl->clear_changes();
4581 pl->clear_owned_changes ();
4583 freezelist.insert (pl);
4588 TimeAxisView* tv = &(*x)->get_time_axis_view();
4589 vector<PlaylistMapping>::iterator z;
4591 for (z = pmap.begin(); z != pmap.end(); ++z) {
4592 if ((*z).tv == tv) {
4597 if (z == pmap.end()) {
4598 pmap.push_back (PlaylistMapping (tv));
4602 for (RegionSelection::iterator x = rs.begin(); x != rs.end(); ) {
4604 boost::shared_ptr<Playlist> pl = (*x)->region()->playlist();
4607 /* region not yet associated with a playlist (e.g. unfinished
4614 TimeAxisView& tv = (*x)->get_time_axis_view();
4615 boost::shared_ptr<Playlist> npl;
4616 RegionSelection::iterator tmp;
4623 vector<PlaylistMapping>::iterator z;
4625 for (z = pmap.begin(); z != pmap.end(); ++z) {
4626 if ((*z).tv == &tv) {
4631 assert (z != pmap.end());
4634 npl = PlaylistFactory::create (pl->data_type(), *_session, "cutlist", true);
4642 boost::shared_ptr<Region> r = (*x)->region();
4643 boost::shared_ptr<Region> _xx;
4649 pl->remove_region (r);
4650 if (Config->get_edit_mode() == Ripple)
4651 pl->ripple (r->position(), -r->length(), boost::shared_ptr<Region>());
4655 _xx = RegionFactory::create (r);
4656 npl->add_region (_xx, r->position() - first_position);
4657 pl->remove_region (r);
4658 if (Config->get_edit_mode() == Ripple)
4659 pl->ripple (r->position(), -r->length(), boost::shared_ptr<Region>());
4663 /* copy region before adding, so we're not putting same object into two different playlists */
4664 npl->add_region (RegionFactory::create (r), r->position() - first_position);
4668 pl->remove_region (r);
4669 if (Config->get_edit_mode() == Ripple)
4670 pl->ripple (r->position(), -r->length(), boost::shared_ptr<Region>());
4679 list<boost::shared_ptr<Playlist> > foo;
4681 /* the pmap is in the same order as the tracks in which selected regions occurred */
4683 for (vector<PlaylistMapping>::iterator i = pmap.begin(); i != pmap.end(); ++i) {
4686 foo.push_back ((*i).pl);
4691 cut_buffer->set (foo);
4695 _last_cut_copy_source_track = 0;
4697 _last_cut_copy_source_track = pmap.front().tv;
4701 for (FreezeList::iterator pl = freezelist.begin(); pl != freezelist.end(); ++pl) {
4704 /* We might have removed regions, which alters other regions' layering_index,
4705 so we need to do a recursive diff here.
4707 vector<Command*> cmds;
4708 (*pl)->rdiff (cmds);
4709 _session->add_commands (cmds);
4711 _session->add_command (new StatefulDiffCommand (*pl));
4716 Editor::cut_copy_ranges (CutCopyOp op)
4718 TrackViewList ts = selection->tracks.filter_to_unique_playlists ();
4720 /* Sort the track selection now, so that it if is used, the playlists
4721 selected by the calls below to cut_copy_clear are in the order that
4722 their tracks appear in the editor. This makes things like paste
4723 of ranges work properly.
4726 sort_track_selection (ts);
4729 if (!entered_track) {
4732 ts.push_back (entered_track);
4735 for (TrackViewList::iterator i = ts.begin(); i != ts.end(); ++i) {
4736 (*i)->cut_copy_clear (*selection, op);
4741 Editor::paste (float times, bool from_context)
4743 DEBUG_TRACE (DEBUG::CutNPaste, "paste to preferred edit pos\n");
4744 MusicSample where (get_preferred_edit_position (EDIT_IGNORE_NONE, from_context), 0);
4745 paste_internal (where.sample, times, 0);
4749 Editor::mouse_paste ()
4751 MusicSample where (0, 0);
4753 if (!mouse_sample (where.sample, ignored)) {
4758 paste_internal (where.sample, 1, where.division);
4762 Editor::paste_internal (samplepos_t position, float times, const int32_t sub_num)
4764 DEBUG_TRACE (DEBUG::CutNPaste, string_compose ("apparent paste position is %1\n", position));
4766 if (cut_buffer->empty(internal_editing())) {
4770 if (position == max_samplepos) {
4771 position = get_preferred_edit_position();
4772 DEBUG_TRACE (DEBUG::CutNPaste, string_compose ("preferred edit position is %1\n", position));
4775 if (position == last_paste_pos) {
4776 /* repeated paste in the same position */
4779 /* paste in new location, reset repeated paste state */
4781 last_paste_pos = position;
4784 /* get everything in the correct order */
4787 if (!selection->tracks.empty()) {
4788 /* If there is a track selection, paste into exactly those tracks and
4789 * only those tracks. This allows the user to be explicit and override
4790 * the below "do the reasonable thing" logic. */
4791 ts = selection->tracks.filter_to_unique_playlists ();
4792 sort_track_selection (ts);
4794 /* Figure out which track to base the paste at. */
4795 TimeAxisView* base_track = NULL;
4796 if (_edit_point == Editing::EditAtMouse && entered_track) {
4797 /* With the mouse edit point, paste onto the track under the mouse. */
4798 base_track = entered_track;
4799 } else if (_edit_point == Editing::EditAtMouse && entered_regionview) {
4800 /* With the mouse edit point, paste onto the track of the region under the mouse. */
4801 base_track = &entered_regionview->get_time_axis_view();
4802 } else if (_last_cut_copy_source_track) {
4803 /* Paste to the track that the cut/copy came from (see mantis #333). */
4804 base_track = _last_cut_copy_source_track;
4806 /* This is "impossible" since we've copied... well, do nothing. */
4810 /* Walk up to parent if necessary, so base track is a route. */
4811 while (base_track->get_parent()) {
4812 base_track = base_track->get_parent();
4815 /* Add base track and all tracks below it. The paste logic will select
4816 the appropriate object types from the cut buffer in relative order. */
4817 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
4818 if ((*i)->order() >= base_track->order()) {
4823 /* Sort tracks so the nth track of type T will pick the nth object of type T. */
4824 sort_track_selection (ts);
4826 /* Add automation children of each track in order, for pasting several lines. */
4827 for (TrackViewList::iterator i = ts.begin(); i != ts.end();) {
4828 /* Add any automation children for pasting several lines */
4829 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*>(*i++);
4834 typedef RouteTimeAxisView::AutomationTracks ATracks;
4835 const ATracks& atracks = rtv->automation_tracks();
4836 for (ATracks::const_iterator a = atracks.begin(); a != atracks.end(); ++a) {
4837 i = ts.insert(i, a->second.get());
4842 /* We now have a list of trackviews starting at base_track, including
4843 automation children, in the order shown in the editor, e.g. R1,
4844 R1.A1, R1.A2, R2, R2.A1, ... */
4847 begin_reversible_command (Operations::paste);
4849 if (ts.size() == 1 && cut_buffer->lines.size() == 1 &&
4850 dynamic_cast<AutomationTimeAxisView*>(ts.front())) {
4851 /* Only one line copied, and one automation track selected. Do a
4852 "greedy" paste from one automation type to another. */
4854 PasteContext ctx(paste_count, times, ItemCounts(), true);
4855 ts.front()->paste (position, *cut_buffer, ctx, sub_num);
4859 /* Paste into tracks */
4861 PasteContext ctx(paste_count, times, ItemCounts(), false);
4862 for (TrackViewList::iterator i = ts.begin(); i != ts.end(); ++i) {
4863 (*i)->paste (position, *cut_buffer, ctx, sub_num);
4867 commit_reversible_command ();
4871 Editor::duplicate_regions (float times)
4873 RegionSelection rs (get_regions_from_selection_and_entered());
4874 duplicate_some_regions (rs, times);
4878 Editor::duplicate_some_regions (RegionSelection& regions, float times)
4880 if (regions.empty ()) {
4884 boost::shared_ptr<Playlist> playlist;
4885 RegionSelection sel = regions; // clear (below) may clear the argument list if its the current region selection
4886 RegionSelection foo;
4888 samplepos_t const start_sample = regions.start ();
4889 samplepos_t const end_sample = regions.end_sample ();
4890 samplecnt_t const gap = end_sample - start_sample + 1;
4892 begin_reversible_command (Operations::duplicate_region);
4894 selection->clear_regions ();
4896 for (RegionSelection::iterator i = sel.begin(); i != sel.end(); ++i) {
4898 boost::shared_ptr<Region> r ((*i)->region());
4900 TimeAxisView& tv = (*i)->get_time_axis_view();
4901 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (&tv);
4902 latest_regionviews.clear ();
4903 sigc::connection c = rtv->view()->RegionViewAdded.connect (sigc::mem_fun(*this, &Editor::collect_new_region_view));
4905 samplepos_t const position = end_sample + (r->first_sample() - start_sample + 1);
4906 playlist = (*i)->region()->playlist();
4907 playlist->clear_changes ();
4908 playlist->duplicate (r, position, gap, times);
4909 _session->add_command(new StatefulDiffCommand (playlist));
4913 foo.insert (foo.end(), latest_regionviews.begin(), latest_regionviews.end());
4917 selection->set (foo);
4920 commit_reversible_command ();
4924 Editor::duplicate_selection (float times)
4926 if (selection->time.empty() || selection->tracks.empty()) {
4930 boost::shared_ptr<Playlist> playlist;
4932 TrackViewList ts = selection->tracks.filter_to_unique_playlists ();
4934 bool in_command = false;
4936 for (TrackViewList::iterator i = ts.begin(); i != ts.end(); ++i) {
4937 if ((playlist = (*i)->playlist()) == 0) {
4940 playlist->clear_changes ();
4942 if (clicked_selection) {
4943 playlist->duplicate_range (selection->time[clicked_selection], times);
4945 playlist->duplicate_ranges (selection->time, times);
4949 begin_reversible_command (_("duplicate range selection"));
4952 _session->add_command (new StatefulDiffCommand (playlist));
4957 if (times == 1.0f) {
4958 // now "move" range selection to after the current range selection
4959 samplecnt_t distance = 0;
4961 if (clicked_selection) {
4963 selection->time[clicked_selection].end - selection->time[clicked_selection].start;
4965 distance = selection->time.end_sample () - selection->time.start ();
4968 selection->move_time (distance);
4970 commit_reversible_command ();
4974 /** Reset all selected points to the relevant default value */
4976 Editor::reset_point_selection ()
4978 for (PointSelection::iterator i = selection->points.begin(); i != selection->points.end(); ++i) {
4979 ARDOUR::AutomationList::iterator j = (*i)->model ();
4980 (*j)->value = (*i)->line().the_list()->descriptor ().normal;
4985 Editor::center_playhead ()
4987 float const page = _visible_canvas_width * samples_per_pixel;
4988 center_screen_internal (playhead_cursor->current_sample (), page);
4992 Editor::center_edit_point ()
4994 float const page = _visible_canvas_width * samples_per_pixel;
4995 center_screen_internal (get_preferred_edit_position(), page);
4998 /** Caller must begin and commit a reversible command */
5000 Editor::clear_playlist (boost::shared_ptr<Playlist> playlist)
5002 playlist->clear_changes ();
5004 _session->add_command (new StatefulDiffCommand (playlist));
5008 Editor::nudge_track (bool use_edit, bool forwards)
5010 boost::shared_ptr<Playlist> playlist;
5011 samplepos_t distance;
5012 samplepos_t next_distance;
5016 start = get_preferred_edit_position();
5021 if ((distance = get_nudge_distance (start, next_distance)) == 0) {
5025 if (selection->tracks.empty()) {
5029 TrackViewList ts = selection->tracks.filter_to_unique_playlists ();
5030 bool in_command = false;
5032 for (TrackViewList::iterator i = ts.begin(); i != ts.end(); ++i) {
5034 if ((playlist = (*i)->playlist()) == 0) {
5038 playlist->clear_changes ();
5039 playlist->clear_owned_changes ();
5041 playlist->nudge_after (start, distance, forwards);
5044 begin_reversible_command (_("nudge track"));
5047 vector<Command*> cmds;
5049 playlist->rdiff (cmds);
5050 _session->add_commands (cmds);
5052 _session->add_command (new StatefulDiffCommand (playlist));
5056 commit_reversible_command ();
5061 Editor::remove_last_capture ()
5063 vector<string> choices;
5070 if (Config->get_verify_remove_last_capture()) {
5071 prompt = _("Do you really want to destroy the last capture?"
5072 "\n(This is destructive and cannot be undone)");
5074 choices.push_back (_("No, do nothing."));
5075 choices.push_back (_("Yes, destroy it."));
5077 Choice prompter (_("Destroy last capture"), prompt, choices);
5079 if (prompter.run () == 1) {
5080 _session->remove_last_capture ();
5081 _regions->redisplay ();
5085 _session->remove_last_capture();
5086 _regions->redisplay ();
5091 Editor::normalize_region ()
5097 RegionSelection rs = get_regions_from_selection_and_entered ();
5103 NormalizeDialog dialog (rs.size() > 1);
5105 if (dialog.run () != RESPONSE_ACCEPT) {
5109 CursorContext::Handle cursor_ctx = CursorContext::create(*this, _cursors->wait);
5112 /* XXX: should really only count audio regions here */
5113 int const regions = rs.size ();
5115 /* Make a list of the selected audio regions' maximum amplitudes, and also
5116 obtain the maximum amplitude of them all.
5118 list<double> max_amps;
5119 list<double> rms_vals;
5122 bool use_rms = dialog.constrain_rms ();
5124 for (RegionSelection::const_iterator i = rs.begin(); i != rs.end(); ++i) {
5125 AudioRegionView const * arv = dynamic_cast<AudioRegionView const *> (*i);
5129 dialog.descend (1.0 / regions);
5130 double const a = arv->audio_region()->maximum_amplitude (&dialog);
5132 double r = arv->audio_region()->rms (&dialog);
5133 max_rms = max (max_rms, r);
5134 rms_vals.push_back (r);
5138 /* the user cancelled the operation */
5142 max_amps.push_back (a);
5143 max_amp = max (max_amp, a);
5147 list<double>::const_iterator a = max_amps.begin ();
5148 list<double>::const_iterator l = rms_vals.begin ();
5149 bool in_command = false;
5151 for (RegionSelection::iterator r = rs.begin(); r != rs.end(); ++r) {
5152 AudioRegionView* const arv = dynamic_cast<AudioRegionView*> (*r);
5157 arv->region()->clear_changes ();
5159 double amp = dialog.normalize_individually() ? *a : max_amp;
5160 double target = dialog.target_peak (); // dB
5163 double const amp_rms = dialog.normalize_individually() ? *l : max_rms;
5164 const double t_rms = dialog.target_rms ();
5165 const gain_t c_peak = dB_to_coefficient (target);
5166 const gain_t c_rms = dB_to_coefficient (t_rms);
5167 if ((amp_rms / c_rms) > (amp / c_peak)) {
5173 arv->audio_region()->normalize (amp, target);
5176 begin_reversible_command (_("normalize"));
5179 _session->add_command (new StatefulDiffCommand (arv->region()));
5186 commit_reversible_command ();
5192 Editor::reset_region_scale_amplitude ()
5198 RegionSelection rs = get_regions_from_selection_and_entered ();
5204 bool in_command = false;
5206 for (RegionSelection::iterator r = rs.begin(); r != rs.end(); ++r) {
5207 AudioRegionView* const arv = dynamic_cast<AudioRegionView*>(*r);
5210 arv->region()->clear_changes ();
5211 arv->audio_region()->set_scale_amplitude (1.0f);
5214 begin_reversible_command ("reset gain");
5217 _session->add_command (new StatefulDiffCommand (arv->region()));
5221 commit_reversible_command ();
5226 Editor::adjust_region_gain (bool up)
5228 RegionSelection rs = get_regions_from_selection_and_entered ();
5230 if (!_session || rs.empty()) {
5234 bool in_command = false;
5236 for (RegionSelection::iterator r = rs.begin(); r != rs.end(); ++r) {
5237 AudioRegionView* const arv = dynamic_cast<AudioRegionView*>(*r);
5242 arv->region()->clear_changes ();
5244 double dB = accurate_coefficient_to_dB (arv->audio_region()->scale_amplitude ());
5252 arv->audio_region()->set_scale_amplitude (dB_to_coefficient (dB));
5255 begin_reversible_command ("adjust region gain");
5258 _session->add_command (new StatefulDiffCommand (arv->region()));
5262 commit_reversible_command ();
5267 Editor::reset_region_gain ()
5269 RegionSelection rs = get_regions_from_selection_and_entered ();
5271 if (!_session || rs.empty()) {
5275 bool in_command = false;
5277 for (RegionSelection::iterator r = rs.begin(); r != rs.end(); ++r) {
5278 AudioRegionView* const arv = dynamic_cast<AudioRegionView*>(*r);
5283 arv->region()->clear_changes ();
5285 arv->audio_region()->set_scale_amplitude (1.0f);
5288 begin_reversible_command ("reset region gain");
5291 _session->add_command (new StatefulDiffCommand (arv->region()));
5295 commit_reversible_command ();
5300 Editor::reverse_region ()
5306 Reverse rev (*_session);
5307 apply_filter (rev, _("reverse regions"));
5311 Editor::strip_region_silence ()
5317 RegionSelection rs = get_regions_from_selection_and_entered ();
5323 std::list<RegionView*> audio_only;
5325 for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
5326 AudioRegionView* const arv = dynamic_cast<AudioRegionView*> (*i);
5328 audio_only.push_back (arv);
5332 assert (!audio_only.empty());
5334 StripSilenceDialog d (_session, audio_only);
5335 int const r = d.run ();
5339 if (r == Gtk::RESPONSE_OK) {
5340 ARDOUR::AudioIntervalMap silences;
5341 d.silences (silences);
5342 StripSilence s (*_session, silences, d.fade_length());
5344 apply_filter (s, _("strip silence"), &d);
5349 Editor::apply_midi_note_edit_op_to_region (MidiOperator& op, MidiRegionView& mrv)
5351 Evoral::Sequence<Temporal::Beats>::Notes selected;
5352 mrv.selection_as_notelist (selected, true);
5354 vector<Evoral::Sequence<Temporal::Beats>::Notes> v;
5355 v.push_back (selected);
5357 Temporal::Beats pos_beats = Temporal::Beats (mrv.midi_region()->beat()) - mrv.midi_region()->start_beats();
5359 return op (mrv.midi_region()->model(), pos_beats, v);
5363 Editor::apply_midi_note_edit_op (MidiOperator& op, const RegionSelection& rs)
5369 bool in_command = false;
5371 for (RegionSelection::const_iterator r = rs.begin(); r != rs.end(); ) {
5372 RegionSelection::const_iterator tmp = r;
5375 MidiRegionView* const mrv = dynamic_cast<MidiRegionView*> (*r);
5378 Command* cmd = apply_midi_note_edit_op_to_region (op, *mrv);
5381 begin_reversible_command (op.name ());
5385 _session->add_command (cmd);
5393 commit_reversible_command ();
5394 _session->set_dirty ();
5399 Editor::fork_region ()
5401 RegionSelection rs = get_regions_from_selection_and_entered ();
5407 CursorContext::Handle cursor_ctx = CursorContext::create(*this, _cursors->wait);
5408 bool in_command = false;
5412 for (RegionSelection::iterator r = rs.begin(); r != rs.end(); ) {
5413 RegionSelection::iterator tmp = r;
5416 MidiRegionView* const mrv = dynamic_cast<MidiRegionView*>(*r);
5420 boost::shared_ptr<Playlist> playlist = mrv->region()->playlist();
5421 boost::shared_ptr<MidiSource> new_source = _session->create_midi_source_by_stealing_name (mrv->midi_view()->track());
5422 boost::shared_ptr<MidiRegion> newregion = mrv->midi_region()->clone (new_source);
5425 begin_reversible_command (_("Fork Region(s)"));
5428 playlist->clear_changes ();
5429 playlist->replace_region (mrv->region(), newregion, mrv->region()->position());
5430 _session->add_command(new StatefulDiffCommand (playlist));
5432 error << string_compose (_("Could not unlink %1"), mrv->region()->name()) << endmsg;
5440 commit_reversible_command ();
5445 Editor::quantize_region ()
5448 quantize_regions(get_regions_from_selection_and_entered ());
5453 Editor::quantize_regions (const RegionSelection& rs)
5455 if (rs.n_midi_regions() == 0) {
5459 if (!quantize_dialog) {
5460 quantize_dialog = new QuantizeDialog (*this);
5463 if (quantize_dialog->is_mapped()) {
5464 /* in progress already */
5468 quantize_dialog->present ();
5469 const int r = quantize_dialog->run ();
5470 quantize_dialog->hide ();
5472 if (r == Gtk::RESPONSE_OK) {
5473 Quantize quant (quantize_dialog->snap_start(),
5474 quantize_dialog->snap_end(),
5475 quantize_dialog->start_grid_size(),
5476 quantize_dialog->end_grid_size(),
5477 quantize_dialog->strength(),
5478 quantize_dialog->swing(),
5479 quantize_dialog->threshold());
5481 apply_midi_note_edit_op (quant, rs);
5486 Editor::legatize_region (bool shrink_only)
5489 legatize_regions(get_regions_from_selection_and_entered (), shrink_only);
5494 Editor::legatize_regions (const RegionSelection& rs, bool shrink_only)
5496 if (rs.n_midi_regions() == 0) {
5500 Legatize legatize(shrink_only);
5501 apply_midi_note_edit_op (legatize, rs);
5505 Editor::transform_region ()
5508 transform_regions(get_regions_from_selection_and_entered ());
5513 Editor::transform_regions (const RegionSelection& rs)
5515 if (rs.n_midi_regions() == 0) {
5522 const int r = td.run();
5525 if (r == Gtk::RESPONSE_OK) {
5526 Transform transform(td.get());
5527 apply_midi_note_edit_op(transform, rs);
5532 Editor::transpose_region ()
5535 transpose_regions(get_regions_from_selection_and_entered ());
5540 Editor::transpose_regions (const RegionSelection& rs)
5542 if (rs.n_midi_regions() == 0) {
5547 int const r = d.run ();
5549 if (r == RESPONSE_ACCEPT) {
5550 Transpose transpose(d.semitones ());
5551 apply_midi_note_edit_op (transpose, rs);
5556 Editor::insert_patch_change (bool from_context)
5558 RegionSelection rs = get_regions_from_selection_and_entered ();
5564 const samplepos_t p = get_preferred_edit_position (EDIT_IGNORE_NONE, from_context);
5566 /* XXX: bit of a hack; use the MIDNAM from the first selected region;
5567 there may be more than one, but the PatchChangeDialog can only offer
5568 one set of patch menus.
5570 MidiRegionView* first = dynamic_cast<MidiRegionView*> (rs.front ());
5572 Evoral::PatchChange<Temporal::Beats> empty (Temporal::Beats(), 0, 0, 0);
5573 PatchChangeDialog d (0, _session, empty, first->instrument_info(), Gtk::Stock::ADD);
5575 if (d.run() == RESPONSE_CANCEL) {
5579 for (RegionSelection::iterator i = rs.begin (); i != rs.end(); ++i) {
5580 MidiRegionView* const mrv = dynamic_cast<MidiRegionView*> (*i);
5582 if (p >= mrv->region()->first_sample() && p <= mrv->region()->last_sample()) {
5583 mrv->add_patch_change (p - mrv->region()->position(), d.patch ());
5590 Editor::apply_filter (Filter& filter, string command, ProgressReporter* progress)
5592 RegionSelection rs = get_regions_from_selection_and_entered ();
5598 CursorContext::Handle cursor_ctx = CursorContext::create(*this, _cursors->wait);
5599 bool in_command = false;
5604 int const N = rs.size ();
5606 for (RegionSelection::iterator r = rs.begin(); r != rs.end(); ) {
5607 RegionSelection::iterator tmp = r;
5610 AudioRegionView* const arv = dynamic_cast<AudioRegionView*>(*r);
5612 boost::shared_ptr<Playlist> playlist = arv->region()->playlist();
5615 progress->descend (1.0 / N);
5618 if (arv->audio_region()->apply (filter, progress) == 0) {
5620 playlist->clear_changes ();
5621 playlist->clear_owned_changes ();
5624 begin_reversible_command (command);
5628 if (filter.results.empty ()) {
5630 /* no regions returned; remove the old one */
5631 playlist->remove_region (arv->region ());
5635 std::vector<boost::shared_ptr<Region> >::iterator res = filter.results.begin ();
5637 /* first region replaces the old one */
5638 playlist->replace_region (arv->region(), *res, (*res)->position());
5642 while (res != filter.results.end()) {
5643 playlist->add_region (*res, (*res)->position());
5649 /* We might have removed regions, which alters other regions' layering_index,
5650 so we need to do a recursive diff here.
5652 vector<Command*> cmds;
5653 playlist->rdiff (cmds);
5654 _session->add_commands (cmds);
5656 _session->add_command(new StatefulDiffCommand (playlist));
5660 progress->ascend ();
5669 commit_reversible_command ();
5674 Editor::external_edit_region ()
5680 Editor::reset_region_gain_envelopes ()
5682 RegionSelection rs = get_regions_from_selection_and_entered ();
5684 if (!_session || rs.empty()) {
5688 bool in_command = false;
5690 for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
5691 AudioRegionView* const arv = dynamic_cast<AudioRegionView*>(*i);
5693 boost::shared_ptr<AutomationList> alist (arv->audio_region()->envelope());
5694 XMLNode& before (alist->get_state());
5696 arv->audio_region()->set_default_envelope ();
5699 begin_reversible_command (_("reset region gain"));
5702 _session->add_command (new MementoCommand<AutomationList>(*arv->audio_region()->envelope().get(), &before, &alist->get_state()));
5707 commit_reversible_command ();
5712 Editor::set_region_gain_visibility (RegionView* rv)
5714 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (rv);
5716 arv->update_envelope_visibility();
5721 Editor::set_gain_envelope_visibility ()
5727 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
5728 AudioTimeAxisView* v = dynamic_cast<AudioTimeAxisView*>(*i);
5730 v->audio_view()->foreach_regionview (sigc::mem_fun (this, &Editor::set_region_gain_visibility));
5736 Editor::toggle_gain_envelope_active ()
5738 if (_ignore_region_action) {
5742 RegionSelection rs = get_regions_from_selection_and_entered ();
5744 if (!_session || rs.empty()) {
5748 bool in_command = false;
5750 for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
5751 AudioRegionView* const arv = dynamic_cast<AudioRegionView*>(*i);
5753 arv->region()->clear_changes ();
5754 arv->audio_region()->set_envelope_active (!arv->audio_region()->envelope_active());
5757 begin_reversible_command (_("region gain envelope active"));
5760 _session->add_command (new StatefulDiffCommand (arv->region()));
5765 commit_reversible_command ();
5770 Editor::toggle_region_lock ()
5772 if (_ignore_region_action) {
5776 RegionSelection rs = get_regions_from_selection_and_entered ();
5778 if (!_session || rs.empty()) {
5782 begin_reversible_command (_("toggle region lock"));
5784 for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
5785 (*i)->region()->clear_changes ();
5786 (*i)->region()->set_locked (!(*i)->region()->locked());
5787 _session->add_command (new StatefulDiffCommand ((*i)->region()));
5790 commit_reversible_command ();
5794 Editor::toggle_region_video_lock ()
5796 if (_ignore_region_action) {
5800 RegionSelection rs = get_regions_from_selection_and_entered ();
5802 if (!_session || rs.empty()) {
5806 begin_reversible_command (_("Toggle Video Lock"));
5808 for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
5809 (*i)->region()->clear_changes ();
5810 (*i)->region()->set_video_locked (!(*i)->region()->video_locked());
5811 _session->add_command (new StatefulDiffCommand ((*i)->region()));
5814 commit_reversible_command ();
5818 Editor::toggle_region_lock_style ()
5820 if (_ignore_region_action) {
5824 RegionSelection rs = get_regions_from_selection_and_entered ();
5826 if (!_session || rs.empty()) {
5830 Glib::RefPtr<ToggleAction> a = Glib::RefPtr<ToggleAction>::cast_dynamic (_region_actions->get_action("toggle-region-lock-style"));
5831 vector<Widget*> proxies = a->get_proxies();
5832 Gtk::CheckMenuItem* cmi = dynamic_cast<Gtk::CheckMenuItem*> (proxies.front());
5836 begin_reversible_command (_("toggle region lock style"));
5838 for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
5839 (*i)->region()->clear_changes ();
5840 PositionLockStyle const ns = ((*i)->region()->position_lock_style() == AudioTime && !cmi->get_inconsistent()) ? MusicTime : AudioTime;
5841 (*i)->region()->set_position_lock_style (ns);
5842 _session->add_command (new StatefulDiffCommand ((*i)->region()));
5845 commit_reversible_command ();
5849 Editor::toggle_opaque_region ()
5851 if (_ignore_region_action) {
5855 RegionSelection rs = get_regions_from_selection_and_entered ();
5857 if (!_session || rs.empty()) {
5861 begin_reversible_command (_("change region opacity"));
5863 for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
5864 (*i)->region()->clear_changes ();
5865 (*i)->region()->set_opaque (!(*i)->region()->opaque());
5866 _session->add_command (new StatefulDiffCommand ((*i)->region()));
5869 commit_reversible_command ();
5873 Editor::toggle_record_enable ()
5875 bool new_state = false;
5877 for (TrackSelection::iterator i = selection->tracks.begin(); i != selection->tracks.end(); ++i) {
5878 RouteTimeAxisView *rtav = dynamic_cast<RouteTimeAxisView *>(*i);
5881 if (!rtav->is_track())
5885 new_state = !rtav->track()->rec_enable_control()->get_value();
5889 rtav->track()->rec_enable_control()->set_value (new_state, Controllable::UseGroup);
5894 tracklist_to_stripables( TrackViewList list )
5898 for (TrackSelection::iterator i = list.begin(); i != list.end(); ++i) {
5899 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> ((*i));
5901 if (rtv && rtv->is_track()) {
5902 ret.push_back( rtv->track() );
5910 Editor::play_solo_selection (bool restart)
5912 //note: session::solo_selection takes care of invalidating the region playlist
5914 if ( (!selection->tracks.empty()) && selection->time.length() > 0 ) { //a range is selected; solo the tracks and roll
5916 StripableList sl = tracklist_to_stripables (selection->tracks);
5917 _session->solo_selection( sl, true );
5920 samplepos_t start = selection->time.start();
5921 samplepos_t end = selection->time.end_sample();
5922 _session->request_bounded_roll (start, end);
5924 } else if ( ! selection->tracks.empty() ) { //no range is selected, but tracks are selected; solo the tracks and roll
5925 StripableList sl = tracklist_to_stripables (selection->tracks);
5926 _session->solo_selection( sl, true );
5927 _session->request_cancel_play_range();
5928 transition_to_rolling (true);
5930 } else if ( ! selection->regions.empty() ) { //solo any tracks with selected regions, and roll
5931 StripableList sl = tracklist_to_stripables ( get_tracks_for_range_action() );
5932 _session->solo_selection( sl, true );
5933 _session->request_cancel_play_range();
5934 transition_to_rolling (true);
5936 _session->request_cancel_play_range();
5937 transition_to_rolling (true); //no selection. just roll.
5942 Editor::toggle_solo ()
5944 bool new_state = false;
5946 boost::shared_ptr<ControlList> cl (new ControlList);
5948 for (TrackSelection::iterator i = selection->tracks.begin(); i != selection->tracks.end(); ++i) {
5949 StripableTimeAxisView *stav = dynamic_cast<StripableTimeAxisView *>(*i);
5951 if (!stav || !stav->stripable()->solo_control()) {
5956 new_state = !stav->stripable()->solo_control()->soloed ();
5960 cl->push_back (stav->stripable()->solo_control());
5963 _session->set_controls (cl, new_state ? 1.0 : 0.0, Controllable::UseGroup);
5967 Editor::toggle_mute ()
5969 bool new_state = false;
5971 boost::shared_ptr<ControlList> cl (new ControlList);
5973 for (TrackSelection::iterator i = selection->tracks.begin(); i != selection->tracks.end(); ++i) {
5974 StripableTimeAxisView *stav = dynamic_cast<StripableTimeAxisView *>(*i);
5976 if (!stav || !stav->stripable()->mute_control()) {
5981 new_state = !stav->stripable()->mute_control()->muted();
5985 cl->push_back (stav->stripable()->mute_control());
5988 _session->set_controls (cl, new_state, Controllable::UseGroup);
5992 Editor::toggle_solo_isolate ()
5998 Editor::fade_range ()
6000 TrackViewList ts = selection->tracks.filter_to_unique_playlists ();
6002 begin_reversible_command (_("fade range"));
6004 for (TrackViewList::iterator i = ts.begin(); i != ts.end(); ++i) {
6005 (*i)->fade_range (selection->time);
6008 commit_reversible_command ();
6013 Editor::set_fade_length (bool in)
6015 RegionSelection rs = get_regions_from_selection_and_entered ();
6021 /* we need a region to measure the offset from the start */
6023 RegionView* rv = rs.front ();
6025 samplepos_t pos = get_preferred_edit_position();
6029 if (pos > rv->region()->last_sample() || pos < rv->region()->first_sample()) {
6030 /* edit point is outside the relevant region */
6035 if (pos <= rv->region()->position()) {
6039 len = pos - rv->region()->position();
6040 cmd = _("set fade in length");
6042 if (pos >= rv->region()->last_sample()) {
6046 len = rv->region()->last_sample() - pos;
6047 cmd = _("set fade out length");
6050 bool in_command = false;
6052 for (RegionSelection::iterator x = rs.begin(); x != rs.end(); ++x) {
6053 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (*x);
6059 boost::shared_ptr<AutomationList> alist;
6061 alist = tmp->audio_region()->fade_in();
6063 alist = tmp->audio_region()->fade_out();
6066 XMLNode &before = alist->get_state();
6069 tmp->audio_region()->set_fade_in_length (len);
6070 tmp->audio_region()->set_fade_in_active (true);
6072 tmp->audio_region()->set_fade_out_length (len);
6073 tmp->audio_region()->set_fade_out_active (true);
6077 begin_reversible_command (cmd);
6080 XMLNode &after = alist->get_state();
6081 _session->add_command(new MementoCommand<AutomationList>(*alist, &before, &after));
6085 commit_reversible_command ();
6090 Editor::set_fade_in_shape (FadeShape shape)
6092 RegionSelection rs = get_regions_from_selection_and_entered ();
6097 bool in_command = false;
6099 for (RegionSelection::iterator x = rs.begin(); x != rs.end(); ++x) {
6100 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (*x);
6106 boost::shared_ptr<AutomationList> alist = tmp->audio_region()->fade_in();
6107 XMLNode &before = alist->get_state();
6109 tmp->audio_region()->set_fade_in_shape (shape);
6112 begin_reversible_command (_("set fade in shape"));
6115 XMLNode &after = alist->get_state();
6116 _session->add_command(new MementoCommand<AutomationList>(*alist.get(), &before, &after));
6120 commit_reversible_command ();
6125 Editor::set_fade_out_shape (FadeShape shape)
6127 RegionSelection rs = get_regions_from_selection_and_entered ();
6132 bool in_command = false;
6134 for (RegionSelection::iterator x = rs.begin(); x != rs.end(); ++x) {
6135 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (*x);
6141 boost::shared_ptr<AutomationList> alist = tmp->audio_region()->fade_out();
6142 XMLNode &before = alist->get_state();
6144 tmp->audio_region()->set_fade_out_shape (shape);
6147 begin_reversible_command (_("set fade out shape"));
6150 XMLNode &after = alist->get_state();
6151 _session->add_command(new MementoCommand<AutomationList>(*alist.get(), &before, &after));
6155 commit_reversible_command ();
6160 Editor::set_fade_in_active (bool yn)
6162 RegionSelection rs = get_regions_from_selection_and_entered ();
6167 bool in_command = false;
6169 for (RegionSelection::iterator x = rs.begin(); x != rs.end(); ++x) {
6170 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (*x);
6177 boost::shared_ptr<AudioRegion> ar (tmp->audio_region());
6179 ar->clear_changes ();
6180 ar->set_fade_in_active (yn);
6183 begin_reversible_command (_("set fade in active"));
6186 _session->add_command (new StatefulDiffCommand (ar));
6190 commit_reversible_command ();
6195 Editor::set_fade_out_active (bool yn)
6197 RegionSelection rs = get_regions_from_selection_and_entered ();
6202 bool in_command = false;
6204 for (RegionSelection::iterator x = rs.begin(); x != rs.end(); ++x) {
6205 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (*x);
6211 boost::shared_ptr<AudioRegion> ar (tmp->audio_region());
6213 ar->clear_changes ();
6214 ar->set_fade_out_active (yn);
6217 begin_reversible_command (_("set fade out active"));
6220 _session->add_command(new StatefulDiffCommand (ar));
6224 commit_reversible_command ();
6229 Editor::toggle_region_fades (int dir)
6231 if (_ignore_region_action) {
6235 boost::shared_ptr<AudioRegion> ar;
6238 RegionSelection rs = get_regions_from_selection_and_entered ();
6244 RegionSelection::iterator i;
6245 for (i = rs.begin(); i != rs.end(); ++i) {
6246 if ((ar = boost::dynamic_pointer_cast<AudioRegion>((*i)->region())) != 0) {
6248 yn = ar->fade_out_active ();
6250 yn = ar->fade_in_active ();
6256 if (i == rs.end()) {
6260 /* XXX should this undo-able? */
6261 bool in_command = false;
6263 for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
6264 if ((ar = boost::dynamic_pointer_cast<AudioRegion>((*i)->region())) == 0) {
6267 ar->clear_changes ();
6269 if (dir == 1 || dir == 0) {
6270 ar->set_fade_in_active (!yn);
6273 if (dir == -1 || dir == 0) {
6274 ar->set_fade_out_active (!yn);
6277 begin_reversible_command (_("toggle fade active"));
6280 _session->add_command(new StatefulDiffCommand (ar));
6284 commit_reversible_command ();
6289 /** Update region fade visibility after its configuration has been changed */
6291 Editor::update_region_fade_visibility ()
6293 bool _fade_visibility = _session->config.get_show_region_fades ();
6295 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
6296 AudioTimeAxisView* v = dynamic_cast<AudioTimeAxisView*>(*i);
6298 if (_fade_visibility) {
6299 v->audio_view()->show_all_fades ();
6301 v->audio_view()->hide_all_fades ();
6308 Editor::set_edit_point ()
6311 MusicSample where (0, 0);
6313 if (!mouse_sample (where.sample, ignored)) {
6319 if (selection->markers.empty()) {
6321 mouse_add_new_marker (where.sample);
6326 Location* loc = find_location_from_marker (selection->markers.front(), ignored);
6329 loc->move_to (where.sample, where.division);
6335 Editor::set_playhead_cursor ()
6337 if (entered_marker) {
6338 _session->request_locate (entered_marker->position(), _session->transport_rolling());
6340 MusicSample where (0, 0);
6343 if (!mouse_sample (where.sample, ignored)) {
6350 _session->request_locate (where.sample, _session->transport_rolling());
6354 //not sure what this was for; remove it for now.
6355 // if (UIConfiguration::instance().get_follow_edits() && (!_session || !_session->config.get_external_sync())) {
6356 // cancel_time_selection();
6362 Editor::split_region ()
6364 if (_drags->active ()) {
6368 //if a range is selected, separate it
6369 if (!selection->time.empty()) {
6370 separate_regions_between (selection->time);
6374 //if no range was selected, try to find some regions to split
6375 if (current_mouse_mode() == MouseObject) { //don't try this for Internal Edit, Stretch, Draw, etc.
6377 RegionSelection rs = get_regions_from_selection_and_edit_point ();
6378 const samplepos_t pos = get_preferred_edit_position();
6379 const int32_t division = get_grid_music_divisions (0);
6380 MusicSample where (pos, division);
6386 split_regions_at (where, rs);
6392 Editor::select_next_stripable (bool routes_only)
6394 if (selection->tracks.empty()) {
6395 selection->set (track_views.front());
6399 TimeAxisView* current = selection->tracks.front();
6403 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
6405 if (*i == current) {
6407 if (i != track_views.end()) {
6410 current = (*(track_views.begin()));
6411 //selection->set (*(track_views.begin()));
6418 RouteUI* rui = dynamic_cast<RouteUI *>(current);
6419 valid = rui && rui->route()->active();
6421 valid = 0 != current->stripable ().get();
6424 } while (current->hidden() || !valid);
6426 selection->set (current);
6428 ensure_time_axis_view_is_visible (*current, false);
6432 Editor::select_prev_stripable (bool routes_only)
6434 if (selection->tracks.empty()) {
6435 selection->set (track_views.front());
6439 TimeAxisView* current = selection->tracks.front();
6443 for (TrackViewList::reverse_iterator i = track_views.rbegin(); i != track_views.rend(); ++i) {
6445 if (*i == current) {
6447 if (i != track_views.rend()) {
6450 current = *(track_views.rbegin());
6456 RouteUI* rui = dynamic_cast<RouteUI *>(current);
6457 valid = rui && rui->route()->active();
6459 valid = 0 != current->stripable ().get();
6462 } while (current->hidden() || !valid);
6464 selection->set (current);
6466 ensure_time_axis_view_is_visible (*current, false);
6470 Editor::set_loop_from_selection (bool play)
6472 if (_session == 0) {
6476 samplepos_t start, end;
6477 if (!get_selection_extents (start, end))
6480 set_loop_range (start, end, _("set loop range from selection"));
6483 _session->request_play_loop (true, true);
6488 Editor::set_loop_from_region (bool play)
6490 samplepos_t start, end;
6491 if (!get_selection_extents (start, end))
6494 set_loop_range (start, end, _("set loop range from region"));
6497 _session->request_locate (start, true);
6498 _session->request_play_loop (true);
6503 Editor::set_punch_from_selection ()
6505 if (_session == 0) {
6509 samplepos_t start, end;
6510 if (!get_selection_extents (start, end))
6513 set_punch_range (start, end, _("set punch range from selection"));
6517 Editor::set_auto_punch_range ()
6519 // auto punch in/out button from a single button
6520 // If Punch In is unset, set punch range from playhead to end, enable punch in
6521 // If Punch In is set, the next punch sets Punch Out, unless the playhead has been
6522 // rewound beyond the Punch In marker, in which case that marker will be moved back
6523 // to the current playhead position.
6524 // If punch out is set, it clears the punch range and Punch In/Out buttons
6526 if (_session == 0) {
6530 Location* tpl = transport_punch_location();
6531 samplepos_t now = playhead_cursor->current_sample();
6532 samplepos_t begin = now;
6533 samplepos_t end = _session->current_end_sample();
6535 if (!_session->config.get_punch_in()) {
6536 // First Press - set punch in and create range from here to eternity
6537 set_punch_range (begin, end, _("Auto Punch In"));
6538 _session->config.set_punch_in(true);
6539 } else if (tpl && !_session->config.get_punch_out()) {
6540 // Second press - update end range marker and set punch_out
6541 if (now < tpl->start()) {
6542 // playhead has been rewound - move start back and pretend nothing happened
6544 set_punch_range (begin, end, _("Auto Punch In/Out"));
6546 // normal case for 2nd press - set the punch out
6547 end = playhead_cursor->current_sample ();
6548 set_punch_range (tpl->start(), now, _("Auto Punch In/Out"));
6549 _session->config.set_punch_out(true);
6552 if (_session->config.get_punch_out()) {
6553 _session->config.set_punch_out(false);
6556 if (_session->config.get_punch_in()) {
6557 _session->config.set_punch_in(false);
6562 // third press - unset punch in/out and remove range
6563 _session->locations()->remove(tpl);
6570 Editor::set_session_extents_from_selection ()
6572 if (_session == 0) {
6576 samplepos_t start, end;
6577 if (!get_selection_extents (start, end))
6581 if ((loc = _session->locations()->session_range_location()) == 0) {
6582 _session->set_session_extents (start, end); // this will create a new session range; no need for UNDO
6584 XMLNode &before = loc->get_state();
6586 _session->set_session_extents (start, end);
6588 XMLNode &after = loc->get_state();
6590 begin_reversible_command (_("set session start/end from selection"));
6592 _session->add_command (new MementoCommand<Location>(*loc, &before, &after));
6594 commit_reversible_command ();
6597 _session->set_end_is_free (false);
6601 Editor::set_punch_start_from_edit_point ()
6605 MusicSample start (0, 0);
6606 samplepos_t end = max_samplepos;
6608 //use the existing punch end, if any
6609 Location* tpl = transport_punch_location();
6614 if ((_edit_point == EditAtPlayhead) && _session->transport_rolling()) {
6615 start.sample = _session->audible_sample();
6617 start.sample = get_preferred_edit_position();
6620 //snap the selection start/end
6623 //if there's not already a sensible selection endpoint, go "forever"
6624 if (start.sample > end ) {
6625 end = max_samplepos;
6628 set_punch_range (start.sample, end, _("set punch start from EP"));
6634 Editor::set_punch_end_from_edit_point ()
6638 samplepos_t start = 0;
6639 MusicSample end (max_samplepos, 0);
6641 //use the existing punch start, if any
6642 Location* tpl = transport_punch_location();
6644 start = tpl->start();
6647 if ((_edit_point == EditAtPlayhead) && _session->transport_rolling()) {
6648 end.sample = _session->audible_sample();
6650 end.sample = get_preferred_edit_position();
6653 //snap the selection start/end
6656 set_punch_range (start, end.sample, _("set punch end from EP"));
6662 Editor::set_loop_start_from_edit_point ()
6666 MusicSample start (0, 0);
6667 samplepos_t end = max_samplepos;
6669 //use the existing loop end, if any
6670 Location* tpl = transport_loop_location();
6675 if ((_edit_point == EditAtPlayhead) && _session->transport_rolling()) {
6676 start.sample = _session->audible_sample();
6678 start.sample = get_preferred_edit_position();
6681 //snap the selection start/end
6684 //if there's not already a sensible selection endpoint, go "forever"
6685 if (start.sample > end ) {
6686 end = max_samplepos;
6689 set_loop_range (start.sample, end, _("set loop start from EP"));
6695 Editor::set_loop_end_from_edit_point ()
6699 samplepos_t start = 0;
6700 MusicSample end (max_samplepos, 0);
6702 //use the existing loop start, if any
6703 Location* tpl = transport_loop_location();
6705 start = tpl->start();
6708 if ((_edit_point == EditAtPlayhead) && _session->transport_rolling()) {
6709 end.sample = _session->audible_sample();
6711 end.sample = get_preferred_edit_position();
6714 //snap the selection start/end
6717 set_loop_range (start, end.sample, _("set loop end from EP"));
6722 Editor::set_punch_from_region ()
6724 samplepos_t start, end;
6725 if (!get_selection_extents (start, end))
6728 set_punch_range (start, end, _("set punch range from region"));
6732 Editor::pitch_shift_region ()
6734 RegionSelection rs = get_regions_from_selection_and_entered ();
6736 RegionSelection audio_rs;
6737 for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
6738 if (dynamic_cast<AudioRegionView*> (*i)) {
6739 audio_rs.push_back (*i);
6743 if (audio_rs.empty()) {
6747 pitch_shift (audio_rs, 1.2);
6751 Editor::set_tempo_from_region ()
6753 RegionSelection rs = get_regions_from_selection_and_entered ();
6755 if (!_session || rs.empty()) {
6759 RegionView* rv = rs.front();
6761 define_one_bar (rv->region()->position(), rv->region()->last_sample() + 1);
6765 Editor::use_range_as_bar ()
6767 samplepos_t start, end;
6768 if (get_edit_op_range (start, end)) {
6769 define_one_bar (start, end);
6774 Editor::define_one_bar (samplepos_t start, samplepos_t end)
6776 samplepos_t length = end - start;
6778 const Meter& m (_session->tempo_map().meter_at_sample (start));
6780 /* length = 1 bar */
6782 /* We're going to deliver a constant tempo here,
6783 so we can use samples per beat to determine length.
6784 now we want samples per beat.
6785 we have samples per bar, and beats per bar, so ...
6788 /* XXXX METER MATH */
6790 double samples_per_beat = length / m.divisions_per_bar();
6792 /* beats per minute = */
6794 double beats_per_minute = (_session->sample_rate() * 60.0) / samples_per_beat;
6796 /* now decide whether to:
6798 (a) set global tempo
6799 (b) add a new tempo marker
6803 const TempoSection& t (_session->tempo_map().tempo_section_at_sample (start));
6805 bool do_global = false;
6807 if ((_session->tempo_map().n_tempos() == 1) && (_session->tempo_map().n_meters() == 1)) {
6809 /* only 1 tempo & 1 meter: ask if the user wants to set the tempo
6810 at the start, or create a new marker
6813 vector<string> options;
6814 options.push_back (_("Cancel"));
6815 options.push_back (_("Add new marker"));
6816 options.push_back (_("Set global tempo"));
6819 _("Define one bar"),
6820 _("Do you want to set the global tempo or add a new tempo marker?"),
6824 c.set_default_response (2);
6840 /* more than 1 tempo and/or meter section already, go ahead do the "usual":
6841 if the marker is at the region starter, change it, otherwise add
6846 begin_reversible_command (_("set tempo from region"));
6847 XMLNode& before (_session->tempo_map().get_state());
6850 _session->tempo_map().change_initial_tempo (beats_per_minute, t.note_type(), t.end_note_types_per_minute());
6851 } else if (t.sample() == start) {
6852 _session->tempo_map().change_existing_tempo_at (start, beats_per_minute, t.note_type(), t.end_note_types_per_minute());
6854 /* constant tempo */
6855 const Tempo tempo (beats_per_minute, t.note_type());
6856 _session->tempo_map().add_tempo (tempo, 0.0, start, AudioTime);
6859 XMLNode& after (_session->tempo_map().get_state());
6861 _session->add_command (new MementoCommand<TempoMap>(_session->tempo_map(), &before, &after));
6862 commit_reversible_command ();
6866 Editor::split_region_at_transients ()
6868 AnalysisFeatureList positions;
6870 RegionSelection rs = get_regions_from_selection_and_entered ();
6872 if (!_session || rs.empty()) {
6876 begin_reversible_command (_("split regions"));
6878 for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ) {
6880 RegionSelection::iterator tmp;
6885 boost::shared_ptr<AudioRegion> ar = boost::dynamic_pointer_cast<AudioRegion> ((*i)->region());
6888 ar->transients (positions);
6889 split_region_at_points ((*i)->region(), positions, true);
6896 commit_reversible_command ();
6901 Editor::split_region_at_points (boost::shared_ptr<Region> r, AnalysisFeatureList& positions, bool can_ferret, bool select_new)
6903 bool use_rhythmic_rodent = false;
6905 boost::shared_ptr<Playlist> pl = r->playlist();
6907 list<boost::shared_ptr<Region> > new_regions;
6913 if (positions.empty()) {
6917 if (positions.size() > 20 && can_ferret) {
6918 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);
6919 MessageDialog msg (msgstr,
6922 Gtk::BUTTONS_OK_CANCEL);
6925 msg.add_button (_("Call for the Ferret!"), RESPONSE_APPLY);
6926 msg.set_secondary_text (_("Press OK to continue with this split operation\nor ask the Ferret dialog to tune the analysis"));
6928 msg.set_secondary_text (_("Press OK to continue with this split operation"));
6931 msg.set_title (_("Excessive split?"));
6934 int response = msg.run();
6940 case RESPONSE_APPLY:
6941 use_rhythmic_rodent = true;
6948 if (use_rhythmic_rodent) {
6949 show_rhythm_ferret ();
6953 AnalysisFeatureList::const_iterator x;
6955 pl->clear_changes ();
6956 pl->clear_owned_changes ();
6958 x = positions.begin();
6960 if (x == positions.end()) {
6965 pl->remove_region (r);
6967 samplepos_t pos = 0;
6969 samplepos_t rstart = r->first_sample ();
6970 samplepos_t rend = r->last_sample ();
6972 while (x != positions.end()) {
6974 /* deal with positons that are out of scope of present region bounds */
6975 if (*x <= rstart || *x > rend) {
6980 /* file start = original start + how far we from the initial position ? */
6982 samplepos_t file_start = r->start() + pos;
6984 /* length = next position - current position */
6986 samplepos_t len = (*x) - pos - rstart;
6988 /* XXX we do we really want to allow even single-sample regions?
6989 * shouldn't we have some kind of lower limit on region size?
6998 if (RegionFactory::region_name (new_name, r->name())) {
7002 /* do NOT announce new regions 1 by one, just wait till they are all done */
7006 plist.add (ARDOUR::Properties::start, file_start);
7007 plist.add (ARDOUR::Properties::length, len);
7008 plist.add (ARDOUR::Properties::name, new_name);
7009 plist.add (ARDOUR::Properties::layer, 0);
7010 // TODO set transients_offset
7012 boost::shared_ptr<Region> nr = RegionFactory::create (r->sources(), plist, false);
7013 /* because we set annouce to false, manually add the new region to the
7016 RegionFactory::map_add (nr);
7018 pl->add_region (nr, rstart + pos);
7021 new_regions.push_front(nr);
7030 RegionFactory::region_name (new_name, r->name());
7032 /* Add the final region */
7035 plist.add (ARDOUR::Properties::start, r->start() + pos);
7036 plist.add (ARDOUR::Properties::length, r->last_sample() - (r->position() + pos) + 1);
7037 plist.add (ARDOUR::Properties::name, new_name);
7038 plist.add (ARDOUR::Properties::layer, 0);
7040 boost::shared_ptr<Region> nr = RegionFactory::create (r->sources(), plist, false);
7041 /* because we set annouce to false, manually add the new region to the
7044 RegionFactory::map_add (nr);
7045 pl->add_region (nr, r->position() + pos);
7048 new_regions.push_front(nr);
7053 /* We might have removed regions, which alters other regions' layering_index,
7054 so we need to do a recursive diff here.
7056 vector<Command*> cmds;
7058 _session->add_commands (cmds);
7060 _session->add_command (new StatefulDiffCommand (pl));
7064 for (list<boost::shared_ptr<Region> >::iterator i = new_regions.begin(); i != new_regions.end(); ++i){
7065 set_selected_regionview_from_region_list ((*i), Selection::Add);
7071 Editor::place_transient()
7077 RegionSelection rs = get_regions_from_selection_and_edit_point ();
7083 samplepos_t where = get_preferred_edit_position();
7085 begin_reversible_command (_("place transient"));
7087 for (RegionSelection::iterator r = rs.begin(); r != rs.end(); ++r) {
7088 (*r)->region()->add_transient(where);
7091 commit_reversible_command ();
7095 Editor::remove_transient(ArdourCanvas::Item* item)
7101 ArdourCanvas::Line* _line = reinterpret_cast<ArdourCanvas::Line*> (item);
7104 AudioRegionView* _arv = reinterpret_cast<AudioRegionView*> (item->get_data ("regionview"));
7105 _arv->remove_transient (*(float*) _line->get_data ("position"));
7109 Editor::snap_regions_to_grid ()
7111 list <boost::shared_ptr<Playlist > > used_playlists;
7113 RegionSelection rs = get_regions_from_selection_and_entered ();
7115 if (!_session || rs.empty()) {
7119 begin_reversible_command (_("snap regions to grid"));
7121 for (RegionSelection::iterator r = rs.begin(); r != rs.end(); ++r) {
7123 boost::shared_ptr<Playlist> pl = (*r)->region()->playlist();
7125 if (!pl->frozen()) {
7126 /* we haven't seen this playlist before */
7128 /* remember used playlists so we can thaw them later */
7129 used_playlists.push_back(pl);
7132 (*r)->region()->clear_changes ();
7134 MusicSample start ((*r)->region()->first_sample (), 0);
7135 snap_to (start, RoundNearest, SnapToGrid );
7136 (*r)->region()->set_position (start.sample, start.division);
7137 _session->add_command(new StatefulDiffCommand ((*r)->region()));
7140 while (used_playlists.size() > 0) {
7141 list <boost::shared_ptr<Playlist > >::iterator i = used_playlists.begin();
7143 used_playlists.pop_front();
7146 commit_reversible_command ();
7150 Editor::close_region_gaps ()
7152 list <boost::shared_ptr<Playlist > > used_playlists;
7154 RegionSelection rs = get_regions_from_selection_and_entered ();
7156 if (!_session || rs.empty()) {
7160 Dialog dialog (_("Close Region Gaps"));
7163 table.set_spacings (12);
7164 table.set_border_width (12);
7165 Label* l = manage (left_aligned_label (_("Crossfade length")));
7166 table.attach (*l, 0, 1, 0, 1);
7168 SpinButton spin_crossfade (1, 0);
7169 spin_crossfade.set_range (0, 15);
7170 spin_crossfade.set_increments (1, 1);
7171 spin_crossfade.set_value (5);
7172 table.attach (spin_crossfade, 1, 2, 0, 1);
7174 table.attach (*manage (new Label (_("ms"))), 2, 3, 0, 1);
7176 l = manage (left_aligned_label (_("Pull-back length")));
7177 table.attach (*l, 0, 1, 1, 2);
7179 SpinButton spin_pullback (1, 0);
7180 spin_pullback.set_range (0, 100);
7181 spin_pullback.set_increments (1, 1);
7182 spin_pullback.set_value(30);
7183 table.attach (spin_pullback, 1, 2, 1, 2);
7185 table.attach (*manage (new Label (_("ms"))), 2, 3, 1, 2);
7187 dialog.get_vbox()->pack_start (table);
7188 dialog.add_button (Stock::CANCEL, RESPONSE_CANCEL);
7189 dialog.add_button (_("Ok"), RESPONSE_ACCEPT);
7192 if (dialog.run () == RESPONSE_CANCEL) {
7196 samplepos_t crossfade_len = spin_crossfade.get_value();
7197 samplepos_t pull_back_samples = spin_pullback.get_value();
7199 crossfade_len = lrintf (crossfade_len * _session->sample_rate()/1000);
7200 pull_back_samples = lrintf (pull_back_samples * _session->sample_rate()/1000);
7202 /* Iterate over the region list and make adjacent regions overlap by crossfade_len_ms */
7204 begin_reversible_command (_("close region gaps"));
7207 boost::shared_ptr<Region> last_region;
7209 rs.sort_by_position_and_track();
7211 for (RegionSelection::iterator r = rs.begin(); r != rs.end(); ++r) {
7213 boost::shared_ptr<Playlist> pl = (*r)->region()->playlist();
7215 if (!pl->frozen()) {
7216 /* we haven't seen this playlist before */
7218 /* remember used playlists so we can thaw them later */
7219 used_playlists.push_back(pl);
7223 samplepos_t position = (*r)->region()->position();
7225 if (idx == 0 || position < last_region->position()){
7226 last_region = (*r)->region();
7231 (*r)->region()->clear_changes ();
7232 (*r)->region()->trim_front((position - pull_back_samples));
7234 last_region->clear_changes ();
7235 last_region->trim_end ((position - pull_back_samples + crossfade_len));
7237 _session->add_command (new StatefulDiffCommand ((*r)->region()));
7238 _session->add_command (new StatefulDiffCommand (last_region));
7240 last_region = (*r)->region();
7244 while (used_playlists.size() > 0) {
7245 list <boost::shared_ptr<Playlist > >::iterator i = used_playlists.begin();
7247 used_playlists.pop_front();
7250 commit_reversible_command ();
7254 Editor::tab_to_transient (bool forward)
7256 AnalysisFeatureList positions;
7258 RegionSelection rs = get_regions_from_selection_and_entered ();
7264 samplepos_t pos = _session->audible_sample ();
7266 if (!selection->tracks.empty()) {
7268 /* don't waste time searching for transients in duplicate playlists.
7271 TrackViewList ts = selection->tracks.filter_to_unique_playlists ();
7273 for (TrackViewList::iterator t = ts.begin(); t != ts.end(); ++t) {
7275 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (*t);
7278 boost::shared_ptr<Track> tr = rtv->track();
7280 boost::shared_ptr<Playlist> pl = tr->playlist ();
7282 samplepos_t result = pl->find_next_transient (pos, forward ? 1 : -1);
7285 positions.push_back (result);
7298 for (RegionSelection::iterator r = rs.begin(); r != rs.end(); ++r) {
7299 (*r)->region()->get_transients (positions);
7303 TransientDetector::cleanup_transients (positions, _session->sample_rate(), 3.0);
7306 AnalysisFeatureList::iterator x;
7308 for (x = positions.begin(); x != positions.end(); ++x) {
7314 if (x != positions.end ()) {
7315 _session->request_locate (*x);
7319 AnalysisFeatureList::reverse_iterator x;
7321 for (x = positions.rbegin(); x != positions.rend(); ++x) {
7327 if (x != positions.rend ()) {
7328 _session->request_locate (*x);
7334 Editor::playhead_forward_to_grid ()
7340 MusicSample pos (playhead_cursor->current_sample (), 0);
7342 if (pos.sample < max_samplepos - 1) {
7344 snap_to_internal (pos, RoundUpAlways, SnapToGrid, false, true);
7345 _session->request_locate (pos.sample);
7351 Editor::playhead_backward_to_grid ()
7357 MusicSample pos (playhead_cursor->current_sample (), 0);
7359 if (pos.sample > 2) {
7361 snap_to_internal (pos, RoundDownAlways, SnapToGrid, false, true);
7362 _session->request_locate (pos.sample);
7367 Editor::set_track_height (Height h)
7369 TrackSelection& ts (selection->tracks);
7371 for (TrackSelection::iterator x = ts.begin(); x != ts.end(); ++x) {
7372 (*x)->set_height_enum (h);
7377 Editor::toggle_tracks_active ()
7379 TrackSelection& ts (selection->tracks);
7381 bool target = false;
7387 for (TrackSelection::iterator x = ts.begin(); x != ts.end(); ++x) {
7388 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*>(*x);
7392 target = !rtv->_route->active();
7395 rtv->_route->set_active (target, this);
7401 Editor::remove_tracks ()
7403 /* this will delete GUI objects that may be the subject of an event
7404 handler in which this method is called. Defer actual deletion to the
7405 next idle callback, when all event handling is finished.
7407 Glib::signal_idle().connect (sigc::mem_fun (*this, &Editor::idle_remove_tracks));
7411 Editor::idle_remove_tracks ()
7413 Session::StateProtector sp (_session);
7415 return false; /* do not call again */
7419 Editor::_remove_tracks ()
7421 TrackSelection& ts (selection->tracks);
7427 vector<string> choices;
7432 const char* trackstr;
7435 vector<boost::shared_ptr<Route> > routes;
7436 vector<boost::shared_ptr<VCA> > vcas;
7437 bool special_bus = false;
7439 for (TrackSelection::iterator x = ts.begin(); x != ts.end(); ++x) {
7440 VCATimeAxisView* vtv = dynamic_cast<VCATimeAxisView*> (*x);
7442 vcas.push_back (vtv->vca());
7446 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (*x);
7450 if (rtv->is_track()) {
7455 routes.push_back (rtv->_route);
7457 if (rtv->route()->is_master() || rtv->route()->is_monitor()) {
7462 if (special_bus && !Config->get_allow_special_bus_removal()) {
7463 MessageDialog msg (_("That would be bad news ...."),
7467 msg.set_secondary_text (string_compose (_(
7468 "Removing the master or monitor bus is such a bad idea\n\
7469 that %1 is not going to allow it.\n\
7471 If you really want to do this sort of thing\n\
7472 edit your ardour.rc file to set the\n\
7473 \"allow-special-bus-removal\" option to be \"yes\""), PROGRAM_NAME));
7480 if (ntracks + nbusses + nvcas == 0) {
7486 trackstr = P_("track", "tracks", ntracks);
7487 busstr = P_("bus", "busses", nbusses);
7488 vcastr = P_("VCA", "VCAs", nvcas);
7490 if (ntracks > 0 && nbusses > 0 && nvcas > 0) {
7491 title = _("Remove various strips");
7492 prompt = string_compose (_("Do you really want to remove %1 %2, %3 %4 and %5 %6?"),
7493 ntracks, trackstr, nbusses, busstr, nvcas, vcastr);
7495 else if (ntracks > 0 && nbusses > 0) {
7496 title = string_compose (_("Remove %1 and %2"), trackstr, busstr);
7497 prompt = string_compose (_("Do you really want to remove %1 %2 and %3 %4?"),
7498 ntracks, trackstr, nbusses, busstr);
7500 else if (ntracks > 0 && nvcas > 0) {
7501 title = string_compose (_("Remove %1 and %2"), trackstr, vcastr);
7502 prompt = string_compose (_("Do you really want to remove %1 %2 and %3 %4?"),
7503 ntracks, trackstr, nvcas, vcastr);
7505 else if (nbusses > 0 && nvcas > 0) {
7506 title = string_compose (_("Remove %1 and %2"), busstr, vcastr);
7507 prompt = string_compose (_("Do you really want to remove %1 %2 and %3 %4?"),
7508 nbusses, busstr, nvcas, vcastr);
7510 else if (ntracks > 0) {
7511 title = string_compose (_("Remove %1"), trackstr);
7512 prompt = string_compose (_("Do you really want to remove %1 %2?"),
7515 else if (nbusses > 0) {
7516 title = string_compose (_("Remove %1"), busstr);
7517 prompt = string_compose (_("Do you really want to remove %1 %2?"),
7520 else if (nvcas > 0) {
7521 title = string_compose (_("Remove %1"), vcastr);
7522 prompt = string_compose (_("Do you really want to remove %1 %2?"),
7530 prompt += "\n" + string_compose ("(You may also lose the playlists associated with the %1)", trackstr) + "\n";
7533 prompt += "\n" + string(_("This action cannot be undone, and the session file will be overwritten!"));
7535 choices.push_back (_("No, do nothing."));
7536 if (ntracks + nbusses + nvcas > 1) {
7537 choices.push_back (_("Yes, remove them."));
7539 choices.push_back (_("Yes, remove it."));
7542 Choice prompter (title, prompt, choices);
7544 if (prompter.run () != 1) {
7548 if (current_mixer_strip && routes.size () > 1 && std::find (routes.begin(), routes.end(), current_mixer_strip->route()) != routes.end ()) {
7549 /* Route deletion calls Editor::timeaxisview_deleted() iteratively (for each deleted
7550 * route). If the deleted route is currently displayed in the Editor-Mixer (highly
7551 * likely because deletion requires selection) this will call
7552 * Editor::set_selected_mixer_strip () which is expensive ( MixerStrip::set_route() ).
7553 * It's likewise likely that the route that has just been displayed in the
7554 * Editor-Mixer will be next in line for deletion.
7556 * So simply switch to the master-bus (if present)
7558 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
7559 if ((*i)->stripable ()->is_master ()) {
7560 set_selected_mixer_strip (*(*i));
7567 PresentationInfo::ChangeSuspender cs;
7568 DisplaySuspender ds;
7570 boost::shared_ptr<RouteList> rl (new RouteList);
7571 for (vector<boost::shared_ptr<Route> >::iterator x = routes.begin(); x != routes.end(); ++x) {
7574 _session->remove_routes (rl);
7576 for (vector<boost::shared_ptr<VCA> >::iterator x = vcas.begin(); x != vcas.end(); ++x) {
7577 _session->vca_manager().remove_vca (*x);
7581 /* TrackSelection and RouteList leave scope,
7582 * destructors are called,
7583 * diskstream drops references, save_state is called (again for every track)
7588 Editor::do_insert_time ()
7590 if (selection->tracks.empty()) {
7591 MessageDialog msg (_("You must first select some tracks to Insert Time."),
7592 true, MESSAGE_INFO, BUTTONS_OK, true);
7593 msg.set_position (WIN_POS_MOUSE);
7598 if (Config->get_edit_mode() == Lock) {
7599 MessageDialog msg (_("You cannot insert time in Lock Edit mode."),
7600 true, MESSAGE_INFO, BUTTONS_OK, true);
7601 msg.set_position (WIN_POS_MOUSE);
7606 InsertRemoveTimeDialog d (*this);
7607 int response = d.run ();
7609 if (response != RESPONSE_OK) {
7613 if (d.distance() == 0) {
7620 d.intersected_region_action (),
7624 d.move_glued_markers(),
7625 d.move_locked_markers(),
7631 Editor::insert_time (
7632 samplepos_t pos, samplecnt_t samples, InsertTimeOption opt,
7633 bool all_playlists, bool ignore_music_glue, bool markers_too, bool glued_markers_too, bool locked_markers_too, bool tempo_too
7637 if (Config->get_edit_mode() == Lock) {
7640 bool in_command = false;
7642 TrackViewList ts = selection->tracks.filter_to_unique_playlists ();
7644 for (TrackViewList::iterator x = ts.begin(); x != ts.end(); ++x) {
7648 /* don't operate on any playlist more than once, which could
7649 * happen if "all playlists" is enabled, but there is more
7650 * than 1 track using playlists "from" a given track.
7653 set<boost::shared_ptr<Playlist> > pl;
7655 if (all_playlists) {
7656 RouteTimeAxisView* rtav = dynamic_cast<RouteTimeAxisView*> (*x);
7657 if (rtav && rtav->track ()) {
7658 vector<boost::shared_ptr<Playlist> > all = _session->playlists->playlists_for_track (rtav->track ());
7659 for (vector<boost::shared_ptr<Playlist> >::iterator p = all.begin(); p != all.end(); ++p) {
7664 if ((*x)->playlist ()) {
7665 pl.insert ((*x)->playlist ());
7669 for (set<boost::shared_ptr<Playlist> >::iterator i = pl.begin(); i != pl.end(); ++i) {
7671 (*i)->clear_changes ();
7672 (*i)->clear_owned_changes ();
7675 begin_reversible_command (_("insert time"));
7679 if (opt == SplitIntersected) {
7680 /* non musical split */
7681 (*i)->split (MusicSample (pos, 0));
7684 (*i)->shift (pos, samples, (opt == MoveIntersected), ignore_music_glue);
7686 vector<Command*> cmds;
7688 _session->add_commands (cmds);
7690 _session->add_command (new StatefulDiffCommand (*i));
7694 RouteTimeAxisView* rtav = dynamic_cast<RouteTimeAxisView*> (*x);
7697 begin_reversible_command (_("insert time"));
7700 rtav->route ()->shift (pos, samples);
7707 const int32_t divisions = get_grid_music_divisions (0);
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) {
7713 Locations::LocationList::const_iterator tmp;
7715 if ((*i)->position_lock_style() == AudioTime || glued_markers_too) {
7716 bool const was_locked = (*i)->locked ();
7717 if (locked_markers_too) {
7721 if ((*i)->start() >= pos) {
7722 // move end first, in case we're moving by more than the length of the range
7723 if (!(*i)->is_mark()) {
7724 (*i)->set_end ((*i)->end() + samples, false, true, divisions);
7726 (*i)->set_start ((*i)->start() + samples, false, true, divisions);
7738 begin_reversible_command (_("insert time"));
7741 XMLNode& after (_session->locations()->get_state());
7742 _session->add_command (new MementoCommand<Locations>(*_session->locations(), &before, &after));
7748 begin_reversible_command (_("insert time"));
7751 XMLNode& before (_session->tempo_map().get_state());
7752 _session->tempo_map().insert_time (pos, samples);
7753 XMLNode& after (_session->tempo_map().get_state());
7754 _session->add_command (new MementoCommand<TempoMap>(_session->tempo_map(), &before, &after));
7758 commit_reversible_command ();
7763 Editor::do_remove_time ()
7765 if (selection->tracks.empty()) {
7766 MessageDialog msg (_("You must first select some tracks to Remove Time."),
7767 true, MESSAGE_INFO, BUTTONS_OK, true);
7768 msg.set_position (WIN_POS_MOUSE);
7773 if (Config->get_edit_mode() == Lock) {
7774 MessageDialog msg (_("You cannot remove time in Lock Edit mode."),
7775 true, MESSAGE_INFO, BUTTONS_OK, true);
7776 msg.set_position (WIN_POS_MOUSE);
7781 InsertRemoveTimeDialog d (*this, true);
7783 int response = d.run ();
7785 if (response != RESPONSE_OK) {
7789 samplecnt_t distance = d.distance();
7791 if (distance == 0) {
7801 d.move_glued_markers(),
7802 d.move_locked_markers(),
7808 Editor::remove_time (samplepos_t pos, samplecnt_t samples, InsertTimeOption opt,
7809 bool ignore_music_glue, bool markers_too, bool glued_markers_too, bool locked_markers_too, bool tempo_too)
7811 if (Config->get_edit_mode() == Lock) {
7812 error << (_("Cannot insert or delete time when in Lock edit.")) << endmsg;
7815 bool in_command = false;
7817 for (TrackSelection::iterator x = selection->tracks.begin(); x != selection->tracks.end(); ++x) {
7819 boost::shared_ptr<Playlist> pl = (*x)->playlist();
7823 XMLNode &before = pl->get_state();
7826 begin_reversible_command (_("remove time"));
7830 std::list<AudioRange> rl;
7831 AudioRange ar(pos, pos+samples, 0);
7834 pl->shift (pos, -samples, true, ignore_music_glue);
7836 XMLNode &after = pl->get_state();
7838 _session->add_command (new MementoCommand<Playlist> (*pl, &before, &after));
7842 RouteTimeAxisView* rtav = dynamic_cast<RouteTimeAxisView*> (*x);
7845 begin_reversible_command (_("remove time"));
7848 rtav->route ()->shift (pos, -samples);
7852 const int32_t divisions = get_grid_music_divisions (0);
7853 std::list<Location*> loc_kill_list;
7858 XMLNode& before (_session->locations()->get_state());
7859 Locations::LocationList copy (_session->locations()->list());
7861 for (Locations::LocationList::iterator i = copy.begin(); i != copy.end(); ++i) {
7862 if ((*i)->position_lock_style() == AudioTime || glued_markers_too) {
7864 bool const was_locked = (*i)->locked ();
7865 if (locked_markers_too) {
7869 if (!(*i)->is_mark()) { // it's a range; have to handle both start and end
7870 if ((*i)->end() >= pos
7871 && (*i)->end() < pos+samples
7872 && (*i)->start() >= pos
7873 && (*i)->end() < pos+samples) { // range is completely enclosed; kill it
7875 loc_kill_list.push_back(*i);
7876 } else { // only start or end is included, try to do the right thing
7877 // move start before moving end, to avoid trying to move the end to before the start
7878 // if we're removing more time than the length of the range
7879 if ((*i)->start() >= pos && (*i)->start() < pos+samples) {
7880 // start is within cut
7881 (*i)->set_start (pos, false, true,divisions); // bring the start marker to the beginning of the cut
7883 } else if ((*i)->start() >= pos+samples) {
7884 // start (and thus entire range) lies beyond end of cut
7885 (*i)->set_start ((*i)->start() - samples, false, true, divisions); // slip the start marker back
7888 if ((*i)->end() >= pos && (*i)->end() < pos+samples) {
7889 // end is inside cut
7890 (*i)->set_end (pos, false, true, divisions); // bring the end to the cut
7892 } else if ((*i)->end() >= pos+samples) {
7893 // end is beyond end of cut
7894 (*i)->set_end ((*i)->end() - samples, false, true, divisions); // slip the end marker back
7899 } else if ((*i)->start() >= pos && (*i)->start() < pos+samples) {
7900 loc_kill_list.push_back(*i);
7902 } else if ((*i)->start() >= pos) {
7903 (*i)->set_start ((*i)->start() -samples, false, true, divisions);
7913 for (list<Location*>::iterator i = loc_kill_list.begin(); i != loc_kill_list.end(); ++i) {
7914 _session->locations()->remove (*i);
7919 begin_reversible_command (_("remove time"));
7922 XMLNode& after (_session->locations()->get_state());
7923 _session->add_command (new MementoCommand<Locations>(*_session->locations(), &before, &after));
7928 XMLNode& before (_session->tempo_map().get_state());
7930 if (_session->tempo_map().remove_time (pos, samples)) {
7932 begin_reversible_command (_("remove time"));
7935 XMLNode& after (_session->tempo_map().get_state());
7936 _session->add_command (new MementoCommand<TempoMap>(_session->tempo_map(), &before, &after));
7941 commit_reversible_command ();
7946 Editor::fit_selection ()
7948 if (!selection->tracks.empty()) {
7949 fit_tracks (selection->tracks);
7953 /* no selected tracks - use tracks with selected regions */
7955 if (!selection->regions.empty()) {
7956 for (RegionSelection::iterator r = selection->regions.begin(); r != selection->regions.end(); ++r) {
7957 tvl.push_back (&(*r)->get_time_axis_view ());
7963 } else if (internal_editing()) {
7964 /* no selected tracks, or regions, but in internal edit mode, so follow the mouse and use
7967 if (entered_track) {
7968 tvl.push_back (entered_track);
7976 Editor::fit_tracks (TrackViewList & tracks)
7978 if (tracks.empty()) {
7982 uint32_t child_heights = 0;
7983 int visible_tracks = 0;
7985 for (TrackSelection::iterator t = tracks.begin(); t != tracks.end(); ++t) {
7987 if (!(*t)->marked_for_display()) {
7991 child_heights += (*t)->effective_height() - (*t)->current_height();
7995 /* compute the per-track height from:
7997 * total canvas visible height
7998 * - height that will be taken by visible children of selected tracks
7999 * - height of the ruler/hscroll area
8001 uint32_t h = (uint32_t) floor ((trackviews_height() - child_heights) / visible_tracks);
8002 double first_y_pos = DBL_MAX;
8004 if (h < TimeAxisView::preset_height (HeightSmall)) {
8005 MessageDialog msg (_("There are too many tracks to fit in the current window"));
8006 /* too small to be displayed */
8010 undo_visual_stack.push_back (current_visual_state (true));
8011 PBD::Unwinder<bool> nsv (no_save_visual, true);
8013 /* build a list of all tracks, including children */
8016 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
8018 TimeAxisView::Children c = (*i)->get_child_list ();
8019 for (TimeAxisView::Children::iterator j = c.begin(); j != c.end(); ++j) {
8020 all.push_back (j->get());
8025 // find selection range.
8026 // if someone knows how to user TrackViewList::iterator for this
8028 int selected_top = -1;
8029 int selected_bottom = -1;
8031 for (TrackViewList::iterator t = all.begin(); t != all.end(); ++t, ++i) {
8032 if ((*t)->marked_for_display ()) {
8033 if (tracks.contains(*t)) {
8034 if (selected_top == -1) {
8037 selected_bottom = i;
8043 for (TrackViewList::iterator t = all.begin(); t != all.end(); ++t, ++i) {
8044 if ((*t)->marked_for_display ()) {
8045 if (tracks.contains(*t)) {
8046 (*t)->set_height (h);
8047 first_y_pos = std::min ((*t)->y_position (), first_y_pos);
8049 if (i > selected_top && i < selected_bottom) {
8050 hide_track_in_display (*t);
8057 set the controls_layout height now, because waiting for its size
8058 request signal handler will cause the vertical adjustment setting to fail
8061 controls_layout.property_height () = _full_canvas_height;
8062 vertical_adjustment.set_value (first_y_pos);
8064 redo_visual_stack.push_back (current_visual_state (true));
8066 visible_tracks_selector.set_text (_("Sel"));
8070 Editor::save_visual_state (uint32_t n)
8072 while (visual_states.size() <= n) {
8073 visual_states.push_back (0);
8076 if (visual_states[n] != 0) {
8077 delete visual_states[n];
8080 visual_states[n] = current_visual_state (true);
8085 Editor::goto_visual_state (uint32_t n)
8087 if (visual_states.size() <= n) {
8091 if (visual_states[n] == 0) {
8095 use_visual_state (*visual_states[n]);
8099 Editor::start_visual_state_op (uint32_t n)
8101 save_visual_state (n);
8103 PopUp* pup = new PopUp (WIN_POS_MOUSE, 1000, true);
8105 snprintf (buf, sizeof (buf), _("Saved view %u"), n+1);
8106 pup->set_text (buf);
8111 Editor::cancel_visual_state_op (uint32_t n)
8113 goto_visual_state (n);
8117 Editor::toggle_region_mute ()
8119 if (_ignore_region_action) {
8123 RegionSelection rs = get_regions_from_selection_and_entered ();
8129 if (rs.size() > 1) {
8130 begin_reversible_command (_("mute regions"));
8132 begin_reversible_command (_("mute region"));
8135 for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
8137 (*i)->region()->playlist()->clear_changes ();
8138 (*i)->region()->set_muted (!(*i)->region()->muted ());
8139 _session->add_command (new StatefulDiffCommand ((*i)->region()));
8143 commit_reversible_command ();
8147 Editor::combine_regions ()
8149 /* foreach track with selected regions, take all selected regions
8150 and join them into a new region containing the subregions (as a
8154 typedef set<RouteTimeAxisView*> RTVS;
8157 if (selection->regions.empty()) {
8161 for (RegionSelection::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
8162 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*>(&(*i)->get_time_axis_view());
8165 tracks.insert (rtv);
8169 begin_reversible_command (_("combine regions"));
8171 vector<RegionView*> new_selection;
8173 for (RTVS::iterator i = tracks.begin(); i != tracks.end(); ++i) {
8176 if ((rv = (*i)->combine_regions ()) != 0) {
8177 new_selection.push_back (rv);
8181 selection->clear_regions ();
8182 for (vector<RegionView*>::iterator i = new_selection.begin(); i != new_selection.end(); ++i) {
8183 selection->add (*i);
8186 commit_reversible_command ();
8190 Editor::uncombine_regions ()
8192 typedef set<RouteTimeAxisView*> RTVS;
8195 if (selection->regions.empty()) {
8199 for (RegionSelection::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
8200 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*>(&(*i)->get_time_axis_view());
8203 tracks.insert (rtv);
8207 begin_reversible_command (_("uncombine regions"));
8209 for (RTVS::iterator i = tracks.begin(); i != tracks.end(); ++i) {
8210 (*i)->uncombine_regions ();
8213 commit_reversible_command ();
8217 Editor::toggle_midi_input_active (bool flip_others)
8220 boost::shared_ptr<RouteList> rl (new RouteList);
8222 for (TrackSelection::iterator i = selection->tracks.begin(); i != selection->tracks.end(); ++i) {
8223 RouteTimeAxisView *rtav = dynamic_cast<RouteTimeAxisView *>(*i);
8229 boost::shared_ptr<MidiTrack> mt = rtav->midi_track();
8232 rl->push_back (rtav->route());
8233 onoff = !mt->input_active();
8237 _session->set_exclusive_input_active (rl, onoff, flip_others);
8240 static bool ok_fine (GdkEventAny*) { return true; }
8246 lock_dialog = new Gtk::Dialog (string_compose (_("%1: Locked"), PROGRAM_NAME), true);
8248 Gtk::Image* padlock = manage (new Gtk::Image (ARDOUR_UI_UTILS::get_icon ("padlock_closed")));
8249 lock_dialog->get_vbox()->pack_start (*padlock);
8250 lock_dialog->signal_delete_event ().connect (sigc::ptr_fun (ok_fine));
8252 ArdourButton* b = manage (new ArdourButton);
8253 b->set_name ("lock button");
8254 b->set_text (_("Click to unlock"));
8255 b->signal_clicked.connect (sigc::mem_fun (*this, &Editor::unlock));
8256 lock_dialog->get_vbox()->pack_start (*b);
8258 lock_dialog->get_vbox()->show_all ();
8259 lock_dialog->set_size_request (200, 200);
8262 delete _main_menu_disabler;
8263 _main_menu_disabler = new MainMenuDisabler;
8265 lock_dialog->present ();
8267 lock_dialog->get_window()->set_decorations (Gdk::WMDecoration (0));
8273 lock_dialog->hide ();
8275 delete _main_menu_disabler;
8276 _main_menu_disabler = 0;
8278 if (UIConfiguration::instance().get_lock_gui_after_seconds()) {
8279 start_lock_event_timing ();
8284 Editor::bring_in_callback (Gtk::Label* label, uint32_t n, uint32_t total, string name)
8286 Gtkmm2ext::UI::instance()->call_slot (invalidator (*this), boost::bind (&Editor::update_bring_in_message, this, label, n, total, name));
8290 Editor::update_bring_in_message (Gtk::Label* label, uint32_t n, uint32_t total, string name)
8292 Timers::TimerSuspender t;
8293 label->set_text (string_compose ("Copying %1, %2 of %3", name, n, total));
8294 Gtkmm2ext::UI::instance()->flush_pending (1);
8298 Editor::bring_all_sources_into_session ()
8305 ArdourDialog w (_("Moving embedded files into session folder"));
8306 w.get_vbox()->pack_start (msg);
8309 /* flush all pending GUI events because we're about to start copying
8313 Timers::TimerSuspender t;
8314 Gtkmm2ext::UI::instance()->flush_pending (3);
8318 _session->bring_all_sources_into_session (boost::bind (&Editor::bring_in_callback, this, &msg, _1, _2, _3));