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 /* if no snap selections are set, boundary cache should be left empty */
745 if ( interesting_points.empty() ) {
749 TimeAxisView *ontrack = 0;
752 tlist = track_views.filter_to_unique_playlists ();
754 if (maybe_first_sample) {
755 TrackViewList::const_iterator i;
756 for (i = tlist.begin(); i != tlist.end(); ++i) {
757 boost::shared_ptr<Playlist> pl = (*i)->playlist();
758 if (pl && pl->count_regions_at (0)) {
759 region_boundary_cache.push_back (0);
765 std::pair<samplepos_t, samplepos_t> ext = session_gui_extents (false);
766 samplepos_t session_end = ext.second;
768 while (pos < session_end && !at_end) {
771 samplepos_t lpos = session_end;
773 for (vector<RegionPoint>::iterator p = interesting_points.begin(); p != interesting_points.end(); ++p) {
775 if ((r = find_next_region (pos, *p, 1, tlist, &ontrack)) == 0) {
776 if (*p == interesting_points.back()) {
779 /* move to next point type */
785 rpos = r->first_sample();
789 rpos = r->last_sample();
793 rpos = r->sync_position ();
804 /* prevent duplicates, but we don't use set<> because we want to be able
808 vector<samplepos_t>::iterator ri;
810 for (ri = region_boundary_cache.begin(); ri != region_boundary_cache.end(); ++ri) {
816 if (ri == region_boundary_cache.end()) {
817 region_boundary_cache.push_back (rpos);
824 /* finally sort to be sure that the order is correct */
826 sort (region_boundary_cache.begin(), region_boundary_cache.end());
828 _region_boundary_cache_dirty = false;
831 boost::shared_ptr<Region>
832 Editor::find_next_region (samplepos_t sample, RegionPoint point, int32_t dir, TrackViewList& tracks, TimeAxisView **ontrack)
834 TrackViewList::iterator i;
835 samplepos_t closest = max_samplepos;
836 boost::shared_ptr<Region> ret;
837 samplepos_t rpos = 0;
839 samplepos_t track_sample;
841 for (i = tracks.begin(); i != tracks.end(); ++i) {
843 samplecnt_t distance;
844 boost::shared_ptr<Region> r;
846 track_sample = sample;
848 if ((r = (*i)->find_next_region (track_sample, point, dir)) == 0) {
854 rpos = r->first_sample ();
858 rpos = r->last_sample ();
862 rpos = r->sync_position ();
867 distance = rpos - sample;
869 distance = sample - rpos;
872 if (distance < closest) {
884 Editor::find_next_region_boundary (samplepos_t pos, int32_t dir, const TrackViewList& tracks)
886 samplecnt_t distance = max_samplepos;
887 samplepos_t current_nearest = -1;
889 for (TrackViewList::const_iterator i = tracks.begin(); i != tracks.end(); ++i) {
890 samplepos_t contender;
893 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (*i);
899 if ((contender = rtv->find_next_region_boundary (pos, dir)) < 0) {
903 d = ::llabs (pos - contender);
906 current_nearest = contender;
911 return current_nearest;
915 Editor::get_region_boundary (samplepos_t pos, int32_t dir, bool with_selection, bool only_onscreen)
920 if (with_selection && Config->get_region_boundaries_from_selected_tracks()) {
922 if (!selection->tracks.empty()) {
924 target = find_next_region_boundary (pos, dir, selection->tracks);
928 if (only_onscreen || Config->get_region_boundaries_from_onscreen_tracks()) {
929 get_onscreen_tracks (tvl);
930 target = find_next_region_boundary (pos, dir, tvl);
932 target = find_next_region_boundary (pos, dir, track_views);
938 if (only_onscreen || Config->get_region_boundaries_from_onscreen_tracks()) {
939 get_onscreen_tracks (tvl);
940 target = find_next_region_boundary (pos, dir, tvl);
942 target = find_next_region_boundary (pos, dir, track_views);
950 Editor::cursor_to_region_boundary (bool with_selection, int32_t dir)
952 samplepos_t pos = playhead_cursor->current_sample ();
959 // so we don't find the current region again..
960 if (dir > 0 || pos > 0) {
964 if ((target = get_region_boundary (pos, dir, with_selection, false)) < 0) {
968 _session->request_locate (target);
972 Editor::cursor_to_next_region_boundary (bool with_selection)
974 cursor_to_region_boundary (with_selection, 1);
978 Editor::cursor_to_previous_region_boundary (bool with_selection)
980 cursor_to_region_boundary (with_selection, -1);
984 Editor::cursor_to_region_point (EditorCursor* cursor, RegionPoint point, int32_t dir)
986 boost::shared_ptr<Region> r;
987 samplepos_t pos = cursor->current_sample ();
993 TimeAxisView *ontrack = 0;
995 // so we don't find the current region again..
999 if (!selection->tracks.empty()) {
1001 r = find_next_region (pos, point, dir, selection->tracks, &ontrack);
1003 } else if (clicked_axisview) {
1006 t.push_back (clicked_axisview);
1008 r = find_next_region (pos, point, dir, t, &ontrack);
1012 r = find_next_region (pos, point, dir, track_views, &ontrack);
1021 pos = r->first_sample ();
1025 pos = r->last_sample ();
1029 pos = r->sync_position ();
1033 if (cursor == playhead_cursor) {
1034 _session->request_locate (pos);
1036 cursor->set_position (pos);
1041 Editor::cursor_to_next_region_point (EditorCursor* cursor, RegionPoint point)
1043 cursor_to_region_point (cursor, point, 1);
1047 Editor::cursor_to_previous_region_point (EditorCursor* cursor, RegionPoint point)
1049 cursor_to_region_point (cursor, point, -1);
1053 Editor::cursor_to_selection_start (EditorCursor *cursor)
1055 samplepos_t pos = 0;
1057 switch (mouse_mode) {
1059 if (!selection->regions.empty()) {
1060 pos = selection->regions.start();
1065 if (!selection->time.empty()) {
1066 pos = selection->time.start ();
1074 if (cursor == playhead_cursor) {
1075 _session->request_locate (pos);
1077 cursor->set_position (pos);
1082 Editor::cursor_to_selection_end (EditorCursor *cursor)
1084 samplepos_t pos = 0;
1086 switch (mouse_mode) {
1088 if (!selection->regions.empty()) {
1089 pos = selection->regions.end_sample();
1094 if (!selection->time.empty()) {
1095 pos = selection->time.end_sample ();
1103 if (cursor == playhead_cursor) {
1104 _session->request_locate (pos);
1106 cursor->set_position (pos);
1111 Editor::selected_marker_to_region_boundary (bool with_selection, int32_t dir)
1121 if (selection->markers.empty()) {
1125 if (!mouse_sample (mouse, ignored)) {
1129 add_location_mark (mouse);
1132 if ((loc = find_location_from_marker (selection->markers.front(), ignored)) == 0) {
1136 samplepos_t pos = loc->start();
1138 // so we don't find the current region again..
1139 if (dir > 0 || pos > 0) {
1143 if ((target = get_region_boundary (pos, dir, with_selection, false)) < 0) {
1147 loc->move_to (target, 0);
1151 Editor::selected_marker_to_next_region_boundary (bool with_selection)
1153 selected_marker_to_region_boundary (with_selection, 1);
1157 Editor::selected_marker_to_previous_region_boundary (bool with_selection)
1159 selected_marker_to_region_boundary (with_selection, -1);
1163 Editor::selected_marker_to_region_point (RegionPoint point, int32_t dir)
1165 boost::shared_ptr<Region> r;
1170 if (!_session || selection->markers.empty()) {
1174 if ((loc = find_location_from_marker (selection->markers.front(), ignored)) == 0) {
1178 TimeAxisView *ontrack = 0;
1182 // so we don't find the current region again..
1186 if (!selection->tracks.empty()) {
1188 r = find_next_region (pos, point, dir, selection->tracks, &ontrack);
1192 r = find_next_region (pos, point, dir, track_views, &ontrack);
1201 pos = r->first_sample ();
1205 pos = r->last_sample ();
1209 pos = r->adjust_to_sync (r->first_sample());
1213 loc->move_to (pos, 0);
1217 Editor::selected_marker_to_next_region_point (RegionPoint point)
1219 selected_marker_to_region_point (point, 1);
1223 Editor::selected_marker_to_previous_region_point (RegionPoint point)
1225 selected_marker_to_region_point (point, -1);
1229 Editor::selected_marker_to_selection_start ()
1231 samplepos_t pos = 0;
1235 if (!_session || selection->markers.empty()) {
1239 if ((loc = find_location_from_marker (selection->markers.front(), ignored)) == 0) {
1243 switch (mouse_mode) {
1245 if (!selection->regions.empty()) {
1246 pos = selection->regions.start();
1251 if (!selection->time.empty()) {
1252 pos = selection->time.start ();
1260 loc->move_to (pos, 0);
1264 Editor::selected_marker_to_selection_end ()
1266 samplepos_t pos = 0;
1270 if (!_session || selection->markers.empty()) {
1274 if ((loc = find_location_from_marker (selection->markers.front(), ignored)) == 0) {
1278 switch (mouse_mode) {
1280 if (!selection->regions.empty()) {
1281 pos = selection->regions.end_sample();
1286 if (!selection->time.empty()) {
1287 pos = selection->time.end_sample ();
1295 loc->move_to (pos, 0);
1299 Editor::scroll_playhead (bool forward)
1301 samplepos_t pos = playhead_cursor->current_sample ();
1302 samplecnt_t delta = (samplecnt_t) floor (current_page_samples() / 0.8);
1305 if (pos == max_samplepos) {
1309 if (pos < max_samplepos - delta) {
1312 pos = max_samplepos;
1328 _session->request_locate (pos);
1332 Editor::cursor_align (bool playhead_to_edit)
1338 if (playhead_to_edit) {
1340 if (selection->markers.empty()) {
1344 _session->request_locate (selection->markers.front()->position(), _session->transport_rolling());
1347 const int32_t divisions = get_grid_music_divisions (0);
1348 /* move selected markers to playhead */
1350 for (MarkerSelection::iterator i = selection->markers.begin(); i != selection->markers.end(); ++i) {
1353 Location* loc = find_location_from_marker (*i, ignored);
1355 if (loc->is_mark()) {
1356 loc->set_start (playhead_cursor->current_sample (), false, true, divisions);
1358 loc->set (playhead_cursor->current_sample (),
1359 playhead_cursor->current_sample () + loc->length(), true, divisions);
1366 Editor::scroll_backward (float pages)
1368 samplepos_t const one_page = (samplepos_t) rint (_visible_canvas_width * samples_per_pixel);
1369 samplepos_t const cnt = (samplepos_t) floor (pages * one_page);
1372 if (_leftmost_sample < cnt) {
1375 sample = _leftmost_sample - cnt;
1378 reset_x_origin (sample);
1382 Editor::scroll_forward (float pages)
1384 samplepos_t const one_page = (samplepos_t) rint (_visible_canvas_width * samples_per_pixel);
1385 samplepos_t const cnt = (samplepos_t) floor (pages * one_page);
1388 if (max_samplepos - cnt < _leftmost_sample) {
1389 sample = max_samplepos - cnt;
1391 sample = _leftmost_sample + cnt;
1394 reset_x_origin (sample);
1398 Editor::scroll_tracks_down ()
1400 double vert_value = vertical_adjustment.get_value() + vertical_adjustment.get_page_size();
1401 if (vert_value > vertical_adjustment.get_upper() - _visible_canvas_height) {
1402 vert_value = vertical_adjustment.get_upper() - _visible_canvas_height;
1405 vertical_adjustment.set_value (vert_value);
1409 Editor::scroll_tracks_up ()
1411 vertical_adjustment.set_value (vertical_adjustment.get_value() - vertical_adjustment.get_page_size());
1415 Editor::scroll_tracks_down_line ()
1417 double vert_value = vertical_adjustment.get_value() + 60;
1419 if (vert_value > vertical_adjustment.get_upper() - _visible_canvas_height) {
1420 vert_value = vertical_adjustment.get_upper() - _visible_canvas_height;
1423 vertical_adjustment.set_value (vert_value);
1427 Editor::scroll_tracks_up_line ()
1429 reset_y_origin (vertical_adjustment.get_value() - 60);
1433 Editor::select_topmost_track ()
1435 const double top_of_trackviews = vertical_adjustment.get_value();
1436 for (TrackViewList::iterator t = track_views.begin(); t != track_views.end(); ++t) {
1437 if ((*t)->hidden()) {
1440 std::pair<TimeAxisView*,double> res = (*t)->covers_y_position (top_of_trackviews);
1442 selection->set (*t);
1449 Editor::scroll_down_one_track (bool skip_child_views)
1451 TrackViewList::reverse_iterator next = track_views.rend();
1452 const double top_of_trackviews = vertical_adjustment.get_value();
1454 for (TrackViewList::reverse_iterator t = track_views.rbegin(); t != track_views.rend(); ++t) {
1455 if ((*t)->hidden()) {
1459 /* If this is the upper-most visible trackview, we want to display
1460 * the one above it (next)
1462 * Note that covers_y_position() is recursive and includes child views
1464 std::pair<TimeAxisView*,double> res = (*t)->covers_y_position (top_of_trackviews);
1467 if (skip_child_views) {
1470 /* automation lane (one level, non-recursive)
1472 * - if no automation lane exists -> move to next tack
1473 * - if the first (here: bottom-most) matches -> move to next tack
1474 * - if no y-axis match is found -> the current track is at the top
1475 * -> move to last (here: top-most) automation lane
1477 TimeAxisView::Children kids = (*t)->get_child_list();
1478 TimeAxisView::Children::reverse_iterator nkid = kids.rend();
1480 for (TimeAxisView::Children::reverse_iterator ci = kids.rbegin(); ci != kids.rend(); ++ci) {
1481 if ((*ci)->hidden()) {
1485 std::pair<TimeAxisView*,double> dev;
1486 dev = (*ci)->covers_y_position (top_of_trackviews);
1488 /* some automation lane is currently at the top */
1489 if (ci == kids.rbegin()) {
1490 /* first (bottom-most) autmation lane is at the top.
1491 * -> move to next track
1500 if (nkid != kids.rend()) {
1501 ensure_time_axis_view_is_visible (**nkid, true);
1509 /* move to the track below the first one that covers the */
1511 if (next != track_views.rend()) {
1512 ensure_time_axis_view_is_visible (**next, true);
1520 Editor::scroll_up_one_track (bool skip_child_views)
1522 TrackViewList::iterator prev = track_views.end();
1523 double top_of_trackviews = vertical_adjustment.get_value ();
1525 for (TrackViewList::iterator t = track_views.begin(); t != track_views.end(); ++t) {
1527 if ((*t)->hidden()) {
1531 /* find the trackview at the top of the trackview group
1533 * Note that covers_y_position() is recursive and includes child views
1535 std::pair<TimeAxisView*,double> res = (*t)->covers_y_position (top_of_trackviews);
1538 if (skip_child_views) {
1541 /* automation lane (one level, non-recursive)
1543 * - if no automation lane exists -> move to prev tack
1544 * - if no y-axis match is found -> the current track is at the top -> move to prev track
1545 * (actually last automation lane of previous track, see below)
1546 * - if first (top-most) lane is at the top -> move to this track
1547 * - else move up one lane
1549 TimeAxisView::Children kids = (*t)->get_child_list();
1550 TimeAxisView::Children::iterator pkid = kids.end();
1552 for (TimeAxisView::Children::iterator ci = kids.begin(); ci != kids.end(); ++ci) {
1553 if ((*ci)->hidden()) {
1557 std::pair<TimeAxisView*,double> dev;
1558 dev = (*ci)->covers_y_position (top_of_trackviews);
1560 /* some automation lane is currently at the top */
1561 if (ci == kids.begin()) {
1562 /* first (top-most) autmation lane is at the top.
1563 * jump directly to this track's top
1565 ensure_time_axis_view_is_visible (**t, true);
1568 else if (pkid != kids.end()) {
1569 /* some other automation lane is at the top.
1570 * move up to prev automation lane.
1572 ensure_time_axis_view_is_visible (**pkid, true);
1575 assert(0); // not reached
1586 if (prev != track_views.end()) {
1587 // move to bottom-most automation-lane of the previous track
1588 TimeAxisView::Children kids = (*prev)->get_child_list();
1589 TimeAxisView::Children::reverse_iterator pkid = kids.rend();
1590 if (!skip_child_views) {
1591 // find the last visible lane
1592 for (TimeAxisView::Children::reverse_iterator ci = kids.rbegin(); ci != kids.rend(); ++ci) {
1593 if (!(*ci)->hidden()) {
1599 if (pkid != kids.rend()) {
1600 ensure_time_axis_view_is_visible (**pkid, true);
1602 ensure_time_axis_view_is_visible (**prev, true);
1611 Editor::scroll_left_step ()
1613 samplepos_t xdelta = (current_page_samples() / 8);
1615 if (_leftmost_sample > xdelta) {
1616 reset_x_origin (_leftmost_sample - xdelta);
1624 Editor::scroll_right_step ()
1626 samplepos_t xdelta = (current_page_samples() / 8);
1628 if (max_samplepos - xdelta > _leftmost_sample) {
1629 reset_x_origin (_leftmost_sample + xdelta);
1631 reset_x_origin (max_samplepos - current_page_samples());
1636 Editor::scroll_left_half_page ()
1638 samplepos_t xdelta = (current_page_samples() / 2);
1639 if (_leftmost_sample > xdelta) {
1640 reset_x_origin (_leftmost_sample - xdelta);
1647 Editor::scroll_right_half_page ()
1649 samplepos_t xdelta = (current_page_samples() / 2);
1650 if (max_samplepos - xdelta > _leftmost_sample) {
1651 reset_x_origin (_leftmost_sample + xdelta);
1653 reset_x_origin (max_samplepos - current_page_samples());
1660 Editor::tav_zoom_step (bool coarser)
1662 DisplaySuspender ds;
1666 if (selection->tracks.empty()) {
1669 ts = &selection->tracks;
1672 for (TrackViewList::iterator i = ts->begin(); i != ts->end(); ++i) {
1673 TimeAxisView *tv = (static_cast<TimeAxisView*>(*i));
1674 tv->step_height (coarser);
1679 Editor::tav_zoom_smooth (bool coarser, bool force_all)
1681 DisplaySuspender ds;
1685 if (selection->tracks.empty() || force_all) {
1688 ts = &selection->tracks;
1691 for (TrackViewList::iterator i = ts->begin(); i != ts->end(); ++i) {
1692 TimeAxisView *tv = (static_cast<TimeAxisView*>(*i));
1693 uint32_t h = tv->current_height ();
1698 if (h >= TimeAxisView::preset_height (HeightSmall)) {
1703 tv->set_height (h + 5);
1709 Editor::temporal_zoom_step_mouse_focus_scale (bool zoom_out, double scale)
1711 Editing::ZoomFocus temp_focus = zoom_focus;
1712 zoom_focus = Editing::ZoomFocusMouse;
1713 temporal_zoom_step_scale (zoom_out, scale);
1714 zoom_focus = temp_focus;
1718 Editor::temporal_zoom_step_mouse_focus (bool zoom_out)
1720 temporal_zoom_step_mouse_focus_scale (zoom_out, 2.0);
1724 Editor::temporal_zoom_step (bool zoom_out)
1726 temporal_zoom_step_scale (zoom_out, 2.0);
1730 Editor::temporal_zoom_step_scale (bool zoom_out, double scale)
1732 ENSURE_GUI_THREAD (*this, &Editor::temporal_zoom_step, zoom_out, scale)
1734 samplecnt_t nspp = samples_per_pixel;
1738 if (nspp == samples_per_pixel) {
1743 if (nspp == samples_per_pixel) {
1748 //zoom-behavior-tweaks
1749 //limit our maximum zoom to the session gui extents value
1750 std::pair<samplepos_t, samplepos_t> ext = session_gui_extents();
1751 samplecnt_t session_extents_pp = (ext.second - ext.first) / _visible_canvas_width;
1752 if (nspp > session_extents_pp)
1753 nspp = session_extents_pp;
1755 temporal_zoom (nspp);
1759 Editor::temporal_zoom (samplecnt_t fpp)
1765 samplepos_t current_page = current_page_samples();
1766 samplepos_t current_leftmost = _leftmost_sample;
1767 samplepos_t current_rightmost;
1768 samplepos_t current_center;
1769 samplepos_t new_page_size;
1770 samplepos_t half_page_size;
1771 samplepos_t leftmost_after_zoom = 0;
1773 bool in_track_canvas;
1774 bool use_mouse_sample = true;
1778 if (fpp == samples_per_pixel) {
1782 // Imposing an arbitrary limit to zoom out as too much zoom out produces
1783 // segfaults for lack of memory. If somebody decides this is not high enough I
1784 // believe it can be raisen to higher values but some limit must be in place.
1786 // This constant represents 1 day @ 48kHz on a 1600 pixel wide display
1787 // all of which is used for the editor track displays. The whole day
1788 // would be 4147200000 samples, so 2592000 samples per pixel.
1790 nfpp = min (fpp, (samplecnt_t) 2592000);
1791 nfpp = max ((samplecnt_t) 1, nfpp);
1793 new_page_size = (samplepos_t) floor (_visible_canvas_width * nfpp);
1794 half_page_size = new_page_size / 2;
1796 switch (zoom_focus) {
1798 leftmost_after_zoom = current_leftmost;
1801 case ZoomFocusRight:
1802 current_rightmost = _leftmost_sample + current_page;
1803 if (current_rightmost < new_page_size) {
1804 leftmost_after_zoom = 0;
1806 leftmost_after_zoom = current_rightmost - new_page_size;
1810 case ZoomFocusCenter:
1811 current_center = current_leftmost + (current_page/2);
1812 if (current_center < half_page_size) {
1813 leftmost_after_zoom = 0;
1815 leftmost_after_zoom = current_center - half_page_size;
1819 case ZoomFocusPlayhead:
1820 /* centre playhead */
1821 l = playhead_cursor->current_sample () - (new_page_size * 0.5);
1824 leftmost_after_zoom = 0;
1825 } else if (l > max_samplepos) {
1826 leftmost_after_zoom = max_samplepos - new_page_size;
1828 leftmost_after_zoom = (samplepos_t) l;
1832 case ZoomFocusMouse:
1833 /* try to keep the mouse over the same point in the display */
1835 if (_drags->active()) {
1836 where = _drags->current_pointer_sample ();
1837 } else if (!mouse_sample (where, in_track_canvas)) {
1838 use_mouse_sample = false;
1841 if (use_mouse_sample) {
1842 l = - ((new_page_size * ((where - current_leftmost)/(double)current_page)) - where);
1845 leftmost_after_zoom = 0;
1846 } else if (l > max_samplepos) {
1847 leftmost_after_zoom = max_samplepos - new_page_size;
1849 leftmost_after_zoom = (samplepos_t) l;
1852 /* use playhead instead */
1853 where = playhead_cursor->current_sample ();
1855 if (where < half_page_size) {
1856 leftmost_after_zoom = 0;
1858 leftmost_after_zoom = where - half_page_size;
1864 /* try to keep the edit point in the same place */
1865 where = get_preferred_edit_position ();
1869 double l = - ((new_page_size * ((where - current_leftmost)/(double)current_page)) - where);
1872 leftmost_after_zoom = 0;
1873 } else if (l > max_samplepos) {
1874 leftmost_after_zoom = max_samplepos - new_page_size;
1876 leftmost_after_zoom = (samplepos_t) l;
1880 /* edit point not defined */
1887 // leftmost_after_zoom = min (leftmost_after_zoom, _session->current_end_sample());
1889 reposition_and_zoom (leftmost_after_zoom, nfpp);
1893 Editor::calc_extra_zoom_edges(samplepos_t &start, samplepos_t &end)
1895 /* this func helps make sure we leave a little space
1896 at each end of the editor so that the zoom doesn't fit the region
1897 precisely to the screen.
1900 GdkScreen* screen = gdk_screen_get_default ();
1901 const gint pixwidth = gdk_screen_get_width (screen);
1902 const gint mmwidth = gdk_screen_get_width_mm (screen);
1903 const double pix_per_mm = (double) pixwidth/ (double) mmwidth;
1904 const double one_centimeter_in_pixels = pix_per_mm * 10.0;
1906 const samplepos_t range = end - start;
1907 const samplecnt_t new_fpp = (samplecnt_t) ceil ((double) range / (double) _visible_canvas_width);
1908 const samplepos_t extra_samples = (samplepos_t) floor (one_centimeter_in_pixels * new_fpp);
1910 if (start > extra_samples) {
1911 start -= extra_samples;
1916 if (max_samplepos - extra_samples > end) {
1917 end += extra_samples;
1919 end = max_samplepos;
1924 Editor::get_selection_extents (samplepos_t &start, samplepos_t &end) const
1926 start = max_samplepos;
1930 //ToDo: if notes are selected, set extents to that selection
1932 //ToDo: if control points are selected, set extents to that selection
1934 if (!selection->regions.empty()) {
1935 RegionSelection rs = get_regions_from_selection_and_entered ();
1937 for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
1939 if ((*i)->region()->position() < start) {
1940 start = (*i)->region()->position();
1943 if ((*i)->region()->last_sample() + 1 > end) {
1944 end = (*i)->region()->last_sample() + 1;
1948 } else if (!selection->time.empty()) {
1949 start = selection->time.start();
1950 end = selection->time.end_sample();
1952 ret = false; //no selection found
1955 if ((start == 0 && end == 0) || end < start) {
1964 Editor::temporal_zoom_selection (Editing::ZoomAxis axes)
1966 if (!selection) return;
1968 if (selection->regions.empty() && selection->time.empty()) {
1969 if (axes == Horizontal || axes == Both) {
1970 temporal_zoom_step(true);
1972 if (axes == Vertical || axes == Both) {
1973 if (!track_views.empty()) {
1977 //implicit hack: by extending the top & bottom check outside the current view limits, we include the trackviews immediately above & below what is visible
1978 const double top = vertical_adjustment.get_value() - 10;
1979 const double btm = top + _visible_canvas_height + 10;
1981 for (TrackViewList::iterator iter = track_views.begin(); iter != track_views.end(); ++iter) {
1982 if ((*iter)->covered_by_y_range (top, btm)) {
1983 tvl.push_back(*iter);
1993 //ToDo: if notes are selected, zoom to that
1995 //ToDo: if control points are selected, zoom to that
1997 if (axes == Horizontal || axes == Both) {
1999 samplepos_t start, end;
2000 if (get_selection_extents (start, end)) {
2001 calc_extra_zoom_edges (start, end);
2002 temporal_zoom_by_sample (start, end);
2006 if (axes == Vertical || axes == Both) {
2010 //normally, we don't do anything "automatic" to the user's selection.
2011 //but in this case, we will clear the selection after a zoom-to-selection.
2016 Editor::temporal_zoom_session ()
2018 ENSURE_GUI_THREAD (*this, &Editor::temporal_zoom_session)
2021 samplecnt_t start = _session->current_start_sample();
2022 samplecnt_t end = _session->current_end_sample();
2024 if (_session->actively_recording ()) {
2025 samplepos_t cur = playhead_cursor->current_sample ();
2027 /* recording beyond the end marker; zoom out
2028 * by 5 seconds more so that if 'follow
2029 * playhead' is active we don't immediately
2032 end = cur + _session->sample_rate() * 5;
2036 if ((start == 0 && end == 0) || end < start) {
2040 calc_extra_zoom_edges(start, end);
2042 temporal_zoom_by_sample (start, end);
2047 Editor::temporal_zoom_extents ()
2049 ENSURE_GUI_THREAD (*this, &Editor::temporal_zoom_extents)
2052 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
2054 samplecnt_t start = ext.first;
2055 samplecnt_t end = ext.second;
2057 if (_session->actively_recording ()) {
2058 samplepos_t cur = playhead_cursor->current_sample ();
2060 /* recording beyond the end marker; zoom out
2061 * by 5 seconds more so that if 'follow
2062 * playhead' is active we don't immediately
2065 end = cur + _session->sample_rate() * 5;
2069 if ((start == 0 && end == 0) || end < start) {
2073 calc_extra_zoom_edges(start, end);
2075 temporal_zoom_by_sample (start, end);
2080 Editor::temporal_zoom_by_sample (samplepos_t start, samplepos_t end)
2082 if (!_session) return;
2084 if ((start == 0 && end == 0) || end < start) {
2088 samplepos_t range = end - start;
2090 const samplecnt_t new_fpp = (samplecnt_t) ceil ((double) range / (double) _visible_canvas_width);
2092 samplepos_t new_page = range;
2093 samplepos_t middle = (samplepos_t) floor ((double) start + ((double) range / 2.0f));
2094 samplepos_t new_leftmost = (samplepos_t) floor ((double) middle - ((double) new_page / 2.0f));
2096 if (new_leftmost > middle) {
2100 if (new_leftmost < 0) {
2104 reposition_and_zoom (new_leftmost, new_fpp);
2108 Editor::temporal_zoom_to_sample (bool coarser, samplepos_t sample)
2114 samplecnt_t range_before = sample - _leftmost_sample;
2115 samplecnt_t new_spp;
2118 if (samples_per_pixel <= 1) {
2121 new_spp = samples_per_pixel + (samples_per_pixel/2);
2123 range_before += range_before/2;
2125 if (samples_per_pixel >= 1) {
2126 new_spp = samples_per_pixel - (samples_per_pixel/2);
2128 /* could bail out here since we cannot zoom any finer,
2129 but leave that to the equality test below
2131 new_spp = samples_per_pixel;
2134 range_before -= range_before/2;
2137 if (new_spp == samples_per_pixel) {
2141 /* zoom focus is automatically taken as @param sample when this
2145 samplepos_t new_leftmost = sample - (samplepos_t)range_before;
2147 if (new_leftmost > sample) {
2151 if (new_leftmost < 0) {
2155 reposition_and_zoom (new_leftmost, new_spp);
2160 Editor::choose_new_marker_name(string &name) {
2162 if (!UIConfiguration::instance().get_name_new_markers()) {
2163 /* don't prompt user for a new name */
2167 Prompter dialog (true);
2169 dialog.set_prompt (_("New Name:"));
2171 dialog.set_title (_("New Location Marker"));
2173 dialog.set_name ("MarkNameWindow");
2174 dialog.set_size_request (250, -1);
2175 dialog.set_position (Gtk::WIN_POS_MOUSE);
2177 dialog.add_button (Stock::OK, RESPONSE_ACCEPT);
2178 dialog.set_initial_text (name);
2182 switch (dialog.run ()) {
2183 case RESPONSE_ACCEPT:
2189 dialog.get_result(name);
2196 Editor::add_location_from_selection ()
2200 if (selection->time.empty()) {
2204 if (_session == 0 || clicked_axisview == 0) {
2208 samplepos_t start = selection->time[clicked_selection].start;
2209 samplepos_t end = selection->time[clicked_selection].end;
2211 _session->locations()->next_available_name(rangename,"selection");
2212 Location *location = new Location (*_session, start, end, rangename, Location::IsRangeMarker, get_grid_music_divisions(0));
2214 begin_reversible_command (_("add marker"));
2216 XMLNode &before = _session->locations()->get_state();
2217 _session->locations()->add (location, true);
2218 XMLNode &after = _session->locations()->get_state();
2219 _session->add_command(new MementoCommand<Locations>(*(_session->locations()), &before, &after));
2221 commit_reversible_command ();
2225 Editor::add_location_mark (samplepos_t where)
2229 select_new_marker = true;
2231 _session->locations()->next_available_name(markername,"mark");
2232 if (!choose_new_marker_name(markername)) {
2235 Location *location = new Location (*_session, where, where, markername, Location::IsMark, get_grid_music_divisions (0));
2236 begin_reversible_command (_("add marker"));
2238 XMLNode &before = _session->locations()->get_state();
2239 _session->locations()->add (location, true);
2240 XMLNode &after = _session->locations()->get_state();
2241 _session->add_command(new MementoCommand<Locations>(*(_session->locations()), &before, &after));
2243 commit_reversible_command ();
2247 Editor::set_session_start_from_playhead ()
2253 if ((loc = _session->locations()->session_range_location()) == 0) { //should never happen
2254 _session->set_session_extents (_session->audible_sample(), _session->audible_sample());
2256 XMLNode &before = loc->get_state();
2258 _session->set_session_extents (_session->audible_sample(), loc->end());
2260 XMLNode &after = loc->get_state();
2262 begin_reversible_command (_("Set session start"));
2264 _session->add_command (new MementoCommand<Location>(*loc, &before, &after));
2266 commit_reversible_command ();
2271 Editor::set_session_end_from_playhead ()
2277 if ((loc = _session->locations()->session_range_location()) == 0) { //should never happen
2278 _session->set_session_extents (_session->audible_sample(), _session->audible_sample());
2280 XMLNode &before = loc->get_state();
2282 _session->set_session_extents (loc->start(), _session->audible_sample());
2284 XMLNode &after = loc->get_state();
2286 begin_reversible_command (_("Set session start"));
2288 _session->add_command (new MementoCommand<Location>(*loc, &before, &after));
2290 commit_reversible_command ();
2293 _session->set_end_is_free (false);
2298 Editor::toggle_location_at_playhead_cursor ()
2300 if (!do_remove_location_at_playhead_cursor())
2302 add_location_from_playhead_cursor();
2307 Editor::add_location_from_playhead_cursor ()
2309 add_location_mark (_session->audible_sample());
2313 Editor::do_remove_location_at_playhead_cursor ()
2315 bool removed = false;
2318 XMLNode &before = _session->locations()->get_state();
2320 //find location(s) at this time
2321 Locations::LocationList locs;
2322 _session->locations()->find_all_between (_session->audible_sample(), _session->audible_sample()+1, locs, Location::Flags(0));
2323 for (Locations::LocationList::iterator i = locs.begin(); i != locs.end(); ++i) {
2324 if ((*i)->is_mark()) {
2325 _session->locations()->remove (*i);
2332 begin_reversible_command (_("remove marker"));
2333 XMLNode &after = _session->locations()->get_state();
2334 _session->add_command(new MementoCommand<Locations>(*(_session->locations()), &before, &after));
2335 commit_reversible_command ();
2342 Editor::remove_location_at_playhead_cursor ()
2344 do_remove_location_at_playhead_cursor ();
2347 /** Add a range marker around each selected region */
2349 Editor::add_locations_from_region ()
2351 RegionSelection rs = get_regions_from_selection_and_entered ();
2356 bool commit = false;
2358 XMLNode &before = _session->locations()->get_state();
2360 for (RegionSelection::iterator i = rs.begin (); i != rs.end (); ++i) {
2362 boost::shared_ptr<Region> region = (*i)->region ();
2364 Location *location = new Location (*_session, region->position(), region->last_sample(), region->name(), Location::IsRangeMarker, 0);
2366 _session->locations()->add (location, true);
2371 begin_reversible_command (selection->regions.size () > 1 ? _("add markers") : _("add marker"));
2372 XMLNode &after = _session->locations()->get_state();
2373 _session->add_command (new MementoCommand<Locations>(*(_session->locations()), &before, &after));
2374 commit_reversible_command ();
2378 /** Add a single range marker around all selected regions */
2380 Editor::add_location_from_region ()
2382 RegionSelection rs = get_regions_from_selection_and_entered ();
2388 XMLNode &before = _session->locations()->get_state();
2392 if (rs.size() > 1) {
2393 _session->locations()->next_available_name(markername, "regions");
2395 RegionView* rv = *(rs.begin());
2396 boost::shared_ptr<Region> region = rv->region();
2397 markername = region->name();
2400 if (!choose_new_marker_name(markername)) {
2404 // single range spanning all selected
2405 Location *location = new Location (*_session, selection->regions.start(), selection->regions.end_sample(), markername, Location::IsRangeMarker, 0);
2406 _session->locations()->add (location, true);
2408 begin_reversible_command (_("add marker"));
2409 XMLNode &after = _session->locations()->get_state();
2410 _session->add_command (new MementoCommand<Locations>(*(_session->locations()), &before, &after));
2411 commit_reversible_command ();
2417 Editor::jump_forward_to_mark ()
2423 samplepos_t pos = _session->locations()->first_mark_after (playhead_cursor->current_sample());
2429 _session->request_locate (pos, _session->transport_rolling());
2433 Editor::jump_backward_to_mark ()
2439 samplepos_t pos = _session->locations()->first_mark_before (playhead_cursor->current_sample());
2441 //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...
2442 if (_session->transport_rolling()) {
2443 if ((playhead_cursor->current_sample() - pos) < _session->sample_rate()/2) {
2444 samplepos_t prior = _session->locations()->first_mark_before (pos);
2453 _session->request_locate (pos, _session->transport_rolling());
2459 samplepos_t const pos = _session->audible_sample ();
2462 _session->locations()->next_available_name (markername, "mark");
2464 if (!choose_new_marker_name (markername)) {
2468 _session->locations()->add (new Location (*_session, pos, 0, markername, Location::IsMark, 0), true);
2472 Editor::clear_markers ()
2475 begin_reversible_command (_("clear markers"));
2477 XMLNode &before = _session->locations()->get_state();
2478 _session->locations()->clear_markers ();
2479 XMLNode &after = _session->locations()->get_state();
2480 _session->add_command(new MementoCommand<Locations>(*(_session->locations()), &before, &after));
2482 commit_reversible_command ();
2487 Editor::clear_ranges ()
2490 begin_reversible_command (_("clear ranges"));
2492 XMLNode &before = _session->locations()->get_state();
2494 _session->locations()->clear_ranges ();
2496 XMLNode &after = _session->locations()->get_state();
2497 _session->add_command(new MementoCommand<Locations>(*(_session->locations()), &before, &after));
2499 commit_reversible_command ();
2504 Editor::clear_locations ()
2506 begin_reversible_command (_("clear locations"));
2508 XMLNode &before = _session->locations()->get_state();
2509 _session->locations()->clear ();
2510 XMLNode &after = _session->locations()->get_state();
2511 _session->add_command(new MementoCommand<Locations>(*(_session->locations()), &before, &after));
2513 commit_reversible_command ();
2517 Editor::unhide_markers ()
2519 for (LocationMarkerMap::iterator i = location_markers.begin(); i != location_markers.end(); ++i) {
2520 Location *l = (*i).first;
2521 if (l->is_hidden() && l->is_mark()) {
2522 l->set_hidden(false, this);
2528 Editor::unhide_ranges ()
2530 for (LocationMarkerMap::iterator i = location_markers.begin(); i != location_markers.end(); ++i) {
2531 Location *l = (*i).first;
2532 if (l->is_hidden() && l->is_range_marker()) {
2533 l->set_hidden(false, this);
2538 /* INSERT/REPLACE */
2541 Editor::insert_region_list_selection (float times)
2543 RouteTimeAxisView *tv = 0;
2544 boost::shared_ptr<Playlist> playlist;
2546 if (clicked_routeview != 0) {
2547 tv = clicked_routeview;
2548 } else if (!selection->tracks.empty()) {
2549 if ((tv = dynamic_cast<RouteTimeAxisView*>(selection->tracks.front())) == 0) {
2552 } else if (entered_track != 0) {
2553 if ((tv = dynamic_cast<RouteTimeAxisView*>(entered_track)) == 0) {
2560 if ((playlist = tv->playlist()) == 0) {
2564 boost::shared_ptr<Region> region = _regions->get_single_selection ();
2569 begin_reversible_command (_("insert region"));
2570 playlist->clear_changes ();
2571 playlist->add_region ((RegionFactory::create (region, true)), get_preferred_edit_position(), times);
2572 if (Config->get_edit_mode() == Ripple)
2573 playlist->ripple (get_preferred_edit_position(), region->length() * times, boost::shared_ptr<Region>());
2575 _session->add_command(new StatefulDiffCommand (playlist));
2576 commit_reversible_command ();
2579 /* BUILT-IN EFFECTS */
2582 Editor::reverse_selection ()
2587 /* GAIN ENVELOPE EDITING */
2590 Editor::edit_envelope ()
2597 Editor::transition_to_rolling (bool fwd)
2603 if (_session->config.get_external_sync()) {
2604 switch (Config->get_sync_source()) {
2608 /* transport controlled by the master */
2613 if (_session->is_auditioning()) {
2614 _session->cancel_audition ();
2618 _session->request_transport_speed (fwd ? 1.0f : -1.0f);
2622 Editor::play_from_start ()
2624 _session->request_locate (_session->current_start_sample(), true);
2628 Editor::play_from_edit_point ()
2630 _session->request_locate (get_preferred_edit_position(), true);
2634 Editor::play_from_edit_point_and_return ()
2636 samplepos_t start_sample;
2637 samplepos_t return_sample;
2639 start_sample = get_preferred_edit_position (EDIT_IGNORE_PHEAD);
2641 if (_session->transport_rolling()) {
2642 _session->request_locate (start_sample, false);
2646 /* don't reset the return sample if its already set */
2648 if ((return_sample = _session->requested_return_sample()) < 0) {
2649 return_sample = _session->audible_sample();
2652 if (start_sample >= 0) {
2653 _session->request_roll_at_and_return (start_sample, return_sample);
2658 Editor::play_selection ()
2660 samplepos_t start, end;
2661 if (!get_selection_extents (start, end))
2664 AudioRange ar (start, end, 0);
2665 list<AudioRange> lar;
2668 _session->request_play_range (&lar, true);
2673 Editor::maybe_locate_with_edit_preroll (samplepos_t location)
2675 if (_session->transport_rolling() || !UIConfiguration::instance().get_follow_edits() || _session->config.get_external_sync())
2678 location -= _session->preroll_samples (location);
2680 //don't try to locate before the beginning of time
2685 //if follow_playhead is on, keep the playhead on the screen
2686 if (_follow_playhead)
2687 if (location < _leftmost_sample)
2688 location = _leftmost_sample;
2690 _session->request_locate (location);
2694 Editor::play_with_preroll ()
2696 samplepos_t start, end;
2697 if (UIConfiguration::instance().get_follow_edits() && get_selection_extents (start, end)) {
2698 const samplepos_t preroll = _session->preroll_samples (start);
2700 samplepos_t ret = start;
2702 if (start > preroll) {
2703 start = start - preroll;
2706 end = end + preroll; //"post-roll"
2708 AudioRange ar (start, end, 0);
2709 list<AudioRange> lar;
2712 _session->request_play_range (&lar, true);
2713 _session->set_requested_return_sample (ret); //force auto-return to return to range start, without the preroll
2715 samplepos_t ph = playhead_cursor->current_sample ();
2716 const samplepos_t preroll = _session->preroll_samples (ph);
2719 start = ph - preroll;
2723 _session->request_locate (start, true);
2724 _session->set_requested_return_sample (ph); //force auto-return to return to playhead location, without the preroll
2729 Editor::rec_with_preroll ()
2731 samplepos_t ph = playhead_cursor->current_sample ();
2732 samplepos_t preroll = _session->preroll_samples (ph);
2733 _session->request_preroll_record_trim (ph, preroll);
2737 Editor::rec_with_count_in ()
2739 _session->request_count_in_record ();
2743 Editor::play_location (Location& location)
2745 if (location.start() <= location.end()) {
2749 _session->request_bounded_roll (location.start(), location.end());
2753 Editor::loop_location (Location& location)
2755 if (location.start() <= location.end()) {
2761 if ((tll = transport_loop_location()) != 0) {
2762 tll->set (location.start(), location.end());
2764 // enable looping, reposition and start rolling
2765 _session->request_locate (tll->start(), true);
2766 _session->request_play_loop (true);
2771 Editor::do_layer_operation (LayerOperation op)
2773 if (selection->regions.empty ()) {
2777 bool const multiple = selection->regions.size() > 1;
2781 begin_reversible_command (_("raise regions"));
2783 begin_reversible_command (_("raise region"));
2789 begin_reversible_command (_("raise regions to top"));
2791 begin_reversible_command (_("raise region to top"));
2797 begin_reversible_command (_("lower regions"));
2799 begin_reversible_command (_("lower region"));
2805 begin_reversible_command (_("lower regions to bottom"));
2807 begin_reversible_command (_("lower region"));
2812 set<boost::shared_ptr<Playlist> > playlists = selection->regions.playlists ();
2813 for (set<boost::shared_ptr<Playlist> >::iterator i = playlists.begin(); i != playlists.end(); ++i) {
2814 (*i)->clear_owned_changes ();
2817 for (RegionSelection::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
2818 boost::shared_ptr<Region> r = (*i)->region ();
2830 r->lower_to_bottom ();
2834 for (set<boost::shared_ptr<Playlist> >::iterator i = playlists.begin(); i != playlists.end(); ++i) {
2835 vector<Command*> cmds;
2837 _session->add_commands (cmds);
2840 commit_reversible_command ();
2844 Editor::raise_region ()
2846 do_layer_operation (Raise);
2850 Editor::raise_region_to_top ()
2852 do_layer_operation (RaiseToTop);
2856 Editor::lower_region ()
2858 do_layer_operation (Lower);
2862 Editor::lower_region_to_bottom ()
2864 do_layer_operation (LowerToBottom);
2867 /** Show the region editor for the selected regions */
2869 Editor::show_region_properties ()
2871 selection->foreach_regionview (&RegionView::show_region_editor);
2874 /** Show the midi list editor for the selected MIDI regions */
2876 Editor::show_midi_list_editor ()
2878 selection->foreach_midi_regionview (&MidiRegionView::show_list_editor);
2882 Editor::rename_region ()
2884 RegionSelection rs = get_regions_from_selection_and_entered ();
2890 ArdourDialog d (_("Rename Region"), true, false);
2892 Label label (_("New name:"));
2895 hbox.set_spacing (6);
2896 hbox.pack_start (label, false, false);
2897 hbox.pack_start (entry, true, true);
2899 d.get_vbox()->set_border_width (12);
2900 d.get_vbox()->pack_start (hbox, false, false);
2902 d.add_button(Gtk::Stock::CANCEL, Gtk::RESPONSE_CANCEL);
2903 d.add_button(Gtk::Stock::OK, Gtk::RESPONSE_OK);
2905 d.set_size_request (300, -1);
2907 entry.set_text (rs.front()->region()->name());
2908 entry.select_region (0, -1);
2910 entry.signal_activate().connect (sigc::bind (sigc::mem_fun (d, &Dialog::response), RESPONSE_OK));
2916 int const ret = d.run();
2920 if (ret != RESPONSE_OK) {
2924 std::string str = entry.get_text();
2925 strip_whitespace_edges (str);
2927 rs.front()->region()->set_name (str);
2928 _regions->redisplay ();
2932 /** Start an audition of the first selected region */
2934 Editor::play_edit_range ()
2936 samplepos_t start, end;
2938 if (get_edit_op_range (start, end)) {
2939 _session->request_bounded_roll (start, end);
2944 Editor::play_selected_region ()
2946 samplepos_t start = max_samplepos;
2947 samplepos_t end = 0;
2949 RegionSelection rs = get_regions_from_selection_and_entered ();
2955 for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
2956 if ((*i)->region()->position() < start) {
2957 start = (*i)->region()->position();
2959 if ((*i)->region()->last_sample() + 1 > end) {
2960 end = (*i)->region()->last_sample() + 1;
2964 _session->request_bounded_roll (start, end);
2968 Editor::audition_playlist_region_standalone (boost::shared_ptr<Region> region)
2970 _session->audition_region (region);
2974 Editor::region_from_selection ()
2976 if (clicked_axisview == 0) {
2980 if (selection->time.empty()) {
2984 samplepos_t start = selection->time[clicked_selection].start;
2985 samplepos_t end = selection->time[clicked_selection].end;
2987 TrackViewList tracks = get_tracks_for_range_action ();
2989 samplepos_t selection_cnt = end - start + 1;
2991 for (TrackSelection::iterator i = tracks.begin(); i != tracks.end(); ++i) {
2992 boost::shared_ptr<Region> current;
2993 boost::shared_ptr<Playlist> pl;
2994 samplepos_t internal_start;
2997 if ((pl = (*i)->playlist()) == 0) {
3001 if ((current = pl->top_region_at (start)) == 0) {
3005 internal_start = start - current->position();
3006 RegionFactory::region_name (new_name, current->name(), true);
3010 plist.add (ARDOUR::Properties::start, current->start() + internal_start);
3011 plist.add (ARDOUR::Properties::length, selection_cnt);
3012 plist.add (ARDOUR::Properties::name, new_name);
3013 plist.add (ARDOUR::Properties::layer, 0);
3015 boost::shared_ptr<Region> region (RegionFactory::create (current, plist));
3020 Editor::create_region_from_selection (vector<boost::shared_ptr<Region> >& new_regions)
3022 if (selection->time.empty() || selection->tracks.empty()) {
3026 samplepos_t start, end;
3027 if (clicked_selection) {
3028 start = selection->time[clicked_selection].start;
3029 end = selection->time[clicked_selection].end;
3031 start = selection->time.start();
3032 end = selection->time.end_sample();
3035 TrackViewList ts = selection->tracks.filter_to_unique_playlists ();
3036 sort_track_selection (ts);
3038 for (TrackSelection::iterator i = ts.begin(); i != ts.end(); ++i) {
3039 boost::shared_ptr<Region> current;
3040 boost::shared_ptr<Playlist> playlist;
3041 samplepos_t internal_start;
3044 if ((playlist = (*i)->playlist()) == 0) {
3048 if ((current = playlist->top_region_at(start)) == 0) {
3052 internal_start = start - current->position();
3053 RegionFactory::region_name (new_name, current->name(), true);
3057 plist.add (ARDOUR::Properties::start, current->start() + internal_start);
3058 plist.add (ARDOUR::Properties::length, end - start + 1);
3059 plist.add (ARDOUR::Properties::name, new_name);
3061 new_regions.push_back (RegionFactory::create (current, plist));
3066 Editor::split_multichannel_region ()
3068 RegionSelection rs = get_regions_from_selection_and_entered ();
3074 vector< boost::shared_ptr<Region> > v;
3076 for (list<RegionView*>::iterator x = rs.begin(); x != rs.end(); ++x) {
3077 (*x)->region()->separate_by_channel (v);
3082 Editor::new_region_from_selection ()
3084 region_from_selection ();
3085 cancel_selection ();
3089 add_if_covered (RegionView* rv, const AudioRange* ar, RegionSelection* rs)
3091 switch (rv->region()->coverage (ar->start, ar->end - 1)) {
3092 // n.b. -1 because AudioRange::end is one past the end, but coverage expects inclusive ranges
3093 case Evoral::OverlapNone:
3101 * - selected tracks, or if there are none...
3102 * - tracks containing selected regions, or if there are none...
3107 Editor::get_tracks_for_range_action () const
3111 if (selection->tracks.empty()) {
3113 /* use tracks with selected regions */
3115 RegionSelection rs = selection->regions;
3117 for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
3118 TimeAxisView* tv = &(*i)->get_time_axis_view();
3120 if (!t.contains (tv)) {
3126 /* no regions and no tracks: use all tracks */
3132 t = selection->tracks;
3135 return t.filter_to_unique_playlists();
3139 Editor::separate_regions_between (const TimeSelection& ts)
3141 bool in_command = false;
3142 boost::shared_ptr<Playlist> playlist;
3143 RegionSelection new_selection;
3145 TrackViewList tmptracks = get_tracks_for_range_action ();
3146 sort_track_selection (tmptracks);
3148 for (TrackSelection::iterator i = tmptracks.begin(); i != tmptracks.end(); ++i) {
3150 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> ((*i));
3156 if (!rtv->is_track()) {
3160 /* no edits to destructive tracks */
3162 if (rtv->track()->destructive()) {
3166 if ((playlist = rtv->playlist()) != 0) {
3168 playlist->clear_changes ();
3170 /* XXX need to consider musical time selections here at some point */
3172 for (list<AudioRange>::const_iterator t = ts.begin(); t != ts.end(); ++t) {
3174 sigc::connection c = rtv->view()->RegionViewAdded.connect (
3175 sigc::mem_fun(*this, &Editor::collect_new_region_view));
3177 latest_regionviews.clear ();
3179 playlist->partition ((*t).start, (*t).end, false);
3183 if (!latest_regionviews.empty()) {
3185 rtv->view()->foreach_regionview (sigc::bind (
3186 sigc::ptr_fun (add_if_covered),
3187 &(*t), &new_selection));
3190 begin_reversible_command (_("separate"));
3194 /* pick up changes to existing regions */
3196 vector<Command*> cmds;
3197 playlist->rdiff (cmds);
3198 _session->add_commands (cmds);
3200 /* pick up changes to the playlist itself (adds/removes)
3203 _session->add_command(new StatefulDiffCommand (playlist));
3210 // selection->set (new_selection);
3212 commit_reversible_command ();
3216 struct PlaylistState {
3217 boost::shared_ptr<Playlist> playlist;
3221 /** Take tracks from get_tracks_for_range_action and cut any regions
3222 * on those tracks so that the tracks are empty over the time
3226 Editor::separate_region_from_selection ()
3228 /* preferentially use *all* ranges in the time selection if we're in range mode
3229 to allow discontiguous operation, since get_edit_op_range() currently
3230 returns a single range.
3233 if (!selection->time.empty()) {
3235 separate_regions_between (selection->time);
3242 if (get_edit_op_range (start, end)) {
3244 AudioRange ar (start, end, 1);
3248 separate_regions_between (ts);
3254 Editor::separate_region_from_punch ()
3256 Location* loc = _session->locations()->auto_punch_location();
3258 separate_regions_using_location (*loc);
3263 Editor::separate_region_from_loop ()
3265 Location* loc = _session->locations()->auto_loop_location();
3267 separate_regions_using_location (*loc);
3272 Editor::separate_regions_using_location (Location& loc)
3274 if (loc.is_mark()) {
3278 AudioRange ar (loc.start(), loc.end(), 1);
3283 separate_regions_between (ts);
3286 /** Separate regions under the selected region */
3288 Editor::separate_under_selected_regions ()
3290 vector<PlaylistState> playlists;
3294 rs = get_regions_from_selection_and_entered();
3296 if (!_session || rs.empty()) {
3300 begin_reversible_command (_("separate region under"));
3302 list<boost::shared_ptr<Region> > regions_to_remove;
3304 for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
3305 // we can't just remove the region(s) in this loop because
3306 // this removes them from the RegionSelection, and they thus
3307 // disappear from underneath the iterator, and the ++i above
3308 // SEGVs in a puzzling fashion.
3310 // so, first iterate over the regions to be removed from rs and
3311 // add them to the regions_to_remove list, and then
3312 // iterate over the list to actually remove them.
3314 regions_to_remove.push_back ((*i)->region());
3317 for (list<boost::shared_ptr<Region> >::iterator rl = regions_to_remove.begin(); rl != regions_to_remove.end(); ++rl) {
3319 boost::shared_ptr<Playlist> playlist = (*rl)->playlist();
3322 // is this check necessary?
3326 vector<PlaylistState>::iterator i;
3328 //only take state if this is a new playlist.
3329 for (i = playlists.begin(); i != playlists.end(); ++i) {
3330 if ((*i).playlist == playlist) {
3335 if (i == playlists.end()) {
3337 PlaylistState before;
3338 before.playlist = playlist;
3339 before.before = &playlist->get_state();
3340 playlist->clear_changes ();
3341 playlist->freeze ();
3342 playlists.push_back(before);
3345 //Partition on the region bounds
3346 playlist->partition ((*rl)->first_sample() - 1, (*rl)->last_sample() + 1, true);
3348 //Re-add region that was just removed due to the partition operation
3349 playlist->add_region ((*rl), (*rl)->first_sample());
3352 vector<PlaylistState>::iterator pl;
3354 for (pl = playlists.begin(); pl != playlists.end(); ++pl) {
3355 (*pl).playlist->thaw ();
3356 _session->add_command(new MementoCommand<Playlist>(*(*pl).playlist, (*pl).before, &(*pl).playlist->get_state()));
3359 commit_reversible_command ();
3363 Editor::crop_region_to_selection ()
3365 if (!selection->time.empty()) {
3367 begin_reversible_command (_("Crop Regions to Time Selection"));
3368 for (std::list<AudioRange>::iterator i = selection->time.begin(); i != selection->time.end(); ++i) {
3369 crop_region_to ((*i).start, (*i).end);
3371 commit_reversible_command();
3377 if (get_edit_op_range (start, end)) {
3378 begin_reversible_command (_("Crop Regions to Edit Range"));
3380 crop_region_to (start, end);
3382 commit_reversible_command();
3389 Editor::crop_region_to (samplepos_t start, samplepos_t end)
3391 vector<boost::shared_ptr<Playlist> > playlists;
3392 boost::shared_ptr<Playlist> playlist;
3395 if (selection->tracks.empty()) {
3396 ts = track_views.filter_to_unique_playlists();
3398 ts = selection->tracks.filter_to_unique_playlists ();
3401 sort_track_selection (ts);
3403 for (TrackSelection::iterator i = ts.begin(); i != ts.end(); ++i) {
3405 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> ((*i));
3411 boost::shared_ptr<Track> t = rtv->track();
3413 if (t != 0 && ! t->destructive()) {
3415 if ((playlist = rtv->playlist()) != 0) {
3416 playlists.push_back (playlist);
3421 if (playlists.empty()) {
3426 samplepos_t new_start;
3427 samplepos_t new_end;
3428 samplecnt_t new_length;
3430 for (vector<boost::shared_ptr<Playlist> >::iterator i = playlists.begin(); i != playlists.end(); ++i) {
3432 /* Only the top regions at start and end have to be cropped */
3433 boost::shared_ptr<Region> region_at_start = (*i)->top_region_at(start);
3434 boost::shared_ptr<Region> region_at_end = (*i)->top_region_at(end);
3436 vector<boost::shared_ptr<Region> > regions;
3438 if (region_at_start != 0) {
3439 regions.push_back (region_at_start);
3441 if (region_at_end != 0) {
3442 regions.push_back (region_at_end);
3445 /* now adjust lengths */
3446 for (vector<boost::shared_ptr<Region> >::iterator i = regions.begin(); i != regions.end(); ++i) {
3448 pos = (*i)->position();
3449 new_start = max (start, pos);
3450 if (max_samplepos - pos > (*i)->length()) {
3451 new_end = pos + (*i)->length() - 1;
3453 new_end = max_samplepos;
3455 new_end = min (end, new_end);
3456 new_length = new_end - new_start + 1;
3458 (*i)->clear_changes ();
3459 (*i)->trim_to (new_start, new_length);
3460 _session->add_command (new StatefulDiffCommand (*i));
3466 Editor::region_fill_track ()
3468 boost::shared_ptr<Playlist> playlist;
3469 RegionSelection regions = get_regions_from_selection_and_entered ();
3470 RegionSelection foo;
3472 samplepos_t const end = _session->current_end_sample ();
3474 if (regions.empty () || regions.end_sample () + 1 >= end) {
3478 samplepos_t const start_sample = regions.start ();
3479 samplepos_t const end_sample = regions.end_sample ();
3480 samplecnt_t const gap = end_sample - start_sample + 1;
3482 begin_reversible_command (Operations::region_fill);
3484 selection->clear_regions ();
3486 for (RegionSelection::iterator i = regions.begin(); i != regions.end(); ++i) {
3488 boost::shared_ptr<Region> r ((*i)->region());
3490 TimeAxisView& tv = (*i)->get_time_axis_view();
3491 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (&tv);
3492 latest_regionviews.clear ();
3493 sigc::connection c = rtv->view()->RegionViewAdded.connect (sigc::mem_fun(*this, &Editor::collect_new_region_view));
3495 samplepos_t const position = end_sample + (r->first_sample() - start_sample + 1);
3496 playlist = (*i)->region()->playlist();
3497 playlist->clear_changes ();
3498 playlist->duplicate_until (r, position, gap, end);
3499 _session->add_command(new StatefulDiffCommand (playlist));
3503 foo.insert (foo.end(), latest_regionviews.begin(), latest_regionviews.end());
3507 selection->set (foo);
3510 commit_reversible_command ();
3514 Editor::set_region_sync_position ()
3516 set_sync_point (get_preferred_edit_position (), get_regions_from_selection_and_edit_point ());
3520 Editor::set_sync_point (samplepos_t where, const RegionSelection& rs)
3522 bool in_command = false;
3524 for (RegionSelection::const_iterator r = rs.begin(); r != rs.end(); ++r) {
3526 if (!(*r)->region()->covers (where)) {
3530 boost::shared_ptr<Region> region ((*r)->region());
3533 begin_reversible_command (_("set sync point"));
3537 region->clear_changes ();
3538 region->set_sync_position (where);
3539 _session->add_command(new StatefulDiffCommand (region));
3543 commit_reversible_command ();
3547 /** Remove the sync positions of the selection */
3549 Editor::remove_region_sync ()
3551 RegionSelection rs = get_regions_from_selection_and_entered ();
3557 begin_reversible_command (_("remove region sync"));
3559 for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
3561 (*i)->region()->clear_changes ();
3562 (*i)->region()->clear_sync_position ();
3563 _session->add_command(new StatefulDiffCommand ((*i)->region()));
3566 commit_reversible_command ();
3570 Editor::naturalize_region ()
3572 RegionSelection rs = get_regions_from_selection_and_entered ();
3578 if (rs.size() > 1) {
3579 begin_reversible_command (_("move regions to original position"));
3581 begin_reversible_command (_("move region to original position"));
3584 for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
3585 (*i)->region()->clear_changes ();
3586 (*i)->region()->move_to_natural_position ();
3587 _session->add_command (new StatefulDiffCommand ((*i)->region()));
3590 commit_reversible_command ();
3594 Editor::align_regions (RegionPoint what)
3596 RegionSelection const rs = get_regions_from_selection_and_edit_point ();
3602 begin_reversible_command (_("align selection"));
3604 samplepos_t const position = get_preferred_edit_position ();
3606 for (RegionSelection::const_iterator i = rs.begin(); i != rs.end(); ++i) {
3607 align_region_internal ((*i)->region(), what, position);
3610 commit_reversible_command ();
3613 struct RegionSortByTime {
3614 bool operator() (const RegionView* a, const RegionView* b) {
3615 return a->region()->position() < b->region()->position();
3620 Editor::align_regions_relative (RegionPoint point)
3622 RegionSelection const rs = get_regions_from_selection_and_edit_point ();
3628 samplepos_t const position = get_preferred_edit_position ();
3630 samplepos_t distance = 0;
3631 samplepos_t pos = 0;
3634 list<RegionView*> sorted;
3635 rs.by_position (sorted);
3637 boost::shared_ptr<Region> r ((*sorted.begin())->region());
3642 if (position > r->position()) {
3643 distance = position - r->position();
3645 distance = r->position() - position;
3651 if (position > r->last_sample()) {
3652 distance = position - r->last_sample();
3653 pos = r->position() + distance;
3655 distance = r->last_sample() - position;
3656 pos = r->position() - distance;
3662 pos = r->adjust_to_sync (position);
3663 if (pos > r->position()) {
3664 distance = pos - r->position();
3666 distance = r->position() - pos;
3672 if (pos == r->position()) {
3676 begin_reversible_command (_("align selection (relative)"));
3678 /* move first one specially */
3680 r->clear_changes ();
3681 r->set_position (pos);
3682 _session->add_command(new StatefulDiffCommand (r));
3684 /* move rest by the same amount */
3688 for (list<RegionView*>::iterator i = sorted.begin(); i != sorted.end(); ++i) {
3690 boost::shared_ptr<Region> region ((*i)->region());
3692 region->clear_changes ();
3695 region->set_position (region->position() + distance);
3697 region->set_position (region->position() - distance);
3700 _session->add_command(new StatefulDiffCommand (region));
3704 commit_reversible_command ();
3708 Editor::align_region (boost::shared_ptr<Region> region, RegionPoint point, samplepos_t position)
3710 begin_reversible_command (_("align region"));
3711 align_region_internal (region, point, position);
3712 commit_reversible_command ();
3716 Editor::align_region_internal (boost::shared_ptr<Region> region, RegionPoint point, samplepos_t position)
3718 region->clear_changes ();
3722 region->set_position (region->adjust_to_sync (position));
3726 if (position > region->length()) {
3727 region->set_position (position - region->length());
3732 region->set_position (position);
3736 _session->add_command(new StatefulDiffCommand (region));
3740 Editor::trim_region_front ()
3746 Editor::trim_region_back ()
3748 trim_region (false);
3752 Editor::trim_region (bool front)
3754 samplepos_t where = get_preferred_edit_position();
3755 RegionSelection rs = get_regions_from_selection_and_edit_point ();
3761 begin_reversible_command (front ? _("trim front") : _("trim back"));
3763 for (list<RegionView*>::const_iterator i = rs.by_layer().begin(); i != rs.by_layer().end(); ++i) {
3764 if (!(*i)->region()->locked()) {
3766 (*i)->region()->clear_changes ();
3769 (*i)->region()->trim_front (where);
3771 (*i)->region()->trim_end (where);
3774 _session->add_command (new StatefulDiffCommand ((*i)->region()));
3778 commit_reversible_command ();
3781 /** Trim the end of the selected regions to the position of the edit cursor */
3783 Editor::trim_region_to_loop ()
3785 Location* loc = _session->locations()->auto_loop_location();
3789 trim_region_to_location (*loc, _("trim to loop"));
3793 Editor::trim_region_to_punch ()
3795 Location* loc = _session->locations()->auto_punch_location();
3799 trim_region_to_location (*loc, _("trim to punch"));
3803 Editor::trim_region_to_location (const Location& loc, const char* str)
3805 RegionSelection rs = get_regions_from_selection_and_entered ();
3806 bool in_command = false;
3808 for (RegionSelection::iterator x = rs.begin(); x != rs.end(); ++x) {
3809 RegionView* rv = (*x);
3811 /* require region to span proposed trim */
3812 switch (rv->region()->coverage (loc.start(), loc.end())) {
3813 case Evoral::OverlapInternal:
3819 RouteTimeAxisView* tav = dynamic_cast<RouteTimeAxisView*> (&rv->get_time_axis_view());
3827 start = loc.start();
3830 rv->region()->clear_changes ();
3831 rv->region()->trim_to (start, (end - start));
3834 begin_reversible_command (str);
3837 _session->add_command(new StatefulDiffCommand (rv->region()));
3841 commit_reversible_command ();
3846 Editor::trim_region_to_previous_region_end ()
3848 return trim_to_region(false);
3852 Editor::trim_region_to_next_region_start ()
3854 return trim_to_region(true);
3858 Editor::trim_to_region(bool forward)
3860 RegionSelection rs = get_regions_from_selection_and_entered ();
3861 bool in_command = false;
3863 boost::shared_ptr<Region> next_region;
3865 for (RegionSelection::iterator x = rs.begin(); x != rs.end(); ++x) {
3867 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (*x);
3873 AudioTimeAxisView* atav = dynamic_cast<AudioTimeAxisView*> (&arv->get_time_axis_view());
3879 boost::shared_ptr<Region> region = arv->region();
3880 boost::shared_ptr<Playlist> playlist (region->playlist());
3882 region->clear_changes ();
3886 next_region = playlist->find_next_region (region->first_sample(), Start, 1);
3892 region->trim_end (next_region->first_sample() - 1);
3893 arv->region_changed (PropertyChange (ARDOUR::Properties::length));
3897 next_region = playlist->find_next_region (region->first_sample(), Start, 0);
3903 region->trim_front (next_region->last_sample() + 1);
3904 arv->region_changed (ARDOUR::bounds_change);
3908 begin_reversible_command (_("trim to region"));
3911 _session->add_command(new StatefulDiffCommand (region));
3915 commit_reversible_command ();
3920 Editor::unfreeze_route ()
3922 if (clicked_routeview == 0 || !clicked_routeview->is_track()) {
3926 clicked_routeview->track()->unfreeze ();
3930 Editor::_freeze_thread (void* arg)
3932 return static_cast<Editor*>(arg)->freeze_thread ();
3936 Editor::freeze_thread ()
3938 /* create event pool because we may need to talk to the session */
3939 SessionEvent::create_per_thread_pool ("freeze events", 64);
3940 /* create per-thread buffers for process() tree to use */
3941 clicked_routeview->audio_track()->freeze_me (*current_interthread_info);
3942 current_interthread_info->done = true;
3947 Editor::freeze_route ()
3953 /* stop transport before we start. this is important */
3955 _session->request_transport_speed (0.0);
3957 /* wait for just a little while, because the above call is asynchronous */
3959 Glib::usleep (250000);
3961 if (clicked_routeview == 0 || !clicked_routeview->is_audio_track()) {
3965 if (!clicked_routeview->track()->bounceable (clicked_routeview->track()->main_outs(), true)) {
3967 _("This track/bus cannot be frozen because the signal adds or loses channels before reaching the outputs.\n"
3968 "This is typically caused by plugins that generate stereo output from mono input or vice versa.")
3970 d.set_title (_("Cannot freeze"));
3975 if (clicked_routeview->track()->has_external_redirects()) {
3976 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"
3977 "Freezing will only process the signal as far as the first send/insert/return."),
3978 clicked_routeview->track()->name()), true, MESSAGE_INFO, BUTTONS_NONE, true);
3980 d.add_button (_("Freeze anyway"), Gtk::RESPONSE_OK);
3981 d.add_button (_("Don't freeze"), Gtk::RESPONSE_CANCEL);
3982 d.set_title (_("Freeze Limits"));
3984 int response = d.run ();
3987 case Gtk::RESPONSE_CANCEL:
3994 InterThreadInfo itt;
3995 current_interthread_info = &itt;
3997 InterthreadProgressWindow ipw (current_interthread_info, _("Freeze"), _("Cancel Freeze"));
3999 pthread_create_and_store (X_("freezer"), &itt.thread, _freeze_thread, this);
4001 CursorContext::Handle cursor_ctx = CursorContext::create(*this, _cursors->wait);
4003 while (!itt.done && !itt.cancel) {
4004 gtk_main_iteration ();
4007 pthread_join (itt.thread, 0);
4008 current_interthread_info = 0;
4012 Editor::bounce_range_selection (bool replace, bool enable_processing)
4014 if (selection->time.empty()) {
4018 TrackSelection views = selection->tracks;
4020 for (TrackViewList::iterator i = views.begin(); i != views.end(); ++i) {
4022 if (enable_processing) {
4024 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (*i);
4026 if (rtv && rtv->track() && replace && enable_processing && !rtv->track()->bounceable (rtv->track()->main_outs(), false)) {
4028 _("You can't perform this operation because the processing of the signal "
4029 "will cause one or more of the tracks to end up with a region with more channels than this track has inputs.\n\n"
4030 "You can do this without processing, which is a different operation.")
4032 d.set_title (_("Cannot bounce"));
4039 samplepos_t start = selection->time[clicked_selection].start;
4040 samplepos_t end = selection->time[clicked_selection].end;
4041 samplepos_t cnt = end - start + 1;
4042 bool in_command = false;
4044 for (TrackViewList::iterator i = views.begin(); i != views.end(); ++i) {
4046 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (*i);
4052 boost::shared_ptr<Playlist> playlist;
4054 if ((playlist = rtv->playlist()) == 0) {
4058 InterThreadInfo itt;
4060 playlist->clear_changes ();
4061 playlist->clear_owned_changes ();
4063 boost::shared_ptr<Region> r;
4065 if (enable_processing) {
4066 r = rtv->track()->bounce_range (start, start+cnt, itt, rtv->track()->main_outs(), false);
4068 r = rtv->track()->bounce_range (start, start+cnt, itt, boost::shared_ptr<Processor>(), false);
4076 list<AudioRange> ranges;
4077 ranges.push_back (AudioRange (start, start+cnt, 0));
4078 playlist->cut (ranges); // discard result
4079 playlist->add_region (r, start);
4083 begin_reversible_command (_("bounce range"));
4086 vector<Command*> cmds;
4087 playlist->rdiff (cmds);
4088 _session->add_commands (cmds);
4090 _session->add_command (new StatefulDiffCommand (playlist));
4094 commit_reversible_command ();
4098 /** Delete selected regions, automation points or a time range */
4102 //special case: if the user is pointing in the editor/mixer strip, they may be trying to delete a plugin.
4103 //we need this because the editor-mixer strip is in the editor window, so it doesn't get the bindings from the mix window
4104 bool deleted = false;
4105 if (current_mixer_strip && current_mixer_strip == MixerStrip::entered_mixer_strip())
4106 deleted = current_mixer_strip->delete_processors ();
4112 /** Cut selected regions, automation points or a time range */
4119 /** Copy selected regions, automation points or a time range */
4127 /** @return true if a Cut, Copy or Clear is possible */
4129 Editor::can_cut_copy () const
4131 if (!selection->time.empty() || !selection->regions.empty() || !selection->points.empty())
4138 /** Cut, copy or clear selected regions, automation points or a time range.
4139 * @param op Operation (Delete, Cut, Copy or Clear)
4142 Editor::cut_copy (CutCopyOp op)
4144 /* only cancel selection if cut/copy is successful.*/
4150 opname = _("delete");
4159 opname = _("clear");
4163 /* if we're deleting something, and the mouse is still pressed,
4164 the thing we started a drag for will be gone when we release
4165 the mouse button(s). avoid this. see part 2 at the end of
4169 if (op == Delete || op == Cut || op == Clear) {
4170 if (_drags->active ()) {
4175 if (op != Delete) { //"Delete" doesn't change copy/paste buf
4176 cut_buffer->clear ();
4179 if (entered_marker) {
4181 /* cut/delete op while pointing at a marker */
4184 Location* loc = find_location_from_marker (entered_marker, ignored);
4186 if (_session && loc) {
4187 entered_marker = NULL;
4188 Glib::signal_idle().connect (sigc::bind (sigc::mem_fun(*this, &Editor::really_remove_marker), loc));
4195 switch (mouse_mode) {
4198 begin_reversible_command (opname + ' ' + X_("MIDI"));
4200 commit_reversible_command ();
4206 bool did_edit = false;
4208 if (!selection->regions.empty() || !selection->points.empty()) {
4209 begin_reversible_command (opname + ' ' + _("objects"));
4212 if (!selection->regions.empty()) {
4213 cut_copy_regions (op, selection->regions);
4215 if (op == Cut || op == Delete) {
4216 selection->clear_regions ();
4220 if (!selection->points.empty()) {
4221 cut_copy_points (op);
4223 if (op == Cut || op == Delete) {
4224 selection->clear_points ();
4227 } else if (selection->time.empty()) {
4228 samplepos_t start, end;
4229 /* no time selection, see if we can get an edit range
4232 if (get_edit_op_range (start, end)) {
4233 selection->set (start, end);
4235 } else if (!selection->time.empty()) {
4236 begin_reversible_command (opname + ' ' + _("range"));
4239 cut_copy_ranges (op);
4241 if (op == Cut || op == Delete) {
4242 selection->clear_time ();
4247 /* reset repeated paste state */
4249 last_paste_pos = -1;
4250 commit_reversible_command ();
4253 if (op == Delete || op == Cut || op == Clear) {
4259 struct AutomationRecord {
4260 AutomationRecord () : state (0) , line(NULL) {}
4261 AutomationRecord (XMLNode* s, const AutomationLine* l) : state (s) , line (l) {}
4263 XMLNode* state; ///< state before any operation
4264 const AutomationLine* line; ///< line this came from
4265 boost::shared_ptr<Evoral::ControlList> copy; ///< copied events for the cut buffer
4268 struct PointsSelectionPositionSorter {
4269 bool operator() (ControlPoint* a, ControlPoint* b) {
4270 return (*(a->model()))->when < (*(b->model()))->when;
4274 /** Cut, copy or clear selected automation points.
4275 * @param op Operation (Cut, Copy or Clear)
4278 Editor::cut_copy_points (Editing::CutCopyOp op, Temporal::Beats earliest, bool midi)
4280 if (selection->points.empty ()) {
4284 /* XXX: not ideal, as there may be more than one track involved in the point selection */
4285 _last_cut_copy_source_track = &selection->points.front()->line().trackview;
4287 /* Keep a record of the AutomationLists that we end up using in this operation */
4288 typedef std::map<boost::shared_ptr<AutomationList>, AutomationRecord> Lists;
4291 /* user could select points in any order */
4292 selection->points.sort(PointsSelectionPositionSorter ());
4294 /* Go through all selected points, making an AutomationRecord for each distinct AutomationList */
4295 for (PointSelection::iterator sel_point = selection->points.begin(); sel_point != selection->points.end(); ++sel_point) {
4296 const AutomationLine& line = (*sel_point)->line();
4297 const boost::shared_ptr<AutomationList> al = line.the_list();
4298 if (lists.find (al) == lists.end ()) {
4299 /* We haven't seen this list yet, so make a record for it. This includes
4300 taking a copy of its current state, in case this is needed for undo later.
4302 lists[al] = AutomationRecord (&al->get_state (), &line);
4306 if (op == Cut || op == Copy) {
4307 /* This operation will involve putting things in the cut buffer, so create an empty
4308 ControlList for each of our source lists to put the cut buffer data in.
4310 for (Lists::iterator i = lists.begin(); i != lists.end(); ++i) {
4311 i->second.copy = i->first->create (i->first->parameter (), i->first->descriptor());
4314 /* Add all selected points to the relevant copy ControlLists */
4315 MusicSample start (std::numeric_limits<samplepos_t>::max(), 0);
4316 for (PointSelection::iterator sel_point = selection->points.begin(); sel_point != selection->points.end(); ++sel_point) {
4317 boost::shared_ptr<AutomationList> al = (*sel_point)->line().the_list();
4318 AutomationList::const_iterator ctrl_evt = (*sel_point)->model ();
4320 lists[al].copy->fast_simple_add ((*ctrl_evt)->when, (*ctrl_evt)->value);
4322 /* Update earliest MIDI start time in beats */
4323 earliest = std::min(earliest, Temporal::Beats((*ctrl_evt)->when));
4325 /* Update earliest session start time in samples */
4326 start.sample = std::min(start.sample, (*sel_point)->line().session_position(ctrl_evt));
4330 /* Snap start time backwards, so copy/paste is snap aligned. */
4332 if (earliest == std::numeric_limits<Temporal::Beats>::max()) {
4333 earliest = Temporal::Beats(); // Weird... don't offset
4335 earliest.round_down_to_beat();
4337 if (start.sample == std::numeric_limits<double>::max()) {
4338 start.sample = 0; // Weird... don't offset
4340 snap_to(start, RoundDownMaybe);
4343 const double line_offset = midi ? earliest.to_double() : start.sample;
4344 for (Lists::iterator i = lists.begin(); i != lists.end(); ++i) {
4345 /* Correct this copy list so that it is relative to the earliest
4346 start time, so relative ordering between points is preserved
4347 when copying from several lists and the paste starts at the
4348 earliest copied piece of data. */
4349 boost::shared_ptr<Evoral::ControlList> &al_cpy = i->second.copy;
4350 for (AutomationList::iterator ctrl_evt = al_cpy->begin(); ctrl_evt != al_cpy->end(); ++ctrl_evt) {
4351 (*ctrl_evt)->when -= line_offset;
4354 /* And add it to the cut buffer */
4355 cut_buffer->add (al_cpy);
4359 if (op == Delete || op == Cut) {
4360 /* This operation needs to remove things from the main AutomationList, so do that now */
4362 for (Lists::iterator i = lists.begin(); i != lists.end(); ++i) {
4363 i->first->freeze ();
4366 /* Remove each selected point from its AutomationList */
4367 for (PointSelection::iterator sel_point = selection->points.begin(); sel_point != selection->points.end(); ++sel_point) {
4368 AutomationLine& line = (*sel_point)->line ();
4369 boost::shared_ptr<AutomationList> al = line.the_list();
4373 if (dynamic_cast<AudioRegionGainLine*> (&line)) {
4374 /* removing of first and last gain point in region gain lines is prohibited*/
4375 if (line.is_last_point (*(*sel_point)) || line.is_first_point (*(*sel_point))) {
4381 al->erase ((*sel_point)->model ());
4385 /* Thaw the lists and add undo records for them */
4386 for (Lists::iterator i = lists.begin(); i != lists.end(); ++i) {
4387 boost::shared_ptr<AutomationList> al = i->first;
4389 _session->add_command (new MementoCommand<AutomationList> (*al.get(), i->second.state, &(al->get_state ())));
4394 /** Cut, copy or clear selected automation points.
4395 * @param op Operation (Cut, Copy or Clear)
4398 Editor::cut_copy_midi (CutCopyOp op)
4400 Temporal::Beats earliest = std::numeric_limits<Temporal::Beats>::max();
4401 for (MidiRegionSelection::iterator i = selection->midi_regions.begin(); i != selection->midi_regions.end(); ++i) {
4402 MidiRegionView* mrv = dynamic_cast<MidiRegionView*>(*i);
4404 if (!mrv->selection().empty()) {
4405 earliest = std::min(earliest, (*mrv->selection().begin())->note()->time());
4407 mrv->cut_copy_clear (op);
4409 /* XXX: not ideal, as there may be more than one track involved in the selection */
4410 _last_cut_copy_source_track = &mrv->get_time_axis_view();
4414 if (!selection->points.empty()) {
4415 cut_copy_points (op, earliest, true);
4416 if (op == Cut || op == Delete) {
4417 selection->clear_points ();
4422 struct lt_playlist {
4423 bool operator () (const PlaylistState& a, const PlaylistState& b) {
4424 return a.playlist < b.playlist;
4428 struct PlaylistMapping {
4430 boost::shared_ptr<Playlist> pl;
4432 PlaylistMapping (TimeAxisView* tvp) : tv (tvp) {}
4435 /** Remove `clicked_regionview' */
4437 Editor::remove_clicked_region ()
4439 if (clicked_routeview == 0 || clicked_regionview == 0) {
4443 begin_reversible_command (_("remove region"));
4445 boost::shared_ptr<Playlist> playlist = clicked_routeview->playlist();
4447 playlist->clear_changes ();
4448 playlist->clear_owned_changes ();
4449 playlist->remove_region (clicked_regionview->region());
4450 if (Config->get_edit_mode() == Ripple)
4451 playlist->ripple (clicked_regionview->region()->position(), -clicked_regionview->region()->length(), boost::shared_ptr<Region>());
4453 /* We might have removed regions, which alters other regions' layering_index,
4454 so we need to do a recursive diff here.
4456 vector<Command*> cmds;
4457 playlist->rdiff (cmds);
4458 _session->add_commands (cmds);
4460 _session->add_command(new StatefulDiffCommand (playlist));
4461 commit_reversible_command ();
4465 /** Remove the selected regions */
4467 Editor::remove_selected_regions ()
4469 RegionSelection rs = get_regions_from_selection_and_entered ();
4471 if (!_session || rs.empty()) {
4475 list<boost::shared_ptr<Region> > regions_to_remove;
4477 for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
4478 // we can't just remove the region(s) in this loop because
4479 // this removes them from the RegionSelection, and they thus
4480 // disappear from underneath the iterator, and the ++i above
4481 // SEGVs in a puzzling fashion.
4483 // so, first iterate over the regions to be removed from rs and
4484 // add them to the regions_to_remove list, and then
4485 // iterate over the list to actually remove them.
4487 regions_to_remove.push_back ((*i)->region());
4490 vector<boost::shared_ptr<Playlist> > playlists;
4492 for (list<boost::shared_ptr<Region> >::iterator rl = regions_to_remove.begin(); rl != regions_to_remove.end(); ++rl) {
4494 boost::shared_ptr<Playlist> playlist = (*rl)->playlist();
4497 // is this check necessary?
4501 /* get_regions_from_selection_and_entered() guarantees that
4502 the playlists involved are unique, so there is no need
4506 playlists.push_back (playlist);
4508 playlist->clear_changes ();
4509 playlist->clear_owned_changes ();
4510 playlist->freeze ();
4511 playlist->remove_region (*rl);
4512 if (Config->get_edit_mode() == Ripple)
4513 playlist->ripple ((*rl)->position(), -(*rl)->length(), boost::shared_ptr<Region>());
4517 vector<boost::shared_ptr<Playlist> >::iterator pl;
4518 bool in_command = false;
4520 for (pl = playlists.begin(); pl != playlists.end(); ++pl) {
4523 /* We might have removed regions, which alters other regions' layering_index,
4524 so we need to do a recursive diff here.
4528 begin_reversible_command (_("remove region"));
4531 vector<Command*> cmds;
4532 (*pl)->rdiff (cmds);
4533 _session->add_commands (cmds);
4535 _session->add_command(new StatefulDiffCommand (*pl));
4539 commit_reversible_command ();
4543 /** Cut, copy or clear selected regions.
4544 * @param op Operation (Cut, Copy or Clear)
4547 Editor::cut_copy_regions (CutCopyOp op, RegionSelection& rs)
4549 /* we can't use a std::map here because the ordering is important, and we can't trivially sort
4550 a map when we want ordered access to both elements. i think.
4553 vector<PlaylistMapping> pmap;
4555 samplepos_t first_position = max_samplepos;
4557 typedef set<boost::shared_ptr<Playlist> > FreezeList;
4558 FreezeList freezelist;
4560 /* get ordering correct before we cut/copy */
4562 rs.sort_by_position_and_track ();
4564 for (RegionSelection::iterator x = rs.begin(); x != rs.end(); ++x) {
4566 first_position = min ((samplepos_t) (*x)->region()->position(), first_position);
4568 if (op == Cut || op == Clear || op == Delete) {
4569 boost::shared_ptr<Playlist> pl = (*x)->region()->playlist();
4572 FreezeList::iterator fl;
4574 // only take state if this is a new playlist.
4575 for (fl = freezelist.begin(); fl != freezelist.end(); ++fl) {
4581 if (fl == freezelist.end()) {
4582 pl->clear_changes();
4583 pl->clear_owned_changes ();
4585 freezelist.insert (pl);
4590 TimeAxisView* tv = &(*x)->get_time_axis_view();
4591 vector<PlaylistMapping>::iterator z;
4593 for (z = pmap.begin(); z != pmap.end(); ++z) {
4594 if ((*z).tv == tv) {
4599 if (z == pmap.end()) {
4600 pmap.push_back (PlaylistMapping (tv));
4604 for (RegionSelection::iterator x = rs.begin(); x != rs.end(); ) {
4606 boost::shared_ptr<Playlist> pl = (*x)->region()->playlist();
4609 /* region not yet associated with a playlist (e.g. unfinished
4616 TimeAxisView& tv = (*x)->get_time_axis_view();
4617 boost::shared_ptr<Playlist> npl;
4618 RegionSelection::iterator tmp;
4625 vector<PlaylistMapping>::iterator z;
4627 for (z = pmap.begin(); z != pmap.end(); ++z) {
4628 if ((*z).tv == &tv) {
4633 assert (z != pmap.end());
4636 npl = PlaylistFactory::create (pl->data_type(), *_session, "cutlist", true);
4644 boost::shared_ptr<Region> r = (*x)->region();
4645 boost::shared_ptr<Region> _xx;
4651 pl->remove_region (r);
4652 if (Config->get_edit_mode() == Ripple)
4653 pl->ripple (r->position(), -r->length(), boost::shared_ptr<Region>());
4657 _xx = RegionFactory::create (r);
4658 npl->add_region (_xx, r->position() - first_position);
4659 pl->remove_region (r);
4660 if (Config->get_edit_mode() == Ripple)
4661 pl->ripple (r->position(), -r->length(), boost::shared_ptr<Region>());
4665 /* copy region before adding, so we're not putting same object into two different playlists */
4666 npl->add_region (RegionFactory::create (r), r->position() - first_position);
4670 pl->remove_region (r);
4671 if (Config->get_edit_mode() == Ripple)
4672 pl->ripple (r->position(), -r->length(), boost::shared_ptr<Region>());
4681 list<boost::shared_ptr<Playlist> > foo;
4683 /* the pmap is in the same order as the tracks in which selected regions occurred */
4685 for (vector<PlaylistMapping>::iterator i = pmap.begin(); i != pmap.end(); ++i) {
4688 foo.push_back ((*i).pl);
4693 cut_buffer->set (foo);
4697 _last_cut_copy_source_track = 0;
4699 _last_cut_copy_source_track = pmap.front().tv;
4703 for (FreezeList::iterator pl = freezelist.begin(); pl != freezelist.end(); ++pl) {
4706 /* We might have removed regions, which alters other regions' layering_index,
4707 so we need to do a recursive diff here.
4709 vector<Command*> cmds;
4710 (*pl)->rdiff (cmds);
4711 _session->add_commands (cmds);
4713 _session->add_command (new StatefulDiffCommand (*pl));
4718 Editor::cut_copy_ranges (CutCopyOp op)
4720 TrackViewList ts = selection->tracks.filter_to_unique_playlists ();
4722 /* Sort the track selection now, so that it if is used, the playlists
4723 selected by the calls below to cut_copy_clear are in the order that
4724 their tracks appear in the editor. This makes things like paste
4725 of ranges work properly.
4728 sort_track_selection (ts);
4731 if (!entered_track) {
4734 ts.push_back (entered_track);
4737 for (TrackViewList::iterator i = ts.begin(); i != ts.end(); ++i) {
4738 (*i)->cut_copy_clear (*selection, op);
4743 Editor::paste (float times, bool from_context)
4745 DEBUG_TRACE (DEBUG::CutNPaste, "paste to preferred edit pos\n");
4746 MusicSample where (get_preferred_edit_position (EDIT_IGNORE_NONE, from_context), 0);
4747 paste_internal (where.sample, times, 0);
4751 Editor::mouse_paste ()
4753 MusicSample where (0, 0);
4755 if (!mouse_sample (where.sample, ignored)) {
4760 paste_internal (where.sample, 1, where.division);
4764 Editor::paste_internal (samplepos_t position, float times, const int32_t sub_num)
4766 DEBUG_TRACE (DEBUG::CutNPaste, string_compose ("apparent paste position is %1\n", position));
4768 if (cut_buffer->empty(internal_editing())) {
4772 if (position == max_samplepos) {
4773 position = get_preferred_edit_position();
4774 DEBUG_TRACE (DEBUG::CutNPaste, string_compose ("preferred edit position is %1\n", position));
4777 if (position != last_paste_pos) {
4778 /* paste in new location, reset repeated paste state */
4780 last_paste_pos = position;
4783 /* get everything in the correct order */
4786 if (!selection->tracks.empty()) {
4787 /* If there is a track selection, paste into exactly those tracks and
4788 * only those tracks. This allows the user to be explicit and override
4789 * the below "do the reasonable thing" logic. */
4790 ts = selection->tracks.filter_to_unique_playlists ();
4791 sort_track_selection (ts);
4793 /* Figure out which track to base the paste at. */
4794 TimeAxisView* base_track = NULL;
4795 if (_edit_point == Editing::EditAtMouse && entered_track) {
4796 /* With the mouse edit point, paste onto the track under the mouse. */
4797 base_track = entered_track;
4798 } else if (_edit_point == Editing::EditAtMouse && entered_regionview) {
4799 /* With the mouse edit point, paste onto the track of the region under the mouse. */
4800 base_track = &entered_regionview->get_time_axis_view();
4801 } else if (_last_cut_copy_source_track) {
4802 /* Paste to the track that the cut/copy came from (see mantis #333). */
4803 base_track = _last_cut_copy_source_track;
4805 /* This is "impossible" since we've copied... well, do nothing. */
4809 /* Walk up to parent if necessary, so base track is a route. */
4810 while (base_track->get_parent()) {
4811 base_track = base_track->get_parent();
4814 /* Add base track and all tracks below it. The paste logic will select
4815 the appropriate object types from the cut buffer in relative order. */
4816 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
4817 if ((*i)->order() >= base_track->order()) {
4822 /* Sort tracks so the nth track of type T will pick the nth object of type T. */
4823 sort_track_selection (ts);
4825 /* Add automation children of each track in order, for pasting several lines. */
4826 for (TrackViewList::iterator i = ts.begin(); i != ts.end();) {
4827 /* Add any automation children for pasting several lines */
4828 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*>(*i++);
4833 typedef RouteTimeAxisView::AutomationTracks ATracks;
4834 const ATracks& atracks = rtv->automation_tracks();
4835 for (ATracks::const_iterator a = atracks.begin(); a != atracks.end(); ++a) {
4836 i = ts.insert(i, a->second.get());
4841 /* We now have a list of trackviews starting at base_track, including
4842 automation children, in the order shown in the editor, e.g. R1,
4843 R1.A1, R1.A2, R2, R2.A1, ... */
4846 begin_reversible_command (Operations::paste);
4848 if (ts.size() == 1 && cut_buffer->lines.size() == 1 &&
4849 dynamic_cast<AutomationTimeAxisView*>(ts.front())) {
4850 /* Only one line copied, and one automation track selected. Do a
4851 "greedy" paste from one automation type to another. */
4853 PasteContext ctx(paste_count, times, ItemCounts(), true);
4854 ts.front()->paste (position, *cut_buffer, ctx, sub_num);
4858 /* Paste into tracks */
4860 PasteContext ctx(paste_count, times, ItemCounts(), false);
4861 for (TrackViewList::iterator i = ts.begin(); i != ts.end(); ++i) {
4862 (*i)->paste (position, *cut_buffer, ctx, sub_num);
4868 commit_reversible_command ();
4872 Editor::duplicate_regions (float times)
4874 RegionSelection rs (get_regions_from_selection_and_entered());
4875 duplicate_some_regions (rs, times);
4879 Editor::duplicate_some_regions (RegionSelection& regions, float times)
4881 if (regions.empty ()) {
4885 boost::shared_ptr<Playlist> playlist;
4886 RegionSelection sel = regions; // clear (below) may clear the argument list if its the current region selection
4887 RegionSelection foo;
4889 samplepos_t const start_sample = regions.start ();
4890 samplepos_t const end_sample = regions.end_sample ();
4891 samplecnt_t const gap = end_sample - start_sample + 1;
4893 begin_reversible_command (Operations::duplicate_region);
4895 selection->clear_regions ();
4897 for (RegionSelection::iterator i = sel.begin(); i != sel.end(); ++i) {
4899 boost::shared_ptr<Region> r ((*i)->region());
4901 TimeAxisView& tv = (*i)->get_time_axis_view();
4902 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (&tv);
4903 latest_regionviews.clear ();
4904 sigc::connection c = rtv->view()->RegionViewAdded.connect (sigc::mem_fun(*this, &Editor::collect_new_region_view));
4906 samplepos_t const position = end_sample + (r->first_sample() - start_sample + 1);
4907 playlist = (*i)->region()->playlist();
4908 playlist->clear_changes ();
4909 playlist->duplicate (r, position, gap, times);
4910 _session->add_command(new StatefulDiffCommand (playlist));
4914 foo.insert (foo.end(), latest_regionviews.begin(), latest_regionviews.end());
4918 selection->set (foo);
4921 commit_reversible_command ();
4925 Editor::duplicate_selection (float times)
4927 if (selection->time.empty() || selection->tracks.empty()) {
4931 boost::shared_ptr<Playlist> playlist;
4933 TrackViewList ts = selection->tracks.filter_to_unique_playlists ();
4935 bool in_command = false;
4937 for (TrackViewList::iterator i = ts.begin(); i != ts.end(); ++i) {
4938 if ((playlist = (*i)->playlist()) == 0) {
4941 playlist->clear_changes ();
4943 if (clicked_selection) {
4944 playlist->duplicate_range (selection->time[clicked_selection], times);
4946 playlist->duplicate_ranges (selection->time, times);
4950 begin_reversible_command (_("duplicate range selection"));
4953 _session->add_command (new StatefulDiffCommand (playlist));
4958 if (times == 1.0f) {
4959 // now "move" range selection to after the current range selection
4960 samplecnt_t distance = 0;
4962 if (clicked_selection) {
4964 selection->time[clicked_selection].end - selection->time[clicked_selection].start;
4966 distance = selection->time.end_sample () - selection->time.start ();
4969 selection->move_time (distance);
4971 commit_reversible_command ();
4975 /** Reset all selected points to the relevant default value */
4977 Editor::reset_point_selection ()
4979 for (PointSelection::iterator i = selection->points.begin(); i != selection->points.end(); ++i) {
4980 ARDOUR::AutomationList::iterator j = (*i)->model ();
4981 (*j)->value = (*i)->line().the_list()->descriptor ().normal;
4986 Editor::center_playhead ()
4988 float const page = _visible_canvas_width * samples_per_pixel;
4989 center_screen_internal (playhead_cursor->current_sample (), page);
4993 Editor::center_edit_point ()
4995 float const page = _visible_canvas_width * samples_per_pixel;
4996 center_screen_internal (get_preferred_edit_position(), page);
4999 /** Caller must begin and commit a reversible command */
5001 Editor::clear_playlist (boost::shared_ptr<Playlist> playlist)
5003 playlist->clear_changes ();
5005 _session->add_command (new StatefulDiffCommand (playlist));
5009 Editor::nudge_track (bool use_edit, bool forwards)
5011 boost::shared_ptr<Playlist> playlist;
5012 samplepos_t distance;
5013 samplepos_t next_distance;
5017 start = get_preferred_edit_position();
5022 if ((distance = get_nudge_distance (start, next_distance)) == 0) {
5026 if (selection->tracks.empty()) {
5030 TrackViewList ts = selection->tracks.filter_to_unique_playlists ();
5031 bool in_command = false;
5033 for (TrackViewList::iterator i = ts.begin(); i != ts.end(); ++i) {
5035 if ((playlist = (*i)->playlist()) == 0) {
5039 playlist->clear_changes ();
5040 playlist->clear_owned_changes ();
5042 playlist->nudge_after (start, distance, forwards);
5045 begin_reversible_command (_("nudge track"));
5048 vector<Command*> cmds;
5050 playlist->rdiff (cmds);
5051 _session->add_commands (cmds);
5053 _session->add_command (new StatefulDiffCommand (playlist));
5057 commit_reversible_command ();
5062 Editor::remove_last_capture ()
5064 vector<string> choices;
5071 if (Config->get_verify_remove_last_capture()) {
5072 prompt = _("Do you really want to destroy the last capture?"
5073 "\n(This is destructive and cannot be undone)");
5075 choices.push_back (_("No, do nothing."));
5076 choices.push_back (_("Yes, destroy it."));
5078 Choice prompter (_("Destroy last capture"), prompt, choices);
5080 if (prompter.run () == 1) {
5081 _session->remove_last_capture ();
5082 _regions->redisplay ();
5086 _session->remove_last_capture();
5087 _regions->redisplay ();
5092 Editor::normalize_region ()
5098 RegionSelection rs = get_regions_from_selection_and_entered ();
5104 NormalizeDialog dialog (rs.size() > 1);
5106 if (dialog.run () != RESPONSE_ACCEPT) {
5110 CursorContext::Handle cursor_ctx = CursorContext::create(*this, _cursors->wait);
5113 /* XXX: should really only count audio regions here */
5114 int const regions = rs.size ();
5116 /* Make a list of the selected audio regions' maximum amplitudes, and also
5117 obtain the maximum amplitude of them all.
5119 list<double> max_amps;
5120 list<double> rms_vals;
5123 bool use_rms = dialog.constrain_rms ();
5125 for (RegionSelection::const_iterator i = rs.begin(); i != rs.end(); ++i) {
5126 AudioRegionView const * arv = dynamic_cast<AudioRegionView const *> (*i);
5130 dialog.descend (1.0 / regions);
5131 double const a = arv->audio_region()->maximum_amplitude (&dialog);
5133 double r = arv->audio_region()->rms (&dialog);
5134 max_rms = max (max_rms, r);
5135 rms_vals.push_back (r);
5139 /* the user cancelled the operation */
5143 max_amps.push_back (a);
5144 max_amp = max (max_amp, a);
5148 list<double>::const_iterator a = max_amps.begin ();
5149 list<double>::const_iterator l = rms_vals.begin ();
5150 bool in_command = false;
5152 for (RegionSelection::iterator r = rs.begin(); r != rs.end(); ++r) {
5153 AudioRegionView* const arv = dynamic_cast<AudioRegionView*> (*r);
5158 arv->region()->clear_changes ();
5160 double amp = dialog.normalize_individually() ? *a : max_amp;
5161 double target = dialog.target_peak (); // dB
5164 double const amp_rms = dialog.normalize_individually() ? *l : max_rms;
5165 const double t_rms = dialog.target_rms ();
5166 const gain_t c_peak = dB_to_coefficient (target);
5167 const gain_t c_rms = dB_to_coefficient (t_rms);
5168 if ((amp_rms / c_rms) > (amp / c_peak)) {
5174 arv->audio_region()->normalize (amp, target);
5177 begin_reversible_command (_("normalize"));
5180 _session->add_command (new StatefulDiffCommand (arv->region()));
5187 commit_reversible_command ();
5193 Editor::reset_region_scale_amplitude ()
5199 RegionSelection rs = get_regions_from_selection_and_entered ();
5205 bool in_command = false;
5207 for (RegionSelection::iterator r = rs.begin(); r != rs.end(); ++r) {
5208 AudioRegionView* const arv = dynamic_cast<AudioRegionView*>(*r);
5211 arv->region()->clear_changes ();
5212 arv->audio_region()->set_scale_amplitude (1.0f);
5215 begin_reversible_command ("reset gain");
5218 _session->add_command (new StatefulDiffCommand (arv->region()));
5222 commit_reversible_command ();
5227 Editor::adjust_region_gain (bool up)
5229 RegionSelection rs = get_regions_from_selection_and_entered ();
5231 if (!_session || rs.empty()) {
5235 bool in_command = false;
5237 for (RegionSelection::iterator r = rs.begin(); r != rs.end(); ++r) {
5238 AudioRegionView* const arv = dynamic_cast<AudioRegionView*>(*r);
5243 arv->region()->clear_changes ();
5245 double dB = accurate_coefficient_to_dB (arv->audio_region()->scale_amplitude ());
5253 arv->audio_region()->set_scale_amplitude (dB_to_coefficient (dB));
5256 begin_reversible_command ("adjust region gain");
5259 _session->add_command (new StatefulDiffCommand (arv->region()));
5263 commit_reversible_command ();
5268 Editor::reset_region_gain ()
5270 RegionSelection rs = get_regions_from_selection_and_entered ();
5272 if (!_session || rs.empty()) {
5276 bool in_command = false;
5278 for (RegionSelection::iterator r = rs.begin(); r != rs.end(); ++r) {
5279 AudioRegionView* const arv = dynamic_cast<AudioRegionView*>(*r);
5284 arv->region()->clear_changes ();
5286 arv->audio_region()->set_scale_amplitude (1.0f);
5289 begin_reversible_command ("reset region gain");
5292 _session->add_command (new StatefulDiffCommand (arv->region()));
5296 commit_reversible_command ();
5301 Editor::reverse_region ()
5307 Reverse rev (*_session);
5308 apply_filter (rev, _("reverse regions"));
5312 Editor::strip_region_silence ()
5318 RegionSelection rs = get_regions_from_selection_and_entered ();
5324 std::list<RegionView*> audio_only;
5326 for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
5327 AudioRegionView* const arv = dynamic_cast<AudioRegionView*> (*i);
5329 audio_only.push_back (arv);
5333 assert (!audio_only.empty());
5335 StripSilenceDialog d (_session, audio_only);
5336 int const r = d.run ();
5340 if (r == Gtk::RESPONSE_OK) {
5341 ARDOUR::AudioIntervalMap silences;
5342 d.silences (silences);
5343 StripSilence s (*_session, silences, d.fade_length());
5345 apply_filter (s, _("strip silence"), &d);
5350 Editor::apply_midi_note_edit_op_to_region (MidiOperator& op, MidiRegionView& mrv)
5352 Evoral::Sequence<Temporal::Beats>::Notes selected;
5353 mrv.selection_as_notelist (selected, true);
5355 vector<Evoral::Sequence<Temporal::Beats>::Notes> v;
5356 v.push_back (selected);
5358 Temporal::Beats pos_beats = Temporal::Beats (mrv.midi_region()->beat()) - mrv.midi_region()->start_beats();
5360 return op (mrv.midi_region()->model(), pos_beats, v);
5364 Editor::apply_midi_note_edit_op (MidiOperator& op, const RegionSelection& rs)
5370 bool in_command = false;
5372 for (RegionSelection::const_iterator r = rs.begin(); r != rs.end(); ) {
5373 RegionSelection::const_iterator tmp = r;
5376 MidiRegionView* const mrv = dynamic_cast<MidiRegionView*> (*r);
5379 Command* cmd = apply_midi_note_edit_op_to_region (op, *mrv);
5382 begin_reversible_command (op.name ());
5386 _session->add_command (cmd);
5394 commit_reversible_command ();
5395 _session->set_dirty ();
5400 Editor::fork_region ()
5402 RegionSelection rs = get_regions_from_selection_and_entered ();
5408 CursorContext::Handle cursor_ctx = CursorContext::create(*this, _cursors->wait);
5409 bool in_command = false;
5413 for (RegionSelection::iterator r = rs.begin(); r != rs.end(); ) {
5414 RegionSelection::iterator tmp = r;
5417 MidiRegionView* const mrv = dynamic_cast<MidiRegionView*>(*r);
5421 boost::shared_ptr<Playlist> playlist = mrv->region()->playlist();
5422 boost::shared_ptr<MidiSource> new_source = _session->create_midi_source_by_stealing_name (mrv->midi_view()->track());
5423 boost::shared_ptr<MidiRegion> newregion = mrv->midi_region()->clone (new_source);
5426 begin_reversible_command (_("Fork Region(s)"));
5429 playlist->clear_changes ();
5430 playlist->replace_region (mrv->region(), newregion, mrv->region()->position());
5431 _session->add_command(new StatefulDiffCommand (playlist));
5433 error << string_compose (_("Could not unlink %1"), mrv->region()->name()) << endmsg;
5441 commit_reversible_command ();
5446 Editor::quantize_region ()
5449 quantize_regions(get_regions_from_selection_and_entered ());
5454 Editor::quantize_regions (const RegionSelection& rs)
5456 if (rs.n_midi_regions() == 0) {
5460 if (!quantize_dialog) {
5461 quantize_dialog = new QuantizeDialog (*this);
5464 if (quantize_dialog->is_mapped()) {
5465 /* in progress already */
5469 quantize_dialog->present ();
5470 const int r = quantize_dialog->run ();
5471 quantize_dialog->hide ();
5473 if (r == Gtk::RESPONSE_OK) {
5474 Quantize quant (quantize_dialog->snap_start(),
5475 quantize_dialog->snap_end(),
5476 quantize_dialog->start_grid_size(),
5477 quantize_dialog->end_grid_size(),
5478 quantize_dialog->strength(),
5479 quantize_dialog->swing(),
5480 quantize_dialog->threshold());
5482 apply_midi_note_edit_op (quant, rs);
5487 Editor::legatize_region (bool shrink_only)
5490 legatize_regions(get_regions_from_selection_and_entered (), shrink_only);
5495 Editor::legatize_regions (const RegionSelection& rs, bool shrink_only)
5497 if (rs.n_midi_regions() == 0) {
5501 Legatize legatize(shrink_only);
5502 apply_midi_note_edit_op (legatize, rs);
5506 Editor::transform_region ()
5509 transform_regions(get_regions_from_selection_and_entered ());
5514 Editor::transform_regions (const RegionSelection& rs)
5516 if (rs.n_midi_regions() == 0) {
5523 const int r = td.run();
5526 if (r == Gtk::RESPONSE_OK) {
5527 Transform transform(td.get());
5528 apply_midi_note_edit_op(transform, rs);
5533 Editor::transpose_region ()
5536 transpose_regions(get_regions_from_selection_and_entered ());
5541 Editor::transpose_regions (const RegionSelection& rs)
5543 if (rs.n_midi_regions() == 0) {
5548 int const r = d.run ();
5550 if (r == RESPONSE_ACCEPT) {
5551 Transpose transpose(d.semitones ());
5552 apply_midi_note_edit_op (transpose, rs);
5557 Editor::insert_patch_change (bool from_context)
5559 RegionSelection rs = get_regions_from_selection_and_entered ();
5565 const samplepos_t p = get_preferred_edit_position (EDIT_IGNORE_NONE, from_context);
5567 /* XXX: bit of a hack; use the MIDNAM from the first selected region;
5568 there may be more than one, but the PatchChangeDialog can only offer
5569 one set of patch menus.
5571 MidiRegionView* first = dynamic_cast<MidiRegionView*> (rs.front ());
5573 Evoral::PatchChange<Temporal::Beats> empty (Temporal::Beats(), 0, 0, 0);
5574 PatchChangeDialog d (0, _session, empty, first->instrument_info(), Gtk::Stock::ADD);
5576 if (d.run() == RESPONSE_CANCEL) {
5580 for (RegionSelection::iterator i = rs.begin (); i != rs.end(); ++i) {
5581 MidiRegionView* const mrv = dynamic_cast<MidiRegionView*> (*i);
5583 if (p >= mrv->region()->first_sample() && p <= mrv->region()->last_sample()) {
5584 mrv->add_patch_change (p - mrv->region()->position(), d.patch ());
5591 Editor::apply_filter (Filter& filter, string command, ProgressReporter* progress)
5593 RegionSelection rs = get_regions_from_selection_and_entered ();
5599 CursorContext::Handle cursor_ctx = CursorContext::create(*this, _cursors->wait);
5600 bool in_command = false;
5605 int const N = rs.size ();
5607 for (RegionSelection::iterator r = rs.begin(); r != rs.end(); ) {
5608 RegionSelection::iterator tmp = r;
5611 AudioRegionView* const arv = dynamic_cast<AudioRegionView*>(*r);
5613 boost::shared_ptr<Playlist> playlist = arv->region()->playlist();
5616 progress->descend (1.0 / N);
5619 if (arv->audio_region()->apply (filter, progress) == 0) {
5621 playlist->clear_changes ();
5622 playlist->clear_owned_changes ();
5625 begin_reversible_command (command);
5629 if (filter.results.empty ()) {
5631 /* no regions returned; remove the old one */
5632 playlist->remove_region (arv->region ());
5636 std::vector<boost::shared_ptr<Region> >::iterator res = filter.results.begin ();
5638 /* first region replaces the old one */
5639 playlist->replace_region (arv->region(), *res, (*res)->position());
5643 while (res != filter.results.end()) {
5644 playlist->add_region (*res, (*res)->position());
5650 /* We might have removed regions, which alters other regions' layering_index,
5651 so we need to do a recursive diff here.
5653 vector<Command*> cmds;
5654 playlist->rdiff (cmds);
5655 _session->add_commands (cmds);
5657 _session->add_command(new StatefulDiffCommand (playlist));
5661 progress->ascend ();
5670 commit_reversible_command ();
5675 Editor::external_edit_region ()
5681 Editor::reset_region_gain_envelopes ()
5683 RegionSelection rs = get_regions_from_selection_and_entered ();
5685 if (!_session || rs.empty()) {
5689 bool in_command = false;
5691 for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
5692 AudioRegionView* const arv = dynamic_cast<AudioRegionView*>(*i);
5694 boost::shared_ptr<AutomationList> alist (arv->audio_region()->envelope());
5695 XMLNode& before (alist->get_state());
5697 arv->audio_region()->set_default_envelope ();
5700 begin_reversible_command (_("reset region gain"));
5703 _session->add_command (new MementoCommand<AutomationList>(*arv->audio_region()->envelope().get(), &before, &alist->get_state()));
5708 commit_reversible_command ();
5713 Editor::set_region_gain_visibility (RegionView* rv)
5715 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (rv);
5717 arv->update_envelope_visibility();
5722 Editor::set_gain_envelope_visibility ()
5728 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
5729 AudioTimeAxisView* v = dynamic_cast<AudioTimeAxisView*>(*i);
5731 v->audio_view()->foreach_regionview (sigc::mem_fun (this, &Editor::set_region_gain_visibility));
5737 Editor::toggle_gain_envelope_active ()
5739 if (_ignore_region_action) {
5743 RegionSelection rs = get_regions_from_selection_and_entered ();
5745 if (!_session || rs.empty()) {
5749 bool in_command = false;
5751 for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
5752 AudioRegionView* const arv = dynamic_cast<AudioRegionView*>(*i);
5754 arv->region()->clear_changes ();
5755 arv->audio_region()->set_envelope_active (!arv->audio_region()->envelope_active());
5758 begin_reversible_command (_("region gain envelope active"));
5761 _session->add_command (new StatefulDiffCommand (arv->region()));
5766 commit_reversible_command ();
5771 Editor::toggle_region_lock ()
5773 if (_ignore_region_action) {
5777 RegionSelection rs = get_regions_from_selection_and_entered ();
5779 if (!_session || rs.empty()) {
5783 begin_reversible_command (_("toggle region lock"));
5785 for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
5786 (*i)->region()->clear_changes ();
5787 (*i)->region()->set_locked (!(*i)->region()->locked());
5788 _session->add_command (new StatefulDiffCommand ((*i)->region()));
5791 commit_reversible_command ();
5795 Editor::toggle_region_video_lock ()
5797 if (_ignore_region_action) {
5801 RegionSelection rs = get_regions_from_selection_and_entered ();
5803 if (!_session || rs.empty()) {
5807 begin_reversible_command (_("Toggle Video Lock"));
5809 for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
5810 (*i)->region()->clear_changes ();
5811 (*i)->region()->set_video_locked (!(*i)->region()->video_locked());
5812 _session->add_command (new StatefulDiffCommand ((*i)->region()));
5815 commit_reversible_command ();
5819 Editor::toggle_region_lock_style ()
5821 if (_ignore_region_action) {
5825 RegionSelection rs = get_regions_from_selection_and_entered ();
5827 if (!_session || rs.empty()) {
5831 Glib::RefPtr<ToggleAction> a = Glib::RefPtr<ToggleAction>::cast_dynamic (_region_actions->get_action("toggle-region-lock-style"));
5832 vector<Widget*> proxies = a->get_proxies();
5833 Gtk::CheckMenuItem* cmi = dynamic_cast<Gtk::CheckMenuItem*> (proxies.front());
5837 begin_reversible_command (_("toggle region lock style"));
5839 for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
5840 (*i)->region()->clear_changes ();
5841 PositionLockStyle const ns = ((*i)->region()->position_lock_style() == AudioTime && !cmi->get_inconsistent()) ? MusicTime : AudioTime;
5842 (*i)->region()->set_position_lock_style (ns);
5843 _session->add_command (new StatefulDiffCommand ((*i)->region()));
5846 commit_reversible_command ();
5850 Editor::toggle_opaque_region ()
5852 if (_ignore_region_action) {
5856 RegionSelection rs = get_regions_from_selection_and_entered ();
5858 if (!_session || rs.empty()) {
5862 begin_reversible_command (_("change region opacity"));
5864 for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
5865 (*i)->region()->clear_changes ();
5866 (*i)->region()->set_opaque (!(*i)->region()->opaque());
5867 _session->add_command (new StatefulDiffCommand ((*i)->region()));
5870 commit_reversible_command ();
5874 Editor::toggle_record_enable ()
5876 bool new_state = false;
5878 for (TrackSelection::iterator i = selection->tracks.begin(); i != selection->tracks.end(); ++i) {
5879 RouteTimeAxisView *rtav = dynamic_cast<RouteTimeAxisView *>(*i);
5882 if (!rtav->is_track())
5886 new_state = !rtav->track()->rec_enable_control()->get_value();
5890 rtav->track()->rec_enable_control()->set_value (new_state, Controllable::UseGroup);
5895 tracklist_to_stripables (TrackViewList list)
5899 for (TrackSelection::iterator i = list.begin(); i != list.end(); ++i) {
5900 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> ((*i));
5902 if (rtv && rtv->is_track()) {
5903 ret.push_back (rtv->track());
5911 Editor::play_solo_selection (bool restart)
5913 //note: session::solo_selection takes care of invalidating the region playlist
5915 if ((!selection->tracks.empty()) && selection->time.length() > 0) { //a range is selected; solo the tracks and roll
5917 StripableList sl = tracklist_to_stripables (selection->tracks);
5918 _session->solo_selection (sl, true);
5921 samplepos_t start = selection->time.start();
5922 samplepos_t end = selection->time.end_sample();
5923 _session->request_bounded_roll (start, end);
5925 } else if (! selection->tracks.empty()) { //no range is selected, but tracks are selected; solo the tracks and roll
5926 StripableList sl = tracklist_to_stripables (selection->tracks);
5927 _session->solo_selection (sl, true);
5928 _session->request_cancel_play_range();
5929 transition_to_rolling (true);
5931 } else if (! selection->regions.empty()) { //solo any tracks with selected regions, and roll
5932 StripableList sl = tracklist_to_stripables (get_tracks_for_range_action());
5933 _session->solo_selection (sl, true);
5934 _session->request_cancel_play_range();
5935 transition_to_rolling (true);
5937 _session->request_cancel_play_range();
5938 transition_to_rolling (true); //no selection. just roll.
5943 Editor::toggle_solo ()
5945 bool new_state = false;
5947 boost::shared_ptr<ControlList> cl (new ControlList);
5949 for (TrackSelection::iterator i = selection->tracks.begin(); i != selection->tracks.end(); ++i) {
5950 StripableTimeAxisView *stav = dynamic_cast<StripableTimeAxisView *>(*i);
5952 if (!stav || !stav->stripable()->solo_control()) {
5957 new_state = !stav->stripable()->solo_control()->soloed ();
5961 cl->push_back (stav->stripable()->solo_control());
5964 _session->set_controls (cl, new_state ? 1.0 : 0.0, Controllable::UseGroup);
5968 Editor::toggle_mute ()
5970 bool new_state = false;
5972 boost::shared_ptr<ControlList> cl (new ControlList);
5974 for (TrackSelection::iterator i = selection->tracks.begin(); i != selection->tracks.end(); ++i) {
5975 StripableTimeAxisView *stav = dynamic_cast<StripableTimeAxisView *>(*i);
5977 if (!stav || !stav->stripable()->mute_control()) {
5982 new_state = !stav->stripable()->mute_control()->muted();
5986 cl->push_back (stav->stripable()->mute_control());
5989 _session->set_controls (cl, new_state, Controllable::UseGroup);
5993 Editor::toggle_solo_isolate ()
5999 Editor::fade_range ()
6001 TrackViewList ts = selection->tracks.filter_to_unique_playlists ();
6003 begin_reversible_command (_("fade range"));
6005 for (TrackViewList::iterator i = ts.begin(); i != ts.end(); ++i) {
6006 (*i)->fade_range (selection->time);
6009 commit_reversible_command ();
6014 Editor::set_fade_length (bool in)
6016 RegionSelection rs = get_regions_from_selection_and_entered ();
6022 /* we need a region to measure the offset from the start */
6024 RegionView* rv = rs.front ();
6026 samplepos_t pos = get_preferred_edit_position();
6030 if (pos > rv->region()->last_sample() || pos < rv->region()->first_sample()) {
6031 /* edit point is outside the relevant region */
6036 if (pos <= rv->region()->position()) {
6040 len = pos - rv->region()->position();
6041 cmd = _("set fade in length");
6043 if (pos >= rv->region()->last_sample()) {
6047 len = rv->region()->last_sample() - pos;
6048 cmd = _("set fade out length");
6051 bool in_command = false;
6053 for (RegionSelection::iterator x = rs.begin(); x != rs.end(); ++x) {
6054 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (*x);
6060 boost::shared_ptr<AutomationList> alist;
6062 alist = tmp->audio_region()->fade_in();
6064 alist = tmp->audio_region()->fade_out();
6067 XMLNode &before = alist->get_state();
6070 tmp->audio_region()->set_fade_in_length (len);
6071 tmp->audio_region()->set_fade_in_active (true);
6073 tmp->audio_region()->set_fade_out_length (len);
6074 tmp->audio_region()->set_fade_out_active (true);
6078 begin_reversible_command (cmd);
6081 XMLNode &after = alist->get_state();
6082 _session->add_command(new MementoCommand<AutomationList>(*alist, &before, &after));
6086 commit_reversible_command ();
6091 Editor::set_fade_in_shape (FadeShape shape)
6093 RegionSelection rs = get_regions_from_selection_and_entered ();
6098 bool in_command = false;
6100 for (RegionSelection::iterator x = rs.begin(); x != rs.end(); ++x) {
6101 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (*x);
6107 boost::shared_ptr<AutomationList> alist = tmp->audio_region()->fade_in();
6108 XMLNode &before = alist->get_state();
6110 tmp->audio_region()->set_fade_in_shape (shape);
6113 begin_reversible_command (_("set fade in shape"));
6116 XMLNode &after = alist->get_state();
6117 _session->add_command(new MementoCommand<AutomationList>(*alist.get(), &before, &after));
6121 commit_reversible_command ();
6126 Editor::set_fade_out_shape (FadeShape shape)
6128 RegionSelection rs = get_regions_from_selection_and_entered ();
6133 bool in_command = false;
6135 for (RegionSelection::iterator x = rs.begin(); x != rs.end(); ++x) {
6136 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (*x);
6142 boost::shared_ptr<AutomationList> alist = tmp->audio_region()->fade_out();
6143 XMLNode &before = alist->get_state();
6145 tmp->audio_region()->set_fade_out_shape (shape);
6148 begin_reversible_command (_("set fade out shape"));
6151 XMLNode &after = alist->get_state();
6152 _session->add_command(new MementoCommand<AutomationList>(*alist.get(), &before, &after));
6156 commit_reversible_command ();
6161 Editor::set_fade_in_active (bool yn)
6163 RegionSelection rs = get_regions_from_selection_and_entered ();
6168 bool in_command = false;
6170 for (RegionSelection::iterator x = rs.begin(); x != rs.end(); ++x) {
6171 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (*x);
6178 boost::shared_ptr<AudioRegion> ar (tmp->audio_region());
6180 ar->clear_changes ();
6181 ar->set_fade_in_active (yn);
6184 begin_reversible_command (_("set fade in active"));
6187 _session->add_command (new StatefulDiffCommand (ar));
6191 commit_reversible_command ();
6196 Editor::set_fade_out_active (bool yn)
6198 RegionSelection rs = get_regions_from_selection_and_entered ();
6203 bool in_command = false;
6205 for (RegionSelection::iterator x = rs.begin(); x != rs.end(); ++x) {
6206 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (*x);
6212 boost::shared_ptr<AudioRegion> ar (tmp->audio_region());
6214 ar->clear_changes ();
6215 ar->set_fade_out_active (yn);
6218 begin_reversible_command (_("set fade out active"));
6221 _session->add_command(new StatefulDiffCommand (ar));
6225 commit_reversible_command ();
6230 Editor::toggle_region_fades (int dir)
6232 if (_ignore_region_action) {
6236 boost::shared_ptr<AudioRegion> ar;
6239 RegionSelection rs = get_regions_from_selection_and_entered ();
6245 RegionSelection::iterator i;
6246 for (i = rs.begin(); i != rs.end(); ++i) {
6247 if ((ar = boost::dynamic_pointer_cast<AudioRegion>((*i)->region())) != 0) {
6249 yn = ar->fade_out_active ();
6251 yn = ar->fade_in_active ();
6257 if (i == rs.end()) {
6261 /* XXX should this undo-able? */
6262 bool in_command = false;
6264 for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
6265 if ((ar = boost::dynamic_pointer_cast<AudioRegion>((*i)->region())) == 0) {
6268 ar->clear_changes ();
6270 if (dir == 1 || dir == 0) {
6271 ar->set_fade_in_active (!yn);
6274 if (dir == -1 || dir == 0) {
6275 ar->set_fade_out_active (!yn);
6278 begin_reversible_command (_("toggle fade active"));
6281 _session->add_command(new StatefulDiffCommand (ar));
6285 commit_reversible_command ();
6290 /** Update region fade visibility after its configuration has been changed */
6292 Editor::update_region_fade_visibility ()
6294 bool _fade_visibility = _session->config.get_show_region_fades ();
6296 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
6297 AudioTimeAxisView* v = dynamic_cast<AudioTimeAxisView*>(*i);
6299 if (_fade_visibility) {
6300 v->audio_view()->show_all_fades ();
6302 v->audio_view()->hide_all_fades ();
6309 Editor::set_edit_point ()
6312 MusicSample where (0, 0);
6314 if (!mouse_sample (where.sample, ignored)) {
6320 if (selection->markers.empty()) {
6322 mouse_add_new_marker (where.sample);
6327 Location* loc = find_location_from_marker (selection->markers.front(), ignored);
6330 loc->move_to (where.sample, where.division);
6336 Editor::set_playhead_cursor ()
6338 if (entered_marker) {
6339 _session->request_locate (entered_marker->position(), _session->transport_rolling());
6341 MusicSample where (0, 0);
6344 if (!mouse_sample (where.sample, ignored)) {
6351 _session->request_locate (where.sample, _session->transport_rolling());
6355 //not sure what this was for; remove it for now.
6356 // if (UIConfiguration::instance().get_follow_edits() && (!_session || !_session->config.get_external_sync())) {
6357 // cancel_time_selection();
6363 Editor::split_region ()
6365 if (_drags->active ()) {
6369 //if a range is selected, separate it
6370 if (!selection->time.empty()) {
6371 separate_regions_between (selection->time);
6375 //if no range was selected, try to find some regions to split
6376 if (current_mouse_mode() == MouseObject || current_mouse_mode() == MouseRange ) { //don't try this for Internal Edit, Stretch, Draw, etc.
6378 RegionSelection rs = get_regions_from_selection_and_edit_point ();
6379 const samplepos_t pos = get_preferred_edit_position();
6380 const int32_t division = get_grid_music_divisions (0);
6381 MusicSample where (pos, division);
6387 split_regions_at (where, rs);
6393 Editor::select_next_stripable (bool routes_only)
6395 if (selection->tracks.empty()) {
6396 selection->set (track_views.front());
6400 TimeAxisView* current = selection->tracks.front();
6404 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
6406 if (*i == current) {
6408 if (i != track_views.end()) {
6411 current = (*(track_views.begin()));
6412 //selection->set (*(track_views.begin()));
6419 RouteUI* rui = dynamic_cast<RouteUI *>(current);
6420 valid = rui && rui->route()->active();
6422 valid = 0 != current->stripable ().get();
6425 } while (current->hidden() || !valid);
6427 selection->set (current);
6429 ensure_time_axis_view_is_visible (*current, false);
6433 Editor::select_prev_stripable (bool routes_only)
6435 if (selection->tracks.empty()) {
6436 selection->set (track_views.front());
6440 TimeAxisView* current = selection->tracks.front();
6444 for (TrackViewList::reverse_iterator i = track_views.rbegin(); i != track_views.rend(); ++i) {
6446 if (*i == current) {
6448 if (i != track_views.rend()) {
6451 current = *(track_views.rbegin());
6457 RouteUI* rui = dynamic_cast<RouteUI *>(current);
6458 valid = rui && rui->route()->active();
6460 valid = 0 != current->stripable ().get();
6463 } while (current->hidden() || !valid);
6465 selection->set (current);
6467 ensure_time_axis_view_is_visible (*current, false);
6471 Editor::set_loop_from_selection (bool play)
6473 if (_session == 0) {
6477 samplepos_t start, end;
6478 if (!get_selection_extents (start, end))
6481 set_loop_range (start, end, _("set loop range from selection"));
6484 _session->request_play_loop (true, true);
6489 Editor::set_loop_from_region (bool play)
6491 samplepos_t start, end;
6492 if (!get_selection_extents (start, end))
6495 set_loop_range (start, end, _("set loop range from region"));
6498 _session->request_locate (start, true);
6499 _session->request_play_loop (true);
6504 Editor::set_punch_from_selection ()
6506 if (_session == 0) {
6510 samplepos_t start, end;
6511 if (!get_selection_extents (start, end))
6514 set_punch_range (start, end, _("set punch range from selection"));
6518 Editor::set_auto_punch_range ()
6520 // auto punch in/out button from a single button
6521 // If Punch In is unset, set punch range from playhead to end, enable punch in
6522 // If Punch In is set, the next punch sets Punch Out, unless the playhead has been
6523 // rewound beyond the Punch In marker, in which case that marker will be moved back
6524 // to the current playhead position.
6525 // If punch out is set, it clears the punch range and Punch In/Out buttons
6527 if (_session == 0) {
6531 Location* tpl = transport_punch_location();
6532 samplepos_t now = playhead_cursor->current_sample();
6533 samplepos_t begin = now;
6534 samplepos_t end = _session->current_end_sample();
6536 if (!_session->config.get_punch_in()) {
6537 // First Press - set punch in and create range from here to eternity
6538 set_punch_range (begin, end, _("Auto Punch In"));
6539 _session->config.set_punch_in(true);
6540 } else if (tpl && !_session->config.get_punch_out()) {
6541 // Second press - update end range marker and set punch_out
6542 if (now < tpl->start()) {
6543 // playhead has been rewound - move start back and pretend nothing happened
6545 set_punch_range (begin, end, _("Auto Punch In/Out"));
6547 // normal case for 2nd press - set the punch out
6548 end = playhead_cursor->current_sample ();
6549 set_punch_range (tpl->start(), now, _("Auto Punch In/Out"));
6550 _session->config.set_punch_out(true);
6553 if (_session->config.get_punch_out()) {
6554 _session->config.set_punch_out(false);
6557 if (_session->config.get_punch_in()) {
6558 _session->config.set_punch_in(false);
6563 // third press - unset punch in/out and remove range
6564 _session->locations()->remove(tpl);
6571 Editor::set_session_extents_from_selection ()
6573 if (_session == 0) {
6577 samplepos_t start, end;
6578 if (!get_selection_extents (start, end))
6582 if ((loc = _session->locations()->session_range_location()) == 0) {
6583 _session->set_session_extents (start, end); // this will create a new session range; no need for UNDO
6585 XMLNode &before = loc->get_state();
6587 _session->set_session_extents (start, end);
6589 XMLNode &after = loc->get_state();
6591 begin_reversible_command (_("set session start/end from selection"));
6593 _session->add_command (new MementoCommand<Location>(*loc, &before, &after));
6595 commit_reversible_command ();
6598 _session->set_end_is_free (false);
6602 Editor::set_punch_start_from_edit_point ()
6606 MusicSample start (0, 0);
6607 samplepos_t end = max_samplepos;
6609 //use the existing punch end, if any
6610 Location* tpl = transport_punch_location();
6615 if ((_edit_point == EditAtPlayhead) && _session->transport_rolling()) {
6616 start.sample = _session->audible_sample();
6618 start.sample = get_preferred_edit_position();
6621 //snap the selection start/end
6624 //if there's not already a sensible selection endpoint, go "forever"
6625 if (start.sample > end) {
6626 end = max_samplepos;
6629 set_punch_range (start.sample, end, _("set punch start from EP"));
6635 Editor::set_punch_end_from_edit_point ()
6639 samplepos_t start = 0;
6640 MusicSample end (max_samplepos, 0);
6642 //use the existing punch start, if any
6643 Location* tpl = transport_punch_location();
6645 start = tpl->start();
6648 if ((_edit_point == EditAtPlayhead) && _session->transport_rolling()) {
6649 end.sample = _session->audible_sample();
6651 end.sample = get_preferred_edit_position();
6654 //snap the selection start/end
6657 set_punch_range (start, end.sample, _("set punch end from EP"));
6663 Editor::set_loop_start_from_edit_point ()
6667 MusicSample start (0, 0);
6668 samplepos_t end = max_samplepos;
6670 //use the existing loop end, if any
6671 Location* tpl = transport_loop_location();
6676 if ((_edit_point == EditAtPlayhead) && _session->transport_rolling()) {
6677 start.sample = _session->audible_sample();
6679 start.sample = get_preferred_edit_position();
6682 //snap the selection start/end
6685 //if there's not already a sensible selection endpoint, go "forever"
6686 if (start.sample > end) {
6687 end = max_samplepos;
6690 set_loop_range (start.sample, end, _("set loop start from EP"));
6696 Editor::set_loop_end_from_edit_point ()
6700 samplepos_t start = 0;
6701 MusicSample end (max_samplepos, 0);
6703 //use the existing loop start, if any
6704 Location* tpl = transport_loop_location();
6706 start = tpl->start();
6709 if ((_edit_point == EditAtPlayhead) && _session->transport_rolling()) {
6710 end.sample = _session->audible_sample();
6712 end.sample = get_preferred_edit_position();
6715 //snap the selection start/end
6718 set_loop_range (start, end.sample, _("set loop end from EP"));
6723 Editor::set_punch_from_region ()
6725 samplepos_t start, end;
6726 if (!get_selection_extents (start, end))
6729 set_punch_range (start, end, _("set punch range from region"));
6733 Editor::pitch_shift_region ()
6735 RegionSelection rs = get_regions_from_selection_and_entered ();
6737 RegionSelection audio_rs;
6738 for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
6739 if (dynamic_cast<AudioRegionView*> (*i)) {
6740 audio_rs.push_back (*i);
6744 if (audio_rs.empty()) {
6748 pitch_shift (audio_rs, 1.2);
6752 Editor::set_tempo_from_region ()
6754 RegionSelection rs = get_regions_from_selection_and_entered ();
6756 if (!_session || rs.empty()) {
6760 RegionView* rv = rs.front();
6762 define_one_bar (rv->region()->position(), rv->region()->last_sample() + 1);
6766 Editor::use_range_as_bar ()
6768 samplepos_t start, end;
6769 if (get_edit_op_range (start, end)) {
6770 define_one_bar (start, end);
6775 Editor::define_one_bar (samplepos_t start, samplepos_t end)
6777 samplepos_t length = end - start;
6779 const Meter& m (_session->tempo_map().meter_at_sample (start));
6781 /* length = 1 bar */
6783 /* We're going to deliver a constant tempo here,
6784 so we can use samples per beat to determine length.
6785 now we want samples per beat.
6786 we have samples per bar, and beats per bar, so ...
6789 /* XXXX METER MATH */
6791 double samples_per_beat = length / m.divisions_per_bar();
6793 /* beats per minute = */
6795 double beats_per_minute = (_session->sample_rate() * 60.0) / samples_per_beat;
6797 /* now decide whether to:
6799 (a) set global tempo
6800 (b) add a new tempo marker
6804 const TempoSection& t (_session->tempo_map().tempo_section_at_sample (start));
6806 bool do_global = false;
6808 if ((_session->tempo_map().n_tempos() == 1) && (_session->tempo_map().n_meters() == 1)) {
6810 /* only 1 tempo & 1 meter: ask if the user wants to set the tempo
6811 at the start, or create a new marker
6814 vector<string> options;
6815 options.push_back (_("Cancel"));
6816 options.push_back (_("Add new marker"));
6817 options.push_back (_("Set global tempo"));
6820 _("Define one bar"),
6821 _("Do you want to set the global tempo or add a new tempo marker?"),
6825 c.set_default_response (2);
6841 /* more than 1 tempo and/or meter section already, go ahead do the "usual":
6842 if the marker is at the region starter, change it, otherwise add
6847 begin_reversible_command (_("set tempo from region"));
6848 XMLNode& before (_session->tempo_map().get_state());
6851 _session->tempo_map().change_initial_tempo (beats_per_minute, t.note_type(), t.end_note_types_per_minute());
6852 } else if (t.sample() == start) {
6853 _session->tempo_map().change_existing_tempo_at (start, beats_per_minute, t.note_type(), t.end_note_types_per_minute());
6855 /* constant tempo */
6856 const Tempo tempo (beats_per_minute, t.note_type());
6857 _session->tempo_map().add_tempo (tempo, 0.0, start, AudioTime);
6860 XMLNode& after (_session->tempo_map().get_state());
6862 _session->add_command (new MementoCommand<TempoMap>(_session->tempo_map(), &before, &after));
6863 commit_reversible_command ();
6867 Editor::split_region_at_transients ()
6869 AnalysisFeatureList positions;
6871 RegionSelection rs = get_regions_from_selection_and_entered ();
6873 if (!_session || rs.empty()) {
6877 begin_reversible_command (_("split regions"));
6879 for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ) {
6881 RegionSelection::iterator tmp;
6886 boost::shared_ptr<AudioRegion> ar = boost::dynamic_pointer_cast<AudioRegion> ((*i)->region());
6889 ar->transients (positions);
6890 split_region_at_points ((*i)->region(), positions, true);
6897 commit_reversible_command ();
6902 Editor::split_region_at_points (boost::shared_ptr<Region> r, AnalysisFeatureList& positions, bool can_ferret, bool select_new)
6904 bool use_rhythmic_rodent = false;
6906 boost::shared_ptr<Playlist> pl = r->playlist();
6908 list<boost::shared_ptr<Region> > new_regions;
6914 if (positions.empty()) {
6918 if (positions.size() > 20 && can_ferret) {
6919 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);
6920 MessageDialog msg (msgstr,
6923 Gtk::BUTTONS_OK_CANCEL);
6926 msg.add_button (_("Call for the Ferret!"), RESPONSE_APPLY);
6927 msg.set_secondary_text (_("Press OK to continue with this split operation\nor ask the Ferret dialog to tune the analysis"));
6929 msg.set_secondary_text (_("Press OK to continue with this split operation"));
6932 msg.set_title (_("Excessive split?"));
6935 int response = msg.run();
6941 case RESPONSE_APPLY:
6942 use_rhythmic_rodent = true;
6949 if (use_rhythmic_rodent) {
6950 show_rhythm_ferret ();
6954 AnalysisFeatureList::const_iterator x;
6956 pl->clear_changes ();
6957 pl->clear_owned_changes ();
6959 x = positions.begin();
6961 if (x == positions.end()) {
6966 pl->remove_region (r);
6968 samplepos_t pos = 0;
6970 samplepos_t rstart = r->first_sample ();
6971 samplepos_t rend = r->last_sample ();
6973 while (x != positions.end()) {
6975 /* deal with positons that are out of scope of present region bounds */
6976 if (*x <= rstart || *x > rend) {
6981 /* file start = original start + how far we from the initial position ? */
6983 samplepos_t file_start = r->start() + pos;
6985 /* length = next position - current position */
6987 samplepos_t len = (*x) - pos - rstart;
6989 /* XXX we do we really want to allow even single-sample regions?
6990 * shouldn't we have some kind of lower limit on region size?
6999 if (RegionFactory::region_name (new_name, r->name())) {
7003 /* do NOT announce new regions 1 by one, just wait till they are all done */
7007 plist.add (ARDOUR::Properties::start, file_start);
7008 plist.add (ARDOUR::Properties::length, len);
7009 plist.add (ARDOUR::Properties::name, new_name);
7010 plist.add (ARDOUR::Properties::layer, 0);
7011 // TODO set transients_offset
7013 boost::shared_ptr<Region> nr = RegionFactory::create (r->sources(), plist, false);
7014 /* because we set annouce to false, manually add the new region to the
7017 RegionFactory::map_add (nr);
7019 pl->add_region (nr, rstart + pos);
7022 new_regions.push_front(nr);
7031 RegionFactory::region_name (new_name, r->name());
7033 /* Add the final region */
7036 plist.add (ARDOUR::Properties::start, r->start() + pos);
7037 plist.add (ARDOUR::Properties::length, r->last_sample() - (r->position() + pos) + 1);
7038 plist.add (ARDOUR::Properties::name, new_name);
7039 plist.add (ARDOUR::Properties::layer, 0);
7041 boost::shared_ptr<Region> nr = RegionFactory::create (r->sources(), plist, false);
7042 /* because we set annouce to false, manually add the new region to the
7045 RegionFactory::map_add (nr);
7046 pl->add_region (nr, r->position() + pos);
7049 new_regions.push_front(nr);
7054 /* We might have removed regions, which alters other regions' layering_index,
7055 so we need to do a recursive diff here.
7057 vector<Command*> cmds;
7059 _session->add_commands (cmds);
7061 _session->add_command (new StatefulDiffCommand (pl));
7065 for (list<boost::shared_ptr<Region> >::iterator i = new_regions.begin(); i != new_regions.end(); ++i){
7066 set_selected_regionview_from_region_list ((*i), Selection::Add);
7072 Editor::place_transient()
7078 RegionSelection rs = get_regions_from_selection_and_edit_point ();
7084 samplepos_t where = get_preferred_edit_position();
7086 begin_reversible_command (_("place transient"));
7088 for (RegionSelection::iterator r = rs.begin(); r != rs.end(); ++r) {
7089 (*r)->region()->add_transient(where);
7092 commit_reversible_command ();
7096 Editor::remove_transient(ArdourCanvas::Item* item)
7102 ArdourCanvas::Line* _line = reinterpret_cast<ArdourCanvas::Line*> (item);
7105 AudioRegionView* _arv = reinterpret_cast<AudioRegionView*> (item->get_data ("regionview"));
7106 _arv->remove_transient (*(float*) _line->get_data ("position"));
7110 Editor::snap_regions_to_grid ()
7112 list <boost::shared_ptr<Playlist > > used_playlists;
7114 RegionSelection rs = get_regions_from_selection_and_entered ();
7116 if (!_session || rs.empty()) {
7120 begin_reversible_command (_("snap regions to grid"));
7122 for (RegionSelection::iterator r = rs.begin(); r != rs.end(); ++r) {
7124 boost::shared_ptr<Playlist> pl = (*r)->region()->playlist();
7126 if (!pl->frozen()) {
7127 /* we haven't seen this playlist before */
7129 /* remember used playlists so we can thaw them later */
7130 used_playlists.push_back(pl);
7133 (*r)->region()->clear_changes ();
7135 MusicSample start ((*r)->region()->first_sample (), 0);
7136 snap_to (start, RoundNearest, SnapToGrid);
7137 (*r)->region()->set_position (start.sample, start.division);
7138 _session->add_command(new StatefulDiffCommand ((*r)->region()));
7141 while (used_playlists.size() > 0) {
7142 list <boost::shared_ptr<Playlist > >::iterator i = used_playlists.begin();
7144 used_playlists.pop_front();
7147 commit_reversible_command ();
7151 Editor::close_region_gaps ()
7153 list <boost::shared_ptr<Playlist > > used_playlists;
7155 RegionSelection rs = get_regions_from_selection_and_entered ();
7157 if (!_session || rs.empty()) {
7161 Dialog dialog (_("Close Region Gaps"));
7164 table.set_spacings (12);
7165 table.set_border_width (12);
7166 Label* l = manage (left_aligned_label (_("Crossfade length")));
7167 table.attach (*l, 0, 1, 0, 1);
7169 SpinButton spin_crossfade (1, 0);
7170 spin_crossfade.set_range (0, 15);
7171 spin_crossfade.set_increments (1, 1);
7172 spin_crossfade.set_value (5);
7173 table.attach (spin_crossfade, 1, 2, 0, 1);
7175 table.attach (*manage (new Label (_("ms"))), 2, 3, 0, 1);
7177 l = manage (left_aligned_label (_("Pull-back length")));
7178 table.attach (*l, 0, 1, 1, 2);
7180 SpinButton spin_pullback (1, 0);
7181 spin_pullback.set_range (0, 100);
7182 spin_pullback.set_increments (1, 1);
7183 spin_pullback.set_value(30);
7184 table.attach (spin_pullback, 1, 2, 1, 2);
7186 table.attach (*manage (new Label (_("ms"))), 2, 3, 1, 2);
7188 dialog.get_vbox()->pack_start (table);
7189 dialog.add_button (Stock::CANCEL, RESPONSE_CANCEL);
7190 dialog.add_button (_("Ok"), RESPONSE_ACCEPT);
7193 if (dialog.run () == RESPONSE_CANCEL) {
7197 samplepos_t crossfade_len = spin_crossfade.get_value();
7198 samplepos_t pull_back_samples = spin_pullback.get_value();
7200 crossfade_len = lrintf (crossfade_len * _session->sample_rate()/1000);
7201 pull_back_samples = lrintf (pull_back_samples * _session->sample_rate()/1000);
7203 /* Iterate over the region list and make adjacent regions overlap by crossfade_len_ms */
7205 begin_reversible_command (_("close region gaps"));
7208 boost::shared_ptr<Region> last_region;
7210 rs.sort_by_position_and_track();
7212 for (RegionSelection::iterator r = rs.begin(); r != rs.end(); ++r) {
7214 boost::shared_ptr<Playlist> pl = (*r)->region()->playlist();
7216 if (!pl->frozen()) {
7217 /* we haven't seen this playlist before */
7219 /* remember used playlists so we can thaw them later */
7220 used_playlists.push_back(pl);
7224 samplepos_t position = (*r)->region()->position();
7226 if (idx == 0 || position < last_region->position()){
7227 last_region = (*r)->region();
7232 (*r)->region()->clear_changes ();
7233 (*r)->region()->trim_front((position - pull_back_samples));
7235 last_region->clear_changes ();
7236 last_region->trim_end ((position - pull_back_samples + crossfade_len));
7238 _session->add_command (new StatefulDiffCommand ((*r)->region()));
7239 _session->add_command (new StatefulDiffCommand (last_region));
7241 last_region = (*r)->region();
7245 while (used_playlists.size() > 0) {
7246 list <boost::shared_ptr<Playlist > >::iterator i = used_playlists.begin();
7248 used_playlists.pop_front();
7251 commit_reversible_command ();
7255 Editor::tab_to_transient (bool forward)
7257 AnalysisFeatureList positions;
7259 RegionSelection rs = get_regions_from_selection_and_entered ();
7265 samplepos_t pos = _session->audible_sample ();
7267 if (!selection->tracks.empty()) {
7269 /* don't waste time searching for transients in duplicate playlists.
7272 TrackViewList ts = selection->tracks.filter_to_unique_playlists ();
7274 for (TrackViewList::iterator t = ts.begin(); t != ts.end(); ++t) {
7276 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (*t);
7279 boost::shared_ptr<Track> tr = rtv->track();
7281 boost::shared_ptr<Playlist> pl = tr->playlist ();
7283 samplepos_t result = pl->find_next_transient (pos, forward ? 1 : -1);
7286 positions.push_back (result);
7299 for (RegionSelection::iterator r = rs.begin(); r != rs.end(); ++r) {
7300 (*r)->region()->get_transients (positions);
7304 TransientDetector::cleanup_transients (positions, _session->sample_rate(), 3.0);
7307 AnalysisFeatureList::iterator x;
7309 for (x = positions.begin(); x != positions.end(); ++x) {
7315 if (x != positions.end ()) {
7316 _session->request_locate (*x);
7320 AnalysisFeatureList::reverse_iterator x;
7322 for (x = positions.rbegin(); x != positions.rend(); ++x) {
7328 if (x != positions.rend ()) {
7329 _session->request_locate (*x);
7335 Editor::playhead_forward_to_grid ()
7341 MusicSample pos (playhead_cursor->current_sample (), 0);
7343 if (pos.sample < max_samplepos - 1) {
7345 snap_to_internal (pos, RoundUpAlways, SnapToGrid, false, true);
7346 _session->request_locate (pos.sample);
7352 Editor::playhead_backward_to_grid ()
7358 MusicSample pos (playhead_cursor->current_sample (), 0);
7360 if (pos.sample > 2) {
7362 snap_to_internal (pos, RoundDownAlways, SnapToGrid, false, true);
7363 _session->request_locate (pos.sample);
7368 Editor::set_track_height (Height h)
7370 TrackSelection& ts (selection->tracks);
7372 for (TrackSelection::iterator x = ts.begin(); x != ts.end(); ++x) {
7373 (*x)->set_height_enum (h);
7378 Editor::toggle_tracks_active ()
7380 TrackSelection& ts (selection->tracks);
7382 bool target = false;
7388 for (TrackSelection::iterator x = ts.begin(); x != ts.end(); ++x) {
7389 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*>(*x);
7393 target = !rtv->_route->active();
7396 rtv->_route->set_active (target, this);
7402 Editor::remove_tracks ()
7404 /* this will delete GUI objects that may be the subject of an event
7405 handler in which this method is called. Defer actual deletion to the
7406 next idle callback, when all event handling is finished.
7408 Glib::signal_idle().connect (sigc::mem_fun (*this, &Editor::idle_remove_tracks));
7412 Editor::idle_remove_tracks ()
7414 Session::StateProtector sp (_session);
7416 return false; /* do not call again */
7420 Editor::_remove_tracks ()
7422 TrackSelection& ts (selection->tracks);
7428 vector<string> choices;
7433 const char* trackstr;
7436 vector<boost::shared_ptr<Route> > routes;
7437 vector<boost::shared_ptr<VCA> > vcas;
7438 bool special_bus = false;
7440 for (TrackSelection::iterator x = ts.begin(); x != ts.end(); ++x) {
7441 VCATimeAxisView* vtv = dynamic_cast<VCATimeAxisView*> (*x);
7443 vcas.push_back (vtv->vca());
7447 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (*x);
7451 if (rtv->is_track()) {
7456 routes.push_back (rtv->_route);
7458 if (rtv->route()->is_master() || rtv->route()->is_monitor()) {
7463 if (special_bus && !Config->get_allow_special_bus_removal()) {
7464 MessageDialog msg (_("That would be bad news ...."),
7468 msg.set_secondary_text (string_compose (_(
7469 "Removing the master or monitor bus is such a bad idea\n\
7470 that %1 is not going to allow it.\n\
7472 If you really want to do this sort of thing\n\
7473 edit your ardour.rc file to set the\n\
7474 \"allow-special-bus-removal\" option to be \"yes\""), PROGRAM_NAME));
7481 if (ntracks + nbusses + nvcas == 0) {
7487 trackstr = P_("track", "tracks", ntracks);
7488 busstr = P_("bus", "busses", nbusses);
7489 vcastr = P_("VCA", "VCAs", nvcas);
7491 if (ntracks > 0 && nbusses > 0 && nvcas > 0) {
7492 title = _("Remove various strips");
7493 prompt = string_compose (_("Do you really want to remove %1 %2, %3 %4 and %5 %6?"),
7494 ntracks, trackstr, nbusses, busstr, nvcas, vcastr);
7496 else if (ntracks > 0 && nbusses > 0) {
7497 title = string_compose (_("Remove %1 and %2"), trackstr, busstr);
7498 prompt = string_compose (_("Do you really want to remove %1 %2 and %3 %4?"),
7499 ntracks, trackstr, nbusses, busstr);
7501 else if (ntracks > 0 && nvcas > 0) {
7502 title = string_compose (_("Remove %1 and %2"), trackstr, vcastr);
7503 prompt = string_compose (_("Do you really want to remove %1 %2 and %3 %4?"),
7504 ntracks, trackstr, nvcas, vcastr);
7506 else if (nbusses > 0 && nvcas > 0) {
7507 title = string_compose (_("Remove %1 and %2"), busstr, vcastr);
7508 prompt = string_compose (_("Do you really want to remove %1 %2 and %3 %4?"),
7509 nbusses, busstr, nvcas, vcastr);
7511 else if (ntracks > 0) {
7512 title = string_compose (_("Remove %1"), trackstr);
7513 prompt = string_compose (_("Do you really want to remove %1 %2?"),
7516 else if (nbusses > 0) {
7517 title = string_compose (_("Remove %1"), busstr);
7518 prompt = string_compose (_("Do you really want to remove %1 %2?"),
7521 else if (nvcas > 0) {
7522 title = string_compose (_("Remove %1"), vcastr);
7523 prompt = string_compose (_("Do you really want to remove %1 %2?"),
7531 prompt += "\n" + string_compose ("(You may also lose the playlists associated with the %1)", trackstr) + "\n";
7534 prompt += "\n" + string(_("This action cannot be undone, and the session file will be overwritten!"));
7536 choices.push_back (_("No, do nothing."));
7537 if (ntracks + nbusses + nvcas > 1) {
7538 choices.push_back (_("Yes, remove them."));
7540 choices.push_back (_("Yes, remove it."));
7543 Choice prompter (title, prompt, choices);
7545 if (prompter.run () != 1) {
7549 if (current_mixer_strip && routes.size () > 1 && std::find (routes.begin(), routes.end(), current_mixer_strip->route()) != routes.end ()) {
7550 /* Route deletion calls Editor::timeaxisview_deleted() iteratively (for each deleted
7551 * route). If the deleted route is currently displayed in the Editor-Mixer (highly
7552 * likely because deletion requires selection) this will call
7553 * Editor::set_selected_mixer_strip () which is expensive (MixerStrip::set_route()).
7554 * It's likewise likely that the route that has just been displayed in the
7555 * Editor-Mixer will be next in line for deletion.
7557 * So simply switch to the master-bus (if present)
7559 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
7560 if ((*i)->stripable ()->is_master ()) {
7561 set_selected_mixer_strip (*(*i));
7568 PresentationInfo::ChangeSuspender cs;
7569 DisplaySuspender ds;
7571 boost::shared_ptr<RouteList> rl (new RouteList);
7572 for (vector<boost::shared_ptr<Route> >::iterator x = routes.begin(); x != routes.end(); ++x) {
7575 _session->remove_routes (rl);
7577 for (vector<boost::shared_ptr<VCA> >::iterator x = vcas.begin(); x != vcas.end(); ++x) {
7578 _session->vca_manager().remove_vca (*x);
7582 /* TrackSelection and RouteList leave scope,
7583 * destructors are called,
7584 * diskstream drops references, save_state is called (again for every track)
7589 Editor::do_insert_time ()
7591 if (selection->tracks.empty()) {
7592 MessageDialog msg (_("You must first select some tracks to Insert Time."),
7593 true, MESSAGE_INFO, BUTTONS_OK, true);
7594 msg.set_position (WIN_POS_MOUSE);
7599 if (Config->get_edit_mode() == Lock) {
7600 MessageDialog msg (_("You cannot insert time in Lock Edit mode."),
7601 true, MESSAGE_INFO, BUTTONS_OK, true);
7602 msg.set_position (WIN_POS_MOUSE);
7607 InsertRemoveTimeDialog d (*this);
7608 int response = d.run ();
7610 if (response != RESPONSE_OK) {
7614 if (d.distance() == 0) {
7621 d.intersected_region_action (),
7625 d.move_glued_markers(),
7626 d.move_locked_markers(),
7632 Editor::insert_time (
7633 samplepos_t pos, samplecnt_t samples, InsertTimeOption opt,
7634 bool all_playlists, bool ignore_music_glue, bool markers_too, bool glued_markers_too, bool locked_markers_too, bool tempo_too
7638 if (Config->get_edit_mode() == Lock) {
7641 bool in_command = false;
7643 TrackViewList ts = selection->tracks.filter_to_unique_playlists ();
7645 for (TrackViewList::iterator x = ts.begin(); x != ts.end(); ++x) {
7649 /* don't operate on any playlist more than once, which could
7650 * happen if "all playlists" is enabled, but there is more
7651 * than 1 track using playlists "from" a given track.
7654 set<boost::shared_ptr<Playlist> > pl;
7656 if (all_playlists) {
7657 RouteTimeAxisView* rtav = dynamic_cast<RouteTimeAxisView*> (*x);
7658 if (rtav && rtav->track ()) {
7659 vector<boost::shared_ptr<Playlist> > all = _session->playlists->playlists_for_track (rtav->track ());
7660 for (vector<boost::shared_ptr<Playlist> >::iterator p = all.begin(); p != all.end(); ++p) {
7665 if ((*x)->playlist ()) {
7666 pl.insert ((*x)->playlist ());
7670 for (set<boost::shared_ptr<Playlist> >::iterator i = pl.begin(); i != pl.end(); ++i) {
7672 (*i)->clear_changes ();
7673 (*i)->clear_owned_changes ();
7676 begin_reversible_command (_("insert time"));
7680 if (opt == SplitIntersected) {
7681 /* non musical split */
7682 (*i)->split (MusicSample (pos, 0));
7685 (*i)->shift (pos, samples, (opt == MoveIntersected), ignore_music_glue);
7687 vector<Command*> cmds;
7689 _session->add_commands (cmds);
7691 _session->add_command (new StatefulDiffCommand (*i));
7695 RouteTimeAxisView* rtav = dynamic_cast<RouteTimeAxisView*> (*x);
7698 begin_reversible_command (_("insert time"));
7701 rtav->route ()->shift (pos, samples);
7708 const int32_t divisions = get_grid_music_divisions (0);
7709 XMLNode& before (_session->locations()->get_state());
7710 Locations::LocationList copy (_session->locations()->list());
7712 for (Locations::LocationList::iterator i = copy.begin(); i != copy.end(); ++i) {
7714 Locations::LocationList::const_iterator tmp;
7716 if ((*i)->position_lock_style() == AudioTime || glued_markers_too) {
7717 bool const was_locked = (*i)->locked ();
7718 if (locked_markers_too) {
7722 if ((*i)->start() >= pos) {
7723 // move end first, in case we're moving by more than the length of the range
7724 if (!(*i)->is_mark()) {
7725 (*i)->set_end ((*i)->end() + samples, false, true, divisions);
7727 (*i)->set_start ((*i)->start() + samples, false, true, divisions);
7739 begin_reversible_command (_("insert time"));
7742 XMLNode& after (_session->locations()->get_state());
7743 _session->add_command (new MementoCommand<Locations>(*_session->locations(), &before, &after));
7749 begin_reversible_command (_("insert time"));
7752 XMLNode& before (_session->tempo_map().get_state());
7753 _session->tempo_map().insert_time (pos, samples);
7754 XMLNode& after (_session->tempo_map().get_state());
7755 _session->add_command (new MementoCommand<TempoMap>(_session->tempo_map(), &before, &after));
7759 commit_reversible_command ();
7764 Editor::do_remove_time ()
7766 if (selection->tracks.empty()) {
7767 MessageDialog msg (_("You must first select some tracks to Remove Time."),
7768 true, MESSAGE_INFO, BUTTONS_OK, true);
7769 msg.set_position (WIN_POS_MOUSE);
7774 if (Config->get_edit_mode() == Lock) {
7775 MessageDialog msg (_("You cannot remove time in Lock Edit mode."),
7776 true, MESSAGE_INFO, BUTTONS_OK, true);
7777 msg.set_position (WIN_POS_MOUSE);
7782 InsertRemoveTimeDialog d (*this, true);
7784 int response = d.run ();
7786 if (response != RESPONSE_OK) {
7790 samplecnt_t distance = d.distance();
7792 if (distance == 0) {
7802 d.move_glued_markers(),
7803 d.move_locked_markers(),
7809 Editor::remove_time (samplepos_t pos, samplecnt_t samples, InsertTimeOption opt,
7810 bool ignore_music_glue, bool markers_too, bool glued_markers_too, bool locked_markers_too, bool tempo_too)
7812 if (Config->get_edit_mode() == Lock) {
7813 error << (_("Cannot insert or delete time when in Lock edit.")) << endmsg;
7816 bool in_command = false;
7818 for (TrackSelection::iterator x = selection->tracks.begin(); x != selection->tracks.end(); ++x) {
7820 boost::shared_ptr<Playlist> pl = (*x)->playlist();
7824 XMLNode &before = pl->get_state();
7827 begin_reversible_command (_("remove time"));
7831 std::list<AudioRange> rl;
7832 AudioRange ar(pos, pos+samples, 0);
7835 pl->shift (pos, -samples, true, ignore_music_glue);
7837 XMLNode &after = pl->get_state();
7839 _session->add_command (new MementoCommand<Playlist> (*pl, &before, &after));
7843 RouteTimeAxisView* rtav = dynamic_cast<RouteTimeAxisView*> (*x);
7846 begin_reversible_command (_("remove time"));
7849 rtav->route ()->shift (pos, -samples);
7853 const int32_t divisions = get_grid_music_divisions (0);
7854 std::list<Location*> loc_kill_list;
7859 XMLNode& before (_session->locations()->get_state());
7860 Locations::LocationList copy (_session->locations()->list());
7862 for (Locations::LocationList::iterator i = copy.begin(); i != copy.end(); ++i) {
7863 if ((*i)->position_lock_style() == AudioTime || glued_markers_too) {
7865 bool const was_locked = (*i)->locked ();
7866 if (locked_markers_too) {
7870 if (!(*i)->is_mark()) { // it's a range; have to handle both start and end
7871 if ((*i)->end() >= pos
7872 && (*i)->end() < pos+samples
7873 && (*i)->start() >= pos
7874 && (*i)->end() < pos+samples) { // range is completely enclosed; kill it
7876 loc_kill_list.push_back(*i);
7877 } else { // only start or end is included, try to do the right thing
7878 // move start before moving end, to avoid trying to move the end to before the start
7879 // if we're removing more time than the length of the range
7880 if ((*i)->start() >= pos && (*i)->start() < pos+samples) {
7881 // start is within cut
7882 (*i)->set_start (pos, false, true,divisions); // bring the start marker to the beginning of the cut
7884 } else if ((*i)->start() >= pos+samples) {
7885 // start (and thus entire range) lies beyond end of cut
7886 (*i)->set_start ((*i)->start() - samples, false, true, divisions); // slip the start marker back
7889 if ((*i)->end() >= pos && (*i)->end() < pos+samples) {
7890 // end is inside cut
7891 (*i)->set_end (pos, false, true, divisions); // bring the end to the cut
7893 } else if ((*i)->end() >= pos+samples) {
7894 // end is beyond end of cut
7895 (*i)->set_end ((*i)->end() - samples, false, true, divisions); // slip the end marker back
7900 } else if ((*i)->start() >= pos && (*i)->start() < pos+samples) {
7901 loc_kill_list.push_back(*i);
7903 } else if ((*i)->start() >= pos) {
7904 (*i)->set_start ((*i)->start() -samples, false, true, divisions);
7914 for (list<Location*>::iterator i = loc_kill_list.begin(); i != loc_kill_list.end(); ++i) {
7915 _session->locations()->remove (*i);
7920 begin_reversible_command (_("remove time"));
7923 XMLNode& after (_session->locations()->get_state());
7924 _session->add_command (new MementoCommand<Locations>(*_session->locations(), &before, &after));
7929 XMLNode& before (_session->tempo_map().get_state());
7931 if (_session->tempo_map().remove_time (pos, samples)) {
7933 begin_reversible_command (_("remove time"));
7936 XMLNode& after (_session->tempo_map().get_state());
7937 _session->add_command (new MementoCommand<TempoMap>(_session->tempo_map(), &before, &after));
7942 commit_reversible_command ();
7947 Editor::fit_selection ()
7949 if (!selection->tracks.empty()) {
7950 fit_tracks (selection->tracks);
7954 /* no selected tracks - use tracks with selected regions */
7956 if (!selection->regions.empty()) {
7957 for (RegionSelection::iterator r = selection->regions.begin(); r != selection->regions.end(); ++r) {
7958 tvl.push_back (&(*r)->get_time_axis_view ());
7964 } else if (internal_editing()) {
7965 /* no selected tracks, or regions, but in internal edit mode, so follow the mouse and use
7968 if (entered_track) {
7969 tvl.push_back (entered_track);
7977 Editor::fit_tracks (TrackViewList & tracks)
7979 if (tracks.empty()) {
7983 uint32_t child_heights = 0;
7984 int visible_tracks = 0;
7986 for (TrackSelection::iterator t = tracks.begin(); t != tracks.end(); ++t) {
7988 if (!(*t)->marked_for_display()) {
7992 child_heights += (*t)->effective_height() - (*t)->current_height();
7996 /* compute the per-track height from:
7998 * total canvas visible height
7999 * - height that will be taken by visible children of selected tracks
8000 * - height of the ruler/hscroll area
8002 uint32_t h = (uint32_t) floor ((trackviews_height() - child_heights) / visible_tracks);
8003 double first_y_pos = DBL_MAX;
8005 if (h < TimeAxisView::preset_height (HeightSmall)) {
8006 MessageDialog msg (_("There are too many tracks to fit in the current window"));
8007 /* too small to be displayed */
8011 undo_visual_stack.push_back (current_visual_state (true));
8012 PBD::Unwinder<bool> nsv (no_save_visual, true);
8014 /* build a list of all tracks, including children */
8017 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
8019 TimeAxisView::Children c = (*i)->get_child_list ();
8020 for (TimeAxisView::Children::iterator j = c.begin(); j != c.end(); ++j) {
8021 all.push_back (j->get());
8026 // find selection range.
8027 // if someone knows how to user TrackViewList::iterator for this
8029 int selected_top = -1;
8030 int selected_bottom = -1;
8032 for (TrackViewList::iterator t = all.begin(); t != all.end(); ++t, ++i) {
8033 if ((*t)->marked_for_display ()) {
8034 if (tracks.contains(*t)) {
8035 if (selected_top == -1) {
8038 selected_bottom = i;
8044 for (TrackViewList::iterator t = all.begin(); t != all.end(); ++t, ++i) {
8045 if ((*t)->marked_for_display ()) {
8046 if (tracks.contains(*t)) {
8047 (*t)->set_height (h);
8048 first_y_pos = std::min ((*t)->y_position (), first_y_pos);
8050 if (i > selected_top && i < selected_bottom) {
8051 hide_track_in_display (*t);
8058 set the controls_layout height now, because waiting for its size
8059 request signal handler will cause the vertical adjustment setting to fail
8062 controls_layout.property_height () = _full_canvas_height;
8063 vertical_adjustment.set_value (first_y_pos);
8065 redo_visual_stack.push_back (current_visual_state (true));
8067 visible_tracks_selector.set_text (_("Sel"));
8071 Editor::save_visual_state (uint32_t n)
8073 while (visual_states.size() <= n) {
8074 visual_states.push_back (0);
8077 if (visual_states[n] != 0) {
8078 delete visual_states[n];
8081 visual_states[n] = current_visual_state (true);
8086 Editor::goto_visual_state (uint32_t n)
8088 if (visual_states.size() <= n) {
8092 if (visual_states[n] == 0) {
8096 use_visual_state (*visual_states[n]);
8100 Editor::start_visual_state_op (uint32_t n)
8102 save_visual_state (n);
8104 PopUp* pup = new PopUp (WIN_POS_MOUSE, 1000, true);
8106 snprintf (buf, sizeof (buf), _("Saved view %u"), n+1);
8107 pup->set_text (buf);
8112 Editor::cancel_visual_state_op (uint32_t n)
8114 goto_visual_state (n);
8118 Editor::toggle_region_mute ()
8120 if (_ignore_region_action) {
8124 RegionSelection rs = get_regions_from_selection_and_entered ();
8130 if (rs.size() > 1) {
8131 begin_reversible_command (_("mute regions"));
8133 begin_reversible_command (_("mute region"));
8136 for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
8138 (*i)->region()->playlist()->clear_changes ();
8139 (*i)->region()->set_muted (!(*i)->region()->muted ());
8140 _session->add_command (new StatefulDiffCommand ((*i)->region()));
8144 commit_reversible_command ();
8148 Editor::combine_regions ()
8150 /* foreach track with selected regions, take all selected regions
8151 and join them into a new region containing the subregions (as a
8155 typedef set<RouteTimeAxisView*> RTVS;
8158 if (selection->regions.empty()) {
8162 for (RegionSelection::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
8163 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*>(&(*i)->get_time_axis_view());
8166 tracks.insert (rtv);
8170 begin_reversible_command (_("combine regions"));
8172 vector<RegionView*> new_selection;
8174 for (RTVS::iterator i = tracks.begin(); i != tracks.end(); ++i) {
8177 if ((rv = (*i)->combine_regions ()) != 0) {
8178 new_selection.push_back (rv);
8182 selection->clear_regions ();
8183 for (vector<RegionView*>::iterator i = new_selection.begin(); i != new_selection.end(); ++i) {
8184 selection->add (*i);
8187 commit_reversible_command ();
8191 Editor::uncombine_regions ()
8193 typedef set<RouteTimeAxisView*> RTVS;
8196 if (selection->regions.empty()) {
8200 for (RegionSelection::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
8201 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*>(&(*i)->get_time_axis_view());
8204 tracks.insert (rtv);
8208 begin_reversible_command (_("uncombine regions"));
8210 for (RTVS::iterator i = tracks.begin(); i != tracks.end(); ++i) {
8211 (*i)->uncombine_regions ();
8214 commit_reversible_command ();
8218 Editor::toggle_midi_input_active (bool flip_others)
8221 boost::shared_ptr<RouteList> rl (new RouteList);
8223 for (TrackSelection::iterator i = selection->tracks.begin(); i != selection->tracks.end(); ++i) {
8224 RouteTimeAxisView *rtav = dynamic_cast<RouteTimeAxisView *>(*i);
8230 boost::shared_ptr<MidiTrack> mt = rtav->midi_track();
8233 rl->push_back (rtav->route());
8234 onoff = !mt->input_active();
8238 _session->set_exclusive_input_active (rl, onoff, flip_others);
8241 static bool ok_fine (GdkEventAny*) { return true; }
8247 lock_dialog = new Gtk::Dialog (string_compose (_("%1: Locked"), PROGRAM_NAME), true);
8249 Gtk::Image* padlock = manage (new Gtk::Image (ARDOUR_UI_UTILS::get_icon ("padlock_closed")));
8250 lock_dialog->get_vbox()->pack_start (*padlock);
8251 lock_dialog->signal_delete_event ().connect (sigc::ptr_fun (ok_fine));
8253 ArdourButton* b = manage (new ArdourButton);
8254 b->set_name ("lock button");
8255 b->set_text (_("Click to unlock"));
8256 b->signal_clicked.connect (sigc::mem_fun (*this, &Editor::unlock));
8257 lock_dialog->get_vbox()->pack_start (*b);
8259 lock_dialog->get_vbox()->show_all ();
8260 lock_dialog->set_size_request (200, 200);
8263 delete _main_menu_disabler;
8264 _main_menu_disabler = new MainMenuDisabler;
8266 lock_dialog->present ();
8268 lock_dialog->get_window()->set_decorations (Gdk::WMDecoration (0));
8274 lock_dialog->hide ();
8276 delete _main_menu_disabler;
8277 _main_menu_disabler = 0;
8279 if (UIConfiguration::instance().get_lock_gui_after_seconds()) {
8280 start_lock_event_timing ();
8285 Editor::bring_in_callback (Gtk::Label* label, uint32_t n, uint32_t total, string name)
8287 Gtkmm2ext::UI::instance()->call_slot (invalidator (*this), boost::bind (&Editor::update_bring_in_message, this, label, n, total, name));
8291 Editor::update_bring_in_message (Gtk::Label* label, uint32_t n, uint32_t total, string name)
8293 Timers::TimerSuspender t;
8294 label->set_text (string_compose ("Copying %1, %2 of %3", name, n, total));
8295 Gtkmm2ext::UI::instance()->flush_pending (1);
8299 Editor::bring_all_sources_into_session ()
8306 ArdourDialog w (_("Moving embedded files into session folder"));
8307 w.get_vbox()->pack_start (msg);
8310 /* flush all pending GUI events because we're about to start copying
8314 Timers::TimerSuspender t;
8315 Gtkmm2ext::UI::instance()->flush_pending (3);
8319 _session->bring_all_sources_into_session (boost::bind (&Editor::bring_in_callback, this, &msg, _1, _2, _3));