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 /* repeated paste in the same position */
4781 /* paste in new location, reset repeated paste state */
4783 last_paste_pos = position;
4786 /* get everything in the correct order */
4789 if (!selection->tracks.empty()) {
4790 /* If there is a track selection, paste into exactly those tracks and
4791 * only those tracks. This allows the user to be explicit and override
4792 * the below "do the reasonable thing" logic. */
4793 ts = selection->tracks.filter_to_unique_playlists ();
4794 sort_track_selection (ts);
4796 /* Figure out which track to base the paste at. */
4797 TimeAxisView* base_track = NULL;
4798 if (_edit_point == Editing::EditAtMouse && entered_track) {
4799 /* With the mouse edit point, paste onto the track under the mouse. */
4800 base_track = entered_track;
4801 } else if (_edit_point == Editing::EditAtMouse && entered_regionview) {
4802 /* With the mouse edit point, paste onto the track of the region under the mouse. */
4803 base_track = &entered_regionview->get_time_axis_view();
4804 } else if (_last_cut_copy_source_track) {
4805 /* Paste to the track that the cut/copy came from (see mantis #333). */
4806 base_track = _last_cut_copy_source_track;
4808 /* This is "impossible" since we've copied... well, do nothing. */
4812 /* Walk up to parent if necessary, so base track is a route. */
4813 while (base_track->get_parent()) {
4814 base_track = base_track->get_parent();
4817 /* Add base track and all tracks below it. The paste logic will select
4818 the appropriate object types from the cut buffer in relative order. */
4819 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
4820 if ((*i)->order() >= base_track->order()) {
4825 /* Sort tracks so the nth track of type T will pick the nth object of type T. */
4826 sort_track_selection (ts);
4828 /* Add automation children of each track in order, for pasting several lines. */
4829 for (TrackViewList::iterator i = ts.begin(); i != ts.end();) {
4830 /* Add any automation children for pasting several lines */
4831 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*>(*i++);
4836 typedef RouteTimeAxisView::AutomationTracks ATracks;
4837 const ATracks& atracks = rtv->automation_tracks();
4838 for (ATracks::const_iterator a = atracks.begin(); a != atracks.end(); ++a) {
4839 i = ts.insert(i, a->second.get());
4844 /* We now have a list of trackviews starting at base_track, including
4845 automation children, in the order shown in the editor, e.g. R1,
4846 R1.A1, R1.A2, R2, R2.A1, ... */
4849 begin_reversible_command (Operations::paste);
4851 if (ts.size() == 1 && cut_buffer->lines.size() == 1 &&
4852 dynamic_cast<AutomationTimeAxisView*>(ts.front())) {
4853 /* Only one line copied, and one automation track selected. Do a
4854 "greedy" paste from one automation type to another. */
4856 PasteContext ctx(paste_count, times, ItemCounts(), true);
4857 ts.front()->paste (position, *cut_buffer, ctx, sub_num);
4861 /* Paste into tracks */
4863 PasteContext ctx(paste_count, times, ItemCounts(), false);
4864 for (TrackViewList::iterator i = ts.begin(); i != ts.end(); ++i) {
4865 (*i)->paste (position, *cut_buffer, ctx, sub_num);
4869 commit_reversible_command ();
4873 Editor::duplicate_regions (float times)
4875 RegionSelection rs (get_regions_from_selection_and_entered());
4876 duplicate_some_regions (rs, times);
4880 Editor::duplicate_some_regions (RegionSelection& regions, float times)
4882 if (regions.empty ()) {
4886 boost::shared_ptr<Playlist> playlist;
4887 RegionSelection sel = regions; // clear (below) may clear the argument list if its the current region selection
4888 RegionSelection foo;
4890 samplepos_t const start_sample = regions.start ();
4891 samplepos_t const end_sample = regions.end_sample ();
4892 samplecnt_t const gap = end_sample - start_sample + 1;
4894 begin_reversible_command (Operations::duplicate_region);
4896 selection->clear_regions ();
4898 for (RegionSelection::iterator i = sel.begin(); i != sel.end(); ++i) {
4900 boost::shared_ptr<Region> r ((*i)->region());
4902 TimeAxisView& tv = (*i)->get_time_axis_view();
4903 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (&tv);
4904 latest_regionviews.clear ();
4905 sigc::connection c = rtv->view()->RegionViewAdded.connect (sigc::mem_fun(*this, &Editor::collect_new_region_view));
4907 samplepos_t const position = end_sample + (r->first_sample() - start_sample + 1);
4908 playlist = (*i)->region()->playlist();
4909 playlist->clear_changes ();
4910 playlist->duplicate (r, position, gap, times);
4911 _session->add_command(new StatefulDiffCommand (playlist));
4915 foo.insert (foo.end(), latest_regionviews.begin(), latest_regionviews.end());
4919 selection->set (foo);
4922 commit_reversible_command ();
4926 Editor::duplicate_selection (float times)
4928 if (selection->time.empty() || selection->tracks.empty()) {
4932 boost::shared_ptr<Playlist> playlist;
4934 TrackViewList ts = selection->tracks.filter_to_unique_playlists ();
4936 bool in_command = false;
4938 for (TrackViewList::iterator i = ts.begin(); i != ts.end(); ++i) {
4939 if ((playlist = (*i)->playlist()) == 0) {
4942 playlist->clear_changes ();
4944 if (clicked_selection) {
4945 playlist->duplicate_range (selection->time[clicked_selection], times);
4947 playlist->duplicate_ranges (selection->time, times);
4951 begin_reversible_command (_("duplicate range selection"));
4954 _session->add_command (new StatefulDiffCommand (playlist));
4959 if (times == 1.0f) {
4960 // now "move" range selection to after the current range selection
4961 samplecnt_t distance = 0;
4963 if (clicked_selection) {
4965 selection->time[clicked_selection].end - selection->time[clicked_selection].start;
4967 distance = selection->time.end_sample () - selection->time.start ();
4970 selection->move_time (distance);
4972 commit_reversible_command ();
4976 /** Reset all selected points to the relevant default value */
4978 Editor::reset_point_selection ()
4980 for (PointSelection::iterator i = selection->points.begin(); i != selection->points.end(); ++i) {
4981 ARDOUR::AutomationList::iterator j = (*i)->model ();
4982 (*j)->value = (*i)->line().the_list()->descriptor ().normal;
4987 Editor::center_playhead ()
4989 float const page = _visible_canvas_width * samples_per_pixel;
4990 center_screen_internal (playhead_cursor->current_sample (), page);
4994 Editor::center_edit_point ()
4996 float const page = _visible_canvas_width * samples_per_pixel;
4997 center_screen_internal (get_preferred_edit_position(), page);
5000 /** Caller must begin and commit a reversible command */
5002 Editor::clear_playlist (boost::shared_ptr<Playlist> playlist)
5004 playlist->clear_changes ();
5006 _session->add_command (new StatefulDiffCommand (playlist));
5010 Editor::nudge_track (bool use_edit, bool forwards)
5012 boost::shared_ptr<Playlist> playlist;
5013 samplepos_t distance;
5014 samplepos_t next_distance;
5018 start = get_preferred_edit_position();
5023 if ((distance = get_nudge_distance (start, next_distance)) == 0) {
5027 if (selection->tracks.empty()) {
5031 TrackViewList ts = selection->tracks.filter_to_unique_playlists ();
5032 bool in_command = false;
5034 for (TrackViewList::iterator i = ts.begin(); i != ts.end(); ++i) {
5036 if ((playlist = (*i)->playlist()) == 0) {
5040 playlist->clear_changes ();
5041 playlist->clear_owned_changes ();
5043 playlist->nudge_after (start, distance, forwards);
5046 begin_reversible_command (_("nudge track"));
5049 vector<Command*> cmds;
5051 playlist->rdiff (cmds);
5052 _session->add_commands (cmds);
5054 _session->add_command (new StatefulDiffCommand (playlist));
5058 commit_reversible_command ();
5063 Editor::remove_last_capture ()
5065 vector<string> choices;
5072 if (Config->get_verify_remove_last_capture()) {
5073 prompt = _("Do you really want to destroy the last capture?"
5074 "\n(This is destructive and cannot be undone)");
5076 choices.push_back (_("No, do nothing."));
5077 choices.push_back (_("Yes, destroy it."));
5079 Choice prompter (_("Destroy last capture"), prompt, choices);
5081 if (prompter.run () == 1) {
5082 _session->remove_last_capture ();
5083 _regions->redisplay ();
5087 _session->remove_last_capture();
5088 _regions->redisplay ();
5093 Editor::normalize_region ()
5099 RegionSelection rs = get_regions_from_selection_and_entered ();
5105 NormalizeDialog dialog (rs.size() > 1);
5107 if (dialog.run () != RESPONSE_ACCEPT) {
5111 CursorContext::Handle cursor_ctx = CursorContext::create(*this, _cursors->wait);
5114 /* XXX: should really only count audio regions here */
5115 int const regions = rs.size ();
5117 /* Make a list of the selected audio regions' maximum amplitudes, and also
5118 obtain the maximum amplitude of them all.
5120 list<double> max_amps;
5121 list<double> rms_vals;
5124 bool use_rms = dialog.constrain_rms ();
5126 for (RegionSelection::const_iterator i = rs.begin(); i != rs.end(); ++i) {
5127 AudioRegionView const * arv = dynamic_cast<AudioRegionView const *> (*i);
5131 dialog.descend (1.0 / regions);
5132 double const a = arv->audio_region()->maximum_amplitude (&dialog);
5134 double r = arv->audio_region()->rms (&dialog);
5135 max_rms = max (max_rms, r);
5136 rms_vals.push_back (r);
5140 /* the user cancelled the operation */
5144 max_amps.push_back (a);
5145 max_amp = max (max_amp, a);
5149 list<double>::const_iterator a = max_amps.begin ();
5150 list<double>::const_iterator l = rms_vals.begin ();
5151 bool in_command = false;
5153 for (RegionSelection::iterator r = rs.begin(); r != rs.end(); ++r) {
5154 AudioRegionView* const arv = dynamic_cast<AudioRegionView*> (*r);
5159 arv->region()->clear_changes ();
5161 double amp = dialog.normalize_individually() ? *a : max_amp;
5162 double target = dialog.target_peak (); // dB
5165 double const amp_rms = dialog.normalize_individually() ? *l : max_rms;
5166 const double t_rms = dialog.target_rms ();
5167 const gain_t c_peak = dB_to_coefficient (target);
5168 const gain_t c_rms = dB_to_coefficient (t_rms);
5169 if ((amp_rms / c_rms) > (amp / c_peak)) {
5175 arv->audio_region()->normalize (amp, target);
5178 begin_reversible_command (_("normalize"));
5181 _session->add_command (new StatefulDiffCommand (arv->region()));
5188 commit_reversible_command ();
5194 Editor::reset_region_scale_amplitude ()
5200 RegionSelection rs = get_regions_from_selection_and_entered ();
5206 bool in_command = false;
5208 for (RegionSelection::iterator r = rs.begin(); r != rs.end(); ++r) {
5209 AudioRegionView* const arv = dynamic_cast<AudioRegionView*>(*r);
5212 arv->region()->clear_changes ();
5213 arv->audio_region()->set_scale_amplitude (1.0f);
5216 begin_reversible_command ("reset gain");
5219 _session->add_command (new StatefulDiffCommand (arv->region()));
5223 commit_reversible_command ();
5228 Editor::adjust_region_gain (bool up)
5230 RegionSelection rs = get_regions_from_selection_and_entered ();
5232 if (!_session || rs.empty()) {
5236 bool in_command = false;
5238 for (RegionSelection::iterator r = rs.begin(); r != rs.end(); ++r) {
5239 AudioRegionView* const arv = dynamic_cast<AudioRegionView*>(*r);
5244 arv->region()->clear_changes ();
5246 double dB = accurate_coefficient_to_dB (arv->audio_region()->scale_amplitude ());
5254 arv->audio_region()->set_scale_amplitude (dB_to_coefficient (dB));
5257 begin_reversible_command ("adjust region gain");
5260 _session->add_command (new StatefulDiffCommand (arv->region()));
5264 commit_reversible_command ();
5269 Editor::reset_region_gain ()
5271 RegionSelection rs = get_regions_from_selection_and_entered ();
5273 if (!_session || rs.empty()) {
5277 bool in_command = false;
5279 for (RegionSelection::iterator r = rs.begin(); r != rs.end(); ++r) {
5280 AudioRegionView* const arv = dynamic_cast<AudioRegionView*>(*r);
5285 arv->region()->clear_changes ();
5287 arv->audio_region()->set_scale_amplitude (1.0f);
5290 begin_reversible_command ("reset region gain");
5293 _session->add_command (new StatefulDiffCommand (arv->region()));
5297 commit_reversible_command ();
5302 Editor::reverse_region ()
5308 Reverse rev (*_session);
5309 apply_filter (rev, _("reverse regions"));
5313 Editor::strip_region_silence ()
5319 RegionSelection rs = get_regions_from_selection_and_entered ();
5325 std::list<RegionView*> audio_only;
5327 for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
5328 AudioRegionView* const arv = dynamic_cast<AudioRegionView*> (*i);
5330 audio_only.push_back (arv);
5334 assert (!audio_only.empty());
5336 StripSilenceDialog d (_session, audio_only);
5337 int const r = d.run ();
5341 if (r == Gtk::RESPONSE_OK) {
5342 ARDOUR::AudioIntervalMap silences;
5343 d.silences (silences);
5344 StripSilence s (*_session, silences, d.fade_length());
5346 apply_filter (s, _("strip silence"), &d);
5351 Editor::apply_midi_note_edit_op_to_region (MidiOperator& op, MidiRegionView& mrv)
5353 Evoral::Sequence<Temporal::Beats>::Notes selected;
5354 mrv.selection_as_notelist (selected, true);
5356 vector<Evoral::Sequence<Temporal::Beats>::Notes> v;
5357 v.push_back (selected);
5359 Temporal::Beats pos_beats = Temporal::Beats (mrv.midi_region()->beat()) - mrv.midi_region()->start_beats();
5361 return op (mrv.midi_region()->model(), pos_beats, v);
5365 Editor::apply_midi_note_edit_op (MidiOperator& op, const RegionSelection& rs)
5371 bool in_command = false;
5373 for (RegionSelection::const_iterator r = rs.begin(); r != rs.end(); ) {
5374 RegionSelection::const_iterator tmp = r;
5377 MidiRegionView* const mrv = dynamic_cast<MidiRegionView*> (*r);
5380 Command* cmd = apply_midi_note_edit_op_to_region (op, *mrv);
5383 begin_reversible_command (op.name ());
5387 _session->add_command (cmd);
5395 commit_reversible_command ();
5396 _session->set_dirty ();
5401 Editor::fork_region ()
5403 RegionSelection rs = get_regions_from_selection_and_entered ();
5409 CursorContext::Handle cursor_ctx = CursorContext::create(*this, _cursors->wait);
5410 bool in_command = false;
5414 for (RegionSelection::iterator r = rs.begin(); r != rs.end(); ) {
5415 RegionSelection::iterator tmp = r;
5418 MidiRegionView* const mrv = dynamic_cast<MidiRegionView*>(*r);
5422 boost::shared_ptr<Playlist> playlist = mrv->region()->playlist();
5423 boost::shared_ptr<MidiSource> new_source = _session->create_midi_source_by_stealing_name (mrv->midi_view()->track());
5424 boost::shared_ptr<MidiRegion> newregion = mrv->midi_region()->clone (new_source);
5427 begin_reversible_command (_("Fork Region(s)"));
5430 playlist->clear_changes ();
5431 playlist->replace_region (mrv->region(), newregion, mrv->region()->position());
5432 _session->add_command(new StatefulDiffCommand (playlist));
5434 error << string_compose (_("Could not unlink %1"), mrv->region()->name()) << endmsg;
5442 commit_reversible_command ();
5447 Editor::quantize_region ()
5450 quantize_regions(get_regions_from_selection_and_entered ());
5455 Editor::quantize_regions (const RegionSelection& rs)
5457 if (rs.n_midi_regions() == 0) {
5461 if (!quantize_dialog) {
5462 quantize_dialog = new QuantizeDialog (*this);
5465 if (quantize_dialog->is_mapped()) {
5466 /* in progress already */
5470 quantize_dialog->present ();
5471 const int r = quantize_dialog->run ();
5472 quantize_dialog->hide ();
5474 if (r == Gtk::RESPONSE_OK) {
5475 Quantize quant (quantize_dialog->snap_start(),
5476 quantize_dialog->snap_end(),
5477 quantize_dialog->start_grid_size(),
5478 quantize_dialog->end_grid_size(),
5479 quantize_dialog->strength(),
5480 quantize_dialog->swing(),
5481 quantize_dialog->threshold());
5483 apply_midi_note_edit_op (quant, rs);
5488 Editor::legatize_region (bool shrink_only)
5491 legatize_regions(get_regions_from_selection_and_entered (), shrink_only);
5496 Editor::legatize_regions (const RegionSelection& rs, bool shrink_only)
5498 if (rs.n_midi_regions() == 0) {
5502 Legatize legatize(shrink_only);
5503 apply_midi_note_edit_op (legatize, rs);
5507 Editor::transform_region ()
5510 transform_regions(get_regions_from_selection_and_entered ());
5515 Editor::transform_regions (const RegionSelection& rs)
5517 if (rs.n_midi_regions() == 0) {
5524 const int r = td.run();
5527 if (r == Gtk::RESPONSE_OK) {
5528 Transform transform(td.get());
5529 apply_midi_note_edit_op(transform, rs);
5534 Editor::transpose_region ()
5537 transpose_regions(get_regions_from_selection_and_entered ());
5542 Editor::transpose_regions (const RegionSelection& rs)
5544 if (rs.n_midi_regions() == 0) {
5549 int const r = d.run ();
5551 if (r == RESPONSE_ACCEPT) {
5552 Transpose transpose(d.semitones ());
5553 apply_midi_note_edit_op (transpose, rs);
5558 Editor::insert_patch_change (bool from_context)
5560 RegionSelection rs = get_regions_from_selection_and_entered ();
5566 const samplepos_t p = get_preferred_edit_position (EDIT_IGNORE_NONE, from_context);
5568 /* XXX: bit of a hack; use the MIDNAM from the first selected region;
5569 there may be more than one, but the PatchChangeDialog can only offer
5570 one set of patch menus.
5572 MidiRegionView* first = dynamic_cast<MidiRegionView*> (rs.front ());
5574 Evoral::PatchChange<Temporal::Beats> empty (Temporal::Beats(), 0, 0, 0);
5575 PatchChangeDialog d (0, _session, empty, first->instrument_info(), Gtk::Stock::ADD);
5577 if (d.run() == RESPONSE_CANCEL) {
5581 for (RegionSelection::iterator i = rs.begin (); i != rs.end(); ++i) {
5582 MidiRegionView* const mrv = dynamic_cast<MidiRegionView*> (*i);
5584 if (p >= mrv->region()->first_sample() && p <= mrv->region()->last_sample()) {
5585 mrv->add_patch_change (p - mrv->region()->position(), d.patch ());
5592 Editor::apply_filter (Filter& filter, string command, ProgressReporter* progress)
5594 RegionSelection rs = get_regions_from_selection_and_entered ();
5600 CursorContext::Handle cursor_ctx = CursorContext::create(*this, _cursors->wait);
5601 bool in_command = false;
5606 int const N = rs.size ();
5608 for (RegionSelection::iterator r = rs.begin(); r != rs.end(); ) {
5609 RegionSelection::iterator tmp = r;
5612 AudioRegionView* const arv = dynamic_cast<AudioRegionView*>(*r);
5614 boost::shared_ptr<Playlist> playlist = arv->region()->playlist();
5617 progress->descend (1.0 / N);
5620 if (arv->audio_region()->apply (filter, progress) == 0) {
5622 playlist->clear_changes ();
5623 playlist->clear_owned_changes ();
5626 begin_reversible_command (command);
5630 if (filter.results.empty ()) {
5632 /* no regions returned; remove the old one */
5633 playlist->remove_region (arv->region ());
5637 std::vector<boost::shared_ptr<Region> >::iterator res = filter.results.begin ();
5639 /* first region replaces the old one */
5640 playlist->replace_region (arv->region(), *res, (*res)->position());
5644 while (res != filter.results.end()) {
5645 playlist->add_region (*res, (*res)->position());
5651 /* We might have removed regions, which alters other regions' layering_index,
5652 so we need to do a recursive diff here.
5654 vector<Command*> cmds;
5655 playlist->rdiff (cmds);
5656 _session->add_commands (cmds);
5658 _session->add_command(new StatefulDiffCommand (playlist));
5662 progress->ascend ();
5671 commit_reversible_command ();
5676 Editor::external_edit_region ()
5682 Editor::reset_region_gain_envelopes ()
5684 RegionSelection rs = get_regions_from_selection_and_entered ();
5686 if (!_session || rs.empty()) {
5690 bool in_command = false;
5692 for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
5693 AudioRegionView* const arv = dynamic_cast<AudioRegionView*>(*i);
5695 boost::shared_ptr<AutomationList> alist (arv->audio_region()->envelope());
5696 XMLNode& before (alist->get_state());
5698 arv->audio_region()->set_default_envelope ();
5701 begin_reversible_command (_("reset region gain"));
5704 _session->add_command (new MementoCommand<AutomationList>(*arv->audio_region()->envelope().get(), &before, &alist->get_state()));
5709 commit_reversible_command ();
5714 Editor::set_region_gain_visibility (RegionView* rv)
5716 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (rv);
5718 arv->update_envelope_visibility();
5723 Editor::set_gain_envelope_visibility ()
5729 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
5730 AudioTimeAxisView* v = dynamic_cast<AudioTimeAxisView*>(*i);
5732 v->audio_view()->foreach_regionview (sigc::mem_fun (this, &Editor::set_region_gain_visibility));
5738 Editor::toggle_gain_envelope_active ()
5740 if (_ignore_region_action) {
5744 RegionSelection rs = get_regions_from_selection_and_entered ();
5746 if (!_session || rs.empty()) {
5750 bool in_command = false;
5752 for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
5753 AudioRegionView* const arv = dynamic_cast<AudioRegionView*>(*i);
5755 arv->region()->clear_changes ();
5756 arv->audio_region()->set_envelope_active (!arv->audio_region()->envelope_active());
5759 begin_reversible_command (_("region gain envelope active"));
5762 _session->add_command (new StatefulDiffCommand (arv->region()));
5767 commit_reversible_command ();
5772 Editor::toggle_region_lock ()
5774 if (_ignore_region_action) {
5778 RegionSelection rs = get_regions_from_selection_and_entered ();
5780 if (!_session || rs.empty()) {
5784 begin_reversible_command (_("toggle region lock"));
5786 for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
5787 (*i)->region()->clear_changes ();
5788 (*i)->region()->set_locked (!(*i)->region()->locked());
5789 _session->add_command (new StatefulDiffCommand ((*i)->region()));
5792 commit_reversible_command ();
5796 Editor::toggle_region_video_lock ()
5798 if (_ignore_region_action) {
5802 RegionSelection rs = get_regions_from_selection_and_entered ();
5804 if (!_session || rs.empty()) {
5808 begin_reversible_command (_("Toggle Video Lock"));
5810 for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
5811 (*i)->region()->clear_changes ();
5812 (*i)->region()->set_video_locked (!(*i)->region()->video_locked());
5813 _session->add_command (new StatefulDiffCommand ((*i)->region()));
5816 commit_reversible_command ();
5820 Editor::toggle_region_lock_style ()
5822 if (_ignore_region_action) {
5826 RegionSelection rs = get_regions_from_selection_and_entered ();
5828 if (!_session || rs.empty()) {
5832 Glib::RefPtr<ToggleAction> a = Glib::RefPtr<ToggleAction>::cast_dynamic (_region_actions->get_action("toggle-region-lock-style"));
5833 vector<Widget*> proxies = a->get_proxies();
5834 Gtk::CheckMenuItem* cmi = dynamic_cast<Gtk::CheckMenuItem*> (proxies.front());
5838 begin_reversible_command (_("toggle region lock style"));
5840 for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
5841 (*i)->region()->clear_changes ();
5842 PositionLockStyle const ns = ((*i)->region()->position_lock_style() == AudioTime && !cmi->get_inconsistent()) ? MusicTime : AudioTime;
5843 (*i)->region()->set_position_lock_style (ns);
5844 _session->add_command (new StatefulDiffCommand ((*i)->region()));
5847 commit_reversible_command ();
5851 Editor::toggle_opaque_region ()
5853 if (_ignore_region_action) {
5857 RegionSelection rs = get_regions_from_selection_and_entered ();
5859 if (!_session || rs.empty()) {
5863 begin_reversible_command (_("change region opacity"));
5865 for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
5866 (*i)->region()->clear_changes ();
5867 (*i)->region()->set_opaque (!(*i)->region()->opaque());
5868 _session->add_command (new StatefulDiffCommand ((*i)->region()));
5871 commit_reversible_command ();
5875 Editor::toggle_record_enable ()
5877 bool new_state = false;
5879 for (TrackSelection::iterator i = selection->tracks.begin(); i != selection->tracks.end(); ++i) {
5880 RouteTimeAxisView *rtav = dynamic_cast<RouteTimeAxisView *>(*i);
5883 if (!rtav->is_track())
5887 new_state = !rtav->track()->rec_enable_control()->get_value();
5891 rtav->track()->rec_enable_control()->set_value (new_state, Controllable::UseGroup);
5896 tracklist_to_stripables (TrackViewList list)
5900 for (TrackSelection::iterator i = list.begin(); i != list.end(); ++i) {
5901 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> ((*i));
5903 if (rtv && rtv->is_track()) {
5904 ret.push_back (rtv->track());
5912 Editor::play_solo_selection (bool restart)
5914 //note: session::solo_selection takes care of invalidating the region playlist
5916 if ((!selection->tracks.empty()) && selection->time.length() > 0) { //a range is selected; solo the tracks and roll
5918 StripableList sl = tracklist_to_stripables (selection->tracks);
5919 _session->solo_selection (sl, true);
5922 samplepos_t start = selection->time.start();
5923 samplepos_t end = selection->time.end_sample();
5924 _session->request_bounded_roll (start, end);
5926 } else if (! selection->tracks.empty()) { //no range is selected, but tracks are selected; solo the tracks and roll
5927 StripableList sl = tracklist_to_stripables (selection->tracks);
5928 _session->solo_selection (sl, true);
5929 _session->request_cancel_play_range();
5930 transition_to_rolling (true);
5932 } else if (! selection->regions.empty()) { //solo any tracks with selected regions, and roll
5933 StripableList sl = tracklist_to_stripables (get_tracks_for_range_action());
5934 _session->solo_selection (sl, true);
5935 _session->request_cancel_play_range();
5936 transition_to_rolling (true);
5938 _session->request_cancel_play_range();
5939 transition_to_rolling (true); //no selection. just roll.
5944 Editor::toggle_solo ()
5946 bool new_state = false;
5948 boost::shared_ptr<ControlList> cl (new ControlList);
5950 for (TrackSelection::iterator i = selection->tracks.begin(); i != selection->tracks.end(); ++i) {
5951 StripableTimeAxisView *stav = dynamic_cast<StripableTimeAxisView *>(*i);
5953 if (!stav || !stav->stripable()->solo_control()) {
5958 new_state = !stav->stripable()->solo_control()->soloed ();
5962 cl->push_back (stav->stripable()->solo_control());
5965 _session->set_controls (cl, new_state ? 1.0 : 0.0, Controllable::UseGroup);
5969 Editor::toggle_mute ()
5971 bool new_state = false;
5973 boost::shared_ptr<ControlList> cl (new ControlList);
5975 for (TrackSelection::iterator i = selection->tracks.begin(); i != selection->tracks.end(); ++i) {
5976 StripableTimeAxisView *stav = dynamic_cast<StripableTimeAxisView *>(*i);
5978 if (!stav || !stav->stripable()->mute_control()) {
5983 new_state = !stav->stripable()->mute_control()->muted();
5987 cl->push_back (stav->stripable()->mute_control());
5990 _session->set_controls (cl, new_state, Controllable::UseGroup);
5994 Editor::toggle_solo_isolate ()
6000 Editor::fade_range ()
6002 TrackViewList ts = selection->tracks.filter_to_unique_playlists ();
6004 begin_reversible_command (_("fade range"));
6006 for (TrackViewList::iterator i = ts.begin(); i != ts.end(); ++i) {
6007 (*i)->fade_range (selection->time);
6010 commit_reversible_command ();
6015 Editor::set_fade_length (bool in)
6017 RegionSelection rs = get_regions_from_selection_and_entered ();
6023 /* we need a region to measure the offset from the start */
6025 RegionView* rv = rs.front ();
6027 samplepos_t pos = get_preferred_edit_position();
6031 if (pos > rv->region()->last_sample() || pos < rv->region()->first_sample()) {
6032 /* edit point is outside the relevant region */
6037 if (pos <= rv->region()->position()) {
6041 len = pos - rv->region()->position();
6042 cmd = _("set fade in length");
6044 if (pos >= rv->region()->last_sample()) {
6048 len = rv->region()->last_sample() - pos;
6049 cmd = _("set fade out length");
6052 bool in_command = false;
6054 for (RegionSelection::iterator x = rs.begin(); x != rs.end(); ++x) {
6055 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (*x);
6061 boost::shared_ptr<AutomationList> alist;
6063 alist = tmp->audio_region()->fade_in();
6065 alist = tmp->audio_region()->fade_out();
6068 XMLNode &before = alist->get_state();
6071 tmp->audio_region()->set_fade_in_length (len);
6072 tmp->audio_region()->set_fade_in_active (true);
6074 tmp->audio_region()->set_fade_out_length (len);
6075 tmp->audio_region()->set_fade_out_active (true);
6079 begin_reversible_command (cmd);
6082 XMLNode &after = alist->get_state();
6083 _session->add_command(new MementoCommand<AutomationList>(*alist, &before, &after));
6087 commit_reversible_command ();
6092 Editor::set_fade_in_shape (FadeShape shape)
6094 RegionSelection rs = get_regions_from_selection_and_entered ();
6099 bool in_command = false;
6101 for (RegionSelection::iterator x = rs.begin(); x != rs.end(); ++x) {
6102 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (*x);
6108 boost::shared_ptr<AutomationList> alist = tmp->audio_region()->fade_in();
6109 XMLNode &before = alist->get_state();
6111 tmp->audio_region()->set_fade_in_shape (shape);
6114 begin_reversible_command (_("set fade in shape"));
6117 XMLNode &after = alist->get_state();
6118 _session->add_command(new MementoCommand<AutomationList>(*alist.get(), &before, &after));
6122 commit_reversible_command ();
6127 Editor::set_fade_out_shape (FadeShape shape)
6129 RegionSelection rs = get_regions_from_selection_and_entered ();
6134 bool in_command = false;
6136 for (RegionSelection::iterator x = rs.begin(); x != rs.end(); ++x) {
6137 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (*x);
6143 boost::shared_ptr<AutomationList> alist = tmp->audio_region()->fade_out();
6144 XMLNode &before = alist->get_state();
6146 tmp->audio_region()->set_fade_out_shape (shape);
6149 begin_reversible_command (_("set fade out shape"));
6152 XMLNode &after = alist->get_state();
6153 _session->add_command(new MementoCommand<AutomationList>(*alist.get(), &before, &after));
6157 commit_reversible_command ();
6162 Editor::set_fade_in_active (bool yn)
6164 RegionSelection rs = get_regions_from_selection_and_entered ();
6169 bool in_command = false;
6171 for (RegionSelection::iterator x = rs.begin(); x != rs.end(); ++x) {
6172 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (*x);
6179 boost::shared_ptr<AudioRegion> ar (tmp->audio_region());
6181 ar->clear_changes ();
6182 ar->set_fade_in_active (yn);
6185 begin_reversible_command (_("set fade in active"));
6188 _session->add_command (new StatefulDiffCommand (ar));
6192 commit_reversible_command ();
6197 Editor::set_fade_out_active (bool yn)
6199 RegionSelection rs = get_regions_from_selection_and_entered ();
6204 bool in_command = false;
6206 for (RegionSelection::iterator x = rs.begin(); x != rs.end(); ++x) {
6207 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (*x);
6213 boost::shared_ptr<AudioRegion> ar (tmp->audio_region());
6215 ar->clear_changes ();
6216 ar->set_fade_out_active (yn);
6219 begin_reversible_command (_("set fade out active"));
6222 _session->add_command(new StatefulDiffCommand (ar));
6226 commit_reversible_command ();
6231 Editor::toggle_region_fades (int dir)
6233 if (_ignore_region_action) {
6237 boost::shared_ptr<AudioRegion> ar;
6240 RegionSelection rs = get_regions_from_selection_and_entered ();
6246 RegionSelection::iterator i;
6247 for (i = rs.begin(); i != rs.end(); ++i) {
6248 if ((ar = boost::dynamic_pointer_cast<AudioRegion>((*i)->region())) != 0) {
6250 yn = ar->fade_out_active ();
6252 yn = ar->fade_in_active ();
6258 if (i == rs.end()) {
6262 /* XXX should this undo-able? */
6263 bool in_command = false;
6265 for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
6266 if ((ar = boost::dynamic_pointer_cast<AudioRegion>((*i)->region())) == 0) {
6269 ar->clear_changes ();
6271 if (dir == 1 || dir == 0) {
6272 ar->set_fade_in_active (!yn);
6275 if (dir == -1 || dir == 0) {
6276 ar->set_fade_out_active (!yn);
6279 begin_reversible_command (_("toggle fade active"));
6282 _session->add_command(new StatefulDiffCommand (ar));
6286 commit_reversible_command ();
6291 /** Update region fade visibility after its configuration has been changed */
6293 Editor::update_region_fade_visibility ()
6295 bool _fade_visibility = _session->config.get_show_region_fades ();
6297 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
6298 AudioTimeAxisView* v = dynamic_cast<AudioTimeAxisView*>(*i);
6300 if (_fade_visibility) {
6301 v->audio_view()->show_all_fades ();
6303 v->audio_view()->hide_all_fades ();
6310 Editor::set_edit_point ()
6313 MusicSample where (0, 0);
6315 if (!mouse_sample (where.sample, ignored)) {
6321 if (selection->markers.empty()) {
6323 mouse_add_new_marker (where.sample);
6328 Location* loc = find_location_from_marker (selection->markers.front(), ignored);
6331 loc->move_to (where.sample, where.division);
6337 Editor::set_playhead_cursor ()
6339 if (entered_marker) {
6340 _session->request_locate (entered_marker->position(), _session->transport_rolling());
6342 MusicSample where (0, 0);
6345 if (!mouse_sample (where.sample, ignored)) {
6352 _session->request_locate (where.sample, _session->transport_rolling());
6356 //not sure what this was for; remove it for now.
6357 // if (UIConfiguration::instance().get_follow_edits() && (!_session || !_session->config.get_external_sync())) {
6358 // cancel_time_selection();
6364 Editor::split_region ()
6366 if (_drags->active ()) {
6370 //if a range is selected, separate it
6371 if (!selection->time.empty()) {
6372 separate_regions_between (selection->time);
6376 //if no range was selected, try to find some regions to split
6377 if (current_mouse_mode() == MouseObject) { //don't try this for Internal Edit, Stretch, Draw, etc.
6379 RegionSelection rs = get_regions_from_selection_and_edit_point ();
6380 const samplepos_t pos = get_preferred_edit_position();
6381 const int32_t division = get_grid_music_divisions (0);
6382 MusicSample where (pos, division);
6388 split_regions_at (where, rs);
6394 Editor::select_next_stripable (bool routes_only)
6396 if (selection->tracks.empty()) {
6397 selection->set (track_views.front());
6401 TimeAxisView* current = selection->tracks.front();
6405 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
6407 if (*i == current) {
6409 if (i != track_views.end()) {
6412 current = (*(track_views.begin()));
6413 //selection->set (*(track_views.begin()));
6420 RouteUI* rui = dynamic_cast<RouteUI *>(current);
6421 valid = rui && rui->route()->active();
6423 valid = 0 != current->stripable ().get();
6426 } while (current->hidden() || !valid);
6428 selection->set (current);
6430 ensure_time_axis_view_is_visible (*current, false);
6434 Editor::select_prev_stripable (bool routes_only)
6436 if (selection->tracks.empty()) {
6437 selection->set (track_views.front());
6441 TimeAxisView* current = selection->tracks.front();
6445 for (TrackViewList::reverse_iterator i = track_views.rbegin(); i != track_views.rend(); ++i) {
6447 if (*i == current) {
6449 if (i != track_views.rend()) {
6452 current = *(track_views.rbegin());
6458 RouteUI* rui = dynamic_cast<RouteUI *>(current);
6459 valid = rui && rui->route()->active();
6461 valid = 0 != current->stripable ().get();
6464 } while (current->hidden() || !valid);
6466 selection->set (current);
6468 ensure_time_axis_view_is_visible (*current, false);
6472 Editor::set_loop_from_selection (bool play)
6474 if (_session == 0) {
6478 samplepos_t start, end;
6479 if (!get_selection_extents (start, end))
6482 set_loop_range (start, end, _("set loop range from selection"));
6485 _session->request_play_loop (true, true);
6490 Editor::set_loop_from_region (bool play)
6492 samplepos_t start, end;
6493 if (!get_selection_extents (start, end))
6496 set_loop_range (start, end, _("set loop range from region"));
6499 _session->request_locate (start, true);
6500 _session->request_play_loop (true);
6505 Editor::set_punch_from_selection ()
6507 if (_session == 0) {
6511 samplepos_t start, end;
6512 if (!get_selection_extents (start, end))
6515 set_punch_range (start, end, _("set punch range from selection"));
6519 Editor::set_auto_punch_range ()
6521 // auto punch in/out button from a single button
6522 // If Punch In is unset, set punch range from playhead to end, enable punch in
6523 // If Punch In is set, the next punch sets Punch Out, unless the playhead has been
6524 // rewound beyond the Punch In marker, in which case that marker will be moved back
6525 // to the current playhead position.
6526 // If punch out is set, it clears the punch range and Punch In/Out buttons
6528 if (_session == 0) {
6532 Location* tpl = transport_punch_location();
6533 samplepos_t now = playhead_cursor->current_sample();
6534 samplepos_t begin = now;
6535 samplepos_t end = _session->current_end_sample();
6537 if (!_session->config.get_punch_in()) {
6538 // First Press - set punch in and create range from here to eternity
6539 set_punch_range (begin, end, _("Auto Punch In"));
6540 _session->config.set_punch_in(true);
6541 } else if (tpl && !_session->config.get_punch_out()) {
6542 // Second press - update end range marker and set punch_out
6543 if (now < tpl->start()) {
6544 // playhead has been rewound - move start back and pretend nothing happened
6546 set_punch_range (begin, end, _("Auto Punch In/Out"));
6548 // normal case for 2nd press - set the punch out
6549 end = playhead_cursor->current_sample ();
6550 set_punch_range (tpl->start(), now, _("Auto Punch In/Out"));
6551 _session->config.set_punch_out(true);
6554 if (_session->config.get_punch_out()) {
6555 _session->config.set_punch_out(false);
6558 if (_session->config.get_punch_in()) {
6559 _session->config.set_punch_in(false);
6564 // third press - unset punch in/out and remove range
6565 _session->locations()->remove(tpl);
6572 Editor::set_session_extents_from_selection ()
6574 if (_session == 0) {
6578 samplepos_t start, end;
6579 if (!get_selection_extents (start, end))
6583 if ((loc = _session->locations()->session_range_location()) == 0) {
6584 _session->set_session_extents (start, end); // this will create a new session range; no need for UNDO
6586 XMLNode &before = loc->get_state();
6588 _session->set_session_extents (start, end);
6590 XMLNode &after = loc->get_state();
6592 begin_reversible_command (_("set session start/end from selection"));
6594 _session->add_command (new MementoCommand<Location>(*loc, &before, &after));
6596 commit_reversible_command ();
6599 _session->set_end_is_free (false);
6603 Editor::set_punch_start_from_edit_point ()
6607 MusicSample start (0, 0);
6608 samplepos_t end = max_samplepos;
6610 //use the existing punch end, if any
6611 Location* tpl = transport_punch_location();
6616 if ((_edit_point == EditAtPlayhead) && _session->transport_rolling()) {
6617 start.sample = _session->audible_sample();
6619 start.sample = get_preferred_edit_position();
6622 //snap the selection start/end
6625 //if there's not already a sensible selection endpoint, go "forever"
6626 if (start.sample > end) {
6627 end = max_samplepos;
6630 set_punch_range (start.sample, end, _("set punch start from EP"));
6636 Editor::set_punch_end_from_edit_point ()
6640 samplepos_t start = 0;
6641 MusicSample end (max_samplepos, 0);
6643 //use the existing punch start, if any
6644 Location* tpl = transport_punch_location();
6646 start = tpl->start();
6649 if ((_edit_point == EditAtPlayhead) && _session->transport_rolling()) {
6650 end.sample = _session->audible_sample();
6652 end.sample = get_preferred_edit_position();
6655 //snap the selection start/end
6658 set_punch_range (start, end.sample, _("set punch end from EP"));
6664 Editor::set_loop_start_from_edit_point ()
6668 MusicSample start (0, 0);
6669 samplepos_t end = max_samplepos;
6671 //use the existing loop end, if any
6672 Location* tpl = transport_loop_location();
6677 if ((_edit_point == EditAtPlayhead) && _session->transport_rolling()) {
6678 start.sample = _session->audible_sample();
6680 start.sample = get_preferred_edit_position();
6683 //snap the selection start/end
6686 //if there's not already a sensible selection endpoint, go "forever"
6687 if (start.sample > end) {
6688 end = max_samplepos;
6691 set_loop_range (start.sample, end, _("set loop start from EP"));
6697 Editor::set_loop_end_from_edit_point ()
6701 samplepos_t start = 0;
6702 MusicSample end (max_samplepos, 0);
6704 //use the existing loop start, if any
6705 Location* tpl = transport_loop_location();
6707 start = tpl->start();
6710 if ((_edit_point == EditAtPlayhead) && _session->transport_rolling()) {
6711 end.sample = _session->audible_sample();
6713 end.sample = get_preferred_edit_position();
6716 //snap the selection start/end
6719 set_loop_range (start, end.sample, _("set loop end from EP"));
6724 Editor::set_punch_from_region ()
6726 samplepos_t start, end;
6727 if (!get_selection_extents (start, end))
6730 set_punch_range (start, end, _("set punch range from region"));
6734 Editor::pitch_shift_region ()
6736 RegionSelection rs = get_regions_from_selection_and_entered ();
6738 RegionSelection audio_rs;
6739 for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
6740 if (dynamic_cast<AudioRegionView*> (*i)) {
6741 audio_rs.push_back (*i);
6745 if (audio_rs.empty()) {
6749 pitch_shift (audio_rs, 1.2);
6753 Editor::set_tempo_from_region ()
6755 RegionSelection rs = get_regions_from_selection_and_entered ();
6757 if (!_session || rs.empty()) {
6761 RegionView* rv = rs.front();
6763 define_one_bar (rv->region()->position(), rv->region()->last_sample() + 1);
6767 Editor::use_range_as_bar ()
6769 samplepos_t start, end;
6770 if (get_edit_op_range (start, end)) {
6771 define_one_bar (start, end);
6776 Editor::define_one_bar (samplepos_t start, samplepos_t end)
6778 samplepos_t length = end - start;
6780 const Meter& m (_session->tempo_map().meter_at_sample (start));
6782 /* length = 1 bar */
6784 /* We're going to deliver a constant tempo here,
6785 so we can use samples per beat to determine length.
6786 now we want samples per beat.
6787 we have samples per bar, and beats per bar, so ...
6790 /* XXXX METER MATH */
6792 double samples_per_beat = length / m.divisions_per_bar();
6794 /* beats per minute = */
6796 double beats_per_minute = (_session->sample_rate() * 60.0) / samples_per_beat;
6798 /* now decide whether to:
6800 (a) set global tempo
6801 (b) add a new tempo marker
6805 const TempoSection& t (_session->tempo_map().tempo_section_at_sample (start));
6807 bool do_global = false;
6809 if ((_session->tempo_map().n_tempos() == 1) && (_session->tempo_map().n_meters() == 1)) {
6811 /* only 1 tempo & 1 meter: ask if the user wants to set the tempo
6812 at the start, or create a new marker
6815 vector<string> options;
6816 options.push_back (_("Cancel"));
6817 options.push_back (_("Add new marker"));
6818 options.push_back (_("Set global tempo"));
6821 _("Define one bar"),
6822 _("Do you want to set the global tempo or add a new tempo marker?"),
6826 c.set_default_response (2);
6842 /* more than 1 tempo and/or meter section already, go ahead do the "usual":
6843 if the marker is at the region starter, change it, otherwise add
6848 begin_reversible_command (_("set tempo from region"));
6849 XMLNode& before (_session->tempo_map().get_state());
6852 _session->tempo_map().change_initial_tempo (beats_per_minute, t.note_type(), t.end_note_types_per_minute());
6853 } else if (t.sample() == start) {
6854 _session->tempo_map().change_existing_tempo_at (start, beats_per_minute, t.note_type(), t.end_note_types_per_minute());
6856 /* constant tempo */
6857 const Tempo tempo (beats_per_minute, t.note_type());
6858 _session->tempo_map().add_tempo (tempo, 0.0, start, AudioTime);
6861 XMLNode& after (_session->tempo_map().get_state());
6863 _session->add_command (new MementoCommand<TempoMap>(_session->tempo_map(), &before, &after));
6864 commit_reversible_command ();
6868 Editor::split_region_at_transients ()
6870 AnalysisFeatureList positions;
6872 RegionSelection rs = get_regions_from_selection_and_entered ();
6874 if (!_session || rs.empty()) {
6878 begin_reversible_command (_("split regions"));
6880 for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ) {
6882 RegionSelection::iterator tmp;
6887 boost::shared_ptr<AudioRegion> ar = boost::dynamic_pointer_cast<AudioRegion> ((*i)->region());
6890 ar->transients (positions);
6891 split_region_at_points ((*i)->region(), positions, true);
6898 commit_reversible_command ();
6903 Editor::split_region_at_points (boost::shared_ptr<Region> r, AnalysisFeatureList& positions, bool can_ferret, bool select_new)
6905 bool use_rhythmic_rodent = false;
6907 boost::shared_ptr<Playlist> pl = r->playlist();
6909 list<boost::shared_ptr<Region> > new_regions;
6915 if (positions.empty()) {
6919 if (positions.size() > 20 && can_ferret) {
6920 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);
6921 MessageDialog msg (msgstr,
6924 Gtk::BUTTONS_OK_CANCEL);
6927 msg.add_button (_("Call for the Ferret!"), RESPONSE_APPLY);
6928 msg.set_secondary_text (_("Press OK to continue with this split operation\nor ask the Ferret dialog to tune the analysis"));
6930 msg.set_secondary_text (_("Press OK to continue with this split operation"));
6933 msg.set_title (_("Excessive split?"));
6936 int response = msg.run();
6942 case RESPONSE_APPLY:
6943 use_rhythmic_rodent = true;
6950 if (use_rhythmic_rodent) {
6951 show_rhythm_ferret ();
6955 AnalysisFeatureList::const_iterator x;
6957 pl->clear_changes ();
6958 pl->clear_owned_changes ();
6960 x = positions.begin();
6962 if (x == positions.end()) {
6967 pl->remove_region (r);
6969 samplepos_t pos = 0;
6971 samplepos_t rstart = r->first_sample ();
6972 samplepos_t rend = r->last_sample ();
6974 while (x != positions.end()) {
6976 /* deal with positons that are out of scope of present region bounds */
6977 if (*x <= rstart || *x > rend) {
6982 /* file start = original start + how far we from the initial position ? */
6984 samplepos_t file_start = r->start() + pos;
6986 /* length = next position - current position */
6988 samplepos_t len = (*x) - pos - rstart;
6990 /* XXX we do we really want to allow even single-sample regions?
6991 * shouldn't we have some kind of lower limit on region size?
7000 if (RegionFactory::region_name (new_name, r->name())) {
7004 /* do NOT announce new regions 1 by one, just wait till they are all done */
7008 plist.add (ARDOUR::Properties::start, file_start);
7009 plist.add (ARDOUR::Properties::length, len);
7010 plist.add (ARDOUR::Properties::name, new_name);
7011 plist.add (ARDOUR::Properties::layer, 0);
7012 // TODO set transients_offset
7014 boost::shared_ptr<Region> nr = RegionFactory::create (r->sources(), plist, false);
7015 /* because we set annouce to false, manually add the new region to the
7018 RegionFactory::map_add (nr);
7020 pl->add_region (nr, rstart + pos);
7023 new_regions.push_front(nr);
7032 RegionFactory::region_name (new_name, r->name());
7034 /* Add the final region */
7037 plist.add (ARDOUR::Properties::start, r->start() + pos);
7038 plist.add (ARDOUR::Properties::length, r->last_sample() - (r->position() + pos) + 1);
7039 plist.add (ARDOUR::Properties::name, new_name);
7040 plist.add (ARDOUR::Properties::layer, 0);
7042 boost::shared_ptr<Region> nr = RegionFactory::create (r->sources(), plist, false);
7043 /* because we set annouce to false, manually add the new region to the
7046 RegionFactory::map_add (nr);
7047 pl->add_region (nr, r->position() + pos);
7050 new_regions.push_front(nr);
7055 /* We might have removed regions, which alters other regions' layering_index,
7056 so we need to do a recursive diff here.
7058 vector<Command*> cmds;
7060 _session->add_commands (cmds);
7062 _session->add_command (new StatefulDiffCommand (pl));
7066 for (list<boost::shared_ptr<Region> >::iterator i = new_regions.begin(); i != new_regions.end(); ++i){
7067 set_selected_regionview_from_region_list ((*i), Selection::Add);
7073 Editor::place_transient()
7079 RegionSelection rs = get_regions_from_selection_and_edit_point ();
7085 samplepos_t where = get_preferred_edit_position();
7087 begin_reversible_command (_("place transient"));
7089 for (RegionSelection::iterator r = rs.begin(); r != rs.end(); ++r) {
7090 (*r)->region()->add_transient(where);
7093 commit_reversible_command ();
7097 Editor::remove_transient(ArdourCanvas::Item* item)
7103 ArdourCanvas::Line* _line = reinterpret_cast<ArdourCanvas::Line*> (item);
7106 AudioRegionView* _arv = reinterpret_cast<AudioRegionView*> (item->get_data ("regionview"));
7107 _arv->remove_transient (*(float*) _line->get_data ("position"));
7111 Editor::snap_regions_to_grid ()
7113 list <boost::shared_ptr<Playlist > > used_playlists;
7115 RegionSelection rs = get_regions_from_selection_and_entered ();
7117 if (!_session || rs.empty()) {
7121 begin_reversible_command (_("snap regions to grid"));
7123 for (RegionSelection::iterator r = rs.begin(); r != rs.end(); ++r) {
7125 boost::shared_ptr<Playlist> pl = (*r)->region()->playlist();
7127 if (!pl->frozen()) {
7128 /* we haven't seen this playlist before */
7130 /* remember used playlists so we can thaw them later */
7131 used_playlists.push_back(pl);
7134 (*r)->region()->clear_changes ();
7136 MusicSample start ((*r)->region()->first_sample (), 0);
7137 snap_to (start, RoundNearest, SnapToGrid);
7138 (*r)->region()->set_position (start.sample, start.division);
7139 _session->add_command(new StatefulDiffCommand ((*r)->region()));
7142 while (used_playlists.size() > 0) {
7143 list <boost::shared_ptr<Playlist > >::iterator i = used_playlists.begin();
7145 used_playlists.pop_front();
7148 commit_reversible_command ();
7152 Editor::close_region_gaps ()
7154 list <boost::shared_ptr<Playlist > > used_playlists;
7156 RegionSelection rs = get_regions_from_selection_and_entered ();
7158 if (!_session || rs.empty()) {
7162 Dialog dialog (_("Close Region Gaps"));
7165 table.set_spacings (12);
7166 table.set_border_width (12);
7167 Label* l = manage (left_aligned_label (_("Crossfade length")));
7168 table.attach (*l, 0, 1, 0, 1);
7170 SpinButton spin_crossfade (1, 0);
7171 spin_crossfade.set_range (0, 15);
7172 spin_crossfade.set_increments (1, 1);
7173 spin_crossfade.set_value (5);
7174 table.attach (spin_crossfade, 1, 2, 0, 1);
7176 table.attach (*manage (new Label (_("ms"))), 2, 3, 0, 1);
7178 l = manage (left_aligned_label (_("Pull-back length")));
7179 table.attach (*l, 0, 1, 1, 2);
7181 SpinButton spin_pullback (1, 0);
7182 spin_pullback.set_range (0, 100);
7183 spin_pullback.set_increments (1, 1);
7184 spin_pullback.set_value(30);
7185 table.attach (spin_pullback, 1, 2, 1, 2);
7187 table.attach (*manage (new Label (_("ms"))), 2, 3, 1, 2);
7189 dialog.get_vbox()->pack_start (table);
7190 dialog.add_button (Stock::CANCEL, RESPONSE_CANCEL);
7191 dialog.add_button (_("Ok"), RESPONSE_ACCEPT);
7194 if (dialog.run () == RESPONSE_CANCEL) {
7198 samplepos_t crossfade_len = spin_crossfade.get_value();
7199 samplepos_t pull_back_samples = spin_pullback.get_value();
7201 crossfade_len = lrintf (crossfade_len * _session->sample_rate()/1000);
7202 pull_back_samples = lrintf (pull_back_samples * _session->sample_rate()/1000);
7204 /* Iterate over the region list and make adjacent regions overlap by crossfade_len_ms */
7206 begin_reversible_command (_("close region gaps"));
7209 boost::shared_ptr<Region> last_region;
7211 rs.sort_by_position_and_track();
7213 for (RegionSelection::iterator r = rs.begin(); r != rs.end(); ++r) {
7215 boost::shared_ptr<Playlist> pl = (*r)->region()->playlist();
7217 if (!pl->frozen()) {
7218 /* we haven't seen this playlist before */
7220 /* remember used playlists so we can thaw them later */
7221 used_playlists.push_back(pl);
7225 samplepos_t position = (*r)->region()->position();
7227 if (idx == 0 || position < last_region->position()){
7228 last_region = (*r)->region();
7233 (*r)->region()->clear_changes ();
7234 (*r)->region()->trim_front((position - pull_back_samples));
7236 last_region->clear_changes ();
7237 last_region->trim_end ((position - pull_back_samples + crossfade_len));
7239 _session->add_command (new StatefulDiffCommand ((*r)->region()));
7240 _session->add_command (new StatefulDiffCommand (last_region));
7242 last_region = (*r)->region();
7246 while (used_playlists.size() > 0) {
7247 list <boost::shared_ptr<Playlist > >::iterator i = used_playlists.begin();
7249 used_playlists.pop_front();
7252 commit_reversible_command ();
7256 Editor::tab_to_transient (bool forward)
7258 AnalysisFeatureList positions;
7260 RegionSelection rs = get_regions_from_selection_and_entered ();
7266 samplepos_t pos = _session->audible_sample ();
7268 if (!selection->tracks.empty()) {
7270 /* don't waste time searching for transients in duplicate playlists.
7273 TrackViewList ts = selection->tracks.filter_to_unique_playlists ();
7275 for (TrackViewList::iterator t = ts.begin(); t != ts.end(); ++t) {
7277 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (*t);
7280 boost::shared_ptr<Track> tr = rtv->track();
7282 boost::shared_ptr<Playlist> pl = tr->playlist ();
7284 samplepos_t result = pl->find_next_transient (pos, forward ? 1 : -1);
7287 positions.push_back (result);
7300 for (RegionSelection::iterator r = rs.begin(); r != rs.end(); ++r) {
7301 (*r)->region()->get_transients (positions);
7305 TransientDetector::cleanup_transients (positions, _session->sample_rate(), 3.0);
7308 AnalysisFeatureList::iterator x;
7310 for (x = positions.begin(); x != positions.end(); ++x) {
7316 if (x != positions.end ()) {
7317 _session->request_locate (*x);
7321 AnalysisFeatureList::reverse_iterator x;
7323 for (x = positions.rbegin(); x != positions.rend(); ++x) {
7329 if (x != positions.rend ()) {
7330 _session->request_locate (*x);
7336 Editor::playhead_forward_to_grid ()
7342 MusicSample pos (playhead_cursor->current_sample (), 0);
7344 if (pos.sample < max_samplepos - 1) {
7346 snap_to_internal (pos, RoundUpAlways, SnapToGrid, false, true);
7347 _session->request_locate (pos.sample);
7353 Editor::playhead_backward_to_grid ()
7359 MusicSample pos (playhead_cursor->current_sample (), 0);
7361 if (pos.sample > 2) {
7363 snap_to_internal (pos, RoundDownAlways, SnapToGrid, false, true);
7364 _session->request_locate (pos.sample);
7369 Editor::set_track_height (Height h)
7371 TrackSelection& ts (selection->tracks);
7373 for (TrackSelection::iterator x = ts.begin(); x != ts.end(); ++x) {
7374 (*x)->set_height_enum (h);
7379 Editor::toggle_tracks_active ()
7381 TrackSelection& ts (selection->tracks);
7383 bool target = false;
7389 for (TrackSelection::iterator x = ts.begin(); x != ts.end(); ++x) {
7390 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*>(*x);
7394 target = !rtv->_route->active();
7397 rtv->_route->set_active (target, this);
7403 Editor::remove_tracks ()
7405 /* this will delete GUI objects that may be the subject of an event
7406 handler in which this method is called. Defer actual deletion to the
7407 next idle callback, when all event handling is finished.
7409 Glib::signal_idle().connect (sigc::mem_fun (*this, &Editor::idle_remove_tracks));
7413 Editor::idle_remove_tracks ()
7415 Session::StateProtector sp (_session);
7417 return false; /* do not call again */
7421 Editor::_remove_tracks ()
7423 TrackSelection& ts (selection->tracks);
7429 vector<string> choices;
7434 const char* trackstr;
7437 vector<boost::shared_ptr<Route> > routes;
7438 vector<boost::shared_ptr<VCA> > vcas;
7439 bool special_bus = false;
7441 for (TrackSelection::iterator x = ts.begin(); x != ts.end(); ++x) {
7442 VCATimeAxisView* vtv = dynamic_cast<VCATimeAxisView*> (*x);
7444 vcas.push_back (vtv->vca());
7448 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (*x);
7452 if (rtv->is_track()) {
7457 routes.push_back (rtv->_route);
7459 if (rtv->route()->is_master() || rtv->route()->is_monitor()) {
7464 if (special_bus && !Config->get_allow_special_bus_removal()) {
7465 MessageDialog msg (_("That would be bad news ...."),
7469 msg.set_secondary_text (string_compose (_(
7470 "Removing the master or monitor bus is such a bad idea\n\
7471 that %1 is not going to allow it.\n\
7473 If you really want to do this sort of thing\n\
7474 edit your ardour.rc file to set the\n\
7475 \"allow-special-bus-removal\" option to be \"yes\""), PROGRAM_NAME));
7482 if (ntracks + nbusses + nvcas == 0) {
7488 trackstr = P_("track", "tracks", ntracks);
7489 busstr = P_("bus", "busses", nbusses);
7490 vcastr = P_("VCA", "VCAs", nvcas);
7492 if (ntracks > 0 && nbusses > 0 && nvcas > 0) {
7493 title = _("Remove various strips");
7494 prompt = string_compose (_("Do you really want to remove %1 %2, %3 %4 and %5 %6?"),
7495 ntracks, trackstr, nbusses, busstr, nvcas, vcastr);
7497 else if (ntracks > 0 && nbusses > 0) {
7498 title = string_compose (_("Remove %1 and %2"), trackstr, busstr);
7499 prompt = string_compose (_("Do you really want to remove %1 %2 and %3 %4?"),
7500 ntracks, trackstr, nbusses, busstr);
7502 else if (ntracks > 0 && nvcas > 0) {
7503 title = string_compose (_("Remove %1 and %2"), trackstr, vcastr);
7504 prompt = string_compose (_("Do you really want to remove %1 %2 and %3 %4?"),
7505 ntracks, trackstr, nvcas, vcastr);
7507 else if (nbusses > 0 && nvcas > 0) {
7508 title = string_compose (_("Remove %1 and %2"), busstr, vcastr);
7509 prompt = string_compose (_("Do you really want to remove %1 %2 and %3 %4?"),
7510 nbusses, busstr, nvcas, vcastr);
7512 else if (ntracks > 0) {
7513 title = string_compose (_("Remove %1"), trackstr);
7514 prompt = string_compose (_("Do you really want to remove %1 %2?"),
7517 else if (nbusses > 0) {
7518 title = string_compose (_("Remove %1"), busstr);
7519 prompt = string_compose (_("Do you really want to remove %1 %2?"),
7522 else if (nvcas > 0) {
7523 title = string_compose (_("Remove %1"), vcastr);
7524 prompt = string_compose (_("Do you really want to remove %1 %2?"),
7532 prompt += "\n" + string_compose ("(You may also lose the playlists associated with the %1)", trackstr) + "\n";
7535 prompt += "\n" + string(_("This action cannot be undone, and the session file will be overwritten!"));
7537 choices.push_back (_("No, do nothing."));
7538 if (ntracks + nbusses + nvcas > 1) {
7539 choices.push_back (_("Yes, remove them."));
7541 choices.push_back (_("Yes, remove it."));
7544 Choice prompter (title, prompt, choices);
7546 if (prompter.run () != 1) {
7550 if (current_mixer_strip && routes.size () > 1 && std::find (routes.begin(), routes.end(), current_mixer_strip->route()) != routes.end ()) {
7551 /* Route deletion calls Editor::timeaxisview_deleted() iteratively (for each deleted
7552 * route). If the deleted route is currently displayed in the Editor-Mixer (highly
7553 * likely because deletion requires selection) this will call
7554 * Editor::set_selected_mixer_strip () which is expensive (MixerStrip::set_route()).
7555 * It's likewise likely that the route that has just been displayed in the
7556 * Editor-Mixer will be next in line for deletion.
7558 * So simply switch to the master-bus (if present)
7560 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
7561 if ((*i)->stripable ()->is_master ()) {
7562 set_selected_mixer_strip (*(*i));
7569 PresentationInfo::ChangeSuspender cs;
7570 DisplaySuspender ds;
7572 boost::shared_ptr<RouteList> rl (new RouteList);
7573 for (vector<boost::shared_ptr<Route> >::iterator x = routes.begin(); x != routes.end(); ++x) {
7576 _session->remove_routes (rl);
7578 for (vector<boost::shared_ptr<VCA> >::iterator x = vcas.begin(); x != vcas.end(); ++x) {
7579 _session->vca_manager().remove_vca (*x);
7583 /* TrackSelection and RouteList leave scope,
7584 * destructors are called,
7585 * diskstream drops references, save_state is called (again for every track)
7590 Editor::do_insert_time ()
7592 if (selection->tracks.empty()) {
7593 MessageDialog msg (_("You must first select some tracks to Insert Time."),
7594 true, MESSAGE_INFO, BUTTONS_OK, true);
7595 msg.set_position (WIN_POS_MOUSE);
7600 if (Config->get_edit_mode() == Lock) {
7601 MessageDialog msg (_("You cannot insert time in Lock Edit mode."),
7602 true, MESSAGE_INFO, BUTTONS_OK, true);
7603 msg.set_position (WIN_POS_MOUSE);
7608 InsertRemoveTimeDialog d (*this);
7609 int response = d.run ();
7611 if (response != RESPONSE_OK) {
7615 if (d.distance() == 0) {
7622 d.intersected_region_action (),
7626 d.move_glued_markers(),
7627 d.move_locked_markers(),
7633 Editor::insert_time (
7634 samplepos_t pos, samplecnt_t samples, InsertTimeOption opt,
7635 bool all_playlists, bool ignore_music_glue, bool markers_too, bool glued_markers_too, bool locked_markers_too, bool tempo_too
7639 if (Config->get_edit_mode() == Lock) {
7642 bool in_command = false;
7644 TrackViewList ts = selection->tracks.filter_to_unique_playlists ();
7646 for (TrackViewList::iterator x = ts.begin(); x != ts.end(); ++x) {
7650 /* don't operate on any playlist more than once, which could
7651 * happen if "all playlists" is enabled, but there is more
7652 * than 1 track using playlists "from" a given track.
7655 set<boost::shared_ptr<Playlist> > pl;
7657 if (all_playlists) {
7658 RouteTimeAxisView* rtav = dynamic_cast<RouteTimeAxisView*> (*x);
7659 if (rtav && rtav->track ()) {
7660 vector<boost::shared_ptr<Playlist> > all = _session->playlists->playlists_for_track (rtav->track ());
7661 for (vector<boost::shared_ptr<Playlist> >::iterator p = all.begin(); p != all.end(); ++p) {
7666 if ((*x)->playlist ()) {
7667 pl.insert ((*x)->playlist ());
7671 for (set<boost::shared_ptr<Playlist> >::iterator i = pl.begin(); i != pl.end(); ++i) {
7673 (*i)->clear_changes ();
7674 (*i)->clear_owned_changes ();
7677 begin_reversible_command (_("insert time"));
7681 if (opt == SplitIntersected) {
7682 /* non musical split */
7683 (*i)->split (MusicSample (pos, 0));
7686 (*i)->shift (pos, samples, (opt == MoveIntersected), ignore_music_glue);
7688 vector<Command*> cmds;
7690 _session->add_commands (cmds);
7692 _session->add_command (new StatefulDiffCommand (*i));
7696 RouteTimeAxisView* rtav = dynamic_cast<RouteTimeAxisView*> (*x);
7699 begin_reversible_command (_("insert time"));
7702 rtav->route ()->shift (pos, samples);
7709 const int32_t divisions = get_grid_music_divisions (0);
7710 XMLNode& before (_session->locations()->get_state());
7711 Locations::LocationList copy (_session->locations()->list());
7713 for (Locations::LocationList::iterator i = copy.begin(); i != copy.end(); ++i) {
7715 Locations::LocationList::const_iterator tmp;
7717 if ((*i)->position_lock_style() == AudioTime || glued_markers_too) {
7718 bool const was_locked = (*i)->locked ();
7719 if (locked_markers_too) {
7723 if ((*i)->start() >= pos) {
7724 // move end first, in case we're moving by more than the length of the range
7725 if (!(*i)->is_mark()) {
7726 (*i)->set_end ((*i)->end() + samples, false, true, divisions);
7728 (*i)->set_start ((*i)->start() + samples, false, true, divisions);
7740 begin_reversible_command (_("insert time"));
7743 XMLNode& after (_session->locations()->get_state());
7744 _session->add_command (new MementoCommand<Locations>(*_session->locations(), &before, &after));
7750 begin_reversible_command (_("insert time"));
7753 XMLNode& before (_session->tempo_map().get_state());
7754 _session->tempo_map().insert_time (pos, samples);
7755 XMLNode& after (_session->tempo_map().get_state());
7756 _session->add_command (new MementoCommand<TempoMap>(_session->tempo_map(), &before, &after));
7760 commit_reversible_command ();
7765 Editor::do_remove_time ()
7767 if (selection->tracks.empty()) {
7768 MessageDialog msg (_("You must first select some tracks to Remove Time."),
7769 true, MESSAGE_INFO, BUTTONS_OK, true);
7770 msg.set_position (WIN_POS_MOUSE);
7775 if (Config->get_edit_mode() == Lock) {
7776 MessageDialog msg (_("You cannot remove time in Lock Edit mode."),
7777 true, MESSAGE_INFO, BUTTONS_OK, true);
7778 msg.set_position (WIN_POS_MOUSE);
7783 InsertRemoveTimeDialog d (*this, true);
7785 int response = d.run ();
7787 if (response != RESPONSE_OK) {
7791 samplecnt_t distance = d.distance();
7793 if (distance == 0) {
7803 d.move_glued_markers(),
7804 d.move_locked_markers(),
7810 Editor::remove_time (samplepos_t pos, samplecnt_t samples, InsertTimeOption opt,
7811 bool ignore_music_glue, bool markers_too, bool glued_markers_too, bool locked_markers_too, bool tempo_too)
7813 if (Config->get_edit_mode() == Lock) {
7814 error << (_("Cannot insert or delete time when in Lock edit.")) << endmsg;
7817 bool in_command = false;
7819 for (TrackSelection::iterator x = selection->tracks.begin(); x != selection->tracks.end(); ++x) {
7821 boost::shared_ptr<Playlist> pl = (*x)->playlist();
7825 XMLNode &before = pl->get_state();
7828 begin_reversible_command (_("remove time"));
7832 std::list<AudioRange> rl;
7833 AudioRange ar(pos, pos+samples, 0);
7836 pl->shift (pos, -samples, true, ignore_music_glue);
7838 XMLNode &after = pl->get_state();
7840 _session->add_command (new MementoCommand<Playlist> (*pl, &before, &after));
7844 RouteTimeAxisView* rtav = dynamic_cast<RouteTimeAxisView*> (*x);
7847 begin_reversible_command (_("remove time"));
7850 rtav->route ()->shift (pos, -samples);
7854 const int32_t divisions = get_grid_music_divisions (0);
7855 std::list<Location*> loc_kill_list;
7860 XMLNode& before (_session->locations()->get_state());
7861 Locations::LocationList copy (_session->locations()->list());
7863 for (Locations::LocationList::iterator i = copy.begin(); i != copy.end(); ++i) {
7864 if ((*i)->position_lock_style() == AudioTime || glued_markers_too) {
7866 bool const was_locked = (*i)->locked ();
7867 if (locked_markers_too) {
7871 if (!(*i)->is_mark()) { // it's a range; have to handle both start and end
7872 if ((*i)->end() >= pos
7873 && (*i)->end() < pos+samples
7874 && (*i)->start() >= pos
7875 && (*i)->end() < pos+samples) { // range is completely enclosed; kill it
7877 loc_kill_list.push_back(*i);
7878 } else { // only start or end is included, try to do the right thing
7879 // move start before moving end, to avoid trying to move the end to before the start
7880 // if we're removing more time than the length of the range
7881 if ((*i)->start() >= pos && (*i)->start() < pos+samples) {
7882 // start is within cut
7883 (*i)->set_start (pos, false, true,divisions); // bring the start marker to the beginning of the cut
7885 } else if ((*i)->start() >= pos+samples) {
7886 // start (and thus entire range) lies beyond end of cut
7887 (*i)->set_start ((*i)->start() - samples, false, true, divisions); // slip the start marker back
7890 if ((*i)->end() >= pos && (*i)->end() < pos+samples) {
7891 // end is inside cut
7892 (*i)->set_end (pos, false, true, divisions); // bring the end to the cut
7894 } else if ((*i)->end() >= pos+samples) {
7895 // end is beyond end of cut
7896 (*i)->set_end ((*i)->end() - samples, false, true, divisions); // slip the end marker back
7901 } else if ((*i)->start() >= pos && (*i)->start() < pos+samples) {
7902 loc_kill_list.push_back(*i);
7904 } else if ((*i)->start() >= pos) {
7905 (*i)->set_start ((*i)->start() -samples, false, true, divisions);
7915 for (list<Location*>::iterator i = loc_kill_list.begin(); i != loc_kill_list.end(); ++i) {
7916 _session->locations()->remove (*i);
7921 begin_reversible_command (_("remove time"));
7924 XMLNode& after (_session->locations()->get_state());
7925 _session->add_command (new MementoCommand<Locations>(*_session->locations(), &before, &after));
7930 XMLNode& before (_session->tempo_map().get_state());
7932 if (_session->tempo_map().remove_time (pos, samples)) {
7934 begin_reversible_command (_("remove time"));
7937 XMLNode& after (_session->tempo_map().get_state());
7938 _session->add_command (new MementoCommand<TempoMap>(_session->tempo_map(), &before, &after));
7943 commit_reversible_command ();
7948 Editor::fit_selection ()
7950 if (!selection->tracks.empty()) {
7951 fit_tracks (selection->tracks);
7955 /* no selected tracks - use tracks with selected regions */
7957 if (!selection->regions.empty()) {
7958 for (RegionSelection::iterator r = selection->regions.begin(); r != selection->regions.end(); ++r) {
7959 tvl.push_back (&(*r)->get_time_axis_view ());
7965 } else if (internal_editing()) {
7966 /* no selected tracks, or regions, but in internal edit mode, so follow the mouse and use
7969 if (entered_track) {
7970 tvl.push_back (entered_track);
7978 Editor::fit_tracks (TrackViewList & tracks)
7980 if (tracks.empty()) {
7984 uint32_t child_heights = 0;
7985 int visible_tracks = 0;
7987 for (TrackSelection::iterator t = tracks.begin(); t != tracks.end(); ++t) {
7989 if (!(*t)->marked_for_display()) {
7993 child_heights += (*t)->effective_height() - (*t)->current_height();
7997 /* compute the per-track height from:
7999 * total canvas visible height
8000 * - height that will be taken by visible children of selected tracks
8001 * - height of the ruler/hscroll area
8003 uint32_t h = (uint32_t) floor ((trackviews_height() - child_heights) / visible_tracks);
8004 double first_y_pos = DBL_MAX;
8006 if (h < TimeAxisView::preset_height (HeightSmall)) {
8007 MessageDialog msg (_("There are too many tracks to fit in the current window"));
8008 /* too small to be displayed */
8012 undo_visual_stack.push_back (current_visual_state (true));
8013 PBD::Unwinder<bool> nsv (no_save_visual, true);
8015 /* build a list of all tracks, including children */
8018 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
8020 TimeAxisView::Children c = (*i)->get_child_list ();
8021 for (TimeAxisView::Children::iterator j = c.begin(); j != c.end(); ++j) {
8022 all.push_back (j->get());
8027 // find selection range.
8028 // if someone knows how to user TrackViewList::iterator for this
8030 int selected_top = -1;
8031 int selected_bottom = -1;
8033 for (TrackViewList::iterator t = all.begin(); t != all.end(); ++t, ++i) {
8034 if ((*t)->marked_for_display ()) {
8035 if (tracks.contains(*t)) {
8036 if (selected_top == -1) {
8039 selected_bottom = i;
8045 for (TrackViewList::iterator t = all.begin(); t != all.end(); ++t, ++i) {
8046 if ((*t)->marked_for_display ()) {
8047 if (tracks.contains(*t)) {
8048 (*t)->set_height (h);
8049 first_y_pos = std::min ((*t)->y_position (), first_y_pos);
8051 if (i > selected_top && i < selected_bottom) {
8052 hide_track_in_display (*t);
8059 set the controls_layout height now, because waiting for its size
8060 request signal handler will cause the vertical adjustment setting to fail
8063 controls_layout.property_height () = _full_canvas_height;
8064 vertical_adjustment.set_value (first_y_pos);
8066 redo_visual_stack.push_back (current_visual_state (true));
8068 visible_tracks_selector.set_text (_("Sel"));
8072 Editor::save_visual_state (uint32_t n)
8074 while (visual_states.size() <= n) {
8075 visual_states.push_back (0);
8078 if (visual_states[n] != 0) {
8079 delete visual_states[n];
8082 visual_states[n] = current_visual_state (true);
8087 Editor::goto_visual_state (uint32_t n)
8089 if (visual_states.size() <= n) {
8093 if (visual_states[n] == 0) {
8097 use_visual_state (*visual_states[n]);
8101 Editor::start_visual_state_op (uint32_t n)
8103 save_visual_state (n);
8105 PopUp* pup = new PopUp (WIN_POS_MOUSE, 1000, true);
8107 snprintf (buf, sizeof (buf), _("Saved view %u"), n+1);
8108 pup->set_text (buf);
8113 Editor::cancel_visual_state_op (uint32_t n)
8115 goto_visual_state (n);
8119 Editor::toggle_region_mute ()
8121 if (_ignore_region_action) {
8125 RegionSelection rs = get_regions_from_selection_and_entered ();
8131 if (rs.size() > 1) {
8132 begin_reversible_command (_("mute regions"));
8134 begin_reversible_command (_("mute region"));
8137 for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
8139 (*i)->region()->playlist()->clear_changes ();
8140 (*i)->region()->set_muted (!(*i)->region()->muted ());
8141 _session->add_command (new StatefulDiffCommand ((*i)->region()));
8145 commit_reversible_command ();
8149 Editor::combine_regions ()
8151 /* foreach track with selected regions, take all selected regions
8152 and join them into a new region containing the subregions (as a
8156 typedef set<RouteTimeAxisView*> RTVS;
8159 if (selection->regions.empty()) {
8163 for (RegionSelection::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
8164 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*>(&(*i)->get_time_axis_view());
8167 tracks.insert (rtv);
8171 begin_reversible_command (_("combine regions"));
8173 vector<RegionView*> new_selection;
8175 for (RTVS::iterator i = tracks.begin(); i != tracks.end(); ++i) {
8178 if ((rv = (*i)->combine_regions ()) != 0) {
8179 new_selection.push_back (rv);
8183 selection->clear_regions ();
8184 for (vector<RegionView*>::iterator i = new_selection.begin(); i != new_selection.end(); ++i) {
8185 selection->add (*i);
8188 commit_reversible_command ();
8192 Editor::uncombine_regions ()
8194 typedef set<RouteTimeAxisView*> RTVS;
8197 if (selection->regions.empty()) {
8201 for (RegionSelection::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
8202 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*>(&(*i)->get_time_axis_view());
8205 tracks.insert (rtv);
8209 begin_reversible_command (_("uncombine regions"));
8211 for (RTVS::iterator i = tracks.begin(); i != tracks.end(); ++i) {
8212 (*i)->uncombine_regions ();
8215 commit_reversible_command ();
8219 Editor::toggle_midi_input_active (bool flip_others)
8222 boost::shared_ptr<RouteList> rl (new RouteList);
8224 for (TrackSelection::iterator i = selection->tracks.begin(); i != selection->tracks.end(); ++i) {
8225 RouteTimeAxisView *rtav = dynamic_cast<RouteTimeAxisView *>(*i);
8231 boost::shared_ptr<MidiTrack> mt = rtav->midi_track();
8234 rl->push_back (rtav->route());
8235 onoff = !mt->input_active();
8239 _session->set_exclusive_input_active (rl, onoff, flip_others);
8242 static bool ok_fine (GdkEventAny*) { return true; }
8248 lock_dialog = new Gtk::Dialog (string_compose (_("%1: Locked"), PROGRAM_NAME), true);
8250 Gtk::Image* padlock = manage (new Gtk::Image (ARDOUR_UI_UTILS::get_icon ("padlock_closed")));
8251 lock_dialog->get_vbox()->pack_start (*padlock);
8252 lock_dialog->signal_delete_event ().connect (sigc::ptr_fun (ok_fine));
8254 ArdourButton* b = manage (new ArdourButton);
8255 b->set_name ("lock button");
8256 b->set_text (_("Click to unlock"));
8257 b->signal_clicked.connect (sigc::mem_fun (*this, &Editor::unlock));
8258 lock_dialog->get_vbox()->pack_start (*b);
8260 lock_dialog->get_vbox()->show_all ();
8261 lock_dialog->set_size_request (200, 200);
8264 delete _main_menu_disabler;
8265 _main_menu_disabler = new MainMenuDisabler;
8267 lock_dialog->present ();
8269 lock_dialog->get_window()->set_decorations (Gdk::WMDecoration (0));
8275 lock_dialog->hide ();
8277 delete _main_menu_disabler;
8278 _main_menu_disabler = 0;
8280 if (UIConfiguration::instance().get_lock_gui_after_seconds()) {
8281 start_lock_event_timing ();
8286 Editor::bring_in_callback (Gtk::Label* label, uint32_t n, uint32_t total, string name)
8288 Gtkmm2ext::UI::instance()->call_slot (invalidator (*this), boost::bind (&Editor::update_bring_in_message, this, label, n, total, name));
8292 Editor::update_bring_in_message (Gtk::Label* label, uint32_t n, uint32_t total, string name)
8294 Timers::TimerSuspender t;
8295 label->set_text (string_compose ("Copying %1, %2 of %3", name, n, total));
8296 Gtkmm2ext::UI::instance()->flush_pending (1);
8300 Editor::bring_all_sources_into_session ()
8307 ArdourDialog w (_("Moving embedded files into session folder"));
8308 w.get_vbox()->pack_start (msg);
8311 /* flush all pending GUI events because we're about to start copying
8315 Timers::TimerSuspender t;
8316 Gtkmm2ext::UI::instance()->flush_pending (3);
8320 _session->bring_all_sources_into_session (boost::bind (&Editor::bring_in_callback, this, &msg, _1, _2, _3));