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)
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"));
193 if (regions.size() == 1) {
194 /* TODO: if splitting a single region, and snap-to is using
195 region boundaries, mabye we shouldn't pay attention to them? */
198 EditorFreeze(); /* Emit Signal */
201 for (RegionSelection::iterator a = regions.begin(); a != regions.end(); ) {
203 RegionSelection::iterator tmp;
205 /* XXX this test needs to be more complicated, to make sure we really
206 have something to split.
209 if (!(*a)->region()->covers (where.sample)) {
217 boost::shared_ptr<Playlist> pl = (*a)->region()->playlist();
225 /* we haven't seen this playlist before */
227 /* remember used playlists so we can thaw them later */
228 used_playlists.push_back(pl);
230 TimeAxisView& tv = (*a)->get_time_axis_view();
231 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (&tv);
233 used_trackviews.push_back (rtv);
240 pl->clear_changes ();
241 pl->split_region ((*a)->region(), where);
242 _session->add_command (new StatefulDiffCommand (pl));
248 latest_regionviews.clear ();
250 vector<sigc::connection> region_added_connections;
252 for (list<RouteTimeAxisView*>::iterator i = used_trackviews.begin(); i != used_trackviews.end(); ++i) {
253 region_added_connections.push_back ((*i)->view()->RegionViewAdded.connect (sigc::mem_fun(*this, &Editor::collect_new_region_view)));
256 while (used_playlists.size() > 0) {
257 list <boost::shared_ptr<Playlist > >::iterator i = used_playlists.begin();
259 used_playlists.pop_front();
262 for (vector<sigc::connection>::iterator c = region_added_connections.begin(); c != region_added_connections.end(); ++c) {
267 EditorThaw(); /* Emit Signal */
270 if (working_on_selection) {
271 // IFF we were working on selected regions, try to reinstate the other region selections that existed before the freeze/thaw.
273 RegionSelectionAfterSplit rsas = Config->get_region_selection_after_split();
274 /* There are three classes of regions that we might want selected after
275 splitting selected regions:
276 - regions selected before the split operation, and unaffected by it
277 - newly-created regions before the split
278 - newly-created regions after the split
281 if (rsas & Existing) {
282 // region selections that existed before the split.
283 selection->add (pre_selected_regions);
286 for (RegionSelection::iterator ri = latest_regionviews.begin(); ri != latest_regionviews.end(); ri++) {
287 if ((*ri)->region()->position() < where.sample) {
288 // new regions created before the split
289 if (rsas & NewlyCreatedLeft) {
290 selection->add (*ri);
293 // new regions created after the split
294 if (rsas & NewlyCreatedRight) {
295 selection->add (*ri);
301 commit_reversible_command ();
304 /** Move one extreme of the current range selection. If more than one range is selected,
305 * the start of the earliest range or the end of the latest range is moved.
307 * @param move_end true to move the end of the current range selection, false to move
309 * @param next true to move the extreme to the next region boundary, false to move to
313 Editor::move_range_selection_start_or_end_to_region_boundary (bool move_end, bool next)
315 if (selection->time.start() == selection->time.end_sample()) {
319 samplepos_t start = selection->time.start ();
320 samplepos_t end = selection->time.end_sample ();
322 /* the position of the thing we may move */
323 samplepos_t pos = move_end ? end : start;
324 int dir = next ? 1 : -1;
326 /* so we don't find the current region again */
327 if (dir > 0 || pos > 0) {
331 samplepos_t const target = get_region_boundary (pos, dir, true, false);
346 begin_reversible_selection_op (_("alter selection"));
347 selection->set_preserving_all_ranges (start, end);
348 commit_reversible_selection_op ();
352 Editor::nudge_forward_release (GdkEventButton* ev)
354 if (ev->state & Keyboard::PrimaryModifier) {
355 nudge_forward (false, true);
357 nudge_forward (false, false);
363 Editor::nudge_backward_release (GdkEventButton* ev)
365 if (ev->state & Keyboard::PrimaryModifier) {
366 nudge_backward (false, true);
368 nudge_backward (false, false);
375 Editor::nudge_forward (bool next, bool force_playhead)
377 samplepos_t distance;
378 samplepos_t next_distance;
384 RegionSelection rs = get_regions_from_selection_and_entered ();
386 if (!force_playhead && !rs.empty()) {
388 begin_reversible_command (_("nudge regions forward"));
390 for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
391 boost::shared_ptr<Region> r ((*i)->region());
393 distance = get_nudge_distance (r->position(), next_distance);
396 distance = next_distance;
400 r->set_position (r->position() + distance);
401 _session->add_command (new StatefulDiffCommand (r));
404 commit_reversible_command ();
407 } else if (!force_playhead && !selection->markers.empty()) {
410 bool in_command = false;
411 const int32_t divisions = get_grid_music_divisions (0);
413 for (MarkerSelection::iterator i = selection->markers.begin(); i != selection->markers.end(); ++i) {
415 Location* loc = find_location_from_marker ((*i), is_start);
419 XMLNode& before (loc->get_state());
422 distance = get_nudge_distance (loc->start(), next_distance);
424 distance = next_distance;
426 if (max_samplepos - distance > loc->start() + loc->length()) {
427 loc->set_start (loc->start() + distance, false, true, divisions);
429 loc->set_start (max_samplepos - loc->length(), false, true, divisions);
432 distance = get_nudge_distance (loc->end(), next_distance);
434 distance = next_distance;
436 if (max_samplepos - distance > loc->end()) {
437 loc->set_end (loc->end() + distance, false, true, divisions);
439 loc->set_end (max_samplepos, false, true, divisions);
441 if (loc->is_session_range()) {
442 _session->set_end_is_free (false);
446 begin_reversible_command (_("nudge location forward"));
449 XMLNode& after (loc->get_state());
450 _session->add_command (new MementoCommand<Location>(*loc, &before, &after));
455 commit_reversible_command ();
458 distance = get_nudge_distance (playhead_cursor->current_sample (), next_distance);
459 _session->request_locate (playhead_cursor->current_sample () + distance);
464 Editor::nudge_backward (bool next, bool force_playhead)
466 samplepos_t distance;
467 samplepos_t next_distance;
473 RegionSelection rs = get_regions_from_selection_and_entered ();
475 if (!force_playhead && !rs.empty()) {
477 begin_reversible_command (_("nudge regions backward"));
479 for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
480 boost::shared_ptr<Region> r ((*i)->region());
482 distance = get_nudge_distance (r->position(), next_distance);
485 distance = next_distance;
490 if (r->position() > distance) {
491 r->set_position (r->position() - distance);
495 _session->add_command (new StatefulDiffCommand (r));
498 commit_reversible_command ();
500 } else if (!force_playhead && !selection->markers.empty()) {
503 bool in_command = false;
505 for (MarkerSelection::iterator i = selection->markers.begin(); i != selection->markers.end(); ++i) {
507 Location* loc = find_location_from_marker ((*i), is_start);
511 XMLNode& before (loc->get_state());
514 distance = get_nudge_distance (loc->start(), next_distance);
516 distance = next_distance;
518 if (distance < loc->start()) {
519 loc->set_start (loc->start() - distance, false, true, get_grid_music_divisions(0));
521 loc->set_start (0, false, true, get_grid_music_divisions(0));
524 distance = get_nudge_distance (loc->end(), next_distance);
527 distance = next_distance;
530 if (distance < loc->end() - loc->length()) {
531 loc->set_end (loc->end() - distance, false, true, get_grid_music_divisions(0));
533 loc->set_end (loc->length(), false, true, get_grid_music_divisions(0));
535 if (loc->is_session_range()) {
536 _session->set_end_is_free (false);
540 begin_reversible_command (_("nudge location forward"));
543 XMLNode& after (loc->get_state());
544 _session->add_command (new MementoCommand<Location>(*loc, &before, &after));
548 commit_reversible_command ();
553 distance = get_nudge_distance (playhead_cursor->current_sample (), next_distance);
555 if (playhead_cursor->current_sample () > distance) {
556 _session->request_locate (playhead_cursor->current_sample () - distance);
558 _session->goto_start();
564 Editor::nudge_forward_capture_offset ()
566 RegionSelection rs = get_regions_from_selection_and_entered ();
568 if (!_session || rs.empty()) {
572 begin_reversible_command (_("nudge forward"));
574 samplepos_t const distance = _session->worst_output_latency();
576 for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
577 boost::shared_ptr<Region> r ((*i)->region());
580 r->set_position (r->position() + distance);
581 _session->add_command(new StatefulDiffCommand (r));
584 commit_reversible_command ();
588 Editor::nudge_backward_capture_offset ()
590 RegionSelection rs = get_regions_from_selection_and_entered ();
592 if (!_session || rs.empty()) {
596 begin_reversible_command (_("nudge backward"));
598 samplepos_t const distance = _session->worst_output_latency();
600 for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
601 boost::shared_ptr<Region> r ((*i)->region());
605 if (r->position() > distance) {
606 r->set_position (r->position() - distance);
610 _session->add_command(new StatefulDiffCommand (r));
613 commit_reversible_command ();
616 struct RegionSelectionPositionSorter {
617 bool operator() (RegionView* a, RegionView* b) {
618 return a->region()->position() < b->region()->position();
623 Editor::sequence_regions ()
626 samplepos_t r_end_prev;
634 RegionSelection rs = get_regions_from_selection_and_entered ();
635 rs.sort(RegionSelectionPositionSorter());
639 bool in_command = false;
641 for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
642 boost::shared_ptr<Region> r ((*i)->region());
650 if(r->position_locked())
657 r->set_position(r_end_prev);
661 begin_reversible_command (_("sequence regions"));
664 _session->add_command (new StatefulDiffCommand (r));
666 r_end=r->position() + r->length();
672 commit_reversible_command ();
681 Editor::move_to_start ()
683 _session->goto_start ();
687 Editor::move_to_end ()
690 _session->request_locate (_session->current_end_sample());
694 Editor::build_region_boundary_cache ()
697 /* TODO: maybe set a timer so we don't recalutate when lots of changes are coming in */
698 /* TODO: maybe somehow defer this until session is fully loaded. */
700 if (!_region_boundary_cache_dirty)
704 vector<RegionPoint> interesting_points;
705 boost::shared_ptr<Region> r;
706 TrackViewList tracks;
709 region_boundary_cache.clear ();
715 bool maybe_first_sample = false;
717 if (UIConfiguration::instance().get_snap_to_region_start()) {
718 interesting_points.push_back (Start);
719 maybe_first_sample = true;
722 if (UIConfiguration::instance().get_snap_to_region_end()) {
723 interesting_points.push_back (End);
726 if (UIConfiguration::instance().get_snap_to_region_sync()) {
727 interesting_points.push_back (SyncPoint);
730 /* if no snap selections are set, boundary cache should be left empty */
731 if ( interesting_points.empty() ) {
735 TimeAxisView *ontrack = 0;
738 tlist = track_views.filter_to_unique_playlists ();
740 if (maybe_first_sample) {
741 TrackViewList::const_iterator i;
742 for (i = tlist.begin(); i != tlist.end(); ++i) {
743 boost::shared_ptr<Playlist> pl = (*i)->playlist();
744 if (pl && pl->count_regions_at (0)) {
745 region_boundary_cache.push_back (0);
751 std::pair<samplepos_t, samplepos_t> ext = session_gui_extents (false);
752 samplepos_t session_end = ext.second;
754 while (pos < session_end && !at_end) {
757 samplepos_t lpos = session_end;
759 for (vector<RegionPoint>::iterator p = interesting_points.begin(); p != interesting_points.end(); ++p) {
761 if ((r = find_next_region (pos, *p, 1, tlist, &ontrack)) == 0) {
762 if (*p == interesting_points.back()) {
765 /* move to next point type */
771 rpos = r->first_sample();
775 rpos = r->last_sample();
779 rpos = r->sync_position ();
790 /* prevent duplicates, but we don't use set<> because we want to be able
794 vector<samplepos_t>::iterator ri;
796 for (ri = region_boundary_cache.begin(); ri != region_boundary_cache.end(); ++ri) {
802 if (ri == region_boundary_cache.end()) {
803 region_boundary_cache.push_back (rpos);
810 /* finally sort to be sure that the order is correct */
812 sort (region_boundary_cache.begin(), region_boundary_cache.end());
814 _region_boundary_cache_dirty = false;
817 boost::shared_ptr<Region>
818 Editor::find_next_region (samplepos_t sample, RegionPoint point, int32_t dir, TrackViewList& tracks, TimeAxisView **ontrack)
820 TrackViewList::iterator i;
821 samplepos_t closest = max_samplepos;
822 boost::shared_ptr<Region> ret;
823 samplepos_t rpos = 0;
825 samplepos_t track_sample;
827 for (i = tracks.begin(); i != tracks.end(); ++i) {
829 samplecnt_t distance;
830 boost::shared_ptr<Region> r;
832 track_sample = sample;
834 if ((r = (*i)->find_next_region (track_sample, point, dir)) == 0) {
840 rpos = r->first_sample ();
844 rpos = r->last_sample ();
848 rpos = r->sync_position ();
853 distance = rpos - sample;
855 distance = sample - rpos;
858 if (distance < closest) {
870 Editor::find_next_region_boundary (samplepos_t pos, int32_t dir, const TrackViewList& tracks)
872 samplecnt_t distance = max_samplepos;
873 samplepos_t current_nearest = -1;
875 for (TrackViewList::const_iterator i = tracks.begin(); i != tracks.end(); ++i) {
876 samplepos_t contender;
879 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (*i);
885 if ((contender = rtv->find_next_region_boundary (pos, dir)) < 0) {
889 d = ::llabs (pos - contender);
892 current_nearest = contender;
897 return current_nearest;
901 Editor::get_region_boundary (samplepos_t pos, int32_t dir, bool with_selection, bool only_onscreen)
906 if (with_selection && Config->get_region_boundaries_from_selected_tracks()) {
908 if (!selection->tracks.empty()) {
910 target = find_next_region_boundary (pos, dir, selection->tracks);
914 if (only_onscreen || Config->get_region_boundaries_from_onscreen_tracks()) {
915 get_onscreen_tracks (tvl);
916 target = find_next_region_boundary (pos, dir, tvl);
918 target = find_next_region_boundary (pos, dir, track_views);
924 if (only_onscreen || Config->get_region_boundaries_from_onscreen_tracks()) {
925 get_onscreen_tracks (tvl);
926 target = find_next_region_boundary (pos, dir, tvl);
928 target = find_next_region_boundary (pos, dir, track_views);
936 Editor::cursor_to_region_boundary (bool with_selection, int32_t dir)
938 samplepos_t pos = playhead_cursor->current_sample ();
945 // so we don't find the current region again..
946 if (dir > 0 || pos > 0) {
950 if ((target = get_region_boundary (pos, dir, with_selection, false)) < 0) {
954 _session->request_locate (target);
958 Editor::cursor_to_next_region_boundary (bool with_selection)
960 cursor_to_region_boundary (with_selection, 1);
964 Editor::cursor_to_previous_region_boundary (bool with_selection)
966 cursor_to_region_boundary (with_selection, -1);
970 Editor::cursor_to_region_point (EditorCursor* cursor, RegionPoint point, int32_t dir)
972 boost::shared_ptr<Region> r;
973 samplepos_t pos = cursor->current_sample ();
979 TimeAxisView *ontrack = 0;
981 // so we don't find the current region again..
985 if (!selection->tracks.empty()) {
987 r = find_next_region (pos, point, dir, selection->tracks, &ontrack);
989 } else if (clicked_axisview) {
992 t.push_back (clicked_axisview);
994 r = find_next_region (pos, point, dir, t, &ontrack);
998 r = find_next_region (pos, point, dir, track_views, &ontrack);
1007 pos = r->first_sample ();
1011 pos = r->last_sample ();
1015 pos = r->sync_position ();
1019 if (cursor == playhead_cursor) {
1020 _session->request_locate (pos);
1022 cursor->set_position (pos);
1027 Editor::cursor_to_next_region_point (EditorCursor* cursor, RegionPoint point)
1029 cursor_to_region_point (cursor, point, 1);
1033 Editor::cursor_to_previous_region_point (EditorCursor* cursor, RegionPoint point)
1035 cursor_to_region_point (cursor, point, -1);
1039 Editor::cursor_to_selection_start (EditorCursor *cursor)
1041 samplepos_t pos = 0;
1043 switch (mouse_mode) {
1045 if (!selection->regions.empty()) {
1046 pos = selection->regions.start();
1051 if (!selection->time.empty()) {
1052 pos = selection->time.start ();
1060 if (cursor == playhead_cursor) {
1061 _session->request_locate (pos);
1063 cursor->set_position (pos);
1068 Editor::cursor_to_selection_end (EditorCursor *cursor)
1070 samplepos_t pos = 0;
1072 switch (mouse_mode) {
1074 if (!selection->regions.empty()) {
1075 pos = selection->regions.end_sample();
1080 if (!selection->time.empty()) {
1081 pos = selection->time.end_sample ();
1089 if (cursor == playhead_cursor) {
1090 _session->request_locate (pos);
1092 cursor->set_position (pos);
1097 Editor::selected_marker_to_region_boundary (bool with_selection, int32_t dir)
1107 if (selection->markers.empty()) {
1111 if (!mouse_sample (mouse, ignored)) {
1115 add_location_mark (mouse);
1118 if ((loc = find_location_from_marker (selection->markers.front(), ignored)) == 0) {
1122 samplepos_t pos = loc->start();
1124 // so we don't find the current region again..
1125 if (dir > 0 || pos > 0) {
1129 if ((target = get_region_boundary (pos, dir, with_selection, false)) < 0) {
1133 loc->move_to (target, 0);
1137 Editor::selected_marker_to_next_region_boundary (bool with_selection)
1139 selected_marker_to_region_boundary (with_selection, 1);
1143 Editor::selected_marker_to_previous_region_boundary (bool with_selection)
1145 selected_marker_to_region_boundary (with_selection, -1);
1149 Editor::selected_marker_to_region_point (RegionPoint point, int32_t dir)
1151 boost::shared_ptr<Region> r;
1156 if (!_session || selection->markers.empty()) {
1160 if ((loc = find_location_from_marker (selection->markers.front(), ignored)) == 0) {
1164 TimeAxisView *ontrack = 0;
1168 // so we don't find the current region again..
1172 if (!selection->tracks.empty()) {
1174 r = find_next_region (pos, point, dir, selection->tracks, &ontrack);
1178 r = find_next_region (pos, point, dir, track_views, &ontrack);
1187 pos = r->first_sample ();
1191 pos = r->last_sample ();
1195 pos = r->adjust_to_sync (r->first_sample());
1199 loc->move_to (pos, 0);
1203 Editor::selected_marker_to_next_region_point (RegionPoint point)
1205 selected_marker_to_region_point (point, 1);
1209 Editor::selected_marker_to_previous_region_point (RegionPoint point)
1211 selected_marker_to_region_point (point, -1);
1215 Editor::selected_marker_to_selection_start ()
1217 samplepos_t pos = 0;
1221 if (!_session || selection->markers.empty()) {
1225 if ((loc = find_location_from_marker (selection->markers.front(), ignored)) == 0) {
1229 switch (mouse_mode) {
1231 if (!selection->regions.empty()) {
1232 pos = selection->regions.start();
1237 if (!selection->time.empty()) {
1238 pos = selection->time.start ();
1246 loc->move_to (pos, 0);
1250 Editor::selected_marker_to_selection_end ()
1252 samplepos_t pos = 0;
1256 if (!_session || selection->markers.empty()) {
1260 if ((loc = find_location_from_marker (selection->markers.front(), ignored)) == 0) {
1264 switch (mouse_mode) {
1266 if (!selection->regions.empty()) {
1267 pos = selection->regions.end_sample();
1272 if (!selection->time.empty()) {
1273 pos = selection->time.end_sample ();
1281 loc->move_to (pos, 0);
1285 Editor::scroll_playhead (bool forward)
1287 samplepos_t pos = playhead_cursor->current_sample ();
1288 samplecnt_t delta = (samplecnt_t) floor (current_page_samples() / 0.8);
1291 if (pos == max_samplepos) {
1295 if (pos < max_samplepos - delta) {
1298 pos = max_samplepos;
1314 _session->request_locate (pos);
1318 Editor::cursor_align (bool playhead_to_edit)
1324 if (playhead_to_edit) {
1326 if (selection->markers.empty()) {
1330 _session->request_locate (selection->markers.front()->position(), _session->transport_rolling());
1333 const int32_t divisions = get_grid_music_divisions (0);
1334 /* move selected markers to playhead */
1336 for (MarkerSelection::iterator i = selection->markers.begin(); i != selection->markers.end(); ++i) {
1339 Location* loc = find_location_from_marker (*i, ignored);
1341 if (loc->is_mark()) {
1342 loc->set_start (playhead_cursor->current_sample (), false, true, divisions);
1344 loc->set (playhead_cursor->current_sample (),
1345 playhead_cursor->current_sample () + loc->length(), true, divisions);
1352 Editor::scroll_backward (float pages)
1354 samplepos_t const one_page = (samplepos_t) rint (_visible_canvas_width * samples_per_pixel);
1355 samplepos_t const cnt = (samplepos_t) floor (pages * one_page);
1358 if (_leftmost_sample < cnt) {
1361 sample = _leftmost_sample - cnt;
1364 reset_x_origin (sample);
1368 Editor::scroll_forward (float pages)
1370 samplepos_t const one_page = (samplepos_t) rint (_visible_canvas_width * samples_per_pixel);
1371 samplepos_t const cnt = (samplepos_t) floor (pages * one_page);
1374 if (max_samplepos - cnt < _leftmost_sample) {
1375 sample = max_samplepos - cnt;
1377 sample = _leftmost_sample + cnt;
1380 reset_x_origin (sample);
1384 Editor::scroll_tracks_down ()
1386 double vert_value = vertical_adjustment.get_value() + vertical_adjustment.get_page_size();
1387 if (vert_value > vertical_adjustment.get_upper() - _visible_canvas_height) {
1388 vert_value = vertical_adjustment.get_upper() - _visible_canvas_height;
1391 vertical_adjustment.set_value (vert_value);
1395 Editor::scroll_tracks_up ()
1397 vertical_adjustment.set_value (vertical_adjustment.get_value() - vertical_adjustment.get_page_size());
1401 Editor::scroll_tracks_down_line ()
1403 double vert_value = vertical_adjustment.get_value() + 60;
1405 if (vert_value > vertical_adjustment.get_upper() - _visible_canvas_height) {
1406 vert_value = vertical_adjustment.get_upper() - _visible_canvas_height;
1409 vertical_adjustment.set_value (vert_value);
1413 Editor::scroll_tracks_up_line ()
1415 reset_y_origin (vertical_adjustment.get_value() - 60);
1419 Editor::select_topmost_track ()
1421 const double top_of_trackviews = vertical_adjustment.get_value();
1422 for (TrackViewList::iterator t = track_views.begin(); t != track_views.end(); ++t) {
1423 if ((*t)->hidden()) {
1426 std::pair<TimeAxisView*,double> res = (*t)->covers_y_position (top_of_trackviews);
1428 selection->set (*t);
1435 Editor::scroll_down_one_track (bool skip_child_views)
1437 TrackViewList::reverse_iterator next = track_views.rend();
1438 const double top_of_trackviews = vertical_adjustment.get_value();
1440 for (TrackViewList::reverse_iterator t = track_views.rbegin(); t != track_views.rend(); ++t) {
1441 if ((*t)->hidden()) {
1445 /* If this is the upper-most visible trackview, we want to display
1446 * the one above it (next)
1448 * Note that covers_y_position() is recursive and includes child views
1450 std::pair<TimeAxisView*,double> res = (*t)->covers_y_position (top_of_trackviews);
1453 if (skip_child_views) {
1456 /* automation lane (one level, non-recursive)
1458 * - if no automation lane exists -> move to next tack
1459 * - if the first (here: bottom-most) matches -> move to next tack
1460 * - if no y-axis match is found -> the current track is at the top
1461 * -> move to last (here: top-most) automation lane
1463 TimeAxisView::Children kids = (*t)->get_child_list();
1464 TimeAxisView::Children::reverse_iterator nkid = kids.rend();
1466 for (TimeAxisView::Children::reverse_iterator ci = kids.rbegin(); ci != kids.rend(); ++ci) {
1467 if ((*ci)->hidden()) {
1471 std::pair<TimeAxisView*,double> dev;
1472 dev = (*ci)->covers_y_position (top_of_trackviews);
1474 /* some automation lane is currently at the top */
1475 if (ci == kids.rbegin()) {
1476 /* first (bottom-most) autmation lane is at the top.
1477 * -> move to next track
1486 if (nkid != kids.rend()) {
1487 ensure_time_axis_view_is_visible (**nkid, true);
1495 /* move to the track below the first one that covers the */
1497 if (next != track_views.rend()) {
1498 ensure_time_axis_view_is_visible (**next, true);
1506 Editor::scroll_up_one_track (bool skip_child_views)
1508 TrackViewList::iterator prev = track_views.end();
1509 double top_of_trackviews = vertical_adjustment.get_value ();
1511 for (TrackViewList::iterator t = track_views.begin(); t != track_views.end(); ++t) {
1513 if ((*t)->hidden()) {
1517 /* find the trackview at the top of the trackview group
1519 * Note that covers_y_position() is recursive and includes child views
1521 std::pair<TimeAxisView*,double> res = (*t)->covers_y_position (top_of_trackviews);
1524 if (skip_child_views) {
1527 /* automation lane (one level, non-recursive)
1529 * - if no automation lane exists -> move to prev tack
1530 * - if no y-axis match is found -> the current track is at the top -> move to prev track
1531 * (actually last automation lane of previous track, see below)
1532 * - if first (top-most) lane is at the top -> move to this track
1533 * - else move up one lane
1535 TimeAxisView::Children kids = (*t)->get_child_list();
1536 TimeAxisView::Children::iterator pkid = kids.end();
1538 for (TimeAxisView::Children::iterator ci = kids.begin(); ci != kids.end(); ++ci) {
1539 if ((*ci)->hidden()) {
1543 std::pair<TimeAxisView*,double> dev;
1544 dev = (*ci)->covers_y_position (top_of_trackviews);
1546 /* some automation lane is currently at the top */
1547 if (ci == kids.begin()) {
1548 /* first (top-most) autmation lane is at the top.
1549 * jump directly to this track's top
1551 ensure_time_axis_view_is_visible (**t, true);
1554 else if (pkid != kids.end()) {
1555 /* some other automation lane is at the top.
1556 * move up to prev automation lane.
1558 ensure_time_axis_view_is_visible (**pkid, true);
1561 assert(0); // not reached
1572 if (prev != track_views.end()) {
1573 // move to bottom-most automation-lane of the previous track
1574 TimeAxisView::Children kids = (*prev)->get_child_list();
1575 TimeAxisView::Children::reverse_iterator pkid = kids.rend();
1576 if (!skip_child_views) {
1577 // find the last visible lane
1578 for (TimeAxisView::Children::reverse_iterator ci = kids.rbegin(); ci != kids.rend(); ++ci) {
1579 if (!(*ci)->hidden()) {
1585 if (pkid != kids.rend()) {
1586 ensure_time_axis_view_is_visible (**pkid, true);
1588 ensure_time_axis_view_is_visible (**prev, true);
1597 Editor::scroll_left_step ()
1599 samplepos_t xdelta = (current_page_samples() / 8);
1601 if (_leftmost_sample > xdelta) {
1602 reset_x_origin (_leftmost_sample - xdelta);
1610 Editor::scroll_right_step ()
1612 samplepos_t xdelta = (current_page_samples() / 8);
1614 if (max_samplepos - xdelta > _leftmost_sample) {
1615 reset_x_origin (_leftmost_sample + xdelta);
1617 reset_x_origin (max_samplepos - current_page_samples());
1622 Editor::scroll_left_half_page ()
1624 samplepos_t xdelta = (current_page_samples() / 2);
1625 if (_leftmost_sample > xdelta) {
1626 reset_x_origin (_leftmost_sample - xdelta);
1633 Editor::scroll_right_half_page ()
1635 samplepos_t xdelta = (current_page_samples() / 2);
1636 if (max_samplepos - xdelta > _leftmost_sample) {
1637 reset_x_origin (_leftmost_sample + xdelta);
1639 reset_x_origin (max_samplepos - current_page_samples());
1646 Editor::tav_zoom_step (bool coarser)
1648 DisplaySuspender ds;
1652 if (selection->tracks.empty()) {
1655 ts = &selection->tracks;
1658 for (TrackViewList::iterator i = ts->begin(); i != ts->end(); ++i) {
1659 TimeAxisView *tv = (static_cast<TimeAxisView*>(*i));
1660 tv->step_height (coarser);
1665 Editor::tav_zoom_smooth (bool coarser, bool force_all)
1667 DisplaySuspender ds;
1671 if (selection->tracks.empty() || force_all) {
1674 ts = &selection->tracks;
1677 for (TrackViewList::iterator i = ts->begin(); i != ts->end(); ++i) {
1678 TimeAxisView *tv = (static_cast<TimeAxisView*>(*i));
1679 uint32_t h = tv->current_height ();
1684 if (h >= TimeAxisView::preset_height (HeightSmall)) {
1689 tv->set_height (h + 5);
1695 Editor::temporal_zoom_step_mouse_focus_scale (bool zoom_out, double scale)
1697 Editing::ZoomFocus temp_focus = zoom_focus;
1698 zoom_focus = Editing::ZoomFocusMouse;
1699 temporal_zoom_step_scale (zoom_out, scale);
1700 zoom_focus = temp_focus;
1704 Editor::temporal_zoom_step_mouse_focus (bool zoom_out)
1706 temporal_zoom_step_mouse_focus_scale (zoom_out, 2.0);
1710 Editor::temporal_zoom_step (bool zoom_out)
1712 temporal_zoom_step_scale (zoom_out, 2.0);
1716 Editor::temporal_zoom_step_scale (bool zoom_out, double scale)
1718 ENSURE_GUI_THREAD (*this, &Editor::temporal_zoom_step, zoom_out, scale)
1720 samplecnt_t nspp = samples_per_pixel;
1724 if (nspp == samples_per_pixel) {
1729 if (nspp == samples_per_pixel) {
1734 //zoom-behavior-tweaks
1735 //limit our maximum zoom to the session gui extents value
1736 std::pair<samplepos_t, samplepos_t> ext = session_gui_extents();
1737 samplecnt_t session_extents_pp = (ext.second - ext.first) / _visible_canvas_width;
1738 if (nspp > session_extents_pp)
1739 nspp = session_extents_pp;
1741 temporal_zoom (nspp);
1745 Editor::temporal_zoom (samplecnt_t fpp)
1751 samplepos_t current_page = current_page_samples();
1752 samplepos_t current_leftmost = _leftmost_sample;
1753 samplepos_t current_rightmost;
1754 samplepos_t current_center;
1755 samplepos_t new_page_size;
1756 samplepos_t half_page_size;
1757 samplepos_t leftmost_after_zoom = 0;
1759 bool in_track_canvas;
1760 bool use_mouse_sample = true;
1764 if (fpp == samples_per_pixel) {
1768 // Imposing an arbitrary limit to zoom out as too much zoom out produces
1769 // segfaults for lack of memory. If somebody decides this is not high enough I
1770 // believe it can be raisen to higher values but some limit must be in place.
1772 // This constant represents 1 day @ 48kHz on a 1600 pixel wide display
1773 // all of which is used for the editor track displays. The whole day
1774 // would be 4147200000 samples, so 2592000 samples per pixel.
1776 nfpp = min (fpp, (samplecnt_t) 2592000);
1777 nfpp = max ((samplecnt_t) 1, nfpp);
1779 new_page_size = (samplepos_t) floor (_visible_canvas_width * nfpp);
1780 half_page_size = new_page_size / 2;
1782 switch (zoom_focus) {
1784 leftmost_after_zoom = current_leftmost;
1787 case ZoomFocusRight:
1788 current_rightmost = _leftmost_sample + current_page;
1789 if (current_rightmost < new_page_size) {
1790 leftmost_after_zoom = 0;
1792 leftmost_after_zoom = current_rightmost - new_page_size;
1796 case ZoomFocusCenter:
1797 current_center = current_leftmost + (current_page/2);
1798 if (current_center < half_page_size) {
1799 leftmost_after_zoom = 0;
1801 leftmost_after_zoom = current_center - half_page_size;
1805 case ZoomFocusPlayhead:
1806 /* centre playhead */
1807 l = playhead_cursor->current_sample () - (new_page_size * 0.5);
1810 leftmost_after_zoom = 0;
1811 } else if (l > max_samplepos) {
1812 leftmost_after_zoom = max_samplepos - new_page_size;
1814 leftmost_after_zoom = (samplepos_t) l;
1818 case ZoomFocusMouse:
1819 /* try to keep the mouse over the same point in the display */
1821 if (_drags->active()) {
1822 where = _drags->current_pointer_sample ();
1823 } else if (!mouse_sample (where, in_track_canvas)) {
1824 use_mouse_sample = false;
1827 if (use_mouse_sample) {
1828 l = - ((new_page_size * ((where - current_leftmost)/(double)current_page)) - where);
1831 leftmost_after_zoom = 0;
1832 } else if (l > max_samplepos) {
1833 leftmost_after_zoom = max_samplepos - new_page_size;
1835 leftmost_after_zoom = (samplepos_t) l;
1838 /* use playhead instead */
1839 where = playhead_cursor->current_sample ();
1841 if (where < half_page_size) {
1842 leftmost_after_zoom = 0;
1844 leftmost_after_zoom = where - half_page_size;
1850 /* try to keep the edit point in the same place */
1851 where = get_preferred_edit_position ();
1855 double l = - ((new_page_size * ((where - current_leftmost)/(double)current_page)) - where);
1858 leftmost_after_zoom = 0;
1859 } else if (l > max_samplepos) {
1860 leftmost_after_zoom = max_samplepos - new_page_size;
1862 leftmost_after_zoom = (samplepos_t) l;
1866 /* edit point not defined */
1873 // leftmost_after_zoom = min (leftmost_after_zoom, _session->current_end_sample());
1875 reposition_and_zoom (leftmost_after_zoom, nfpp);
1879 Editor::calc_extra_zoom_edges(samplepos_t &start, samplepos_t &end)
1881 /* this func helps make sure we leave a little space
1882 at each end of the editor so that the zoom doesn't fit the region
1883 precisely to the screen.
1886 GdkScreen* screen = gdk_screen_get_default ();
1887 const gint pixwidth = gdk_screen_get_width (screen);
1888 const gint mmwidth = gdk_screen_get_width_mm (screen);
1889 const double pix_per_mm = (double) pixwidth/ (double) mmwidth;
1890 const double one_centimeter_in_pixels = pix_per_mm * 10.0;
1892 const samplepos_t range = end - start;
1893 const samplecnt_t new_fpp = (samplecnt_t) ceil ((double) range / (double) _visible_canvas_width);
1894 const samplepos_t extra_samples = (samplepos_t) floor (one_centimeter_in_pixels * new_fpp);
1896 if (start > extra_samples) {
1897 start -= extra_samples;
1902 if (max_samplepos - extra_samples > end) {
1903 end += extra_samples;
1905 end = max_samplepos;
1910 Editor::get_selection_extents (samplepos_t &start, samplepos_t &end) const
1912 start = max_samplepos;
1916 //ToDo: if notes are selected, set extents to that selection
1918 //ToDo: if control points are selected, set extents to that selection
1920 if (!selection->regions.empty()) {
1921 RegionSelection rs = get_regions_from_selection_and_entered ();
1923 for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
1925 if ((*i)->region()->position() < start) {
1926 start = (*i)->region()->position();
1929 if ((*i)->region()->last_sample() + 1 > end) {
1930 end = (*i)->region()->last_sample() + 1;
1934 } else if (!selection->time.empty()) {
1935 start = selection->time.start();
1936 end = selection->time.end_sample();
1938 ret = false; //no selection found
1941 if ((start == 0 && end == 0) || end < start) {
1950 Editor::temporal_zoom_selection (Editing::ZoomAxis axes)
1952 if (!selection) return;
1954 if (selection->regions.empty() && selection->time.empty()) {
1955 if (axes == Horizontal || axes == Both) {
1956 temporal_zoom_step(true);
1958 if (axes == Vertical || axes == Both) {
1959 if (!track_views.empty()) {
1963 //implicit hack: by extending the top & bottom check outside the current view limits, we include the trackviews immediately above & below what is visible
1964 const double top = vertical_adjustment.get_value() - 10;
1965 const double btm = top + _visible_canvas_height + 10;
1967 for (TrackViewList::iterator iter = track_views.begin(); iter != track_views.end(); ++iter) {
1968 if ((*iter)->covered_by_y_range (top, btm)) {
1969 tvl.push_back(*iter);
1979 //ToDo: if notes are selected, zoom to that
1981 //ToDo: if control points are selected, zoom to that
1983 if (axes == Horizontal || axes == Both) {
1985 samplepos_t start, end;
1986 if (get_selection_extents (start, end)) {
1987 calc_extra_zoom_edges (start, end);
1988 temporal_zoom_by_sample (start, end);
1992 if (axes == Vertical || axes == Both) {
1996 //normally, we don't do anything "automatic" to the user's selection.
1997 //but in this case, we will clear the selection after a zoom-to-selection.
2002 Editor::temporal_zoom_session ()
2004 ENSURE_GUI_THREAD (*this, &Editor::temporal_zoom_session)
2007 samplecnt_t start = _session->current_start_sample();
2008 samplecnt_t end = _session->current_end_sample();
2010 if (_session->actively_recording ()) {
2011 samplepos_t cur = playhead_cursor->current_sample ();
2013 /* recording beyond the end marker; zoom out
2014 * by 5 seconds more so that if 'follow
2015 * playhead' is active we don't immediately
2018 end = cur + _session->sample_rate() * 5;
2022 if ((start == 0 && end == 0) || end < start) {
2026 calc_extra_zoom_edges(start, end);
2028 temporal_zoom_by_sample (start, end);
2033 Editor::temporal_zoom_extents ()
2035 ENSURE_GUI_THREAD (*this, &Editor::temporal_zoom_extents)
2038 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
2040 samplecnt_t start = ext.first;
2041 samplecnt_t end = ext.second;
2043 if (_session->actively_recording ()) {
2044 samplepos_t cur = playhead_cursor->current_sample ();
2046 /* recording beyond the end marker; zoom out
2047 * by 5 seconds more so that if 'follow
2048 * playhead' is active we don't immediately
2051 end = cur + _session->sample_rate() * 5;
2055 if ((start == 0 && end == 0) || end < start) {
2059 calc_extra_zoom_edges(start, end);
2061 temporal_zoom_by_sample (start, end);
2066 Editor::temporal_zoom_by_sample (samplepos_t start, samplepos_t end)
2068 if (!_session) return;
2070 if ((start == 0 && end == 0) || end < start) {
2074 samplepos_t range = end - start;
2076 const samplecnt_t new_fpp = (samplecnt_t) ceil ((double) range / (double) _visible_canvas_width);
2078 samplepos_t new_page = range;
2079 samplepos_t middle = (samplepos_t) floor ((double) start + ((double) range / 2.0f));
2080 samplepos_t new_leftmost = (samplepos_t) floor ((double) middle - ((double) new_page / 2.0f));
2082 if (new_leftmost > middle) {
2086 if (new_leftmost < 0) {
2090 reposition_and_zoom (new_leftmost, new_fpp);
2094 Editor::temporal_zoom_to_sample (bool coarser, samplepos_t sample)
2100 samplecnt_t range_before = sample - _leftmost_sample;
2101 samplecnt_t new_spp;
2104 if (samples_per_pixel <= 1) {
2107 new_spp = samples_per_pixel + (samples_per_pixel/2);
2109 range_before += range_before/2;
2111 if (samples_per_pixel >= 1) {
2112 new_spp = samples_per_pixel - (samples_per_pixel/2);
2114 /* could bail out here since we cannot zoom any finer,
2115 but leave that to the equality test below
2117 new_spp = samples_per_pixel;
2120 range_before -= range_before/2;
2123 if (new_spp == samples_per_pixel) {
2127 /* zoom focus is automatically taken as @param sample when this
2131 samplepos_t new_leftmost = sample - (samplepos_t)range_before;
2133 if (new_leftmost > sample) {
2137 if (new_leftmost < 0) {
2141 reposition_and_zoom (new_leftmost, new_spp);
2146 Editor::choose_new_marker_name(string &name) {
2148 if (!UIConfiguration::instance().get_name_new_markers()) {
2149 /* don't prompt user for a new name */
2153 Prompter dialog (true);
2155 dialog.set_prompt (_("New Name:"));
2157 dialog.set_title (_("New Location Marker"));
2159 dialog.set_name ("MarkNameWindow");
2160 dialog.set_size_request (250, -1);
2161 dialog.set_position (Gtk::WIN_POS_MOUSE);
2163 dialog.add_button (Stock::OK, RESPONSE_ACCEPT);
2164 dialog.set_initial_text (name);
2168 switch (dialog.run ()) {
2169 case RESPONSE_ACCEPT:
2175 dialog.get_result(name);
2182 Editor::add_location_from_selection ()
2186 if (selection->time.empty()) {
2190 if (_session == 0 || clicked_axisview == 0) {
2194 samplepos_t start = selection->time[clicked_selection].start;
2195 samplepos_t end = selection->time[clicked_selection].end;
2197 _session->locations()->next_available_name(rangename,"selection");
2198 Location *location = new Location (*_session, start, end, rangename, Location::IsRangeMarker, get_grid_music_divisions(0));
2200 begin_reversible_command (_("add marker"));
2202 XMLNode &before = _session->locations()->get_state();
2203 _session->locations()->add (location, true);
2204 XMLNode &after = _session->locations()->get_state();
2205 _session->add_command(new MementoCommand<Locations>(*(_session->locations()), &before, &after));
2207 commit_reversible_command ();
2211 Editor::add_location_mark (samplepos_t where)
2215 select_new_marker = true;
2217 _session->locations()->next_available_name(markername,"mark");
2218 if (!choose_new_marker_name(markername)) {
2221 Location *location = new Location (*_session, where, where, markername, Location::IsMark, get_grid_music_divisions (0));
2222 begin_reversible_command (_("add marker"));
2224 XMLNode &before = _session->locations()->get_state();
2225 _session->locations()->add (location, true);
2226 XMLNode &after = _session->locations()->get_state();
2227 _session->add_command(new MementoCommand<Locations>(*(_session->locations()), &before, &after));
2229 commit_reversible_command ();
2233 Editor::set_session_start_from_playhead ()
2239 if ((loc = _session->locations()->session_range_location()) == 0) { //should never happen
2240 _session->set_session_extents (_session->audible_sample(), _session->audible_sample());
2242 XMLNode &before = loc->get_state();
2244 _session->set_session_extents (_session->audible_sample(), loc->end());
2246 XMLNode &after = loc->get_state();
2248 begin_reversible_command (_("Set session start"));
2250 _session->add_command (new MementoCommand<Location>(*loc, &before, &after));
2252 commit_reversible_command ();
2257 Editor::set_session_end_from_playhead ()
2263 if ((loc = _session->locations()->session_range_location()) == 0) { //should never happen
2264 _session->set_session_extents (_session->audible_sample(), _session->audible_sample());
2266 XMLNode &before = loc->get_state();
2268 _session->set_session_extents (loc->start(), _session->audible_sample());
2270 XMLNode &after = loc->get_state();
2272 begin_reversible_command (_("Set session start"));
2274 _session->add_command (new MementoCommand<Location>(*loc, &before, &after));
2276 commit_reversible_command ();
2279 _session->set_end_is_free (false);
2284 Editor::toggle_location_at_playhead_cursor ()
2286 if (!do_remove_location_at_playhead_cursor())
2288 add_location_from_playhead_cursor();
2293 Editor::add_location_from_playhead_cursor ()
2295 add_location_mark (_session->audible_sample());
2299 Editor::do_remove_location_at_playhead_cursor ()
2301 bool removed = false;
2304 XMLNode &before = _session->locations()->get_state();
2306 //find location(s) at this time
2307 Locations::LocationList locs;
2308 _session->locations()->find_all_between (_session->audible_sample(), _session->audible_sample()+1, locs, Location::Flags(0));
2309 for (Locations::LocationList::iterator i = locs.begin(); i != locs.end(); ++i) {
2310 if ((*i)->is_mark()) {
2311 _session->locations()->remove (*i);
2318 begin_reversible_command (_("remove marker"));
2319 XMLNode &after = _session->locations()->get_state();
2320 _session->add_command(new MementoCommand<Locations>(*(_session->locations()), &before, &after));
2321 commit_reversible_command ();
2328 Editor::remove_location_at_playhead_cursor ()
2330 do_remove_location_at_playhead_cursor ();
2333 /** Add a range marker around each selected region */
2335 Editor::add_locations_from_region ()
2337 RegionSelection rs = get_regions_from_selection_and_entered ();
2342 bool commit = false;
2344 XMLNode &before = _session->locations()->get_state();
2346 for (RegionSelection::iterator i = rs.begin (); i != rs.end (); ++i) {
2348 boost::shared_ptr<Region> region = (*i)->region ();
2350 Location *location = new Location (*_session, region->position(), region->last_sample(), region->name(), Location::IsRangeMarker, 0);
2352 _session->locations()->add (location, true);
2357 begin_reversible_command (selection->regions.size () > 1 ? _("add markers") : _("add marker"));
2358 XMLNode &after = _session->locations()->get_state();
2359 _session->add_command (new MementoCommand<Locations>(*(_session->locations()), &before, &after));
2360 commit_reversible_command ();
2364 /** Add a single range marker around all selected regions */
2366 Editor::add_location_from_region ()
2368 RegionSelection rs = get_regions_from_selection_and_entered ();
2374 XMLNode &before = _session->locations()->get_state();
2378 if (rs.size() > 1) {
2379 _session->locations()->next_available_name(markername, "regions");
2381 RegionView* rv = *(rs.begin());
2382 boost::shared_ptr<Region> region = rv->region();
2383 markername = region->name();
2386 if (!choose_new_marker_name(markername)) {
2390 // single range spanning all selected
2391 Location *location = new Location (*_session, selection->regions.start(), selection->regions.end_sample(), markername, Location::IsRangeMarker, 0);
2392 _session->locations()->add (location, true);
2394 begin_reversible_command (_("add marker"));
2395 XMLNode &after = _session->locations()->get_state();
2396 _session->add_command (new MementoCommand<Locations>(*(_session->locations()), &before, &after));
2397 commit_reversible_command ();
2403 Editor::jump_forward_to_mark ()
2409 samplepos_t pos = _session->locations()->first_mark_after (playhead_cursor->current_sample());
2415 _session->request_locate (pos, _session->transport_rolling());
2419 Editor::jump_backward_to_mark ()
2425 samplepos_t pos = _session->locations()->first_mark_before (playhead_cursor->current_sample());
2427 //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...
2428 if (_session->transport_rolling()) {
2429 if ((playhead_cursor->current_sample() - pos) < _session->sample_rate()/2) {
2430 samplepos_t prior = _session->locations()->first_mark_before (pos);
2439 _session->request_locate (pos, _session->transport_rolling());
2445 samplepos_t const pos = _session->audible_sample ();
2448 _session->locations()->next_available_name (markername, "mark");
2450 if (!choose_new_marker_name (markername)) {
2454 _session->locations()->add (new Location (*_session, pos, 0, markername, Location::IsMark, 0), true);
2458 Editor::clear_markers ()
2461 begin_reversible_command (_("clear markers"));
2463 XMLNode &before = _session->locations()->get_state();
2464 _session->locations()->clear_markers ();
2465 XMLNode &after = _session->locations()->get_state();
2466 _session->add_command(new MementoCommand<Locations>(*(_session->locations()), &before, &after));
2468 commit_reversible_command ();
2473 Editor::clear_ranges ()
2476 begin_reversible_command (_("clear ranges"));
2478 XMLNode &before = _session->locations()->get_state();
2480 _session->locations()->clear_ranges ();
2482 XMLNode &after = _session->locations()->get_state();
2483 _session->add_command(new MementoCommand<Locations>(*(_session->locations()), &before, &after));
2485 commit_reversible_command ();
2490 Editor::clear_locations ()
2492 begin_reversible_command (_("clear locations"));
2494 XMLNode &before = _session->locations()->get_state();
2495 _session->locations()->clear ();
2496 XMLNode &after = _session->locations()->get_state();
2497 _session->add_command(new MementoCommand<Locations>(*(_session->locations()), &before, &after));
2499 commit_reversible_command ();
2503 Editor::unhide_markers ()
2505 for (LocationMarkerMap::iterator i = location_markers.begin(); i != location_markers.end(); ++i) {
2506 Location *l = (*i).first;
2507 if (l->is_hidden() && l->is_mark()) {
2508 l->set_hidden(false, this);
2514 Editor::unhide_ranges ()
2516 for (LocationMarkerMap::iterator i = location_markers.begin(); i != location_markers.end(); ++i) {
2517 Location *l = (*i).first;
2518 if (l->is_hidden() && l->is_range_marker()) {
2519 l->set_hidden(false, this);
2524 /* INSERT/REPLACE */
2527 Editor::insert_region_list_selection (float times)
2529 RouteTimeAxisView *tv = 0;
2530 boost::shared_ptr<Playlist> playlist;
2532 if (clicked_routeview != 0) {
2533 tv = clicked_routeview;
2534 } else if (!selection->tracks.empty()) {
2535 if ((tv = dynamic_cast<RouteTimeAxisView*>(selection->tracks.front())) == 0) {
2538 } else if (entered_track != 0) {
2539 if ((tv = dynamic_cast<RouteTimeAxisView*>(entered_track)) == 0) {
2546 if ((playlist = tv->playlist()) == 0) {
2550 boost::shared_ptr<Region> region = _regions->get_single_selection ();
2555 begin_reversible_command (_("insert region"));
2556 playlist->clear_changes ();
2557 playlist->add_region ((RegionFactory::create (region, true)), get_preferred_edit_position(), times);
2558 if (Config->get_edit_mode() == Ripple)
2559 playlist->ripple (get_preferred_edit_position(), region->length() * times, boost::shared_ptr<Region>());
2561 _session->add_command(new StatefulDiffCommand (playlist));
2562 commit_reversible_command ();
2565 /* BUILT-IN EFFECTS */
2568 Editor::reverse_selection ()
2573 /* GAIN ENVELOPE EDITING */
2576 Editor::edit_envelope ()
2583 Editor::transition_to_rolling (bool fwd)
2589 if (_session->config.get_external_sync()) {
2590 switch (Config->get_sync_source()) {
2594 /* transport controlled by the master */
2599 if (_session->is_auditioning()) {
2600 _session->cancel_audition ();
2604 _session->request_transport_speed (fwd ? 1.0f : -1.0f);
2608 Editor::play_from_start ()
2610 _session->request_locate (_session->current_start_sample(), true);
2614 Editor::play_from_edit_point ()
2616 _session->request_locate (get_preferred_edit_position(), true);
2620 Editor::play_from_edit_point_and_return ()
2622 samplepos_t start_sample;
2623 samplepos_t return_sample;
2625 start_sample = get_preferred_edit_position (EDIT_IGNORE_PHEAD);
2627 if (_session->transport_rolling()) {
2628 _session->request_locate (start_sample, false);
2632 /* don't reset the return sample if its already set */
2634 if ((return_sample = _session->requested_return_sample()) < 0) {
2635 return_sample = _session->audible_sample();
2638 if (start_sample >= 0) {
2639 _session->request_roll_at_and_return (start_sample, return_sample);
2644 Editor::play_selection ()
2646 samplepos_t start, end;
2647 if (!get_selection_extents (start, end))
2650 AudioRange ar (start, end, 0);
2651 list<AudioRange> lar;
2654 _session->request_play_range (&lar, true);
2659 Editor::maybe_locate_with_edit_preroll (samplepos_t location)
2661 if (_session->transport_rolling() || !UIConfiguration::instance().get_follow_edits() || _session->config.get_external_sync())
2664 location -= _session->preroll_samples (location);
2666 //don't try to locate before the beginning of time
2671 //if follow_playhead is on, keep the playhead on the screen
2672 if (_follow_playhead)
2673 if (location < _leftmost_sample)
2674 location = _leftmost_sample;
2676 _session->request_locate (location);
2680 Editor::play_with_preroll ()
2682 samplepos_t start, end;
2683 if (UIConfiguration::instance().get_follow_edits() && get_selection_extents (start, end)) {
2684 const samplepos_t preroll = _session->preroll_samples (start);
2686 samplepos_t ret = start;
2688 if (start > preroll) {
2689 start = start - preroll;
2692 end = end + preroll; //"post-roll"
2694 AudioRange ar (start, end, 0);
2695 list<AudioRange> lar;
2698 _session->request_play_range (&lar, true);
2699 _session->set_requested_return_sample (ret); //force auto-return to return to range start, without the preroll
2701 samplepos_t ph = playhead_cursor->current_sample ();
2702 const samplepos_t preroll = _session->preroll_samples (ph);
2705 start = ph - preroll;
2709 _session->request_locate (start, true);
2710 _session->set_requested_return_sample (ph); //force auto-return to return to playhead location, without the preroll
2715 Editor::rec_with_preroll ()
2717 samplepos_t ph = playhead_cursor->current_sample ();
2718 samplepos_t preroll = _session->preroll_samples (ph);
2719 _session->request_preroll_record_trim (ph, preroll);
2723 Editor::rec_with_count_in ()
2725 _session->request_count_in_record ();
2729 Editor::play_location (Location& location)
2731 if (location.start() <= location.end()) {
2735 _session->request_bounded_roll (location.start(), location.end());
2739 Editor::loop_location (Location& location)
2741 if (location.start() <= location.end()) {
2747 if ((tll = transport_loop_location()) != 0) {
2748 tll->set (location.start(), location.end());
2750 // enable looping, reposition and start rolling
2751 _session->request_locate (tll->start(), true);
2752 _session->request_play_loop (true);
2757 Editor::do_layer_operation (LayerOperation op)
2759 if (selection->regions.empty ()) {
2763 bool const multiple = selection->regions.size() > 1;
2767 begin_reversible_command (_("raise regions"));
2769 begin_reversible_command (_("raise region"));
2775 begin_reversible_command (_("raise regions to top"));
2777 begin_reversible_command (_("raise region to top"));
2783 begin_reversible_command (_("lower regions"));
2785 begin_reversible_command (_("lower region"));
2791 begin_reversible_command (_("lower regions to bottom"));
2793 begin_reversible_command (_("lower region"));
2798 set<boost::shared_ptr<Playlist> > playlists = selection->regions.playlists ();
2799 for (set<boost::shared_ptr<Playlist> >::iterator i = playlists.begin(); i != playlists.end(); ++i) {
2800 (*i)->clear_owned_changes ();
2803 for (RegionSelection::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
2804 boost::shared_ptr<Region> r = (*i)->region ();
2816 r->lower_to_bottom ();
2820 for (set<boost::shared_ptr<Playlist> >::iterator i = playlists.begin(); i != playlists.end(); ++i) {
2821 vector<Command*> cmds;
2823 _session->add_commands (cmds);
2826 commit_reversible_command ();
2830 Editor::raise_region ()
2832 do_layer_operation (Raise);
2836 Editor::raise_region_to_top ()
2838 do_layer_operation (RaiseToTop);
2842 Editor::lower_region ()
2844 do_layer_operation (Lower);
2848 Editor::lower_region_to_bottom ()
2850 do_layer_operation (LowerToBottom);
2853 /** Show the region editor for the selected regions */
2855 Editor::show_region_properties ()
2857 selection->foreach_regionview (&RegionView::show_region_editor);
2860 /** Show the midi list editor for the selected MIDI regions */
2862 Editor::show_midi_list_editor ()
2864 selection->foreach_midi_regionview (&MidiRegionView::show_list_editor);
2868 Editor::rename_region ()
2870 RegionSelection rs = get_regions_from_selection_and_entered ();
2876 ArdourDialog d (_("Rename Region"), true, false);
2878 Label label (_("New name:"));
2881 hbox.set_spacing (6);
2882 hbox.pack_start (label, false, false);
2883 hbox.pack_start (entry, true, true);
2885 d.get_vbox()->set_border_width (12);
2886 d.get_vbox()->pack_start (hbox, false, false);
2888 d.add_button(Gtk::Stock::CANCEL, Gtk::RESPONSE_CANCEL);
2889 d.add_button(Gtk::Stock::OK, Gtk::RESPONSE_OK);
2891 d.set_size_request (300, -1);
2893 entry.set_text (rs.front()->region()->name());
2894 entry.select_region (0, -1);
2896 entry.signal_activate().connect (sigc::bind (sigc::mem_fun (d, &Dialog::response), RESPONSE_OK));
2902 int const ret = d.run();
2906 if (ret != RESPONSE_OK) {
2910 std::string str = entry.get_text();
2911 strip_whitespace_edges (str);
2913 rs.front()->region()->set_name (str);
2914 _regions->redisplay ();
2918 /** Start an audition of the first selected region */
2920 Editor::play_edit_range ()
2922 samplepos_t start, end;
2924 if (get_edit_op_range (start, end)) {
2925 _session->request_bounded_roll (start, end);
2930 Editor::play_selected_region ()
2932 samplepos_t start = max_samplepos;
2933 samplepos_t end = 0;
2935 RegionSelection rs = get_regions_from_selection_and_entered ();
2941 for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
2942 if ((*i)->region()->position() < start) {
2943 start = (*i)->region()->position();
2945 if ((*i)->region()->last_sample() + 1 > end) {
2946 end = (*i)->region()->last_sample() + 1;
2950 _session->request_bounded_roll (start, end);
2954 Editor::audition_playlist_region_standalone (boost::shared_ptr<Region> region)
2956 _session->audition_region (region);
2960 Editor::region_from_selection ()
2962 if (clicked_axisview == 0) {
2966 if (selection->time.empty()) {
2970 samplepos_t start = selection->time[clicked_selection].start;
2971 samplepos_t end = selection->time[clicked_selection].end;
2973 TrackViewList tracks = get_tracks_for_range_action ();
2975 samplepos_t selection_cnt = end - start + 1;
2977 for (TrackSelection::iterator i = tracks.begin(); i != tracks.end(); ++i) {
2978 boost::shared_ptr<Region> current;
2979 boost::shared_ptr<Playlist> pl;
2980 samplepos_t internal_start;
2983 if ((pl = (*i)->playlist()) == 0) {
2987 if ((current = pl->top_region_at (start)) == 0) {
2991 internal_start = start - current->position();
2992 RegionFactory::region_name (new_name, current->name(), true);
2996 plist.add (ARDOUR::Properties::start, current->start() + internal_start);
2997 plist.add (ARDOUR::Properties::length, selection_cnt);
2998 plist.add (ARDOUR::Properties::name, new_name);
2999 plist.add (ARDOUR::Properties::layer, 0);
3001 boost::shared_ptr<Region> region (RegionFactory::create (current, plist));
3006 Editor::create_region_from_selection (vector<boost::shared_ptr<Region> >& new_regions)
3008 if (selection->time.empty() || selection->tracks.empty()) {
3012 samplepos_t start, end;
3013 if (clicked_selection) {
3014 start = selection->time[clicked_selection].start;
3015 end = selection->time[clicked_selection].end;
3017 start = selection->time.start();
3018 end = selection->time.end_sample();
3021 TrackViewList ts = selection->tracks.filter_to_unique_playlists ();
3022 sort_track_selection (ts);
3024 for (TrackSelection::iterator i = ts.begin(); i != ts.end(); ++i) {
3025 boost::shared_ptr<Region> current;
3026 boost::shared_ptr<Playlist> playlist;
3027 samplepos_t internal_start;
3030 if ((playlist = (*i)->playlist()) == 0) {
3034 if ((current = playlist->top_region_at(start)) == 0) {
3038 internal_start = start - current->position();
3039 RegionFactory::region_name (new_name, current->name(), true);
3043 plist.add (ARDOUR::Properties::start, current->start() + internal_start);
3044 plist.add (ARDOUR::Properties::length, end - start + 1);
3045 plist.add (ARDOUR::Properties::name, new_name);
3047 new_regions.push_back (RegionFactory::create (current, plist));
3052 Editor::split_multichannel_region ()
3054 RegionSelection rs = get_regions_from_selection_and_entered ();
3060 vector< boost::shared_ptr<Region> > v;
3062 for (list<RegionView*>::iterator x = rs.begin(); x != rs.end(); ++x) {
3063 (*x)->region()->separate_by_channel (v);
3068 Editor::new_region_from_selection ()
3070 region_from_selection ();
3071 cancel_selection ();
3075 add_if_covered (RegionView* rv, const AudioRange* ar, RegionSelection* rs)
3077 switch (rv->region()->coverage (ar->start, ar->end - 1)) {
3078 // n.b. -1 because AudioRange::end is one past the end, but coverage expects inclusive ranges
3079 case Evoral::OverlapNone:
3087 * - selected tracks, or if there are none...
3088 * - tracks containing selected regions, or if there are none...
3093 Editor::get_tracks_for_range_action () const
3097 if (selection->tracks.empty()) {
3099 /* use tracks with selected regions */
3101 RegionSelection rs = selection->regions;
3103 for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
3104 TimeAxisView* tv = &(*i)->get_time_axis_view();
3106 if (!t.contains (tv)) {
3112 /* no regions and no tracks: use all tracks */
3118 t = selection->tracks;
3121 return t.filter_to_unique_playlists();
3125 Editor::separate_regions_between (const TimeSelection& ts)
3127 bool in_command = false;
3128 boost::shared_ptr<Playlist> playlist;
3129 RegionSelection new_selection;
3131 TrackViewList tmptracks = get_tracks_for_range_action ();
3132 sort_track_selection (tmptracks);
3134 for (TrackSelection::iterator i = tmptracks.begin(); i != tmptracks.end(); ++i) {
3136 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> ((*i));
3142 if (!rtv->is_track()) {
3146 /* no edits to destructive tracks */
3148 if (rtv->track()->destructive()) {
3152 if ((playlist = rtv->playlist()) != 0) {
3154 playlist->clear_changes ();
3156 /* XXX need to consider musical time selections here at some point */
3158 for (list<AudioRange>::const_iterator t = ts.begin(); t != ts.end(); ++t) {
3160 sigc::connection c = rtv->view()->RegionViewAdded.connect (
3161 sigc::mem_fun(*this, &Editor::collect_new_region_view));
3163 latest_regionviews.clear ();
3165 playlist->partition ((*t).start, (*t).end, false);
3169 if (!latest_regionviews.empty()) {
3171 rtv->view()->foreach_regionview (sigc::bind (
3172 sigc::ptr_fun (add_if_covered),
3173 &(*t), &new_selection));
3176 begin_reversible_command (_("separate"));
3180 /* pick up changes to existing regions */
3182 vector<Command*> cmds;
3183 playlist->rdiff (cmds);
3184 _session->add_commands (cmds);
3186 /* pick up changes to the playlist itself (adds/removes)
3189 _session->add_command(new StatefulDiffCommand (playlist));
3196 // selection->set (new_selection);
3198 commit_reversible_command ();
3202 struct PlaylistState {
3203 boost::shared_ptr<Playlist> playlist;
3207 /** Take tracks from get_tracks_for_range_action and cut any regions
3208 * on those tracks so that the tracks are empty over the time
3212 Editor::separate_region_from_selection ()
3214 /* preferentially use *all* ranges in the time selection if we're in range mode
3215 to allow discontiguous operation, since get_edit_op_range() currently
3216 returns a single range.
3219 if (!selection->time.empty()) {
3221 separate_regions_between (selection->time);
3228 if (get_edit_op_range (start, end)) {
3230 AudioRange ar (start, end, 1);
3234 separate_regions_between (ts);
3240 Editor::separate_region_from_punch ()
3242 Location* loc = _session->locations()->auto_punch_location();
3244 separate_regions_using_location (*loc);
3249 Editor::separate_region_from_loop ()
3251 Location* loc = _session->locations()->auto_loop_location();
3253 separate_regions_using_location (*loc);
3258 Editor::separate_regions_using_location (Location& loc)
3260 if (loc.is_mark()) {
3264 AudioRange ar (loc.start(), loc.end(), 1);
3269 separate_regions_between (ts);
3272 /** Separate regions under the selected region */
3274 Editor::separate_under_selected_regions ()
3276 vector<PlaylistState> playlists;
3280 rs = get_regions_from_selection_and_entered();
3282 if (!_session || rs.empty()) {
3286 begin_reversible_command (_("separate region under"));
3288 list<boost::shared_ptr<Region> > regions_to_remove;
3290 for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
3291 // we can't just remove the region(s) in this loop because
3292 // this removes them from the RegionSelection, and they thus
3293 // disappear from underneath the iterator, and the ++i above
3294 // SEGVs in a puzzling fashion.
3296 // so, first iterate over the regions to be removed from rs and
3297 // add them to the regions_to_remove list, and then
3298 // iterate over the list to actually remove them.
3300 regions_to_remove.push_back ((*i)->region());
3303 for (list<boost::shared_ptr<Region> >::iterator rl = regions_to_remove.begin(); rl != regions_to_remove.end(); ++rl) {
3305 boost::shared_ptr<Playlist> playlist = (*rl)->playlist();
3308 // is this check necessary?
3312 vector<PlaylistState>::iterator i;
3314 //only take state if this is a new playlist.
3315 for (i = playlists.begin(); i != playlists.end(); ++i) {
3316 if ((*i).playlist == playlist) {
3321 if (i == playlists.end()) {
3323 PlaylistState before;
3324 before.playlist = playlist;
3325 before.before = &playlist->get_state();
3326 playlist->clear_changes ();
3327 playlist->freeze ();
3328 playlists.push_back(before);
3331 //Partition on the region bounds
3332 playlist->partition ((*rl)->first_sample() - 1, (*rl)->last_sample() + 1, true);
3334 //Re-add region that was just removed due to the partition operation
3335 playlist->add_region ((*rl), (*rl)->first_sample());
3338 vector<PlaylistState>::iterator pl;
3340 for (pl = playlists.begin(); pl != playlists.end(); ++pl) {
3341 (*pl).playlist->thaw ();
3342 _session->add_command(new MementoCommand<Playlist>(*(*pl).playlist, (*pl).before, &(*pl).playlist->get_state()));
3345 commit_reversible_command ();
3349 Editor::crop_region_to_selection ()
3351 if (!selection->time.empty()) {
3353 begin_reversible_command (_("Crop Regions to Time Selection"));
3354 for (std::list<AudioRange>::iterator i = selection->time.begin(); i != selection->time.end(); ++i) {
3355 crop_region_to ((*i).start, (*i).end);
3357 commit_reversible_command();
3363 if (get_edit_op_range (start, end)) {
3364 begin_reversible_command (_("Crop Regions to Edit Range"));
3366 crop_region_to (start, end);
3368 commit_reversible_command();
3375 Editor::crop_region_to (samplepos_t start, samplepos_t end)
3377 vector<boost::shared_ptr<Playlist> > playlists;
3378 boost::shared_ptr<Playlist> playlist;
3381 if (selection->tracks.empty()) {
3382 ts = track_views.filter_to_unique_playlists();
3384 ts = selection->tracks.filter_to_unique_playlists ();
3387 sort_track_selection (ts);
3389 for (TrackSelection::iterator i = ts.begin(); i != ts.end(); ++i) {
3391 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> ((*i));
3397 boost::shared_ptr<Track> t = rtv->track();
3399 if (t != 0 && ! t->destructive()) {
3401 if ((playlist = rtv->playlist()) != 0) {
3402 playlists.push_back (playlist);
3407 if (playlists.empty()) {
3412 samplepos_t new_start;
3413 samplepos_t new_end;
3414 samplecnt_t new_length;
3416 for (vector<boost::shared_ptr<Playlist> >::iterator i = playlists.begin(); i != playlists.end(); ++i) {
3418 /* Only the top regions at start and end have to be cropped */
3419 boost::shared_ptr<Region> region_at_start = (*i)->top_region_at(start);
3420 boost::shared_ptr<Region> region_at_end = (*i)->top_region_at(end);
3422 vector<boost::shared_ptr<Region> > regions;
3424 if (region_at_start != 0) {
3425 regions.push_back (region_at_start);
3427 if (region_at_end != 0) {
3428 regions.push_back (region_at_end);
3431 /* now adjust lengths */
3432 for (vector<boost::shared_ptr<Region> >::iterator i = regions.begin(); i != regions.end(); ++i) {
3434 pos = (*i)->position();
3435 new_start = max (start, pos);
3436 if (max_samplepos - pos > (*i)->length()) {
3437 new_end = pos + (*i)->length() - 1;
3439 new_end = max_samplepos;
3441 new_end = min (end, new_end);
3442 new_length = new_end - new_start + 1;
3444 (*i)->clear_changes ();
3445 (*i)->trim_to (new_start, new_length);
3446 _session->add_command (new StatefulDiffCommand (*i));
3452 Editor::region_fill_track ()
3454 boost::shared_ptr<Playlist> playlist;
3455 RegionSelection regions = get_regions_from_selection_and_entered ();
3456 RegionSelection foo;
3458 samplepos_t const end = _session->current_end_sample ();
3460 if (regions.empty () || regions.end_sample () + 1 >= end) {
3464 samplepos_t const start_sample = regions.start ();
3465 samplepos_t const end_sample = regions.end_sample ();
3466 samplecnt_t const gap = end_sample - start_sample + 1;
3468 begin_reversible_command (Operations::region_fill);
3470 selection->clear_regions ();
3472 for (RegionSelection::iterator i = regions.begin(); i != regions.end(); ++i) {
3474 boost::shared_ptr<Region> r ((*i)->region());
3476 TimeAxisView& tv = (*i)->get_time_axis_view();
3477 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (&tv);
3478 latest_regionviews.clear ();
3479 sigc::connection c = rtv->view()->RegionViewAdded.connect (sigc::mem_fun(*this, &Editor::collect_new_region_view));
3481 samplepos_t const position = end_sample + (r->first_sample() - start_sample + 1);
3482 playlist = (*i)->region()->playlist();
3483 playlist->clear_changes ();
3484 playlist->duplicate_until (r, position, gap, end);
3485 _session->add_command(new StatefulDiffCommand (playlist));
3489 foo.insert (foo.end(), latest_regionviews.begin(), latest_regionviews.end());
3493 selection->set (foo);
3496 commit_reversible_command ();
3500 Editor::set_region_sync_position ()
3502 set_sync_point (get_preferred_edit_position (), get_regions_from_selection_and_edit_point ());
3506 Editor::set_sync_point (samplepos_t where, const RegionSelection& rs)
3508 bool in_command = false;
3510 for (RegionSelection::const_iterator r = rs.begin(); r != rs.end(); ++r) {
3512 if (!(*r)->region()->covers (where)) {
3516 boost::shared_ptr<Region> region ((*r)->region());
3519 begin_reversible_command (_("set sync point"));
3523 region->clear_changes ();
3524 region->set_sync_position (where);
3525 _session->add_command(new StatefulDiffCommand (region));
3529 commit_reversible_command ();
3533 /** Remove the sync positions of the selection */
3535 Editor::remove_region_sync ()
3537 RegionSelection rs = get_regions_from_selection_and_entered ();
3543 begin_reversible_command (_("remove region sync"));
3545 for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
3547 (*i)->region()->clear_changes ();
3548 (*i)->region()->clear_sync_position ();
3549 _session->add_command(new StatefulDiffCommand ((*i)->region()));
3552 commit_reversible_command ();
3556 Editor::naturalize_region ()
3558 RegionSelection rs = get_regions_from_selection_and_entered ();
3564 if (rs.size() > 1) {
3565 begin_reversible_command (_("move regions to original position"));
3567 begin_reversible_command (_("move region to original position"));
3570 for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
3571 (*i)->region()->clear_changes ();
3572 (*i)->region()->move_to_natural_position ();
3573 _session->add_command (new StatefulDiffCommand ((*i)->region()));
3576 commit_reversible_command ();
3580 Editor::align_regions (RegionPoint what)
3582 RegionSelection const rs = get_regions_from_selection_and_edit_point ();
3588 begin_reversible_command (_("align selection"));
3590 samplepos_t const position = get_preferred_edit_position ();
3592 for (RegionSelection::const_iterator i = rs.begin(); i != rs.end(); ++i) {
3593 align_region_internal ((*i)->region(), what, position);
3596 commit_reversible_command ();
3599 struct RegionSortByTime {
3600 bool operator() (const RegionView* a, const RegionView* b) {
3601 return a->region()->position() < b->region()->position();
3606 Editor::align_regions_relative (RegionPoint point)
3608 RegionSelection const rs = get_regions_from_selection_and_edit_point ();
3614 samplepos_t const position = get_preferred_edit_position ();
3616 samplepos_t distance = 0;
3617 samplepos_t pos = 0;
3620 list<RegionView*> sorted;
3621 rs.by_position (sorted);
3623 boost::shared_ptr<Region> r ((*sorted.begin())->region());
3628 if (position > r->position()) {
3629 distance = position - r->position();
3631 distance = r->position() - position;
3637 if (position > r->last_sample()) {
3638 distance = position - r->last_sample();
3639 pos = r->position() + distance;
3641 distance = r->last_sample() - position;
3642 pos = r->position() - distance;
3648 pos = r->adjust_to_sync (position);
3649 if (pos > r->position()) {
3650 distance = pos - r->position();
3652 distance = r->position() - pos;
3658 if (pos == r->position()) {
3662 begin_reversible_command (_("align selection (relative)"));
3664 /* move first one specially */
3666 r->clear_changes ();
3667 r->set_position (pos);
3668 _session->add_command(new StatefulDiffCommand (r));
3670 /* move rest by the same amount */
3674 for (list<RegionView*>::iterator i = sorted.begin(); i != sorted.end(); ++i) {
3676 boost::shared_ptr<Region> region ((*i)->region());
3678 region->clear_changes ();
3681 region->set_position (region->position() + distance);
3683 region->set_position (region->position() - distance);
3686 _session->add_command(new StatefulDiffCommand (region));
3690 commit_reversible_command ();
3694 Editor::align_region (boost::shared_ptr<Region> region, RegionPoint point, samplepos_t position)
3696 begin_reversible_command (_("align region"));
3697 align_region_internal (region, point, position);
3698 commit_reversible_command ();
3702 Editor::align_region_internal (boost::shared_ptr<Region> region, RegionPoint point, samplepos_t position)
3704 region->clear_changes ();
3708 region->set_position (region->adjust_to_sync (position));
3712 if (position > region->length()) {
3713 region->set_position (position - region->length());
3718 region->set_position (position);
3722 _session->add_command(new StatefulDiffCommand (region));
3726 Editor::trim_region_front ()
3732 Editor::trim_region_back ()
3734 trim_region (false);
3738 Editor::trim_region (bool front)
3740 samplepos_t where = get_preferred_edit_position();
3741 RegionSelection rs = get_regions_from_selection_and_edit_point ();
3747 begin_reversible_command (front ? _("trim front") : _("trim back"));
3749 for (list<RegionView*>::const_iterator i = rs.by_layer().begin(); i != rs.by_layer().end(); ++i) {
3750 if (!(*i)->region()->locked()) {
3752 (*i)->region()->clear_changes ();
3755 (*i)->region()->trim_front (where);
3757 (*i)->region()->trim_end (where);
3760 _session->add_command (new StatefulDiffCommand ((*i)->region()));
3764 commit_reversible_command ();
3767 /** Trim the end of the selected regions to the position of the edit cursor */
3769 Editor::trim_region_to_loop ()
3771 Location* loc = _session->locations()->auto_loop_location();
3775 trim_region_to_location (*loc, _("trim to loop"));
3779 Editor::trim_region_to_punch ()
3781 Location* loc = _session->locations()->auto_punch_location();
3785 trim_region_to_location (*loc, _("trim to punch"));
3789 Editor::trim_region_to_location (const Location& loc, const char* str)
3791 RegionSelection rs = get_regions_from_selection_and_entered ();
3792 bool in_command = false;
3794 for (RegionSelection::iterator x = rs.begin(); x != rs.end(); ++x) {
3795 RegionView* rv = (*x);
3797 /* require region to span proposed trim */
3798 switch (rv->region()->coverage (loc.start(), loc.end())) {
3799 case Evoral::OverlapInternal:
3805 RouteTimeAxisView* tav = dynamic_cast<RouteTimeAxisView*> (&rv->get_time_axis_view());
3813 start = loc.start();
3816 rv->region()->clear_changes ();
3817 rv->region()->trim_to (start, (end - start));
3820 begin_reversible_command (str);
3823 _session->add_command(new StatefulDiffCommand (rv->region()));
3827 commit_reversible_command ();
3832 Editor::trim_region_to_previous_region_end ()
3834 return trim_to_region(false);
3838 Editor::trim_region_to_next_region_start ()
3840 return trim_to_region(true);
3844 Editor::trim_to_region(bool forward)
3846 RegionSelection rs = get_regions_from_selection_and_entered ();
3847 bool in_command = false;
3849 boost::shared_ptr<Region> next_region;
3851 for (RegionSelection::iterator x = rs.begin(); x != rs.end(); ++x) {
3853 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (*x);
3859 AudioTimeAxisView* atav = dynamic_cast<AudioTimeAxisView*> (&arv->get_time_axis_view());
3865 boost::shared_ptr<Region> region = arv->region();
3866 boost::shared_ptr<Playlist> playlist (region->playlist());
3868 region->clear_changes ();
3872 next_region = playlist->find_next_region (region->first_sample(), Start, 1);
3878 region->trim_end (next_region->first_sample() - 1);
3879 arv->region_changed (PropertyChange (ARDOUR::Properties::length));
3883 next_region = playlist->find_next_region (region->first_sample(), Start, 0);
3889 region->trim_front (next_region->last_sample() + 1);
3890 arv->region_changed (ARDOUR::bounds_change);
3894 begin_reversible_command (_("trim to region"));
3897 _session->add_command(new StatefulDiffCommand (region));
3901 commit_reversible_command ();
3906 Editor::unfreeze_route ()
3908 if (clicked_routeview == 0 || !clicked_routeview->is_track()) {
3912 clicked_routeview->track()->unfreeze ();
3916 Editor::_freeze_thread (void* arg)
3918 return static_cast<Editor*>(arg)->freeze_thread ();
3922 Editor::freeze_thread ()
3924 /* create event pool because we may need to talk to the session */
3925 SessionEvent::create_per_thread_pool ("freeze events", 64);
3926 /* create per-thread buffers for process() tree to use */
3927 clicked_routeview->audio_track()->freeze_me (*current_interthread_info);
3928 current_interthread_info->done = true;
3933 Editor::freeze_route ()
3939 /* stop transport before we start. this is important */
3941 _session->request_transport_speed (0.0);
3943 /* wait for just a little while, because the above call is asynchronous */
3945 Glib::usleep (250000);
3947 if (clicked_routeview == 0 || !clicked_routeview->is_audio_track()) {
3951 if (!clicked_routeview->track()->bounceable (clicked_routeview->track()->main_outs(), true)) {
3953 _("This track/bus cannot be frozen because the signal adds or loses channels before reaching the outputs.\n"
3954 "This is typically caused by plugins that generate stereo output from mono input or vice versa.")
3956 d.set_title (_("Cannot freeze"));
3961 if (clicked_routeview->track()->has_external_redirects()) {
3962 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"
3963 "Freezing will only process the signal as far as the first send/insert/return."),
3964 clicked_routeview->track()->name()), true, MESSAGE_INFO, BUTTONS_NONE, true);
3966 d.add_button (_("Freeze anyway"), Gtk::RESPONSE_OK);
3967 d.add_button (_("Don't freeze"), Gtk::RESPONSE_CANCEL);
3968 d.set_title (_("Freeze Limits"));
3970 int response = d.run ();
3973 case Gtk::RESPONSE_CANCEL:
3980 InterThreadInfo itt;
3981 current_interthread_info = &itt;
3983 InterthreadProgressWindow ipw (current_interthread_info, _("Freeze"), _("Cancel Freeze"));
3985 pthread_create_and_store (X_("freezer"), &itt.thread, _freeze_thread, this);
3987 CursorContext::Handle cursor_ctx = CursorContext::create(*this, _cursors->wait);
3989 while (!itt.done && !itt.cancel) {
3990 gtk_main_iteration ();
3993 pthread_join (itt.thread, 0);
3994 current_interthread_info = 0;
3998 Editor::bounce_range_selection (bool replace, bool enable_processing)
4000 if (selection->time.empty()) {
4004 TrackSelection views = selection->tracks;
4006 for (TrackViewList::iterator i = views.begin(); i != views.end(); ++i) {
4008 if (enable_processing) {
4010 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (*i);
4012 if (rtv && rtv->track() && replace && enable_processing && !rtv->track()->bounceable (rtv->track()->main_outs(), false)) {
4014 _("You can't perform this operation because the processing of the signal "
4015 "will cause one or more of the tracks to end up with a region with more channels than this track has inputs.\n\n"
4016 "You can do this without processing, which is a different operation.")
4018 d.set_title (_("Cannot bounce"));
4025 samplepos_t start = selection->time[clicked_selection].start;
4026 samplepos_t end = selection->time[clicked_selection].end;
4027 samplepos_t cnt = end - start + 1;
4028 bool in_command = false;
4030 for (TrackViewList::iterator i = views.begin(); i != views.end(); ++i) {
4032 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (*i);
4038 boost::shared_ptr<Playlist> playlist;
4040 if ((playlist = rtv->playlist()) == 0) {
4044 InterThreadInfo itt;
4046 playlist->clear_changes ();
4047 playlist->clear_owned_changes ();
4049 boost::shared_ptr<Region> r;
4051 if (enable_processing) {
4052 r = rtv->track()->bounce_range (start, start+cnt, itt, rtv->track()->main_outs(), false);
4054 r = rtv->track()->bounce_range (start, start+cnt, itt, boost::shared_ptr<Processor>(), false);
4062 list<AudioRange> ranges;
4063 ranges.push_back (AudioRange (start, start+cnt, 0));
4064 playlist->cut (ranges); // discard result
4065 playlist->add_region (r, start);
4069 begin_reversible_command (_("bounce range"));
4072 vector<Command*> cmds;
4073 playlist->rdiff (cmds);
4074 _session->add_commands (cmds);
4076 _session->add_command (new StatefulDiffCommand (playlist));
4080 commit_reversible_command ();
4084 /** Delete selected regions, automation points or a time range */
4088 //special case: if the user is pointing in the editor/mixer strip, they may be trying to delete a plugin.
4089 //we need this because the editor-mixer strip is in the editor window, so it doesn't get the bindings from the mix window
4090 bool deleted = false;
4091 if (current_mixer_strip && current_mixer_strip == MixerStrip::entered_mixer_strip())
4092 deleted = current_mixer_strip->delete_processors ();
4098 /** Cut selected regions, automation points or a time range */
4105 /** Copy selected regions, automation points or a time range */
4113 /** @return true if a Cut, Copy or Clear is possible */
4115 Editor::can_cut_copy () const
4117 if (!selection->time.empty() || !selection->regions.empty() || !selection->points.empty())
4124 /** Cut, copy or clear selected regions, automation points or a time range.
4125 * @param op Operation (Delete, Cut, Copy or Clear)
4128 Editor::cut_copy (CutCopyOp op)
4130 /* only cancel selection if cut/copy is successful.*/
4136 opname = _("delete");
4145 opname = _("clear");
4149 /* if we're deleting something, and the mouse is still pressed,
4150 the thing we started a drag for will be gone when we release
4151 the mouse button(s). avoid this. see part 2 at the end of
4155 if (op == Delete || op == Cut || op == Clear) {
4156 if (_drags->active ()) {
4161 if (op != Delete) { //"Delete" doesn't change copy/paste buf
4162 cut_buffer->clear ();
4165 if (entered_marker) {
4167 /* cut/delete op while pointing at a marker */
4170 Location* loc = find_location_from_marker (entered_marker, ignored);
4172 if (_session && loc) {
4173 entered_marker = NULL;
4174 Glib::signal_idle().connect (sigc::bind (sigc::mem_fun(*this, &Editor::really_remove_marker), loc));
4181 switch (mouse_mode) {
4184 begin_reversible_command (opname + ' ' + X_("MIDI"));
4186 commit_reversible_command ();
4192 bool did_edit = false;
4194 if (!selection->regions.empty() || !selection->points.empty()) {
4195 begin_reversible_command (opname + ' ' + _("objects"));
4198 if (!selection->regions.empty()) {
4199 cut_copy_regions (op, selection->regions);
4201 if (op == Cut || op == Delete) {
4202 selection->clear_regions ();
4206 if (!selection->points.empty()) {
4207 cut_copy_points (op);
4209 if (op == Cut || op == Delete) {
4210 selection->clear_points ();
4213 } else if (selection->time.empty()) {
4214 samplepos_t start, end;
4215 /* no time selection, see if we can get an edit range
4218 if (get_edit_op_range (start, end)) {
4219 selection->set (start, end);
4221 } else if (!selection->time.empty()) {
4222 begin_reversible_command (opname + ' ' + _("range"));
4225 cut_copy_ranges (op);
4227 if (op == Cut || op == Delete) {
4228 selection->clear_time ();
4233 /* reset repeated paste state */
4235 last_paste_pos = -1;
4236 commit_reversible_command ();
4239 if (op == Delete || op == Cut || op == Clear) {
4245 struct AutomationRecord {
4246 AutomationRecord () : state (0) , line(NULL) {}
4247 AutomationRecord (XMLNode* s, const AutomationLine* l) : state (s) , line (l) {}
4249 XMLNode* state; ///< state before any operation
4250 const AutomationLine* line; ///< line this came from
4251 boost::shared_ptr<Evoral::ControlList> copy; ///< copied events for the cut buffer
4254 struct PointsSelectionPositionSorter {
4255 bool operator() (ControlPoint* a, ControlPoint* b) {
4256 return (*(a->model()))->when < (*(b->model()))->when;
4260 /** Cut, copy or clear selected automation points.
4261 * @param op Operation (Cut, Copy or Clear)
4264 Editor::cut_copy_points (Editing::CutCopyOp op, Temporal::Beats earliest, bool midi)
4266 if (selection->points.empty ()) {
4270 /* XXX: not ideal, as there may be more than one track involved in the point selection */
4271 _last_cut_copy_source_track = &selection->points.front()->line().trackview;
4273 /* Keep a record of the AutomationLists that we end up using in this operation */
4274 typedef std::map<boost::shared_ptr<AutomationList>, AutomationRecord> Lists;
4277 /* user could select points in any order */
4278 selection->points.sort(PointsSelectionPositionSorter ());
4280 /* Go through all selected points, making an AutomationRecord for each distinct AutomationList */
4281 for (PointSelection::iterator sel_point = selection->points.begin(); sel_point != selection->points.end(); ++sel_point) {
4282 const AutomationLine& line = (*sel_point)->line();
4283 const boost::shared_ptr<AutomationList> al = line.the_list();
4284 if (lists.find (al) == lists.end ()) {
4285 /* We haven't seen this list yet, so make a record for it. This includes
4286 taking a copy of its current state, in case this is needed for undo later.
4288 lists[al] = AutomationRecord (&al->get_state (), &line);
4292 if (op == Cut || op == Copy) {
4293 /* This operation will involve putting things in the cut buffer, so create an empty
4294 ControlList for each of our source lists to put the cut buffer data in.
4296 for (Lists::iterator i = lists.begin(); i != lists.end(); ++i) {
4297 i->second.copy = i->first->create (i->first->parameter (), i->first->descriptor());
4300 /* Add all selected points to the relevant copy ControlLists */
4301 MusicSample start (std::numeric_limits<samplepos_t>::max(), 0);
4302 for (PointSelection::iterator sel_point = selection->points.begin(); sel_point != selection->points.end(); ++sel_point) {
4303 boost::shared_ptr<AutomationList> al = (*sel_point)->line().the_list();
4304 AutomationList::const_iterator ctrl_evt = (*sel_point)->model ();
4306 lists[al].copy->fast_simple_add ((*ctrl_evt)->when, (*ctrl_evt)->value);
4308 /* Update earliest MIDI start time in beats */
4309 earliest = std::min(earliest, Temporal::Beats((*ctrl_evt)->when));
4311 /* Update earliest session start time in samples */
4312 start.sample = std::min(start.sample, (*sel_point)->line().session_position(ctrl_evt));
4316 /* Snap start time backwards, so copy/paste is snap aligned. */
4318 if (earliest == std::numeric_limits<Temporal::Beats>::max()) {
4319 earliest = Temporal::Beats(); // Weird... don't offset
4321 earliest.round_down_to_beat();
4323 if (start.sample == std::numeric_limits<double>::max()) {
4324 start.sample = 0; // Weird... don't offset
4326 snap_to(start, RoundDownMaybe);
4329 const double line_offset = midi ? earliest.to_double() : start.sample;
4330 for (Lists::iterator i = lists.begin(); i != lists.end(); ++i) {
4331 /* Correct this copy list so that it is relative to the earliest
4332 start time, so relative ordering between points is preserved
4333 when copying from several lists and the paste starts at the
4334 earliest copied piece of data. */
4335 boost::shared_ptr<Evoral::ControlList> &al_cpy = i->second.copy;
4336 for (AutomationList::iterator ctrl_evt = al_cpy->begin(); ctrl_evt != al_cpy->end(); ++ctrl_evt) {
4337 (*ctrl_evt)->when -= line_offset;
4340 /* And add it to the cut buffer */
4341 cut_buffer->add (al_cpy);
4345 if (op == Delete || op == Cut) {
4346 /* This operation needs to remove things from the main AutomationList, so do that now */
4348 for (Lists::iterator i = lists.begin(); i != lists.end(); ++i) {
4349 i->first->freeze ();
4352 /* Remove each selected point from its AutomationList */
4353 for (PointSelection::iterator sel_point = selection->points.begin(); sel_point != selection->points.end(); ++sel_point) {
4354 AutomationLine& line = (*sel_point)->line ();
4355 boost::shared_ptr<AutomationList> al = line.the_list();
4359 if (dynamic_cast<AudioRegionGainLine*> (&line)) {
4360 /* removing of first and last gain point in region gain lines is prohibited*/
4361 if (line.is_last_point (*(*sel_point)) || line.is_first_point (*(*sel_point))) {
4367 al->erase ((*sel_point)->model ());
4371 /* Thaw the lists and add undo records for them */
4372 for (Lists::iterator i = lists.begin(); i != lists.end(); ++i) {
4373 boost::shared_ptr<AutomationList> al = i->first;
4375 _session->add_command (new MementoCommand<AutomationList> (*al.get(), i->second.state, &(al->get_state ())));
4380 /** Cut, copy or clear selected automation points.
4381 * @param op Operation (Cut, Copy or Clear)
4384 Editor::cut_copy_midi (CutCopyOp op)
4386 Temporal::Beats earliest = std::numeric_limits<Temporal::Beats>::max();
4387 for (MidiRegionSelection::iterator i = selection->midi_regions.begin(); i != selection->midi_regions.end(); ++i) {
4388 MidiRegionView* mrv = dynamic_cast<MidiRegionView*>(*i);
4390 if (!mrv->selection().empty()) {
4391 earliest = std::min(earliest, (*mrv->selection().begin())->note()->time());
4393 mrv->cut_copy_clear (op);
4395 /* XXX: not ideal, as there may be more than one track involved in the selection */
4396 _last_cut_copy_source_track = &mrv->get_time_axis_view();
4400 if (!selection->points.empty()) {
4401 cut_copy_points (op, earliest, true);
4402 if (op == Cut || op == Delete) {
4403 selection->clear_points ();
4408 struct lt_playlist {
4409 bool operator () (const PlaylistState& a, const PlaylistState& b) {
4410 return a.playlist < b.playlist;
4414 struct PlaylistMapping {
4416 boost::shared_ptr<Playlist> pl;
4418 PlaylistMapping (TimeAxisView* tvp) : tv (tvp) {}
4421 /** Remove `clicked_regionview' */
4423 Editor::remove_clicked_region ()
4425 if (clicked_routeview == 0 || clicked_regionview == 0) {
4429 begin_reversible_command (_("remove region"));
4431 boost::shared_ptr<Playlist> playlist = clicked_routeview->playlist();
4433 playlist->clear_changes ();
4434 playlist->clear_owned_changes ();
4435 playlist->remove_region (clicked_regionview->region());
4436 if (Config->get_edit_mode() == Ripple)
4437 playlist->ripple (clicked_regionview->region()->position(), -clicked_regionview->region()->length(), boost::shared_ptr<Region>());
4439 /* We might have removed regions, which alters other regions' layering_index,
4440 so we need to do a recursive diff here.
4442 vector<Command*> cmds;
4443 playlist->rdiff (cmds);
4444 _session->add_commands (cmds);
4446 _session->add_command(new StatefulDiffCommand (playlist));
4447 commit_reversible_command ();
4451 /** Remove the selected regions */
4453 Editor::remove_selected_regions ()
4455 RegionSelection rs = get_regions_from_selection_and_entered ();
4457 if (!_session || rs.empty()) {
4461 list<boost::shared_ptr<Region> > regions_to_remove;
4463 for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
4464 // we can't just remove the region(s) in this loop because
4465 // this removes them from the RegionSelection, and they thus
4466 // disappear from underneath the iterator, and the ++i above
4467 // SEGVs in a puzzling fashion.
4469 // so, first iterate over the regions to be removed from rs and
4470 // add them to the regions_to_remove list, and then
4471 // iterate over the list to actually remove them.
4473 regions_to_remove.push_back ((*i)->region());
4476 vector<boost::shared_ptr<Playlist> > playlists;
4478 for (list<boost::shared_ptr<Region> >::iterator rl = regions_to_remove.begin(); rl != regions_to_remove.end(); ++rl) {
4480 boost::shared_ptr<Playlist> playlist = (*rl)->playlist();
4483 // is this check necessary?
4487 /* get_regions_from_selection_and_entered() guarantees that
4488 the playlists involved are unique, so there is no need
4492 playlists.push_back (playlist);
4494 playlist->clear_changes ();
4495 playlist->clear_owned_changes ();
4496 playlist->freeze ();
4497 playlist->remove_region (*rl);
4498 if (Config->get_edit_mode() == Ripple)
4499 playlist->ripple ((*rl)->position(), -(*rl)->length(), boost::shared_ptr<Region>());
4503 vector<boost::shared_ptr<Playlist> >::iterator pl;
4504 bool in_command = false;
4506 for (pl = playlists.begin(); pl != playlists.end(); ++pl) {
4509 /* We might have removed regions, which alters other regions' layering_index,
4510 so we need to do a recursive diff here.
4514 begin_reversible_command (_("remove region"));
4517 vector<Command*> cmds;
4518 (*pl)->rdiff (cmds);
4519 _session->add_commands (cmds);
4521 _session->add_command(new StatefulDiffCommand (*pl));
4525 commit_reversible_command ();
4529 /** Cut, copy or clear selected regions.
4530 * @param op Operation (Cut, Copy or Clear)
4533 Editor::cut_copy_regions (CutCopyOp op, RegionSelection& rs)
4535 /* we can't use a std::map here because the ordering is important, and we can't trivially sort
4536 a map when we want ordered access to both elements. i think.
4539 vector<PlaylistMapping> pmap;
4541 samplepos_t first_position = max_samplepos;
4543 typedef set<boost::shared_ptr<Playlist> > FreezeList;
4544 FreezeList freezelist;
4546 /* get ordering correct before we cut/copy */
4548 rs.sort_by_position_and_track ();
4550 for (RegionSelection::iterator x = rs.begin(); x != rs.end(); ++x) {
4552 first_position = min ((samplepos_t) (*x)->region()->position(), first_position);
4554 if (op == Cut || op == Clear || op == Delete) {
4555 boost::shared_ptr<Playlist> pl = (*x)->region()->playlist();
4558 FreezeList::iterator fl;
4560 // only take state if this is a new playlist.
4561 for (fl = freezelist.begin(); fl != freezelist.end(); ++fl) {
4567 if (fl == freezelist.end()) {
4568 pl->clear_changes();
4569 pl->clear_owned_changes ();
4571 freezelist.insert (pl);
4576 TimeAxisView* tv = &(*x)->get_time_axis_view();
4577 vector<PlaylistMapping>::iterator z;
4579 for (z = pmap.begin(); z != pmap.end(); ++z) {
4580 if ((*z).tv == tv) {
4585 if (z == pmap.end()) {
4586 pmap.push_back (PlaylistMapping (tv));
4590 for (RegionSelection::iterator x = rs.begin(); x != rs.end(); ) {
4592 boost::shared_ptr<Playlist> pl = (*x)->region()->playlist();
4595 /* region not yet associated with a playlist (e.g. unfinished
4602 TimeAxisView& tv = (*x)->get_time_axis_view();
4603 boost::shared_ptr<Playlist> npl;
4604 RegionSelection::iterator tmp;
4611 vector<PlaylistMapping>::iterator z;
4613 for (z = pmap.begin(); z != pmap.end(); ++z) {
4614 if ((*z).tv == &tv) {
4619 assert (z != pmap.end());
4622 npl = PlaylistFactory::create (pl->data_type(), *_session, "cutlist", true);
4630 boost::shared_ptr<Region> r = (*x)->region();
4631 boost::shared_ptr<Region> _xx;
4637 pl->remove_region (r);
4638 if (Config->get_edit_mode() == Ripple)
4639 pl->ripple (r->position(), -r->length(), boost::shared_ptr<Region>());
4643 _xx = RegionFactory::create (r);
4644 npl->add_region (_xx, r->position() - first_position);
4645 pl->remove_region (r);
4646 if (Config->get_edit_mode() == Ripple)
4647 pl->ripple (r->position(), -r->length(), boost::shared_ptr<Region>());
4651 /* copy region before adding, so we're not putting same object into two different playlists */
4652 npl->add_region (RegionFactory::create (r), r->position() - first_position);
4656 pl->remove_region (r);
4657 if (Config->get_edit_mode() == Ripple)
4658 pl->ripple (r->position(), -r->length(), boost::shared_ptr<Region>());
4667 list<boost::shared_ptr<Playlist> > foo;
4669 /* the pmap is in the same order as the tracks in which selected regions occurred */
4671 for (vector<PlaylistMapping>::iterator i = pmap.begin(); i != pmap.end(); ++i) {
4674 foo.push_back ((*i).pl);
4679 cut_buffer->set (foo);
4683 _last_cut_copy_source_track = 0;
4685 _last_cut_copy_source_track = pmap.front().tv;
4689 for (FreezeList::iterator pl = freezelist.begin(); pl != freezelist.end(); ++pl) {
4692 /* We might have removed regions, which alters other regions' layering_index,
4693 so we need to do a recursive diff here.
4695 vector<Command*> cmds;
4696 (*pl)->rdiff (cmds);
4697 _session->add_commands (cmds);
4699 _session->add_command (new StatefulDiffCommand (*pl));
4704 Editor::cut_copy_ranges (CutCopyOp op)
4706 TrackViewList ts = selection->tracks.filter_to_unique_playlists ();
4708 /* Sort the track selection now, so that it if is used, the playlists
4709 selected by the calls below to cut_copy_clear are in the order that
4710 their tracks appear in the editor. This makes things like paste
4711 of ranges work properly.
4714 sort_track_selection (ts);
4717 if (!entered_track) {
4720 ts.push_back (entered_track);
4723 for (TrackViewList::iterator i = ts.begin(); i != ts.end(); ++i) {
4724 (*i)->cut_copy_clear (*selection, op);
4729 Editor::paste (float times, bool from_context)
4731 DEBUG_TRACE (DEBUG::CutNPaste, "paste to preferred edit pos\n");
4732 MusicSample where (get_preferred_edit_position (EDIT_IGNORE_NONE, from_context), 0);
4733 paste_internal (where.sample, times, 0);
4737 Editor::mouse_paste ()
4739 MusicSample where (0, 0);
4741 if (!mouse_sample (where.sample, ignored)) {
4746 paste_internal (where.sample, 1, where.division);
4750 Editor::paste_internal (samplepos_t position, float times, const int32_t sub_num)
4752 DEBUG_TRACE (DEBUG::CutNPaste, string_compose ("apparent paste position is %1\n", position));
4754 if (cut_buffer->empty(internal_editing())) {
4758 if (position == max_samplepos) {
4759 position = get_preferred_edit_position();
4760 DEBUG_TRACE (DEBUG::CutNPaste, string_compose ("preferred edit position is %1\n", position));
4763 if (position != last_paste_pos) {
4764 /* paste in new location, reset repeated paste state */
4766 last_paste_pos = position;
4769 /* get everything in the correct order */
4772 if (!selection->tracks.empty()) {
4773 /* If there is a track selection, paste into exactly those tracks and
4774 * only those tracks. This allows the user to be explicit and override
4775 * the below "do the reasonable thing" logic. */
4776 ts = selection->tracks.filter_to_unique_playlists ();
4777 sort_track_selection (ts);
4779 /* Figure out which track to base the paste at. */
4780 TimeAxisView* base_track = NULL;
4781 if (_edit_point == Editing::EditAtMouse && entered_track) {
4782 /* With the mouse edit point, paste onto the track under the mouse. */
4783 base_track = entered_track;
4784 } else if (_edit_point == Editing::EditAtMouse && entered_regionview) {
4785 /* With the mouse edit point, paste onto the track of the region under the mouse. */
4786 base_track = &entered_regionview->get_time_axis_view();
4787 } else if (_last_cut_copy_source_track) {
4788 /* Paste to the track that the cut/copy came from (see mantis #333). */
4789 base_track = _last_cut_copy_source_track;
4791 /* This is "impossible" since we've copied... well, do nothing. */
4795 /* Walk up to parent if necessary, so base track is a route. */
4796 while (base_track->get_parent()) {
4797 base_track = base_track->get_parent();
4800 /* Add base track and all tracks below it. The paste logic will select
4801 the appropriate object types from the cut buffer in relative order. */
4802 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
4803 if ((*i)->order() >= base_track->order()) {
4808 /* Sort tracks so the nth track of type T will pick the nth object of type T. */
4809 sort_track_selection (ts);
4811 /* Add automation children of each track in order, for pasting several lines. */
4812 for (TrackViewList::iterator i = ts.begin(); i != ts.end();) {
4813 /* Add any automation children for pasting several lines */
4814 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*>(*i++);
4819 typedef RouteTimeAxisView::AutomationTracks ATracks;
4820 const ATracks& atracks = rtv->automation_tracks();
4821 for (ATracks::const_iterator a = atracks.begin(); a != atracks.end(); ++a) {
4822 i = ts.insert(i, a->second.get());
4827 /* We now have a list of trackviews starting at base_track, including
4828 automation children, in the order shown in the editor, e.g. R1,
4829 R1.A1, R1.A2, R2, R2.A1, ... */
4832 begin_reversible_command (Operations::paste);
4834 if (ts.size() == 1 && cut_buffer->lines.size() == 1 &&
4835 dynamic_cast<AutomationTimeAxisView*>(ts.front())) {
4836 /* Only one line copied, and one automation track selected. Do a
4837 "greedy" paste from one automation type to another. */
4839 PasteContext ctx(paste_count, times, ItemCounts(), true);
4840 ts.front()->paste (position, *cut_buffer, ctx, sub_num);
4844 /* Paste into tracks */
4846 PasteContext ctx(paste_count, times, ItemCounts(), false);
4847 for (TrackViewList::iterator i = ts.begin(); i != ts.end(); ++i) {
4848 (*i)->paste (position, *cut_buffer, ctx, sub_num);
4854 commit_reversible_command ();
4858 Editor::duplicate_regions (float times)
4860 RegionSelection rs (get_regions_from_selection_and_entered());
4861 duplicate_some_regions (rs, times);
4865 Editor::duplicate_some_regions (RegionSelection& regions, float times)
4867 if (regions.empty ()) {
4871 boost::shared_ptr<Playlist> playlist;
4872 std::set<boost::shared_ptr<Playlist> > playlists; // list of unique playlists affected by duplication
4873 RegionSelection sel = regions; // clear (below) may clear the argument list if its the current region selection
4874 RegionSelection foo;
4876 samplepos_t const start_sample = regions.start ();
4877 samplepos_t const end_sample = regions.end_sample ();
4878 samplecnt_t const span = end_sample - start_sample + 1;
4880 begin_reversible_command (Operations::duplicate_region);
4882 selection->clear_regions ();
4884 /* ripple first so that we don't move the duplicates that will be added */
4886 if (Config->get_edit_mode() == Ripple) {
4888 /* convert RegionSelection into RegionList so that we can pass it to ripple and exclude the regions we will duplicate */
4892 for (RegionSelection::iterator i = sel.begin(); i != sel.end(); ++i) {
4893 exclude.push_back ((*i)->region());
4894 playlist = (*i)->region()->playlist();
4895 if (playlists.insert (playlist).second) {
4896 /* successfully inserted into set, so it's the first time we've seen this playlist */
4897 playlist->clear_changes ();
4901 for (set<boost::shared_ptr<Playlist> >::iterator p = playlists.begin(); p != playlists.end(); ++p) {
4902 (*p)->ripple (start_sample, span * times, &exclude);
4906 for (RegionSelection::iterator i = sel.begin(); i != sel.end(); ++i) {
4908 boost::shared_ptr<Region> r ((*i)->region());
4910 TimeAxisView& tv = (*i)->get_time_axis_view();
4911 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (&tv);
4912 latest_regionviews.clear ();
4913 sigc::connection c = rtv->view()->RegionViewAdded.connect (sigc::mem_fun(*this, &Editor::collect_new_region_view));
4915 samplepos_t const position = end_sample + (r->first_sample() - start_sample + 1);
4916 playlist = (*i)->region()->playlist();
4918 if (Config->get_edit_mode() != Ripple) {
4919 if (playlists.insert (playlist).second) {
4920 playlist->clear_changes ();
4924 playlist->duplicate (r, position, span, times);
4928 foo.insert (foo.end(), latest_regionviews.begin(), latest_regionviews.end());
4931 for (set<boost::shared_ptr<Playlist> >::iterator p = playlists.begin(); p != playlists.end(); ++p) {
4932 _session->add_command (new StatefulDiffCommand (*p));
4933 vector<Command*> cmds;
4935 _session->add_commands (cmds);
4939 selection->set (foo);
4942 commit_reversible_command ();
4946 Editor::duplicate_selection (float times)
4948 if (selection->time.empty() || selection->tracks.empty()) {
4952 boost::shared_ptr<Playlist> playlist;
4954 TrackViewList ts = selection->tracks.filter_to_unique_playlists ();
4956 bool in_command = false;
4958 for (TrackViewList::iterator i = ts.begin(); i != ts.end(); ++i) {
4959 if ((playlist = (*i)->playlist()) == 0) {
4962 playlist->clear_changes ();
4964 if (clicked_selection) {
4965 playlist->duplicate_range (selection->time[clicked_selection], times);
4967 playlist->duplicate_ranges (selection->time, times);
4971 begin_reversible_command (_("duplicate range selection"));
4974 _session->add_command (new StatefulDiffCommand (playlist));
4979 if (times == 1.0f) {
4980 // now "move" range selection to after the current range selection
4981 samplecnt_t distance = 0;
4983 if (clicked_selection) {
4985 selection->time[clicked_selection].end - selection->time[clicked_selection].start;
4987 distance = selection->time.end_sample () - selection->time.start ();
4990 selection->move_time (distance);
4992 commit_reversible_command ();
4996 /** Reset all selected points to the relevant default value */
4998 Editor::reset_point_selection ()
5000 for (PointSelection::iterator i = selection->points.begin(); i != selection->points.end(); ++i) {
5001 ARDOUR::AutomationList::iterator j = (*i)->model ();
5002 (*j)->value = (*i)->line().the_list()->descriptor ().normal;
5007 Editor::center_playhead ()
5009 float const page = _visible_canvas_width * samples_per_pixel;
5010 center_screen_internal (playhead_cursor->current_sample (), page);
5014 Editor::center_edit_point ()
5016 float const page = _visible_canvas_width * samples_per_pixel;
5017 center_screen_internal (get_preferred_edit_position(), page);
5020 /** Caller must begin and commit a reversible command */
5022 Editor::clear_playlist (boost::shared_ptr<Playlist> playlist)
5024 playlist->clear_changes ();
5026 _session->add_command (new StatefulDiffCommand (playlist));
5030 Editor::nudge_track (bool use_edit, bool forwards)
5032 boost::shared_ptr<Playlist> playlist;
5033 samplepos_t distance;
5034 samplepos_t next_distance;
5038 start = get_preferred_edit_position();
5043 if ((distance = get_nudge_distance (start, next_distance)) == 0) {
5047 if (selection->tracks.empty()) {
5051 TrackViewList ts = selection->tracks.filter_to_unique_playlists ();
5052 bool in_command = false;
5054 for (TrackViewList::iterator i = ts.begin(); i != ts.end(); ++i) {
5056 if ((playlist = (*i)->playlist()) == 0) {
5060 playlist->clear_changes ();
5061 playlist->clear_owned_changes ();
5063 playlist->nudge_after (start, distance, forwards);
5066 begin_reversible_command (_("nudge track"));
5069 vector<Command*> cmds;
5071 playlist->rdiff (cmds);
5072 _session->add_commands (cmds);
5074 _session->add_command (new StatefulDiffCommand (playlist));
5078 commit_reversible_command ();
5083 Editor::remove_last_capture ()
5085 vector<string> choices;
5092 if (Config->get_verify_remove_last_capture()) {
5093 prompt = _("Do you really want to destroy the last capture?"
5094 "\n(This is destructive and cannot be undone)");
5096 choices.push_back (_("No, do nothing."));
5097 choices.push_back (_("Yes, destroy it."));
5099 Choice prompter (_("Destroy last capture"), prompt, choices);
5101 if (prompter.run () == 1) {
5102 _session->remove_last_capture ();
5103 _regions->redisplay ();
5107 _session->remove_last_capture();
5108 _regions->redisplay ();
5113 Editor::normalize_region ()
5119 RegionSelection rs = get_regions_from_selection_and_entered ();
5125 NormalizeDialog dialog (rs.size() > 1);
5127 if (dialog.run () != RESPONSE_ACCEPT) {
5131 CursorContext::Handle cursor_ctx = CursorContext::create(*this, _cursors->wait);
5134 /* XXX: should really only count audio regions here */
5135 int const regions = rs.size ();
5137 /* Make a list of the selected audio regions' maximum amplitudes, and also
5138 obtain the maximum amplitude of them all.
5140 list<double> max_amps;
5141 list<double> rms_vals;
5144 bool use_rms = dialog.constrain_rms ();
5146 for (RegionSelection::const_iterator i = rs.begin(); i != rs.end(); ++i) {
5147 AudioRegionView const * arv = dynamic_cast<AudioRegionView const *> (*i);
5151 dialog.descend (1.0 / regions);
5152 double const a = arv->audio_region()->maximum_amplitude (&dialog);
5154 double r = arv->audio_region()->rms (&dialog);
5155 max_rms = max (max_rms, r);
5156 rms_vals.push_back (r);
5160 /* the user cancelled the operation */
5164 max_amps.push_back (a);
5165 max_amp = max (max_amp, a);
5169 list<double>::const_iterator a = max_amps.begin ();
5170 list<double>::const_iterator l = rms_vals.begin ();
5171 bool in_command = false;
5173 for (RegionSelection::iterator r = rs.begin(); r != rs.end(); ++r) {
5174 AudioRegionView* const arv = dynamic_cast<AudioRegionView*> (*r);
5179 arv->region()->clear_changes ();
5181 double amp = dialog.normalize_individually() ? *a : max_amp;
5182 double target = dialog.target_peak (); // dB
5185 double const amp_rms = dialog.normalize_individually() ? *l : max_rms;
5186 const double t_rms = dialog.target_rms ();
5187 const gain_t c_peak = dB_to_coefficient (target);
5188 const gain_t c_rms = dB_to_coefficient (t_rms);
5189 if ((amp_rms / c_rms) > (amp / c_peak)) {
5195 arv->audio_region()->normalize (amp, target);
5198 begin_reversible_command (_("normalize"));
5201 _session->add_command (new StatefulDiffCommand (arv->region()));
5208 commit_reversible_command ();
5214 Editor::reset_region_scale_amplitude ()
5220 RegionSelection rs = get_regions_from_selection_and_entered ();
5226 bool in_command = false;
5228 for (RegionSelection::iterator r = rs.begin(); r != rs.end(); ++r) {
5229 AudioRegionView* const arv = dynamic_cast<AudioRegionView*>(*r);
5232 arv->region()->clear_changes ();
5233 arv->audio_region()->set_scale_amplitude (1.0f);
5236 begin_reversible_command ("reset gain");
5239 _session->add_command (new StatefulDiffCommand (arv->region()));
5243 commit_reversible_command ();
5248 Editor::adjust_region_gain (bool up)
5250 RegionSelection rs = get_regions_from_selection_and_entered ();
5252 if (!_session || rs.empty()) {
5256 bool in_command = false;
5258 for (RegionSelection::iterator r = rs.begin(); r != rs.end(); ++r) {
5259 AudioRegionView* const arv = dynamic_cast<AudioRegionView*>(*r);
5264 arv->region()->clear_changes ();
5266 double dB = accurate_coefficient_to_dB (arv->audio_region()->scale_amplitude ());
5274 arv->audio_region()->set_scale_amplitude (dB_to_coefficient (dB));
5277 begin_reversible_command ("adjust region gain");
5280 _session->add_command (new StatefulDiffCommand (arv->region()));
5284 commit_reversible_command ();
5289 Editor::reset_region_gain ()
5291 RegionSelection rs = get_regions_from_selection_and_entered ();
5293 if (!_session || rs.empty()) {
5297 bool in_command = false;
5299 for (RegionSelection::iterator r = rs.begin(); r != rs.end(); ++r) {
5300 AudioRegionView* const arv = dynamic_cast<AudioRegionView*>(*r);
5305 arv->region()->clear_changes ();
5307 arv->audio_region()->set_scale_amplitude (1.0f);
5310 begin_reversible_command ("reset region gain");
5313 _session->add_command (new StatefulDiffCommand (arv->region()));
5317 commit_reversible_command ();
5322 Editor::reverse_region ()
5328 Reverse rev (*_session);
5329 apply_filter (rev, _("reverse regions"));
5333 Editor::strip_region_silence ()
5339 RegionSelection rs = get_regions_from_selection_and_entered ();
5345 std::list<RegionView*> audio_only;
5347 for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
5348 AudioRegionView* const arv = dynamic_cast<AudioRegionView*> (*i);
5350 audio_only.push_back (arv);
5354 assert (!audio_only.empty());
5356 StripSilenceDialog d (_session, audio_only);
5357 int const r = d.run ();
5361 if (r == Gtk::RESPONSE_OK) {
5362 ARDOUR::AudioIntervalMap silences;
5363 d.silences (silences);
5364 StripSilence s (*_session, silences, d.fade_length());
5366 apply_filter (s, _("strip silence"), &d);
5371 Editor::apply_midi_note_edit_op_to_region (MidiOperator& op, MidiRegionView& mrv)
5373 Evoral::Sequence<Temporal::Beats>::Notes selected;
5374 mrv.selection_as_notelist (selected, true);
5376 vector<Evoral::Sequence<Temporal::Beats>::Notes> v;
5377 v.push_back (selected);
5379 Temporal::Beats pos_beats = Temporal::Beats (mrv.midi_region()->beat()) - mrv.midi_region()->start_beats();
5381 return op (mrv.midi_region()->model(), pos_beats, v);
5385 Editor::apply_midi_note_edit_op (MidiOperator& op, const RegionSelection& rs)
5391 bool in_command = false;
5393 for (RegionSelection::const_iterator r = rs.begin(); r != rs.end(); ) {
5394 RegionSelection::const_iterator tmp = r;
5397 MidiRegionView* const mrv = dynamic_cast<MidiRegionView*> (*r);
5400 Command* cmd = apply_midi_note_edit_op_to_region (op, *mrv);
5403 begin_reversible_command (op.name ());
5407 _session->add_command (cmd);
5415 commit_reversible_command ();
5416 _session->set_dirty ();
5421 Editor::fork_region ()
5423 RegionSelection rs = get_regions_from_selection_and_entered ();
5429 CursorContext::Handle cursor_ctx = CursorContext::create(*this, _cursors->wait);
5430 bool in_command = false;
5434 for (RegionSelection::iterator r = rs.begin(); r != rs.end(); ) {
5435 RegionSelection::iterator tmp = r;
5438 MidiRegionView* const mrv = dynamic_cast<MidiRegionView*>(*r);
5442 boost::shared_ptr<Playlist> playlist = mrv->region()->playlist();
5443 boost::shared_ptr<MidiSource> new_source = _session->create_midi_source_by_stealing_name (mrv->midi_view()->track());
5444 boost::shared_ptr<MidiRegion> newregion = mrv->midi_region()->clone (new_source);
5447 begin_reversible_command (_("Fork Region(s)"));
5450 playlist->clear_changes ();
5451 playlist->replace_region (mrv->region(), newregion, mrv->region()->position());
5452 _session->add_command(new StatefulDiffCommand (playlist));
5454 error << string_compose (_("Could not unlink %1"), mrv->region()->name()) << endmsg;
5462 commit_reversible_command ();
5467 Editor::quantize_region ()
5470 quantize_regions(get_regions_from_selection_and_entered ());
5475 Editor::quantize_regions (const RegionSelection& rs)
5477 if (rs.n_midi_regions() == 0) {
5481 if (!quantize_dialog) {
5482 quantize_dialog = new QuantizeDialog (*this);
5485 if (quantize_dialog->is_mapped()) {
5486 /* in progress already */
5490 quantize_dialog->present ();
5491 const int r = quantize_dialog->run ();
5492 quantize_dialog->hide ();
5494 if (r == Gtk::RESPONSE_OK) {
5495 Quantize quant (quantize_dialog->snap_start(),
5496 quantize_dialog->snap_end(),
5497 quantize_dialog->start_grid_size(),
5498 quantize_dialog->end_grid_size(),
5499 quantize_dialog->strength(),
5500 quantize_dialog->swing(),
5501 quantize_dialog->threshold());
5503 apply_midi_note_edit_op (quant, rs);
5508 Editor::legatize_region (bool shrink_only)
5511 legatize_regions(get_regions_from_selection_and_entered (), shrink_only);
5516 Editor::legatize_regions (const RegionSelection& rs, bool shrink_only)
5518 if (rs.n_midi_regions() == 0) {
5522 Legatize legatize(shrink_only);
5523 apply_midi_note_edit_op (legatize, rs);
5527 Editor::transform_region ()
5530 transform_regions(get_regions_from_selection_and_entered ());
5535 Editor::transform_regions (const RegionSelection& rs)
5537 if (rs.n_midi_regions() == 0) {
5544 const int r = td.run();
5547 if (r == Gtk::RESPONSE_OK) {
5548 Transform transform(td.get());
5549 apply_midi_note_edit_op(transform, rs);
5554 Editor::transpose_region ()
5557 transpose_regions(get_regions_from_selection_and_entered ());
5562 Editor::transpose_regions (const RegionSelection& rs)
5564 if (rs.n_midi_regions() == 0) {
5569 int const r = d.run ();
5571 if (r == RESPONSE_ACCEPT) {
5572 Transpose transpose(d.semitones ());
5573 apply_midi_note_edit_op (transpose, rs);
5578 Editor::insert_patch_change (bool from_context)
5580 RegionSelection rs = get_regions_from_selection_and_entered ();
5586 const samplepos_t p = get_preferred_edit_position (EDIT_IGNORE_NONE, from_context);
5588 /* XXX: bit of a hack; use the MIDNAM from the first selected region;
5589 there may be more than one, but the PatchChangeDialog can only offer
5590 one set of patch menus.
5592 MidiRegionView* first = dynamic_cast<MidiRegionView*> (rs.front ());
5594 Evoral::PatchChange<Temporal::Beats> empty (Temporal::Beats(), 0, 0, 0);
5595 PatchChangeDialog d (0, _session, empty, first->instrument_info(), Gtk::Stock::ADD);
5597 if (d.run() == RESPONSE_CANCEL) {
5601 for (RegionSelection::iterator i = rs.begin (); i != rs.end(); ++i) {
5602 MidiRegionView* const mrv = dynamic_cast<MidiRegionView*> (*i);
5604 if (p >= mrv->region()->first_sample() && p <= mrv->region()->last_sample()) {
5605 mrv->add_patch_change (p - mrv->region()->position(), d.patch ());
5612 Editor::apply_filter (Filter& filter, string command, ProgressReporter* progress)
5614 RegionSelection rs = get_regions_from_selection_and_entered ();
5620 CursorContext::Handle cursor_ctx = CursorContext::create(*this, _cursors->wait);
5621 bool in_command = false;
5626 int const N = rs.size ();
5628 for (RegionSelection::iterator r = rs.begin(); r != rs.end(); ) {
5629 RegionSelection::iterator tmp = r;
5632 AudioRegionView* const arv = dynamic_cast<AudioRegionView*>(*r);
5634 boost::shared_ptr<Playlist> playlist = arv->region()->playlist();
5637 progress->descend (1.0 / N);
5640 if (arv->audio_region()->apply (filter, progress) == 0) {
5642 playlist->clear_changes ();
5643 playlist->clear_owned_changes ();
5646 begin_reversible_command (command);
5650 if (filter.results.empty ()) {
5652 /* no regions returned; remove the old one */
5653 playlist->remove_region (arv->region ());
5657 std::vector<boost::shared_ptr<Region> >::iterator res = filter.results.begin ();
5659 /* first region replaces the old one */
5660 playlist->replace_region (arv->region(), *res, (*res)->position());
5664 while (res != filter.results.end()) {
5665 playlist->add_region (*res, (*res)->position());
5671 /* We might have removed regions, which alters other regions' layering_index,
5672 so we need to do a recursive diff here.
5674 vector<Command*> cmds;
5675 playlist->rdiff (cmds);
5676 _session->add_commands (cmds);
5678 _session->add_command(new StatefulDiffCommand (playlist));
5682 progress->ascend ();
5691 commit_reversible_command ();
5696 Editor::external_edit_region ()
5702 Editor::reset_region_gain_envelopes ()
5704 RegionSelection rs = get_regions_from_selection_and_entered ();
5706 if (!_session || rs.empty()) {
5710 bool in_command = false;
5712 for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
5713 AudioRegionView* const arv = dynamic_cast<AudioRegionView*>(*i);
5715 boost::shared_ptr<AutomationList> alist (arv->audio_region()->envelope());
5716 XMLNode& before (alist->get_state());
5718 arv->audio_region()->set_default_envelope ();
5721 begin_reversible_command (_("reset region gain"));
5724 _session->add_command (new MementoCommand<AutomationList>(*arv->audio_region()->envelope().get(), &before, &alist->get_state()));
5729 commit_reversible_command ();
5734 Editor::set_region_gain_visibility (RegionView* rv)
5736 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (rv);
5738 arv->update_envelope_visibility();
5743 Editor::set_gain_envelope_visibility ()
5749 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
5750 AudioTimeAxisView* v = dynamic_cast<AudioTimeAxisView*>(*i);
5752 v->audio_view()->foreach_regionview (sigc::mem_fun (this, &Editor::set_region_gain_visibility));
5758 Editor::toggle_gain_envelope_active ()
5760 if (_ignore_region_action) {
5764 RegionSelection rs = get_regions_from_selection_and_entered ();
5766 if (!_session || rs.empty()) {
5770 bool in_command = false;
5772 for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
5773 AudioRegionView* const arv = dynamic_cast<AudioRegionView*>(*i);
5775 arv->region()->clear_changes ();
5776 arv->audio_region()->set_envelope_active (!arv->audio_region()->envelope_active());
5779 begin_reversible_command (_("region gain envelope active"));
5782 _session->add_command (new StatefulDiffCommand (arv->region()));
5787 commit_reversible_command ();
5792 Editor::toggle_region_lock ()
5794 if (_ignore_region_action) {
5798 RegionSelection rs = get_regions_from_selection_and_entered ();
5800 if (!_session || rs.empty()) {
5804 begin_reversible_command (_("toggle region lock"));
5806 for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
5807 (*i)->region()->clear_changes ();
5808 (*i)->region()->set_locked (!(*i)->region()->locked());
5809 _session->add_command (new StatefulDiffCommand ((*i)->region()));
5812 commit_reversible_command ();
5816 Editor::toggle_region_video_lock ()
5818 if (_ignore_region_action) {
5822 RegionSelection rs = get_regions_from_selection_and_entered ();
5824 if (!_session || rs.empty()) {
5828 begin_reversible_command (_("Toggle Video Lock"));
5830 for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
5831 (*i)->region()->clear_changes ();
5832 (*i)->region()->set_video_locked (!(*i)->region()->video_locked());
5833 _session->add_command (new StatefulDiffCommand ((*i)->region()));
5836 commit_reversible_command ();
5840 Editor::toggle_region_lock_style ()
5842 if (_ignore_region_action) {
5846 RegionSelection rs = get_regions_from_selection_and_entered ();
5848 if (!_session || rs.empty()) {
5852 Glib::RefPtr<ToggleAction> a = Glib::RefPtr<ToggleAction>::cast_dynamic (_region_actions->get_action("toggle-region-lock-style"));
5853 vector<Widget*> proxies = a->get_proxies();
5854 Gtk::CheckMenuItem* cmi = dynamic_cast<Gtk::CheckMenuItem*> (proxies.front());
5858 begin_reversible_command (_("toggle region lock style"));
5860 for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
5861 (*i)->region()->clear_changes ();
5862 PositionLockStyle const ns = ((*i)->region()->position_lock_style() == AudioTime && !cmi->get_inconsistent()) ? MusicTime : AudioTime;
5863 (*i)->region()->set_position_lock_style (ns);
5864 _session->add_command (new StatefulDiffCommand ((*i)->region()));
5867 commit_reversible_command ();
5871 Editor::toggle_opaque_region ()
5873 if (_ignore_region_action) {
5877 RegionSelection rs = get_regions_from_selection_and_entered ();
5879 if (!_session || rs.empty()) {
5883 begin_reversible_command (_("change region opacity"));
5885 for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
5886 (*i)->region()->clear_changes ();
5887 (*i)->region()->set_opaque (!(*i)->region()->opaque());
5888 _session->add_command (new StatefulDiffCommand ((*i)->region()));
5891 commit_reversible_command ();
5895 Editor::toggle_record_enable ()
5897 bool new_state = false;
5899 for (TrackSelection::iterator i = selection->tracks.begin(); i != selection->tracks.end(); ++i) {
5900 RouteTimeAxisView *rtav = dynamic_cast<RouteTimeAxisView *>(*i);
5903 if (!rtav->is_track())
5907 new_state = !rtav->track()->rec_enable_control()->get_value();
5911 rtav->track()->rec_enable_control()->set_value (new_state, Controllable::UseGroup);
5916 tracklist_to_stripables (TrackViewList list)
5920 for (TrackSelection::iterator i = list.begin(); i != list.end(); ++i) {
5921 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> ((*i));
5923 if (rtv && rtv->is_track()) {
5924 ret.push_back (rtv->track());
5932 Editor::play_solo_selection (bool restart)
5934 //note: session::solo_selection takes care of invalidating the region playlist
5936 if ((!selection->tracks.empty()) && selection->time.length() > 0) { //a range is selected; solo the tracks and roll
5938 StripableList sl = tracklist_to_stripables (selection->tracks);
5939 _session->solo_selection (sl, true);
5942 samplepos_t start = selection->time.start();
5943 samplepos_t end = selection->time.end_sample();
5944 _session->request_bounded_roll (start, end);
5946 } else if (! selection->tracks.empty()) { //no range is selected, but tracks are selected; solo the tracks and roll
5947 StripableList sl = tracklist_to_stripables (selection->tracks);
5948 _session->solo_selection (sl, true);
5949 _session->request_cancel_play_range();
5950 transition_to_rolling (true);
5952 } else if (! selection->regions.empty()) { //solo any tracks with selected regions, and roll
5953 StripableList sl = tracklist_to_stripables (get_tracks_for_range_action());
5954 _session->solo_selection (sl, true);
5955 _session->request_cancel_play_range();
5956 transition_to_rolling (true);
5958 _session->request_cancel_play_range();
5959 transition_to_rolling (true); //no selection. just roll.
5964 Editor::toggle_solo ()
5966 bool new_state = false;
5968 boost::shared_ptr<ControlList> cl (new ControlList);
5970 for (TrackSelection::iterator i = selection->tracks.begin(); i != selection->tracks.end(); ++i) {
5971 StripableTimeAxisView *stav = dynamic_cast<StripableTimeAxisView *>(*i);
5973 if (!stav || !stav->stripable()->solo_control()) {
5978 new_state = !stav->stripable()->solo_control()->soloed ();
5982 cl->push_back (stav->stripable()->solo_control());
5985 _session->set_controls (cl, new_state ? 1.0 : 0.0, Controllable::UseGroup);
5989 Editor::toggle_mute ()
5991 bool new_state = false;
5993 boost::shared_ptr<ControlList> cl (new ControlList);
5995 for (TrackSelection::iterator i = selection->tracks.begin(); i != selection->tracks.end(); ++i) {
5996 StripableTimeAxisView *stav = dynamic_cast<StripableTimeAxisView *>(*i);
5998 if (!stav || !stav->stripable()->mute_control()) {
6003 new_state = !stav->stripable()->mute_control()->muted();
6007 cl->push_back (stav->stripable()->mute_control());
6010 _session->set_controls (cl, new_state, Controllable::UseGroup);
6014 Editor::toggle_solo_isolate ()
6020 Editor::fade_range ()
6022 TrackViewList ts = selection->tracks.filter_to_unique_playlists ();
6024 begin_reversible_command (_("fade range"));
6026 for (TrackViewList::iterator i = ts.begin(); i != ts.end(); ++i) {
6027 (*i)->fade_range (selection->time);
6030 commit_reversible_command ();
6035 Editor::set_fade_length (bool in)
6037 RegionSelection rs = get_regions_from_selection_and_entered ();
6043 /* we need a region to measure the offset from the start */
6045 RegionView* rv = rs.front ();
6047 samplepos_t pos = get_preferred_edit_position();
6051 if (pos > rv->region()->last_sample() || pos < rv->region()->first_sample()) {
6052 /* edit point is outside the relevant region */
6057 if (pos <= rv->region()->position()) {
6061 len = pos - rv->region()->position();
6062 cmd = _("set fade in length");
6064 if (pos >= rv->region()->last_sample()) {
6068 len = rv->region()->last_sample() - pos;
6069 cmd = _("set fade out length");
6072 bool in_command = false;
6074 for (RegionSelection::iterator x = rs.begin(); x != rs.end(); ++x) {
6075 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (*x);
6081 boost::shared_ptr<AutomationList> alist;
6083 alist = tmp->audio_region()->fade_in();
6085 alist = tmp->audio_region()->fade_out();
6088 XMLNode &before = alist->get_state();
6091 tmp->audio_region()->set_fade_in_length (len);
6092 tmp->audio_region()->set_fade_in_active (true);
6094 tmp->audio_region()->set_fade_out_length (len);
6095 tmp->audio_region()->set_fade_out_active (true);
6099 begin_reversible_command (cmd);
6102 XMLNode &after = alist->get_state();
6103 _session->add_command(new MementoCommand<AutomationList>(*alist, &before, &after));
6107 commit_reversible_command ();
6112 Editor::set_fade_in_shape (FadeShape shape)
6114 RegionSelection rs = get_regions_from_selection_and_entered ();
6119 bool in_command = false;
6121 for (RegionSelection::iterator x = rs.begin(); x != rs.end(); ++x) {
6122 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (*x);
6128 boost::shared_ptr<AutomationList> alist = tmp->audio_region()->fade_in();
6129 XMLNode &before = alist->get_state();
6131 tmp->audio_region()->set_fade_in_shape (shape);
6134 begin_reversible_command (_("set fade in shape"));
6137 XMLNode &after = alist->get_state();
6138 _session->add_command(new MementoCommand<AutomationList>(*alist.get(), &before, &after));
6142 commit_reversible_command ();
6147 Editor::set_fade_out_shape (FadeShape shape)
6149 RegionSelection rs = get_regions_from_selection_and_entered ();
6154 bool in_command = false;
6156 for (RegionSelection::iterator x = rs.begin(); x != rs.end(); ++x) {
6157 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (*x);
6163 boost::shared_ptr<AutomationList> alist = tmp->audio_region()->fade_out();
6164 XMLNode &before = alist->get_state();
6166 tmp->audio_region()->set_fade_out_shape (shape);
6169 begin_reversible_command (_("set fade out shape"));
6172 XMLNode &after = alist->get_state();
6173 _session->add_command(new MementoCommand<AutomationList>(*alist.get(), &before, &after));
6177 commit_reversible_command ();
6182 Editor::set_fade_in_active (bool yn)
6184 RegionSelection rs = get_regions_from_selection_and_entered ();
6189 bool in_command = false;
6191 for (RegionSelection::iterator x = rs.begin(); x != rs.end(); ++x) {
6192 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (*x);
6199 boost::shared_ptr<AudioRegion> ar (tmp->audio_region());
6201 ar->clear_changes ();
6202 ar->set_fade_in_active (yn);
6205 begin_reversible_command (_("set fade in active"));
6208 _session->add_command (new StatefulDiffCommand (ar));
6212 commit_reversible_command ();
6217 Editor::set_fade_out_active (bool yn)
6219 RegionSelection rs = get_regions_from_selection_and_entered ();
6224 bool in_command = false;
6226 for (RegionSelection::iterator x = rs.begin(); x != rs.end(); ++x) {
6227 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (*x);
6233 boost::shared_ptr<AudioRegion> ar (tmp->audio_region());
6235 ar->clear_changes ();
6236 ar->set_fade_out_active (yn);
6239 begin_reversible_command (_("set fade out active"));
6242 _session->add_command(new StatefulDiffCommand (ar));
6246 commit_reversible_command ();
6251 Editor::toggle_region_fades (int dir)
6253 if (_ignore_region_action) {
6257 boost::shared_ptr<AudioRegion> ar;
6260 RegionSelection rs = get_regions_from_selection_and_entered ();
6266 RegionSelection::iterator i;
6267 for (i = rs.begin(); i != rs.end(); ++i) {
6268 if ((ar = boost::dynamic_pointer_cast<AudioRegion>((*i)->region())) != 0) {
6270 yn = ar->fade_out_active ();
6272 yn = ar->fade_in_active ();
6278 if (i == rs.end()) {
6282 /* XXX should this undo-able? */
6283 bool in_command = false;
6285 for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
6286 if ((ar = boost::dynamic_pointer_cast<AudioRegion>((*i)->region())) == 0) {
6289 ar->clear_changes ();
6291 if (dir == 1 || dir == 0) {
6292 ar->set_fade_in_active (!yn);
6295 if (dir == -1 || dir == 0) {
6296 ar->set_fade_out_active (!yn);
6299 begin_reversible_command (_("toggle fade active"));
6302 _session->add_command(new StatefulDiffCommand (ar));
6306 commit_reversible_command ();
6311 /** Update region fade visibility after its configuration has been changed */
6313 Editor::update_region_fade_visibility ()
6315 bool _fade_visibility = _session->config.get_show_region_fades ();
6317 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
6318 AudioTimeAxisView* v = dynamic_cast<AudioTimeAxisView*>(*i);
6320 if (_fade_visibility) {
6321 v->audio_view()->show_all_fades ();
6323 v->audio_view()->hide_all_fades ();
6330 Editor::set_edit_point ()
6333 MusicSample where (0, 0);
6335 if (!mouse_sample (where.sample, ignored)) {
6341 if (selection->markers.empty()) {
6343 mouse_add_new_marker (where.sample);
6348 Location* loc = find_location_from_marker (selection->markers.front(), ignored);
6351 loc->move_to (where.sample, where.division);
6357 Editor::set_playhead_cursor ()
6359 if (entered_marker) {
6360 _session->request_locate (entered_marker->position(), _session->transport_rolling());
6362 MusicSample where (0, 0);
6365 if (!mouse_sample (where.sample, ignored)) {
6372 _session->request_locate (where.sample, _session->transport_rolling());
6376 //not sure what this was for; remove it for now.
6377 // if (UIConfiguration::instance().get_follow_edits() && (!_session || !_session->config.get_external_sync())) {
6378 // cancel_time_selection();
6384 Editor::split_region ()
6386 if (_dragging_playhead) {
6388 } else if (_drags->active ()) {
6389 /*any other kind of drag, bail out so we avoid Undo snafu*/
6393 //if a range is selected, separate it
6394 if (!selection->time.empty()) {
6395 separate_regions_between (selection->time);
6399 //if no range was selected, try to find some regions to split
6400 if (current_mouse_mode() == MouseObject || current_mouse_mode() == MouseRange ) { //don't try this for Internal Edit, Stretch, Draw, etc.
6402 RegionSelection rs = get_regions_from_selection_and_edit_point ();
6403 const samplepos_t pos = get_preferred_edit_position();
6404 const int32_t division = get_grid_music_divisions (0);
6405 MusicSample where (pos, division);
6411 split_regions_at (where, rs);
6417 Editor::select_next_stripable (bool routes_only)
6419 if (selection->tracks.empty()) {
6420 selection->set (track_views.front());
6424 TimeAxisView* current = selection->tracks.front();
6428 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
6430 if (*i == current) {
6432 if (i != track_views.end()) {
6435 current = (*(track_views.begin()));
6436 //selection->set (*(track_views.begin()));
6443 RouteUI* rui = dynamic_cast<RouteUI *>(current);
6444 valid = rui && rui->route()->active();
6446 valid = 0 != current->stripable ().get();
6449 } while (current->hidden() || !valid);
6451 selection->set (current);
6453 ensure_time_axis_view_is_visible (*current, false);
6457 Editor::select_prev_stripable (bool routes_only)
6459 if (selection->tracks.empty()) {
6460 selection->set (track_views.front());
6464 TimeAxisView* current = selection->tracks.front();
6468 for (TrackViewList::reverse_iterator i = track_views.rbegin(); i != track_views.rend(); ++i) {
6470 if (*i == current) {
6472 if (i != track_views.rend()) {
6475 current = *(track_views.rbegin());
6481 RouteUI* rui = dynamic_cast<RouteUI *>(current);
6482 valid = rui && rui->route()->active();
6484 valid = 0 != current->stripable ().get();
6487 } while (current->hidden() || !valid);
6489 selection->set (current);
6491 ensure_time_axis_view_is_visible (*current, false);
6495 Editor::set_loop_from_selection (bool play)
6497 if (_session == 0) {
6501 samplepos_t start, end;
6502 if (!get_selection_extents (start, end))
6505 set_loop_range (start, end, _("set loop range from selection"));
6508 _session->request_play_loop (true, true);
6513 Editor::set_loop_from_region (bool play)
6515 samplepos_t start, end;
6516 if (!get_selection_extents (start, end))
6519 set_loop_range (start, end, _("set loop range from region"));
6522 _session->request_locate (start, true);
6523 _session->request_play_loop (true);
6528 Editor::set_punch_from_selection ()
6530 if (_session == 0) {
6534 samplepos_t start, end;
6535 if (!get_selection_extents (start, end))
6538 set_punch_range (start, end, _("set punch range from selection"));
6542 Editor::set_auto_punch_range ()
6544 // auto punch in/out button from a single button
6545 // If Punch In is unset, set punch range from playhead to end, enable punch in
6546 // If Punch In is set, the next punch sets Punch Out, unless the playhead has been
6547 // rewound beyond the Punch In marker, in which case that marker will be moved back
6548 // to the current playhead position.
6549 // If punch out is set, it clears the punch range and Punch In/Out buttons
6551 if (_session == 0) {
6555 Location* tpl = transport_punch_location();
6556 samplepos_t now = playhead_cursor->current_sample();
6557 samplepos_t begin = now;
6558 samplepos_t end = _session->current_end_sample();
6560 if (!_session->config.get_punch_in()) {
6561 // First Press - set punch in and create range from here to eternity
6562 set_punch_range (begin, end, _("Auto Punch In"));
6563 _session->config.set_punch_in(true);
6564 } else if (tpl && !_session->config.get_punch_out()) {
6565 // Second press - update end range marker and set punch_out
6566 if (now < tpl->start()) {
6567 // playhead has been rewound - move start back and pretend nothing happened
6569 set_punch_range (begin, end, _("Auto Punch In/Out"));
6571 // normal case for 2nd press - set the punch out
6572 end = playhead_cursor->current_sample ();
6573 set_punch_range (tpl->start(), now, _("Auto Punch In/Out"));
6574 _session->config.set_punch_out(true);
6577 if (_session->config.get_punch_out()) {
6578 _session->config.set_punch_out(false);
6581 if (_session->config.get_punch_in()) {
6582 _session->config.set_punch_in(false);
6587 // third press - unset punch in/out and remove range
6588 _session->locations()->remove(tpl);
6595 Editor::set_session_extents_from_selection ()
6597 if (_session == 0) {
6601 samplepos_t start, end;
6602 if (!get_selection_extents (start, end))
6606 if ((loc = _session->locations()->session_range_location()) == 0) {
6607 _session->set_session_extents (start, end); // this will create a new session range; no need for UNDO
6609 XMLNode &before = loc->get_state();
6611 _session->set_session_extents (start, end);
6613 XMLNode &after = loc->get_state();
6615 begin_reversible_command (_("set session start/end from selection"));
6617 _session->add_command (new MementoCommand<Location>(*loc, &before, &after));
6619 commit_reversible_command ();
6622 _session->set_end_is_free (false);
6626 Editor::set_punch_start_from_edit_point ()
6630 MusicSample start (0, 0);
6631 samplepos_t end = max_samplepos;
6633 //use the existing punch end, if any
6634 Location* tpl = transport_punch_location();
6639 if ((_edit_point == EditAtPlayhead) && _session->transport_rolling()) {
6640 start.sample = _session->audible_sample();
6642 start.sample = get_preferred_edit_position();
6645 //if there's not already a sensible selection endpoint, go "forever"
6646 if (start.sample > end) {
6647 end = max_samplepos;
6650 set_punch_range (start.sample, end, _("set punch start from EP"));
6656 Editor::set_punch_end_from_edit_point ()
6660 samplepos_t start = 0;
6661 MusicSample end (max_samplepos, 0);
6663 //use the existing punch start, if any
6664 Location* tpl = transport_punch_location();
6666 start = tpl->start();
6669 if ((_edit_point == EditAtPlayhead) && _session->transport_rolling()) {
6670 end.sample = _session->audible_sample();
6672 end.sample = get_preferred_edit_position();
6675 set_punch_range (start, end.sample, _("set punch end from EP"));
6681 Editor::set_loop_start_from_edit_point ()
6685 MusicSample start (0, 0);
6686 samplepos_t end = max_samplepos;
6688 //use the existing loop end, if any
6689 Location* tpl = transport_loop_location();
6694 if ((_edit_point == EditAtPlayhead) && _session->transport_rolling()) {
6695 start.sample = _session->audible_sample();
6697 start.sample = get_preferred_edit_position();
6700 //if there's not already a sensible selection endpoint, go "forever"
6701 if (start.sample > end) {
6702 end = max_samplepos;
6705 set_loop_range (start.sample, end, _("set loop start from EP"));
6711 Editor::set_loop_end_from_edit_point ()
6715 samplepos_t start = 0;
6716 MusicSample end (max_samplepos, 0);
6718 //use the existing loop start, if any
6719 Location* tpl = transport_loop_location();
6721 start = tpl->start();
6724 if ((_edit_point == EditAtPlayhead) && _session->transport_rolling()) {
6725 end.sample = _session->audible_sample();
6727 end.sample = get_preferred_edit_position();
6730 set_loop_range (start, end.sample, _("set loop end from EP"));
6735 Editor::set_punch_from_region ()
6737 samplepos_t start, end;
6738 if (!get_selection_extents (start, end))
6741 set_punch_range (start, end, _("set punch range from region"));
6745 Editor::pitch_shift_region ()
6747 RegionSelection rs = get_regions_from_selection_and_entered ();
6749 RegionSelection audio_rs;
6750 for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
6751 if (dynamic_cast<AudioRegionView*> (*i)) {
6752 audio_rs.push_back (*i);
6756 if (audio_rs.empty()) {
6760 pitch_shift (audio_rs, 1.2);
6764 Editor::set_tempo_from_region ()
6766 RegionSelection rs = get_regions_from_selection_and_entered ();
6768 if (!_session || rs.empty()) {
6772 RegionView* rv = rs.front();
6774 define_one_bar (rv->region()->position(), rv->region()->last_sample() + 1);
6778 Editor::use_range_as_bar ()
6780 samplepos_t start, end;
6781 if (get_edit_op_range (start, end)) {
6782 define_one_bar (start, end);
6787 Editor::define_one_bar (samplepos_t start, samplepos_t end)
6789 samplepos_t length = end - start;
6791 const Meter& m (_session->tempo_map().meter_at_sample (start));
6793 /* length = 1 bar */
6795 /* We're going to deliver a constant tempo here,
6796 so we can use samples per beat to determine length.
6797 now we want samples per beat.
6798 we have samples per bar, and beats per bar, so ...
6801 /* XXXX METER MATH */
6803 double samples_per_beat = length / m.divisions_per_bar();
6805 /* beats per minute = */
6807 double beats_per_minute = (_session->sample_rate() * 60.0) / samples_per_beat;
6809 /* now decide whether to:
6811 (a) set global tempo
6812 (b) add a new tempo marker
6816 const TempoSection& t (_session->tempo_map().tempo_section_at_sample (start));
6818 bool do_global = false;
6820 if ((_session->tempo_map().n_tempos() == 1) && (_session->tempo_map().n_meters() == 1)) {
6822 /* only 1 tempo & 1 meter: ask if the user wants to set the tempo
6823 at the start, or create a new marker
6826 vector<string> options;
6827 options.push_back (_("Cancel"));
6828 options.push_back (_("Add new marker"));
6829 options.push_back (_("Set global tempo"));
6832 _("Define one bar"),
6833 _("Do you want to set the global tempo or add a new tempo marker?"),
6837 c.set_default_response (2);
6853 /* more than 1 tempo and/or meter section already, go ahead do the "usual":
6854 if the marker is at the region starter, change it, otherwise add
6859 begin_reversible_command (_("set tempo from region"));
6860 XMLNode& before (_session->tempo_map().get_state());
6863 _session->tempo_map().change_initial_tempo (beats_per_minute, t.note_type(), t.end_note_types_per_minute());
6864 } else if (t.sample() == start) {
6865 _session->tempo_map().change_existing_tempo_at (start, beats_per_minute, t.note_type(), t.end_note_types_per_minute());
6867 /* constant tempo */
6868 const Tempo tempo (beats_per_minute, t.note_type());
6869 _session->tempo_map().add_tempo (tempo, 0.0, start, AudioTime);
6872 XMLNode& after (_session->tempo_map().get_state());
6874 _session->add_command (new MementoCommand<TempoMap>(_session->tempo_map(), &before, &after));
6875 commit_reversible_command ();
6879 Editor::split_region_at_transients ()
6881 AnalysisFeatureList positions;
6883 RegionSelection rs = get_regions_from_selection_and_entered ();
6885 if (!_session || rs.empty()) {
6889 begin_reversible_command (_("split regions"));
6891 for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ) {
6893 RegionSelection::iterator tmp;
6898 boost::shared_ptr<AudioRegion> ar = boost::dynamic_pointer_cast<AudioRegion> ((*i)->region());
6901 ar->transients (positions);
6902 split_region_at_points ((*i)->region(), positions, true);
6909 commit_reversible_command ();
6914 Editor::split_region_at_points (boost::shared_ptr<Region> r, AnalysisFeatureList& positions, bool can_ferret, bool select_new)
6916 bool use_rhythmic_rodent = false;
6918 boost::shared_ptr<Playlist> pl = r->playlist();
6920 list<boost::shared_ptr<Region> > new_regions;
6926 if (positions.empty()) {
6930 if (positions.size() > 20 && can_ferret) {
6931 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);
6932 MessageDialog msg (msgstr,
6935 Gtk::BUTTONS_OK_CANCEL);
6938 msg.add_button (_("Call for the Ferret!"), RESPONSE_APPLY);
6939 msg.set_secondary_text (_("Press OK to continue with this split operation\nor ask the Ferret dialog to tune the analysis"));
6941 msg.set_secondary_text (_("Press OK to continue with this split operation"));
6944 msg.set_title (_("Excessive split?"));
6947 int response = msg.run();
6953 case RESPONSE_APPLY:
6954 use_rhythmic_rodent = true;
6961 if (use_rhythmic_rodent) {
6962 show_rhythm_ferret ();
6966 AnalysisFeatureList::const_iterator x;
6968 pl->clear_changes ();
6969 pl->clear_owned_changes ();
6971 x = positions.begin();
6973 if (x == positions.end()) {
6978 pl->remove_region (r);
6980 samplepos_t pos = 0;
6982 samplepos_t rstart = r->first_sample ();
6983 samplepos_t rend = r->last_sample ();
6985 while (x != positions.end()) {
6987 /* deal with positons that are out of scope of present region bounds */
6988 if (*x <= rstart || *x > rend) {
6993 /* file start = original start + how far we from the initial position ? */
6995 samplepos_t file_start = r->start() + pos;
6997 /* length = next position - current position */
6999 samplepos_t len = (*x) - pos - rstart;
7001 /* XXX we do we really want to allow even single-sample regions?
7002 * shouldn't we have some kind of lower limit on region size?
7011 if (RegionFactory::region_name (new_name, r->name())) {
7015 /* do NOT announce new regions 1 by one, just wait till they are all done */
7019 plist.add (ARDOUR::Properties::start, file_start);
7020 plist.add (ARDOUR::Properties::length, len);
7021 plist.add (ARDOUR::Properties::name, new_name);
7022 plist.add (ARDOUR::Properties::layer, 0);
7023 // TODO set transients_offset
7025 boost::shared_ptr<Region> nr = RegionFactory::create (r->sources(), plist, false);
7026 /* because we set annouce to false, manually add the new region to the
7029 RegionFactory::map_add (nr);
7031 pl->add_region (nr, rstart + pos);
7034 new_regions.push_front(nr);
7043 RegionFactory::region_name (new_name, r->name());
7045 /* Add the final region */
7048 plist.add (ARDOUR::Properties::start, r->start() + pos);
7049 plist.add (ARDOUR::Properties::length, r->last_sample() - (r->position() + pos) + 1);
7050 plist.add (ARDOUR::Properties::name, new_name);
7051 plist.add (ARDOUR::Properties::layer, 0);
7053 boost::shared_ptr<Region> nr = RegionFactory::create (r->sources(), plist, false);
7054 /* because we set annouce to false, manually add the new region to the
7057 RegionFactory::map_add (nr);
7058 pl->add_region (nr, r->position() + pos);
7061 new_regions.push_front(nr);
7066 /* We might have removed regions, which alters other regions' layering_index,
7067 so we need to do a recursive diff here.
7069 vector<Command*> cmds;
7071 _session->add_commands (cmds);
7073 _session->add_command (new StatefulDiffCommand (pl));
7077 for (list<boost::shared_ptr<Region> >::iterator i = new_regions.begin(); i != new_regions.end(); ++i){
7078 set_selected_regionview_from_region_list ((*i), Selection::Add);
7084 Editor::place_transient()
7090 RegionSelection rs = get_regions_from_selection_and_edit_point ();
7096 samplepos_t where = get_preferred_edit_position();
7098 begin_reversible_command (_("place transient"));
7100 for (RegionSelection::iterator r = rs.begin(); r != rs.end(); ++r) {
7101 (*r)->region()->add_transient(where);
7104 commit_reversible_command ();
7108 Editor::remove_transient(ArdourCanvas::Item* item)
7114 ArdourCanvas::Line* _line = reinterpret_cast<ArdourCanvas::Line*> (item);
7117 AudioRegionView* _arv = reinterpret_cast<AudioRegionView*> (item->get_data ("regionview"));
7118 _arv->remove_transient (*(float*) _line->get_data ("position"));
7122 Editor::snap_regions_to_grid ()
7124 list <boost::shared_ptr<Playlist > > used_playlists;
7126 RegionSelection rs = get_regions_from_selection_and_entered ();
7128 if (!_session || rs.empty()) {
7132 begin_reversible_command (_("snap regions to grid"));
7134 for (RegionSelection::iterator r = rs.begin(); r != rs.end(); ++r) {
7136 boost::shared_ptr<Playlist> pl = (*r)->region()->playlist();
7138 if (!pl->frozen()) {
7139 /* we haven't seen this playlist before */
7141 /* remember used playlists so we can thaw them later */
7142 used_playlists.push_back(pl);
7145 (*r)->region()->clear_changes ();
7147 MusicSample start ((*r)->region()->first_sample (), 0);
7148 snap_to (start, RoundNearest, SnapToGrid);
7149 (*r)->region()->set_position (start.sample, start.division);
7150 _session->add_command(new StatefulDiffCommand ((*r)->region()));
7153 while (used_playlists.size() > 0) {
7154 list <boost::shared_ptr<Playlist > >::iterator i = used_playlists.begin();
7156 used_playlists.pop_front();
7159 commit_reversible_command ();
7163 Editor::close_region_gaps ()
7165 list <boost::shared_ptr<Playlist > > used_playlists;
7167 RegionSelection rs = get_regions_from_selection_and_entered ();
7169 if (!_session || rs.empty()) {
7173 Dialog dialog (_("Close Region Gaps"));
7176 table.set_spacings (12);
7177 table.set_border_width (12);
7178 Label* l = manage (left_aligned_label (_("Crossfade length")));
7179 table.attach (*l, 0, 1, 0, 1);
7181 SpinButton spin_crossfade (1, 0);
7182 spin_crossfade.set_range (0, 15);
7183 spin_crossfade.set_increments (1, 1);
7184 spin_crossfade.set_value (5);
7185 table.attach (spin_crossfade, 1, 2, 0, 1);
7187 table.attach (*manage (new Label (_("ms"))), 2, 3, 0, 1);
7189 l = manage (left_aligned_label (_("Pull-back length")));
7190 table.attach (*l, 0, 1, 1, 2);
7192 SpinButton spin_pullback (1, 0);
7193 spin_pullback.set_range (0, 100);
7194 spin_pullback.set_increments (1, 1);
7195 spin_pullback.set_value(30);
7196 table.attach (spin_pullback, 1, 2, 1, 2);
7198 table.attach (*manage (new Label (_("ms"))), 2, 3, 1, 2);
7200 dialog.get_vbox()->pack_start (table);
7201 dialog.add_button (Stock::CANCEL, RESPONSE_CANCEL);
7202 dialog.add_button (_("Ok"), RESPONSE_ACCEPT);
7205 if (dialog.run () == RESPONSE_CANCEL) {
7209 samplepos_t crossfade_len = spin_crossfade.get_value();
7210 samplepos_t pull_back_samples = spin_pullback.get_value();
7212 crossfade_len = lrintf (crossfade_len * _session->sample_rate()/1000);
7213 pull_back_samples = lrintf (pull_back_samples * _session->sample_rate()/1000);
7215 /* Iterate over the region list and make adjacent regions overlap by crossfade_len_ms */
7217 begin_reversible_command (_("close region gaps"));
7220 boost::shared_ptr<Region> last_region;
7222 rs.sort_by_position_and_track();
7224 for (RegionSelection::iterator r = rs.begin(); r != rs.end(); ++r) {
7226 boost::shared_ptr<Playlist> pl = (*r)->region()->playlist();
7228 if (!pl->frozen()) {
7229 /* we haven't seen this playlist before */
7231 /* remember used playlists so we can thaw them later */
7232 used_playlists.push_back(pl);
7236 samplepos_t position = (*r)->region()->position();
7238 if (idx == 0 || position < last_region->position()){
7239 last_region = (*r)->region();
7244 (*r)->region()->clear_changes ();
7245 (*r)->region()->trim_front((position - pull_back_samples));
7247 last_region->clear_changes ();
7248 last_region->trim_end ((position - pull_back_samples + crossfade_len));
7250 _session->add_command (new StatefulDiffCommand ((*r)->region()));
7251 _session->add_command (new StatefulDiffCommand (last_region));
7253 last_region = (*r)->region();
7257 while (used_playlists.size() > 0) {
7258 list <boost::shared_ptr<Playlist > >::iterator i = used_playlists.begin();
7260 used_playlists.pop_front();
7263 commit_reversible_command ();
7267 Editor::tab_to_transient (bool forward)
7269 AnalysisFeatureList positions;
7271 RegionSelection rs = get_regions_from_selection_and_entered ();
7277 samplepos_t pos = _session->audible_sample ();
7279 if (!selection->tracks.empty()) {
7281 /* don't waste time searching for transients in duplicate playlists.
7284 TrackViewList ts = selection->tracks.filter_to_unique_playlists ();
7286 for (TrackViewList::iterator t = ts.begin(); t != ts.end(); ++t) {
7288 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (*t);
7291 boost::shared_ptr<Track> tr = rtv->track();
7293 boost::shared_ptr<Playlist> pl = tr->playlist ();
7295 samplepos_t result = pl->find_next_transient (pos, forward ? 1 : -1);
7298 positions.push_back (result);
7311 for (RegionSelection::iterator r = rs.begin(); r != rs.end(); ++r) {
7312 (*r)->region()->get_transients (positions);
7316 TransientDetector::cleanup_transients (positions, _session->sample_rate(), 3.0);
7319 AnalysisFeatureList::iterator x;
7321 for (x = positions.begin(); x != positions.end(); ++x) {
7327 if (x != positions.end ()) {
7328 _session->request_locate (*x);
7332 AnalysisFeatureList::reverse_iterator x;
7334 for (x = positions.rbegin(); x != positions.rend(); ++x) {
7340 if (x != positions.rend ()) {
7341 _session->request_locate (*x);
7347 Editor::playhead_forward_to_grid ()
7353 MusicSample pos (playhead_cursor->current_sample (), 0);
7355 if (pos.sample < max_samplepos - 1) {
7357 snap_to_internal (pos, RoundUpAlways, SnapToGrid, false, true);
7358 _session->request_locate (pos.sample);
7364 Editor::playhead_backward_to_grid ()
7370 MusicSample pos (playhead_cursor->current_sample (), 0);
7372 if (pos.sample > 2) {
7374 snap_to_internal (pos, RoundDownAlways, SnapToGrid, false, true);
7375 _session->request_locate (pos.sample);
7380 Editor::set_track_height (Height h)
7382 TrackSelection& ts (selection->tracks);
7384 for (TrackSelection::iterator x = ts.begin(); x != ts.end(); ++x) {
7385 (*x)->set_height_enum (h);
7390 Editor::toggle_tracks_active ()
7392 TrackSelection& ts (selection->tracks);
7394 bool target = false;
7400 for (TrackSelection::iterator x = ts.begin(); x != ts.end(); ++x) {
7401 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*>(*x);
7405 target = !rtv->_route->active();
7408 rtv->_route->set_active (target, this);
7414 Editor::remove_tracks ()
7416 /* this will delete GUI objects that may be the subject of an event
7417 handler in which this method is called. Defer actual deletion to the
7418 next idle callback, when all event handling is finished.
7420 Glib::signal_idle().connect (sigc::mem_fun (*this, &Editor::idle_remove_tracks));
7424 Editor::idle_remove_tracks ()
7426 Session::StateProtector sp (_session);
7428 return false; /* do not call again */
7432 Editor::_remove_tracks ()
7434 TrackSelection& ts (selection->tracks);
7440 vector<string> choices;
7445 const char* trackstr;
7448 vector<boost::shared_ptr<Route> > routes;
7449 vector<boost::shared_ptr<VCA> > vcas;
7450 bool special_bus = false;
7452 for (TrackSelection::iterator x = ts.begin(); x != ts.end(); ++x) {
7453 VCATimeAxisView* vtv = dynamic_cast<VCATimeAxisView*> (*x);
7455 vcas.push_back (vtv->vca());
7459 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (*x);
7463 if (rtv->is_track()) {
7468 routes.push_back (rtv->_route);
7470 if (rtv->route()->is_master() || rtv->route()->is_monitor()) {
7475 if (special_bus && !Config->get_allow_special_bus_removal()) {
7476 MessageDialog msg (_("That would be bad news ...."),
7480 msg.set_secondary_text (string_compose (_(
7481 "Removing the master or monitor bus is such a bad idea\n\
7482 that %1 is not going to allow it.\n\
7484 If you really want to do this sort of thing\n\
7485 edit your ardour.rc file to set the\n\
7486 \"allow-special-bus-removal\" option to be \"yes\""), PROGRAM_NAME));
7493 if (ntracks + nbusses + nvcas == 0) {
7499 trackstr = P_("track", "tracks", ntracks);
7500 busstr = P_("bus", "busses", nbusses);
7501 vcastr = P_("VCA", "VCAs", nvcas);
7503 if (ntracks > 0 && nbusses > 0 && nvcas > 0) {
7504 title = _("Remove various strips");
7505 prompt = string_compose (_("Do you really want to remove %1 %2, %3 %4 and %5 %6?"),
7506 ntracks, trackstr, nbusses, busstr, nvcas, vcastr);
7508 else if (ntracks > 0 && nbusses > 0) {
7509 title = string_compose (_("Remove %1 and %2"), trackstr, busstr);
7510 prompt = string_compose (_("Do you really want to remove %1 %2 and %3 %4?"),
7511 ntracks, trackstr, nbusses, busstr);
7513 else if (ntracks > 0 && nvcas > 0) {
7514 title = string_compose (_("Remove %1 and %2"), trackstr, vcastr);
7515 prompt = string_compose (_("Do you really want to remove %1 %2 and %3 %4?"),
7516 ntracks, trackstr, nvcas, vcastr);
7518 else if (nbusses > 0 && nvcas > 0) {
7519 title = string_compose (_("Remove %1 and %2"), busstr, vcastr);
7520 prompt = string_compose (_("Do you really want to remove %1 %2 and %3 %4?"),
7521 nbusses, busstr, nvcas, vcastr);
7523 else if (ntracks > 0) {
7524 title = string_compose (_("Remove %1"), trackstr);
7525 prompt = string_compose (_("Do you really want to remove %1 %2?"),
7528 else if (nbusses > 0) {
7529 title = string_compose (_("Remove %1"), busstr);
7530 prompt = string_compose (_("Do you really want to remove %1 %2?"),
7533 else if (nvcas > 0) {
7534 title = string_compose (_("Remove %1"), vcastr);
7535 prompt = string_compose (_("Do you really want to remove %1 %2?"),
7543 prompt += "\n" + string_compose ("(You may also lose the playlists associated with the %1)", trackstr) + "\n";
7546 prompt += "\n" + string(_("This action cannot be undone, and the session file will be overwritten!"));
7548 choices.push_back (_("No, do nothing."));
7549 if (ntracks + nbusses + nvcas > 1) {
7550 choices.push_back (_("Yes, remove them."));
7552 choices.push_back (_("Yes, remove it."));
7555 Choice prompter (title, prompt, choices);
7557 if (prompter.run () != 1) {
7561 if (current_mixer_strip && routes.size () > 1 && std::find (routes.begin(), routes.end(), current_mixer_strip->route()) != routes.end ()) {
7562 /* Route deletion calls Editor::timeaxisview_deleted() iteratively (for each deleted
7563 * route). If the deleted route is currently displayed in the Editor-Mixer (highly
7564 * likely because deletion requires selection) this will call
7565 * Editor::set_selected_mixer_strip () which is expensive (MixerStrip::set_route()).
7566 * It's likewise likely that the route that has just been displayed in the
7567 * Editor-Mixer will be next in line for deletion.
7569 * So simply switch to the master-bus (if present)
7571 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
7572 if ((*i)->stripable ()->is_master ()) {
7573 set_selected_mixer_strip (*(*i));
7580 PresentationInfo::ChangeSuspender cs;
7581 DisplaySuspender ds;
7583 boost::shared_ptr<RouteList> rl (new RouteList);
7584 for (vector<boost::shared_ptr<Route> >::iterator x = routes.begin(); x != routes.end(); ++x) {
7587 _session->remove_routes (rl);
7589 for (vector<boost::shared_ptr<VCA> >::iterator x = vcas.begin(); x != vcas.end(); ++x) {
7590 _session->vca_manager().remove_vca (*x);
7594 /* TrackSelection and RouteList leave scope,
7595 * destructors are called,
7596 * diskstream drops references, save_state is called (again for every track)
7601 Editor::do_insert_time ()
7603 if (selection->tracks.empty()) {
7604 MessageDialog msg (_("You must first select some tracks to Insert Time."),
7605 true, MESSAGE_INFO, BUTTONS_OK, true);
7606 msg.set_position (WIN_POS_MOUSE);
7611 if (Config->get_edit_mode() == Lock) {
7612 MessageDialog msg (_("You cannot insert time in Lock Edit mode."),
7613 true, MESSAGE_INFO, BUTTONS_OK, true);
7614 msg.set_position (WIN_POS_MOUSE);
7619 InsertRemoveTimeDialog d (*this);
7620 int response = d.run ();
7622 if (response != RESPONSE_OK) {
7626 if (d.distance() == 0) {
7633 d.intersected_region_action (),
7637 d.move_glued_markers(),
7638 d.move_locked_markers(),
7644 Editor::insert_time (
7645 samplepos_t pos, samplecnt_t samples, InsertTimeOption opt,
7646 bool all_playlists, bool ignore_music_glue, bool markers_too, bool glued_markers_too, bool locked_markers_too, bool tempo_too
7650 if (Config->get_edit_mode() == Lock) {
7653 bool in_command = false;
7655 TrackViewList ts = selection->tracks.filter_to_unique_playlists ();
7657 for (TrackViewList::iterator x = ts.begin(); x != ts.end(); ++x) {
7661 /* don't operate on any playlist more than once, which could
7662 * happen if "all playlists" is enabled, but there is more
7663 * than 1 track using playlists "from" a given track.
7666 set<boost::shared_ptr<Playlist> > pl;
7668 if (all_playlists) {
7669 RouteTimeAxisView* rtav = dynamic_cast<RouteTimeAxisView*> (*x);
7670 if (rtav && rtav->track ()) {
7671 vector<boost::shared_ptr<Playlist> > all = _session->playlists->playlists_for_track (rtav->track ());
7672 for (vector<boost::shared_ptr<Playlist> >::iterator p = all.begin(); p != all.end(); ++p) {
7677 if ((*x)->playlist ()) {
7678 pl.insert ((*x)->playlist ());
7682 for (set<boost::shared_ptr<Playlist> >::iterator i = pl.begin(); i != pl.end(); ++i) {
7684 (*i)->clear_changes ();
7685 (*i)->clear_owned_changes ();
7688 begin_reversible_command (_("insert time"));
7692 if (opt == SplitIntersected) {
7693 /* non musical split */
7694 (*i)->split (MusicSample (pos, 0));
7697 (*i)->shift (pos, samples, (opt == MoveIntersected), ignore_music_glue);
7699 vector<Command*> cmds;
7701 _session->add_commands (cmds);
7703 _session->add_command (new StatefulDiffCommand (*i));
7707 RouteTimeAxisView* rtav = dynamic_cast<RouteTimeAxisView*> (*x);
7710 begin_reversible_command (_("insert time"));
7713 rtav->route ()->shift (pos, samples);
7720 const int32_t divisions = get_grid_music_divisions (0);
7721 XMLNode& before (_session->locations()->get_state());
7722 Locations::LocationList copy (_session->locations()->list());
7724 for (Locations::LocationList::iterator i = copy.begin(); i != copy.end(); ++i) {
7726 Locations::LocationList::const_iterator tmp;
7728 if ((*i)->position_lock_style() == AudioTime || glued_markers_too) {
7729 bool const was_locked = (*i)->locked ();
7730 if (locked_markers_too) {
7734 if ((*i)->start() >= pos) {
7735 // move end first, in case we're moving by more than the length of the range
7736 if (!(*i)->is_mark()) {
7737 (*i)->set_end ((*i)->end() + samples, false, true, divisions);
7739 (*i)->set_start ((*i)->start() + samples, false, true, divisions);
7751 begin_reversible_command (_("insert time"));
7754 XMLNode& after (_session->locations()->get_state());
7755 _session->add_command (new MementoCommand<Locations>(*_session->locations(), &before, &after));
7761 begin_reversible_command (_("insert time"));
7764 XMLNode& before (_session->tempo_map().get_state());
7765 _session->tempo_map().insert_time (pos, samples);
7766 XMLNode& after (_session->tempo_map().get_state());
7767 _session->add_command (new MementoCommand<TempoMap>(_session->tempo_map(), &before, &after));
7771 commit_reversible_command ();
7776 Editor::do_remove_time ()
7778 if (selection->tracks.empty()) {
7779 MessageDialog msg (_("You must first select some tracks to Remove Time."),
7780 true, MESSAGE_INFO, BUTTONS_OK, true);
7781 msg.set_position (WIN_POS_MOUSE);
7786 if (Config->get_edit_mode() == Lock) {
7787 MessageDialog msg (_("You cannot remove time in Lock Edit mode."),
7788 true, MESSAGE_INFO, BUTTONS_OK, true);
7789 msg.set_position (WIN_POS_MOUSE);
7794 InsertRemoveTimeDialog d (*this, true);
7796 int response = d.run ();
7798 if (response != RESPONSE_OK) {
7802 samplecnt_t distance = d.distance();
7804 if (distance == 0) {
7814 d.move_glued_markers(),
7815 d.move_locked_markers(),
7821 Editor::remove_time (samplepos_t pos, samplecnt_t samples, InsertTimeOption opt,
7822 bool ignore_music_glue, bool markers_too, bool glued_markers_too, bool locked_markers_too, bool tempo_too)
7824 if (Config->get_edit_mode() == Lock) {
7825 error << (_("Cannot insert or delete time when in Lock edit.")) << endmsg;
7828 bool in_command = false;
7830 for (TrackSelection::iterator x = selection->tracks.begin(); x != selection->tracks.end(); ++x) {
7832 boost::shared_ptr<Playlist> pl = (*x)->playlist();
7836 XMLNode &before = pl->get_state();
7839 begin_reversible_command (_("remove time"));
7843 std::list<AudioRange> rl;
7844 AudioRange ar(pos, pos+samples, 0);
7847 pl->shift (pos, -samples, true, ignore_music_glue);
7849 XMLNode &after = pl->get_state();
7851 _session->add_command (new MementoCommand<Playlist> (*pl, &before, &after));
7855 RouteTimeAxisView* rtav = dynamic_cast<RouteTimeAxisView*> (*x);
7858 begin_reversible_command (_("remove time"));
7861 rtav->route ()->shift (pos, -samples);
7865 const int32_t divisions = get_grid_music_divisions (0);
7866 std::list<Location*> loc_kill_list;
7871 XMLNode& before (_session->locations()->get_state());
7872 Locations::LocationList copy (_session->locations()->list());
7874 for (Locations::LocationList::iterator i = copy.begin(); i != copy.end(); ++i) {
7875 if ((*i)->position_lock_style() == AudioTime || glued_markers_too) {
7877 bool const was_locked = (*i)->locked ();
7878 if (locked_markers_too) {
7882 if (!(*i)->is_mark()) { // it's a range; have to handle both start and end
7883 if ((*i)->end() >= pos
7884 && (*i)->end() < pos+samples
7885 && (*i)->start() >= pos
7886 && (*i)->end() < pos+samples) { // range is completely enclosed; kill it
7888 loc_kill_list.push_back(*i);
7889 } else { // only start or end is included, try to do the right thing
7890 // move start before moving end, to avoid trying to move the end to before the start
7891 // if we're removing more time than the length of the range
7892 if ((*i)->start() >= pos && (*i)->start() < pos+samples) {
7893 // start is within cut
7894 (*i)->set_start (pos, false, true,divisions); // bring the start marker to the beginning of the cut
7896 } else if ((*i)->start() >= pos+samples) {
7897 // start (and thus entire range) lies beyond end of cut
7898 (*i)->set_start ((*i)->start() - samples, false, true, divisions); // slip the start marker back
7901 if ((*i)->end() >= pos && (*i)->end() < pos+samples) {
7902 // end is inside cut
7903 (*i)->set_end (pos, false, true, divisions); // bring the end to the cut
7905 } else if ((*i)->end() >= pos+samples) {
7906 // end is beyond end of cut
7907 (*i)->set_end ((*i)->end() - samples, false, true, divisions); // slip the end marker back
7912 } else if ((*i)->start() >= pos && (*i)->start() < pos+samples) {
7913 loc_kill_list.push_back(*i);
7915 } else if ((*i)->start() >= pos) {
7916 (*i)->set_start ((*i)->start() -samples, false, true, divisions);
7926 for (list<Location*>::iterator i = loc_kill_list.begin(); i != loc_kill_list.end(); ++i) {
7927 _session->locations()->remove (*i);
7932 begin_reversible_command (_("remove time"));
7935 XMLNode& after (_session->locations()->get_state());
7936 _session->add_command (new MementoCommand<Locations>(*_session->locations(), &before, &after));
7941 XMLNode& before (_session->tempo_map().get_state());
7943 if (_session->tempo_map().remove_time (pos, samples)) {
7945 begin_reversible_command (_("remove time"));
7948 XMLNode& after (_session->tempo_map().get_state());
7949 _session->add_command (new MementoCommand<TempoMap>(_session->tempo_map(), &before, &after));
7954 commit_reversible_command ();
7959 Editor::fit_selection ()
7961 if (!selection->tracks.empty()) {
7962 fit_tracks (selection->tracks);
7966 /* no selected tracks - use tracks with selected regions */
7968 if (!selection->regions.empty()) {
7969 for (RegionSelection::iterator r = selection->regions.begin(); r != selection->regions.end(); ++r) {
7970 tvl.push_back (&(*r)->get_time_axis_view ());
7976 } else if (internal_editing()) {
7977 /* no selected tracks, or regions, but in internal edit mode, so follow the mouse and use
7980 if (entered_track) {
7981 tvl.push_back (entered_track);
7989 Editor::fit_tracks (TrackViewList & tracks)
7991 if (tracks.empty()) {
7995 uint32_t child_heights = 0;
7996 int visible_tracks = 0;
7998 for (TrackSelection::iterator t = tracks.begin(); t != tracks.end(); ++t) {
8000 if (!(*t)->marked_for_display()) {
8004 child_heights += (*t)->effective_height() - (*t)->current_height();
8008 /* compute the per-track height from:
8010 * total canvas visible height
8011 * - height that will be taken by visible children of selected tracks
8012 * - height of the ruler/hscroll area
8014 uint32_t h = (uint32_t) floor ((trackviews_height() - child_heights) / visible_tracks);
8015 double first_y_pos = DBL_MAX;
8017 if (h < TimeAxisView::preset_height (HeightSmall)) {
8018 MessageDialog msg (_("There are too many tracks to fit in the current window"));
8019 /* too small to be displayed */
8023 undo_visual_stack.push_back (current_visual_state (true));
8024 PBD::Unwinder<bool> nsv (no_save_visual, true);
8026 /* build a list of all tracks, including children */
8029 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
8031 TimeAxisView::Children c = (*i)->get_child_list ();
8032 for (TimeAxisView::Children::iterator j = c.begin(); j != c.end(); ++j) {
8033 all.push_back (j->get());
8038 // find selection range.
8039 // if someone knows how to user TrackViewList::iterator for this
8041 int selected_top = -1;
8042 int selected_bottom = -1;
8044 for (TrackViewList::iterator t = all.begin(); t != all.end(); ++t, ++i) {
8045 if ((*t)->marked_for_display ()) {
8046 if (tracks.contains(*t)) {
8047 if (selected_top == -1) {
8050 selected_bottom = i;
8056 for (TrackViewList::iterator t = all.begin(); t != all.end(); ++t, ++i) {
8057 if ((*t)->marked_for_display ()) {
8058 if (tracks.contains(*t)) {
8059 (*t)->set_height (h);
8060 first_y_pos = std::min ((*t)->y_position (), first_y_pos);
8062 if (i > selected_top && i < selected_bottom) {
8063 hide_track_in_display (*t);
8070 set the controls_layout height now, because waiting for its size
8071 request signal handler will cause the vertical adjustment setting to fail
8074 controls_layout.property_height () = _full_canvas_height;
8075 vertical_adjustment.set_value (first_y_pos);
8077 redo_visual_stack.push_back (current_visual_state (true));
8079 visible_tracks_selector.set_text (_("Sel"));
8083 Editor::save_visual_state (uint32_t n)
8085 while (visual_states.size() <= n) {
8086 visual_states.push_back (0);
8089 if (visual_states[n] != 0) {
8090 delete visual_states[n];
8093 visual_states[n] = current_visual_state (true);
8098 Editor::goto_visual_state (uint32_t n)
8100 if (visual_states.size() <= n) {
8104 if (visual_states[n] == 0) {
8108 use_visual_state (*visual_states[n]);
8112 Editor::start_visual_state_op (uint32_t n)
8114 save_visual_state (n);
8116 PopUp* pup = new PopUp (WIN_POS_MOUSE, 1000, true);
8118 snprintf (buf, sizeof (buf), _("Saved view %u"), n+1);
8119 pup->set_text (buf);
8124 Editor::cancel_visual_state_op (uint32_t n)
8126 goto_visual_state (n);
8130 Editor::toggle_region_mute ()
8132 if (_ignore_region_action) {
8136 RegionSelection rs = get_regions_from_selection_and_entered ();
8142 if (rs.size() > 1) {
8143 begin_reversible_command (_("mute regions"));
8145 begin_reversible_command (_("mute region"));
8148 for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
8150 (*i)->region()->playlist()->clear_changes ();
8151 (*i)->region()->set_muted (!(*i)->region()->muted ());
8152 _session->add_command (new StatefulDiffCommand ((*i)->region()));
8156 commit_reversible_command ();
8160 Editor::combine_regions ()
8162 /* foreach track with selected regions, take all selected regions
8163 and join them into a new region containing the subregions (as a
8167 typedef set<RouteTimeAxisView*> RTVS;
8170 if (selection->regions.empty()) {
8174 for (RegionSelection::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
8175 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*>(&(*i)->get_time_axis_view());
8178 tracks.insert (rtv);
8182 begin_reversible_command (_("combine regions"));
8184 vector<RegionView*> new_selection;
8186 for (RTVS::iterator i = tracks.begin(); i != tracks.end(); ++i) {
8189 if ((rv = (*i)->combine_regions ()) != 0) {
8190 new_selection.push_back (rv);
8194 selection->clear_regions ();
8195 for (vector<RegionView*>::iterator i = new_selection.begin(); i != new_selection.end(); ++i) {
8196 selection->add (*i);
8199 commit_reversible_command ();
8203 Editor::uncombine_regions ()
8205 typedef set<RouteTimeAxisView*> RTVS;
8208 if (selection->regions.empty()) {
8212 for (RegionSelection::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
8213 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*>(&(*i)->get_time_axis_view());
8216 tracks.insert (rtv);
8220 begin_reversible_command (_("uncombine regions"));
8222 for (RTVS::iterator i = tracks.begin(); i != tracks.end(); ++i) {
8223 (*i)->uncombine_regions ();
8226 commit_reversible_command ();
8230 Editor::toggle_midi_input_active (bool flip_others)
8233 boost::shared_ptr<RouteList> rl (new RouteList);
8235 for (TrackSelection::iterator i = selection->tracks.begin(); i != selection->tracks.end(); ++i) {
8236 RouteTimeAxisView *rtav = dynamic_cast<RouteTimeAxisView *>(*i);
8242 boost::shared_ptr<MidiTrack> mt = rtav->midi_track();
8245 rl->push_back (rtav->route());
8246 onoff = !mt->input_active();
8250 _session->set_exclusive_input_active (rl, onoff, flip_others);
8253 static bool ok_fine (GdkEventAny*) { return true; }
8259 lock_dialog = new Gtk::Dialog (string_compose (_("%1: Locked"), PROGRAM_NAME), true);
8261 Gtk::Image* padlock = manage (new Gtk::Image (ARDOUR_UI_UTILS::get_icon ("padlock_closed")));
8262 lock_dialog->get_vbox()->pack_start (*padlock);
8263 lock_dialog->signal_delete_event ().connect (sigc::ptr_fun (ok_fine));
8265 ArdourButton* b = manage (new ArdourButton);
8266 b->set_name ("lock button");
8267 b->set_text (_("Click to unlock"));
8268 b->signal_clicked.connect (sigc::mem_fun (*this, &Editor::unlock));
8269 lock_dialog->get_vbox()->pack_start (*b);
8271 lock_dialog->get_vbox()->show_all ();
8272 lock_dialog->set_size_request (200, 200);
8275 delete _main_menu_disabler;
8276 _main_menu_disabler = new MainMenuDisabler;
8278 lock_dialog->present ();
8280 lock_dialog->get_window()->set_decorations (Gdk::WMDecoration (0));
8286 lock_dialog->hide ();
8288 delete _main_menu_disabler;
8289 _main_menu_disabler = 0;
8291 if (UIConfiguration::instance().get_lock_gui_after_seconds()) {
8292 start_lock_event_timing ();
8297 Editor::bring_in_callback (Gtk::Label* label, uint32_t n, uint32_t total, string name)
8299 Gtkmm2ext::UI::instance()->call_slot (invalidator (*this), boost::bind (&Editor::update_bring_in_message, this, label, n, total, name));
8303 Editor::update_bring_in_message (Gtk::Label* label, uint32_t n, uint32_t total, string name)
8305 Timers::TimerSuspender t;
8306 label->set_text (string_compose ("Copying %1, %2 of %3", name, n, total));
8307 Gtkmm2ext::UI::instance()->flush_pending (1);
8311 Editor::bring_all_sources_into_session ()
8318 ArdourDialog w (_("Moving embedded files into session folder"));
8319 w.get_vbox()->pack_start (msg);
8322 /* flush all pending GUI events because we're about to start copying
8326 Timers::TimerSuspender t;
8327 Gtkmm2ext::UI::instance()->flush_pending (3);
8331 _session->bring_all_sources_into_session (boost::bind (&Editor::bring_in_callback, this, &msg, _1, _2, _3));