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 RegionSelection sel = regions; // clear (below) may clear the argument list if its the current region selection
4873 RegionSelection foo;
4875 samplepos_t const start_sample = regions.start ();
4876 samplepos_t const end_sample = regions.end_sample ();
4877 samplecnt_t const gap = end_sample - start_sample + 1;
4879 begin_reversible_command (Operations::duplicate_region);
4881 selection->clear_regions ();
4883 for (RegionSelection::iterator i = sel.begin(); i != sel.end(); ++i) {
4885 boost::shared_ptr<Region> r ((*i)->region());
4887 TimeAxisView& tv = (*i)->get_time_axis_view();
4888 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (&tv);
4889 latest_regionviews.clear ();
4890 sigc::connection c = rtv->view()->RegionViewAdded.connect (sigc::mem_fun(*this, &Editor::collect_new_region_view));
4892 samplepos_t const position = end_sample + (r->first_sample() - start_sample + 1);
4893 playlist = (*i)->region()->playlist();
4894 playlist->clear_changes ();
4895 playlist->duplicate (r, position, gap, times);
4896 _session->add_command(new StatefulDiffCommand (playlist));
4900 foo.insert (foo.end(), latest_regionviews.begin(), latest_regionviews.end());
4904 selection->set (foo);
4907 commit_reversible_command ();
4911 Editor::duplicate_selection (float times)
4913 if (selection->time.empty() || selection->tracks.empty()) {
4917 boost::shared_ptr<Playlist> playlist;
4919 TrackViewList ts = selection->tracks.filter_to_unique_playlists ();
4921 bool in_command = false;
4923 for (TrackViewList::iterator i = ts.begin(); i != ts.end(); ++i) {
4924 if ((playlist = (*i)->playlist()) == 0) {
4927 playlist->clear_changes ();
4929 if (clicked_selection) {
4930 playlist->duplicate_range (selection->time[clicked_selection], times);
4932 playlist->duplicate_ranges (selection->time, times);
4936 begin_reversible_command (_("duplicate range selection"));
4939 _session->add_command (new StatefulDiffCommand (playlist));
4944 if (times == 1.0f) {
4945 // now "move" range selection to after the current range selection
4946 samplecnt_t distance = 0;
4948 if (clicked_selection) {
4950 selection->time[clicked_selection].end - selection->time[clicked_selection].start;
4952 distance = selection->time.end_sample () - selection->time.start ();
4955 selection->move_time (distance);
4957 commit_reversible_command ();
4961 /** Reset all selected points to the relevant default value */
4963 Editor::reset_point_selection ()
4965 for (PointSelection::iterator i = selection->points.begin(); i != selection->points.end(); ++i) {
4966 ARDOUR::AutomationList::iterator j = (*i)->model ();
4967 (*j)->value = (*i)->line().the_list()->descriptor ().normal;
4972 Editor::center_playhead ()
4974 float const page = _visible_canvas_width * samples_per_pixel;
4975 center_screen_internal (playhead_cursor->current_sample (), page);
4979 Editor::center_edit_point ()
4981 float const page = _visible_canvas_width * samples_per_pixel;
4982 center_screen_internal (get_preferred_edit_position(), page);
4985 /** Caller must begin and commit a reversible command */
4987 Editor::clear_playlist (boost::shared_ptr<Playlist> playlist)
4989 playlist->clear_changes ();
4991 _session->add_command (new StatefulDiffCommand (playlist));
4995 Editor::nudge_track (bool use_edit, bool forwards)
4997 boost::shared_ptr<Playlist> playlist;
4998 samplepos_t distance;
4999 samplepos_t next_distance;
5003 start = get_preferred_edit_position();
5008 if ((distance = get_nudge_distance (start, next_distance)) == 0) {
5012 if (selection->tracks.empty()) {
5016 TrackViewList ts = selection->tracks.filter_to_unique_playlists ();
5017 bool in_command = false;
5019 for (TrackViewList::iterator i = ts.begin(); i != ts.end(); ++i) {
5021 if ((playlist = (*i)->playlist()) == 0) {
5025 playlist->clear_changes ();
5026 playlist->clear_owned_changes ();
5028 playlist->nudge_after (start, distance, forwards);
5031 begin_reversible_command (_("nudge track"));
5034 vector<Command*> cmds;
5036 playlist->rdiff (cmds);
5037 _session->add_commands (cmds);
5039 _session->add_command (new StatefulDiffCommand (playlist));
5043 commit_reversible_command ();
5048 Editor::remove_last_capture ()
5050 vector<string> choices;
5057 if (Config->get_verify_remove_last_capture()) {
5058 prompt = _("Do you really want to destroy the last capture?"
5059 "\n(This is destructive and cannot be undone)");
5061 choices.push_back (_("No, do nothing."));
5062 choices.push_back (_("Yes, destroy it."));
5064 Choice prompter (_("Destroy last capture"), prompt, choices);
5066 if (prompter.run () == 1) {
5067 _session->remove_last_capture ();
5068 _regions->redisplay ();
5072 _session->remove_last_capture();
5073 _regions->redisplay ();
5078 Editor::normalize_region ()
5084 RegionSelection rs = get_regions_from_selection_and_entered ();
5090 NormalizeDialog dialog (rs.size() > 1);
5092 if (dialog.run () != RESPONSE_ACCEPT) {
5096 CursorContext::Handle cursor_ctx = CursorContext::create(*this, _cursors->wait);
5099 /* XXX: should really only count audio regions here */
5100 int const regions = rs.size ();
5102 /* Make a list of the selected audio regions' maximum amplitudes, and also
5103 obtain the maximum amplitude of them all.
5105 list<double> max_amps;
5106 list<double> rms_vals;
5109 bool use_rms = dialog.constrain_rms ();
5111 for (RegionSelection::const_iterator i = rs.begin(); i != rs.end(); ++i) {
5112 AudioRegionView const * arv = dynamic_cast<AudioRegionView const *> (*i);
5116 dialog.descend (1.0 / regions);
5117 double const a = arv->audio_region()->maximum_amplitude (&dialog);
5119 double r = arv->audio_region()->rms (&dialog);
5120 max_rms = max (max_rms, r);
5121 rms_vals.push_back (r);
5125 /* the user cancelled the operation */
5129 max_amps.push_back (a);
5130 max_amp = max (max_amp, a);
5134 list<double>::const_iterator a = max_amps.begin ();
5135 list<double>::const_iterator l = rms_vals.begin ();
5136 bool in_command = false;
5138 for (RegionSelection::iterator r = rs.begin(); r != rs.end(); ++r) {
5139 AudioRegionView* const arv = dynamic_cast<AudioRegionView*> (*r);
5144 arv->region()->clear_changes ();
5146 double amp = dialog.normalize_individually() ? *a : max_amp;
5147 double target = dialog.target_peak (); // dB
5150 double const amp_rms = dialog.normalize_individually() ? *l : max_rms;
5151 const double t_rms = dialog.target_rms ();
5152 const gain_t c_peak = dB_to_coefficient (target);
5153 const gain_t c_rms = dB_to_coefficient (t_rms);
5154 if ((amp_rms / c_rms) > (amp / c_peak)) {
5160 arv->audio_region()->normalize (amp, target);
5163 begin_reversible_command (_("normalize"));
5166 _session->add_command (new StatefulDiffCommand (arv->region()));
5173 commit_reversible_command ();
5179 Editor::reset_region_scale_amplitude ()
5185 RegionSelection rs = get_regions_from_selection_and_entered ();
5191 bool in_command = false;
5193 for (RegionSelection::iterator r = rs.begin(); r != rs.end(); ++r) {
5194 AudioRegionView* const arv = dynamic_cast<AudioRegionView*>(*r);
5197 arv->region()->clear_changes ();
5198 arv->audio_region()->set_scale_amplitude (1.0f);
5201 begin_reversible_command ("reset gain");
5204 _session->add_command (new StatefulDiffCommand (arv->region()));
5208 commit_reversible_command ();
5213 Editor::adjust_region_gain (bool up)
5215 RegionSelection rs = get_regions_from_selection_and_entered ();
5217 if (!_session || rs.empty()) {
5221 bool in_command = false;
5223 for (RegionSelection::iterator r = rs.begin(); r != rs.end(); ++r) {
5224 AudioRegionView* const arv = dynamic_cast<AudioRegionView*>(*r);
5229 arv->region()->clear_changes ();
5231 double dB = accurate_coefficient_to_dB (arv->audio_region()->scale_amplitude ());
5239 arv->audio_region()->set_scale_amplitude (dB_to_coefficient (dB));
5242 begin_reversible_command ("adjust region gain");
5245 _session->add_command (new StatefulDiffCommand (arv->region()));
5249 commit_reversible_command ();
5254 Editor::reset_region_gain ()
5256 RegionSelection rs = get_regions_from_selection_and_entered ();
5258 if (!_session || rs.empty()) {
5262 bool in_command = false;
5264 for (RegionSelection::iterator r = rs.begin(); r != rs.end(); ++r) {
5265 AudioRegionView* const arv = dynamic_cast<AudioRegionView*>(*r);
5270 arv->region()->clear_changes ();
5272 arv->audio_region()->set_scale_amplitude (1.0f);
5275 begin_reversible_command ("reset region gain");
5278 _session->add_command (new StatefulDiffCommand (arv->region()));
5282 commit_reversible_command ();
5287 Editor::reverse_region ()
5293 Reverse rev (*_session);
5294 apply_filter (rev, _("reverse regions"));
5298 Editor::strip_region_silence ()
5304 RegionSelection rs = get_regions_from_selection_and_entered ();
5310 std::list<RegionView*> audio_only;
5312 for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
5313 AudioRegionView* const arv = dynamic_cast<AudioRegionView*> (*i);
5315 audio_only.push_back (arv);
5319 assert (!audio_only.empty());
5321 StripSilenceDialog d (_session, audio_only);
5322 int const r = d.run ();
5326 if (r == Gtk::RESPONSE_OK) {
5327 ARDOUR::AudioIntervalMap silences;
5328 d.silences (silences);
5329 StripSilence s (*_session, silences, d.fade_length());
5331 apply_filter (s, _("strip silence"), &d);
5336 Editor::apply_midi_note_edit_op_to_region (MidiOperator& op, MidiRegionView& mrv)
5338 Evoral::Sequence<Temporal::Beats>::Notes selected;
5339 mrv.selection_as_notelist (selected, true);
5341 vector<Evoral::Sequence<Temporal::Beats>::Notes> v;
5342 v.push_back (selected);
5344 Temporal::Beats pos_beats = Temporal::Beats (mrv.midi_region()->beat()) - mrv.midi_region()->start_beats();
5346 return op (mrv.midi_region()->model(), pos_beats, v);
5350 Editor::apply_midi_note_edit_op (MidiOperator& op, const RegionSelection& rs)
5356 bool in_command = false;
5358 for (RegionSelection::const_iterator r = rs.begin(); r != rs.end(); ) {
5359 RegionSelection::const_iterator tmp = r;
5362 MidiRegionView* const mrv = dynamic_cast<MidiRegionView*> (*r);
5365 Command* cmd = apply_midi_note_edit_op_to_region (op, *mrv);
5368 begin_reversible_command (op.name ());
5372 _session->add_command (cmd);
5380 commit_reversible_command ();
5381 _session->set_dirty ();
5386 Editor::fork_region ()
5388 RegionSelection rs = get_regions_from_selection_and_entered ();
5394 CursorContext::Handle cursor_ctx = CursorContext::create(*this, _cursors->wait);
5395 bool in_command = false;
5399 for (RegionSelection::iterator r = rs.begin(); r != rs.end(); ) {
5400 RegionSelection::iterator tmp = r;
5403 MidiRegionView* const mrv = dynamic_cast<MidiRegionView*>(*r);
5407 boost::shared_ptr<Playlist> playlist = mrv->region()->playlist();
5408 boost::shared_ptr<MidiSource> new_source = _session->create_midi_source_by_stealing_name (mrv->midi_view()->track());
5409 boost::shared_ptr<MidiRegion> newregion = mrv->midi_region()->clone (new_source);
5412 begin_reversible_command (_("Fork Region(s)"));
5415 playlist->clear_changes ();
5416 playlist->replace_region (mrv->region(), newregion, mrv->region()->position());
5417 _session->add_command(new StatefulDiffCommand (playlist));
5419 error << string_compose (_("Could not unlink %1"), mrv->region()->name()) << endmsg;
5427 commit_reversible_command ();
5432 Editor::quantize_region ()
5435 quantize_regions(get_regions_from_selection_and_entered ());
5440 Editor::quantize_regions (const RegionSelection& rs)
5442 if (rs.n_midi_regions() == 0) {
5446 if (!quantize_dialog) {
5447 quantize_dialog = new QuantizeDialog (*this);
5450 if (quantize_dialog->is_mapped()) {
5451 /* in progress already */
5455 quantize_dialog->present ();
5456 const int r = quantize_dialog->run ();
5457 quantize_dialog->hide ();
5459 if (r == Gtk::RESPONSE_OK) {
5460 Quantize quant (quantize_dialog->snap_start(),
5461 quantize_dialog->snap_end(),
5462 quantize_dialog->start_grid_size(),
5463 quantize_dialog->end_grid_size(),
5464 quantize_dialog->strength(),
5465 quantize_dialog->swing(),
5466 quantize_dialog->threshold());
5468 apply_midi_note_edit_op (quant, rs);
5473 Editor::legatize_region (bool shrink_only)
5476 legatize_regions(get_regions_from_selection_and_entered (), shrink_only);
5481 Editor::legatize_regions (const RegionSelection& rs, bool shrink_only)
5483 if (rs.n_midi_regions() == 0) {
5487 Legatize legatize(shrink_only);
5488 apply_midi_note_edit_op (legatize, rs);
5492 Editor::transform_region ()
5495 transform_regions(get_regions_from_selection_and_entered ());
5500 Editor::transform_regions (const RegionSelection& rs)
5502 if (rs.n_midi_regions() == 0) {
5509 const int r = td.run();
5512 if (r == Gtk::RESPONSE_OK) {
5513 Transform transform(td.get());
5514 apply_midi_note_edit_op(transform, rs);
5519 Editor::transpose_region ()
5522 transpose_regions(get_regions_from_selection_and_entered ());
5527 Editor::transpose_regions (const RegionSelection& rs)
5529 if (rs.n_midi_regions() == 0) {
5534 int const r = d.run ();
5536 if (r == RESPONSE_ACCEPT) {
5537 Transpose transpose(d.semitones ());
5538 apply_midi_note_edit_op (transpose, rs);
5543 Editor::insert_patch_change (bool from_context)
5545 RegionSelection rs = get_regions_from_selection_and_entered ();
5551 const samplepos_t p = get_preferred_edit_position (EDIT_IGNORE_NONE, from_context);
5553 /* XXX: bit of a hack; use the MIDNAM from the first selected region;
5554 there may be more than one, but the PatchChangeDialog can only offer
5555 one set of patch menus.
5557 MidiRegionView* first = dynamic_cast<MidiRegionView*> (rs.front ());
5559 Evoral::PatchChange<Temporal::Beats> empty (Temporal::Beats(), 0, 0, 0);
5560 PatchChangeDialog d (0, _session, empty, first->instrument_info(), Gtk::Stock::ADD);
5562 if (d.run() == RESPONSE_CANCEL) {
5566 for (RegionSelection::iterator i = rs.begin (); i != rs.end(); ++i) {
5567 MidiRegionView* const mrv = dynamic_cast<MidiRegionView*> (*i);
5569 if (p >= mrv->region()->first_sample() && p <= mrv->region()->last_sample()) {
5570 mrv->add_patch_change (p - mrv->region()->position(), d.patch ());
5577 Editor::apply_filter (Filter& filter, string command, ProgressReporter* progress)
5579 RegionSelection rs = get_regions_from_selection_and_entered ();
5585 CursorContext::Handle cursor_ctx = CursorContext::create(*this, _cursors->wait);
5586 bool in_command = false;
5591 int const N = rs.size ();
5593 for (RegionSelection::iterator r = rs.begin(); r != rs.end(); ) {
5594 RegionSelection::iterator tmp = r;
5597 AudioRegionView* const arv = dynamic_cast<AudioRegionView*>(*r);
5599 boost::shared_ptr<Playlist> playlist = arv->region()->playlist();
5602 progress->descend (1.0 / N);
5605 if (arv->audio_region()->apply (filter, progress) == 0) {
5607 playlist->clear_changes ();
5608 playlist->clear_owned_changes ();
5611 begin_reversible_command (command);
5615 if (filter.results.empty ()) {
5617 /* no regions returned; remove the old one */
5618 playlist->remove_region (arv->region ());
5622 std::vector<boost::shared_ptr<Region> >::iterator res = filter.results.begin ();
5624 /* first region replaces the old one */
5625 playlist->replace_region (arv->region(), *res, (*res)->position());
5629 while (res != filter.results.end()) {
5630 playlist->add_region (*res, (*res)->position());
5636 /* We might have removed regions, which alters other regions' layering_index,
5637 so we need to do a recursive diff here.
5639 vector<Command*> cmds;
5640 playlist->rdiff (cmds);
5641 _session->add_commands (cmds);
5643 _session->add_command(new StatefulDiffCommand (playlist));
5647 progress->ascend ();
5656 commit_reversible_command ();
5661 Editor::external_edit_region ()
5667 Editor::reset_region_gain_envelopes ()
5669 RegionSelection rs = get_regions_from_selection_and_entered ();
5671 if (!_session || rs.empty()) {
5675 bool in_command = false;
5677 for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
5678 AudioRegionView* const arv = dynamic_cast<AudioRegionView*>(*i);
5680 boost::shared_ptr<AutomationList> alist (arv->audio_region()->envelope());
5681 XMLNode& before (alist->get_state());
5683 arv->audio_region()->set_default_envelope ();
5686 begin_reversible_command (_("reset region gain"));
5689 _session->add_command (new MementoCommand<AutomationList>(*arv->audio_region()->envelope().get(), &before, &alist->get_state()));
5694 commit_reversible_command ();
5699 Editor::set_region_gain_visibility (RegionView* rv)
5701 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (rv);
5703 arv->update_envelope_visibility();
5708 Editor::set_gain_envelope_visibility ()
5714 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
5715 AudioTimeAxisView* v = dynamic_cast<AudioTimeAxisView*>(*i);
5717 v->audio_view()->foreach_regionview (sigc::mem_fun (this, &Editor::set_region_gain_visibility));
5723 Editor::toggle_gain_envelope_active ()
5725 if (_ignore_region_action) {
5729 RegionSelection rs = get_regions_from_selection_and_entered ();
5731 if (!_session || rs.empty()) {
5735 bool in_command = false;
5737 for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
5738 AudioRegionView* const arv = dynamic_cast<AudioRegionView*>(*i);
5740 arv->region()->clear_changes ();
5741 arv->audio_region()->set_envelope_active (!arv->audio_region()->envelope_active());
5744 begin_reversible_command (_("region gain envelope active"));
5747 _session->add_command (new StatefulDiffCommand (arv->region()));
5752 commit_reversible_command ();
5757 Editor::toggle_region_lock ()
5759 if (_ignore_region_action) {
5763 RegionSelection rs = get_regions_from_selection_and_entered ();
5765 if (!_session || rs.empty()) {
5769 begin_reversible_command (_("toggle region lock"));
5771 for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
5772 (*i)->region()->clear_changes ();
5773 (*i)->region()->set_locked (!(*i)->region()->locked());
5774 _session->add_command (new StatefulDiffCommand ((*i)->region()));
5777 commit_reversible_command ();
5781 Editor::toggle_region_video_lock ()
5783 if (_ignore_region_action) {
5787 RegionSelection rs = get_regions_from_selection_and_entered ();
5789 if (!_session || rs.empty()) {
5793 begin_reversible_command (_("Toggle Video Lock"));
5795 for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
5796 (*i)->region()->clear_changes ();
5797 (*i)->region()->set_video_locked (!(*i)->region()->video_locked());
5798 _session->add_command (new StatefulDiffCommand ((*i)->region()));
5801 commit_reversible_command ();
5805 Editor::toggle_region_lock_style ()
5807 if (_ignore_region_action) {
5811 RegionSelection rs = get_regions_from_selection_and_entered ();
5813 if (!_session || rs.empty()) {
5817 Glib::RefPtr<ToggleAction> a = Glib::RefPtr<ToggleAction>::cast_dynamic (_region_actions->get_action("toggle-region-lock-style"));
5818 vector<Widget*> proxies = a->get_proxies();
5819 Gtk::CheckMenuItem* cmi = dynamic_cast<Gtk::CheckMenuItem*> (proxies.front());
5823 begin_reversible_command (_("toggle region lock style"));
5825 for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
5826 (*i)->region()->clear_changes ();
5827 PositionLockStyle const ns = ((*i)->region()->position_lock_style() == AudioTime && !cmi->get_inconsistent()) ? MusicTime : AudioTime;
5828 (*i)->region()->set_position_lock_style (ns);
5829 _session->add_command (new StatefulDiffCommand ((*i)->region()));
5832 commit_reversible_command ();
5836 Editor::toggle_opaque_region ()
5838 if (_ignore_region_action) {
5842 RegionSelection rs = get_regions_from_selection_and_entered ();
5844 if (!_session || rs.empty()) {
5848 begin_reversible_command (_("change region opacity"));
5850 for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
5851 (*i)->region()->clear_changes ();
5852 (*i)->region()->set_opaque (!(*i)->region()->opaque());
5853 _session->add_command (new StatefulDiffCommand ((*i)->region()));
5856 commit_reversible_command ();
5860 Editor::toggle_record_enable ()
5862 bool new_state = false;
5864 for (TrackSelection::iterator i = selection->tracks.begin(); i != selection->tracks.end(); ++i) {
5865 RouteTimeAxisView *rtav = dynamic_cast<RouteTimeAxisView *>(*i);
5868 if (!rtav->is_track())
5872 new_state = !rtav->track()->rec_enable_control()->get_value();
5876 rtav->track()->rec_enable_control()->set_value (new_state, Controllable::UseGroup);
5881 tracklist_to_stripables (TrackViewList list)
5885 for (TrackSelection::iterator i = list.begin(); i != list.end(); ++i) {
5886 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> ((*i));
5888 if (rtv && rtv->is_track()) {
5889 ret.push_back (rtv->track());
5897 Editor::play_solo_selection (bool restart)
5899 //note: session::solo_selection takes care of invalidating the region playlist
5901 if ((!selection->tracks.empty()) && selection->time.length() > 0) { //a range is selected; solo the tracks and roll
5903 StripableList sl = tracklist_to_stripables (selection->tracks);
5904 _session->solo_selection (sl, true);
5907 samplepos_t start = selection->time.start();
5908 samplepos_t end = selection->time.end_sample();
5909 _session->request_bounded_roll (start, end);
5911 } else if (! selection->tracks.empty()) { //no range is selected, but tracks are selected; solo the tracks and roll
5912 StripableList sl = tracklist_to_stripables (selection->tracks);
5913 _session->solo_selection (sl, true);
5914 _session->request_cancel_play_range();
5915 transition_to_rolling (true);
5917 } else if (! selection->regions.empty()) { //solo any tracks with selected regions, and roll
5918 StripableList sl = tracklist_to_stripables (get_tracks_for_range_action());
5919 _session->solo_selection (sl, true);
5920 _session->request_cancel_play_range();
5921 transition_to_rolling (true);
5923 _session->request_cancel_play_range();
5924 transition_to_rolling (true); //no selection. just roll.
5929 Editor::toggle_solo ()
5931 bool new_state = false;
5933 boost::shared_ptr<ControlList> cl (new ControlList);
5935 for (TrackSelection::iterator i = selection->tracks.begin(); i != selection->tracks.end(); ++i) {
5936 StripableTimeAxisView *stav = dynamic_cast<StripableTimeAxisView *>(*i);
5938 if (!stav || !stav->stripable()->solo_control()) {
5943 new_state = !stav->stripable()->solo_control()->soloed ();
5947 cl->push_back (stav->stripable()->solo_control());
5950 _session->set_controls (cl, new_state ? 1.0 : 0.0, Controllable::UseGroup);
5954 Editor::toggle_mute ()
5956 bool new_state = false;
5958 boost::shared_ptr<ControlList> cl (new ControlList);
5960 for (TrackSelection::iterator i = selection->tracks.begin(); i != selection->tracks.end(); ++i) {
5961 StripableTimeAxisView *stav = dynamic_cast<StripableTimeAxisView *>(*i);
5963 if (!stav || !stav->stripable()->mute_control()) {
5968 new_state = !stav->stripable()->mute_control()->muted();
5972 cl->push_back (stav->stripable()->mute_control());
5975 _session->set_controls (cl, new_state, Controllable::UseGroup);
5979 Editor::toggle_solo_isolate ()
5985 Editor::fade_range ()
5987 TrackViewList ts = selection->tracks.filter_to_unique_playlists ();
5989 begin_reversible_command (_("fade range"));
5991 for (TrackViewList::iterator i = ts.begin(); i != ts.end(); ++i) {
5992 (*i)->fade_range (selection->time);
5995 commit_reversible_command ();
6000 Editor::set_fade_length (bool in)
6002 RegionSelection rs = get_regions_from_selection_and_entered ();
6008 /* we need a region to measure the offset from the start */
6010 RegionView* rv = rs.front ();
6012 samplepos_t pos = get_preferred_edit_position();
6016 if (pos > rv->region()->last_sample() || pos < rv->region()->first_sample()) {
6017 /* edit point is outside the relevant region */
6022 if (pos <= rv->region()->position()) {
6026 len = pos - rv->region()->position();
6027 cmd = _("set fade in length");
6029 if (pos >= rv->region()->last_sample()) {
6033 len = rv->region()->last_sample() - pos;
6034 cmd = _("set fade out length");
6037 bool in_command = false;
6039 for (RegionSelection::iterator x = rs.begin(); x != rs.end(); ++x) {
6040 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (*x);
6046 boost::shared_ptr<AutomationList> alist;
6048 alist = tmp->audio_region()->fade_in();
6050 alist = tmp->audio_region()->fade_out();
6053 XMLNode &before = alist->get_state();
6056 tmp->audio_region()->set_fade_in_length (len);
6057 tmp->audio_region()->set_fade_in_active (true);
6059 tmp->audio_region()->set_fade_out_length (len);
6060 tmp->audio_region()->set_fade_out_active (true);
6064 begin_reversible_command (cmd);
6067 XMLNode &after = alist->get_state();
6068 _session->add_command(new MementoCommand<AutomationList>(*alist, &before, &after));
6072 commit_reversible_command ();
6077 Editor::set_fade_in_shape (FadeShape shape)
6079 RegionSelection rs = get_regions_from_selection_and_entered ();
6084 bool in_command = false;
6086 for (RegionSelection::iterator x = rs.begin(); x != rs.end(); ++x) {
6087 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (*x);
6093 boost::shared_ptr<AutomationList> alist = tmp->audio_region()->fade_in();
6094 XMLNode &before = alist->get_state();
6096 tmp->audio_region()->set_fade_in_shape (shape);
6099 begin_reversible_command (_("set fade in shape"));
6102 XMLNode &after = alist->get_state();
6103 _session->add_command(new MementoCommand<AutomationList>(*alist.get(), &before, &after));
6107 commit_reversible_command ();
6112 Editor::set_fade_out_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_out();
6129 XMLNode &before = alist->get_state();
6131 tmp->audio_region()->set_fade_out_shape (shape);
6134 begin_reversible_command (_("set fade out 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_in_active (bool yn)
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);
6164 boost::shared_ptr<AudioRegion> ar (tmp->audio_region());
6166 ar->clear_changes ();
6167 ar->set_fade_in_active (yn);
6170 begin_reversible_command (_("set fade in active"));
6173 _session->add_command (new StatefulDiffCommand (ar));
6177 commit_reversible_command ();
6182 Editor::set_fade_out_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);
6198 boost::shared_ptr<AudioRegion> ar (tmp->audio_region());
6200 ar->clear_changes ();
6201 ar->set_fade_out_active (yn);
6204 begin_reversible_command (_("set fade out active"));
6207 _session->add_command(new StatefulDiffCommand (ar));
6211 commit_reversible_command ();
6216 Editor::toggle_region_fades (int dir)
6218 if (_ignore_region_action) {
6222 boost::shared_ptr<AudioRegion> ar;
6225 RegionSelection rs = get_regions_from_selection_and_entered ();
6231 RegionSelection::iterator i;
6232 for (i = rs.begin(); i != rs.end(); ++i) {
6233 if ((ar = boost::dynamic_pointer_cast<AudioRegion>((*i)->region())) != 0) {
6235 yn = ar->fade_out_active ();
6237 yn = ar->fade_in_active ();
6243 if (i == rs.end()) {
6247 /* XXX should this undo-able? */
6248 bool in_command = false;
6250 for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
6251 if ((ar = boost::dynamic_pointer_cast<AudioRegion>((*i)->region())) == 0) {
6254 ar->clear_changes ();
6256 if (dir == 1 || dir == 0) {
6257 ar->set_fade_in_active (!yn);
6260 if (dir == -1 || dir == 0) {
6261 ar->set_fade_out_active (!yn);
6264 begin_reversible_command (_("toggle fade active"));
6267 _session->add_command(new StatefulDiffCommand (ar));
6271 commit_reversible_command ();
6276 /** Update region fade visibility after its configuration has been changed */
6278 Editor::update_region_fade_visibility ()
6280 bool _fade_visibility = _session->config.get_show_region_fades ();
6282 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
6283 AudioTimeAxisView* v = dynamic_cast<AudioTimeAxisView*>(*i);
6285 if (_fade_visibility) {
6286 v->audio_view()->show_all_fades ();
6288 v->audio_view()->hide_all_fades ();
6295 Editor::set_edit_point ()
6298 MusicSample where (0, 0);
6300 if (!mouse_sample (where.sample, ignored)) {
6306 if (selection->markers.empty()) {
6308 mouse_add_new_marker (where.sample);
6313 Location* loc = find_location_from_marker (selection->markers.front(), ignored);
6316 loc->move_to (where.sample, where.division);
6322 Editor::set_playhead_cursor ()
6324 if (entered_marker) {
6325 _session->request_locate (entered_marker->position(), _session->transport_rolling());
6327 MusicSample where (0, 0);
6330 if (!mouse_sample (where.sample, ignored)) {
6337 _session->request_locate (where.sample, _session->transport_rolling());
6341 //not sure what this was for; remove it for now.
6342 // if (UIConfiguration::instance().get_follow_edits() && (!_session || !_session->config.get_external_sync())) {
6343 // cancel_time_selection();
6349 Editor::split_region ()
6351 if (_drags->active ()) {
6355 //if a range is selected, separate it
6356 if (!selection->time.empty()) {
6357 separate_regions_between (selection->time);
6361 //if no range was selected, try to find some regions to split
6362 if (current_mouse_mode() == MouseObject || current_mouse_mode() == MouseRange ) { //don't try this for Internal Edit, Stretch, Draw, etc.
6364 RegionSelection rs = get_regions_from_selection_and_edit_point ();
6365 const samplepos_t pos = get_preferred_edit_position();
6366 const int32_t division = get_grid_music_divisions (0);
6367 MusicSample where (pos, division);
6373 split_regions_at (where, rs);
6379 Editor::select_next_stripable (bool routes_only)
6381 if (selection->tracks.empty()) {
6382 selection->set (track_views.front());
6386 TimeAxisView* current = selection->tracks.front();
6390 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
6392 if (*i == current) {
6394 if (i != track_views.end()) {
6397 current = (*(track_views.begin()));
6398 //selection->set (*(track_views.begin()));
6405 RouteUI* rui = dynamic_cast<RouteUI *>(current);
6406 valid = rui && rui->route()->active();
6408 valid = 0 != current->stripable ().get();
6411 } while (current->hidden() || !valid);
6413 selection->set (current);
6415 ensure_time_axis_view_is_visible (*current, false);
6419 Editor::select_prev_stripable (bool routes_only)
6421 if (selection->tracks.empty()) {
6422 selection->set (track_views.front());
6426 TimeAxisView* current = selection->tracks.front();
6430 for (TrackViewList::reverse_iterator i = track_views.rbegin(); i != track_views.rend(); ++i) {
6432 if (*i == current) {
6434 if (i != track_views.rend()) {
6437 current = *(track_views.rbegin());
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::set_loop_from_selection (bool play)
6459 if (_session == 0) {
6463 samplepos_t start, end;
6464 if (!get_selection_extents (start, end))
6467 set_loop_range (start, end, _("set loop range from selection"));
6470 _session->request_play_loop (true, true);
6475 Editor::set_loop_from_region (bool play)
6477 samplepos_t start, end;
6478 if (!get_selection_extents (start, end))
6481 set_loop_range (start, end, _("set loop range from region"));
6484 _session->request_locate (start, true);
6485 _session->request_play_loop (true);
6490 Editor::set_punch_from_selection ()
6492 if (_session == 0) {
6496 samplepos_t start, end;
6497 if (!get_selection_extents (start, end))
6500 set_punch_range (start, end, _("set punch range from selection"));
6504 Editor::set_auto_punch_range ()
6506 // auto punch in/out button from a single button
6507 // If Punch In is unset, set punch range from playhead to end, enable punch in
6508 // If Punch In is set, the next punch sets Punch Out, unless the playhead has been
6509 // rewound beyond the Punch In marker, in which case that marker will be moved back
6510 // to the current playhead position.
6511 // If punch out is set, it clears the punch range and Punch In/Out buttons
6513 if (_session == 0) {
6517 Location* tpl = transport_punch_location();
6518 samplepos_t now = playhead_cursor->current_sample();
6519 samplepos_t begin = now;
6520 samplepos_t end = _session->current_end_sample();
6522 if (!_session->config.get_punch_in()) {
6523 // First Press - set punch in and create range from here to eternity
6524 set_punch_range (begin, end, _("Auto Punch In"));
6525 _session->config.set_punch_in(true);
6526 } else if (tpl && !_session->config.get_punch_out()) {
6527 // Second press - update end range marker and set punch_out
6528 if (now < tpl->start()) {
6529 // playhead has been rewound - move start back and pretend nothing happened
6531 set_punch_range (begin, end, _("Auto Punch In/Out"));
6533 // normal case for 2nd press - set the punch out
6534 end = playhead_cursor->current_sample ();
6535 set_punch_range (tpl->start(), now, _("Auto Punch In/Out"));
6536 _session->config.set_punch_out(true);
6539 if (_session->config.get_punch_out()) {
6540 _session->config.set_punch_out(false);
6543 if (_session->config.get_punch_in()) {
6544 _session->config.set_punch_in(false);
6549 // third press - unset punch in/out and remove range
6550 _session->locations()->remove(tpl);
6557 Editor::set_session_extents_from_selection ()
6559 if (_session == 0) {
6563 samplepos_t start, end;
6564 if (!get_selection_extents (start, end))
6568 if ((loc = _session->locations()->session_range_location()) == 0) {
6569 _session->set_session_extents (start, end); // this will create a new session range; no need for UNDO
6571 XMLNode &before = loc->get_state();
6573 _session->set_session_extents (start, end);
6575 XMLNode &after = loc->get_state();
6577 begin_reversible_command (_("set session start/end from selection"));
6579 _session->add_command (new MementoCommand<Location>(*loc, &before, &after));
6581 commit_reversible_command ();
6584 _session->set_end_is_free (false);
6588 Editor::set_punch_start_from_edit_point ()
6592 MusicSample start (0, 0);
6593 samplepos_t end = max_samplepos;
6595 //use the existing punch end, if any
6596 Location* tpl = transport_punch_location();
6601 if ((_edit_point == EditAtPlayhead) && _session->transport_rolling()) {
6602 start.sample = _session->audible_sample();
6604 start.sample = get_preferred_edit_position();
6607 //if there's not already a sensible selection endpoint, go "forever"
6608 if (start.sample > end) {
6609 end = max_samplepos;
6612 set_punch_range (start.sample, end, _("set punch start from EP"));
6618 Editor::set_punch_end_from_edit_point ()
6622 samplepos_t start = 0;
6623 MusicSample end (max_samplepos, 0);
6625 //use the existing punch start, if any
6626 Location* tpl = transport_punch_location();
6628 start = tpl->start();
6631 if ((_edit_point == EditAtPlayhead) && _session->transport_rolling()) {
6632 end.sample = _session->audible_sample();
6634 end.sample = get_preferred_edit_position();
6637 set_punch_range (start, end.sample, _("set punch end from EP"));
6643 Editor::set_loop_start_from_edit_point ()
6647 MusicSample start (0, 0);
6648 samplepos_t end = max_samplepos;
6650 //use the existing loop end, if any
6651 Location* tpl = transport_loop_location();
6656 if ((_edit_point == EditAtPlayhead) && _session->transport_rolling()) {
6657 start.sample = _session->audible_sample();
6659 start.sample = get_preferred_edit_position();
6662 //if there's not already a sensible selection endpoint, go "forever"
6663 if (start.sample > end) {
6664 end = max_samplepos;
6667 set_loop_range (start.sample, end, _("set loop start from EP"));
6673 Editor::set_loop_end_from_edit_point ()
6677 samplepos_t start = 0;
6678 MusicSample end (max_samplepos, 0);
6680 //use the existing loop start, if any
6681 Location* tpl = transport_loop_location();
6683 start = tpl->start();
6686 if ((_edit_point == EditAtPlayhead) && _session->transport_rolling()) {
6687 end.sample = _session->audible_sample();
6689 end.sample = get_preferred_edit_position();
6692 set_loop_range (start, end.sample, _("set loop end from EP"));
6697 Editor::set_punch_from_region ()
6699 samplepos_t start, end;
6700 if (!get_selection_extents (start, end))
6703 set_punch_range (start, end, _("set punch range from region"));
6707 Editor::pitch_shift_region ()
6709 RegionSelection rs = get_regions_from_selection_and_entered ();
6711 RegionSelection audio_rs;
6712 for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
6713 if (dynamic_cast<AudioRegionView*> (*i)) {
6714 audio_rs.push_back (*i);
6718 if (audio_rs.empty()) {
6722 pitch_shift (audio_rs, 1.2);
6726 Editor::set_tempo_from_region ()
6728 RegionSelection rs = get_regions_from_selection_and_entered ();
6730 if (!_session || rs.empty()) {
6734 RegionView* rv = rs.front();
6736 define_one_bar (rv->region()->position(), rv->region()->last_sample() + 1);
6740 Editor::use_range_as_bar ()
6742 samplepos_t start, end;
6743 if (get_edit_op_range (start, end)) {
6744 define_one_bar (start, end);
6749 Editor::define_one_bar (samplepos_t start, samplepos_t end)
6751 samplepos_t length = end - start;
6753 const Meter& m (_session->tempo_map().meter_at_sample (start));
6755 /* length = 1 bar */
6757 /* We're going to deliver a constant tempo here,
6758 so we can use samples per beat to determine length.
6759 now we want samples per beat.
6760 we have samples per bar, and beats per bar, so ...
6763 /* XXXX METER MATH */
6765 double samples_per_beat = length / m.divisions_per_bar();
6767 /* beats per minute = */
6769 double beats_per_minute = (_session->sample_rate() * 60.0) / samples_per_beat;
6771 /* now decide whether to:
6773 (a) set global tempo
6774 (b) add a new tempo marker
6778 const TempoSection& t (_session->tempo_map().tempo_section_at_sample (start));
6780 bool do_global = false;
6782 if ((_session->tempo_map().n_tempos() == 1) && (_session->tempo_map().n_meters() == 1)) {
6784 /* only 1 tempo & 1 meter: ask if the user wants to set the tempo
6785 at the start, or create a new marker
6788 vector<string> options;
6789 options.push_back (_("Cancel"));
6790 options.push_back (_("Add new marker"));
6791 options.push_back (_("Set global tempo"));
6794 _("Define one bar"),
6795 _("Do you want to set the global tempo or add a new tempo marker?"),
6799 c.set_default_response (2);
6815 /* more than 1 tempo and/or meter section already, go ahead do the "usual":
6816 if the marker is at the region starter, change it, otherwise add
6821 begin_reversible_command (_("set tempo from region"));
6822 XMLNode& before (_session->tempo_map().get_state());
6825 _session->tempo_map().change_initial_tempo (beats_per_minute, t.note_type(), t.end_note_types_per_minute());
6826 } else if (t.sample() == start) {
6827 _session->tempo_map().change_existing_tempo_at (start, beats_per_minute, t.note_type(), t.end_note_types_per_minute());
6829 /* constant tempo */
6830 const Tempo tempo (beats_per_minute, t.note_type());
6831 _session->tempo_map().add_tempo (tempo, 0.0, start, AudioTime);
6834 XMLNode& after (_session->tempo_map().get_state());
6836 _session->add_command (new MementoCommand<TempoMap>(_session->tempo_map(), &before, &after));
6837 commit_reversible_command ();
6841 Editor::split_region_at_transients ()
6843 AnalysisFeatureList positions;
6845 RegionSelection rs = get_regions_from_selection_and_entered ();
6847 if (!_session || rs.empty()) {
6851 begin_reversible_command (_("split regions"));
6853 for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ) {
6855 RegionSelection::iterator tmp;
6860 boost::shared_ptr<AudioRegion> ar = boost::dynamic_pointer_cast<AudioRegion> ((*i)->region());
6863 ar->transients (positions);
6864 split_region_at_points ((*i)->region(), positions, true);
6871 commit_reversible_command ();
6876 Editor::split_region_at_points (boost::shared_ptr<Region> r, AnalysisFeatureList& positions, bool can_ferret, bool select_new)
6878 bool use_rhythmic_rodent = false;
6880 boost::shared_ptr<Playlist> pl = r->playlist();
6882 list<boost::shared_ptr<Region> > new_regions;
6888 if (positions.empty()) {
6892 if (positions.size() > 20 && can_ferret) {
6893 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);
6894 MessageDialog msg (msgstr,
6897 Gtk::BUTTONS_OK_CANCEL);
6900 msg.add_button (_("Call for the Ferret!"), RESPONSE_APPLY);
6901 msg.set_secondary_text (_("Press OK to continue with this split operation\nor ask the Ferret dialog to tune the analysis"));
6903 msg.set_secondary_text (_("Press OK to continue with this split operation"));
6906 msg.set_title (_("Excessive split?"));
6909 int response = msg.run();
6915 case RESPONSE_APPLY:
6916 use_rhythmic_rodent = true;
6923 if (use_rhythmic_rodent) {
6924 show_rhythm_ferret ();
6928 AnalysisFeatureList::const_iterator x;
6930 pl->clear_changes ();
6931 pl->clear_owned_changes ();
6933 x = positions.begin();
6935 if (x == positions.end()) {
6940 pl->remove_region (r);
6942 samplepos_t pos = 0;
6944 samplepos_t rstart = r->first_sample ();
6945 samplepos_t rend = r->last_sample ();
6947 while (x != positions.end()) {
6949 /* deal with positons that are out of scope of present region bounds */
6950 if (*x <= rstart || *x > rend) {
6955 /* file start = original start + how far we from the initial position ? */
6957 samplepos_t file_start = r->start() + pos;
6959 /* length = next position - current position */
6961 samplepos_t len = (*x) - pos - rstart;
6963 /* XXX we do we really want to allow even single-sample regions?
6964 * shouldn't we have some kind of lower limit on region size?
6973 if (RegionFactory::region_name (new_name, r->name())) {
6977 /* do NOT announce new regions 1 by one, just wait till they are all done */
6981 plist.add (ARDOUR::Properties::start, file_start);
6982 plist.add (ARDOUR::Properties::length, len);
6983 plist.add (ARDOUR::Properties::name, new_name);
6984 plist.add (ARDOUR::Properties::layer, 0);
6985 // TODO set transients_offset
6987 boost::shared_ptr<Region> nr = RegionFactory::create (r->sources(), plist, false);
6988 /* because we set annouce to false, manually add the new region to the
6991 RegionFactory::map_add (nr);
6993 pl->add_region (nr, rstart + pos);
6996 new_regions.push_front(nr);
7005 RegionFactory::region_name (new_name, r->name());
7007 /* Add the final region */
7010 plist.add (ARDOUR::Properties::start, r->start() + pos);
7011 plist.add (ARDOUR::Properties::length, r->last_sample() - (r->position() + pos) + 1);
7012 plist.add (ARDOUR::Properties::name, new_name);
7013 plist.add (ARDOUR::Properties::layer, 0);
7015 boost::shared_ptr<Region> nr = RegionFactory::create (r->sources(), plist, false);
7016 /* because we set annouce to false, manually add the new region to the
7019 RegionFactory::map_add (nr);
7020 pl->add_region (nr, r->position() + pos);
7023 new_regions.push_front(nr);
7028 /* We might have removed regions, which alters other regions' layering_index,
7029 so we need to do a recursive diff here.
7031 vector<Command*> cmds;
7033 _session->add_commands (cmds);
7035 _session->add_command (new StatefulDiffCommand (pl));
7039 for (list<boost::shared_ptr<Region> >::iterator i = new_regions.begin(); i != new_regions.end(); ++i){
7040 set_selected_regionview_from_region_list ((*i), Selection::Add);
7046 Editor::place_transient()
7052 RegionSelection rs = get_regions_from_selection_and_edit_point ();
7058 samplepos_t where = get_preferred_edit_position();
7060 begin_reversible_command (_("place transient"));
7062 for (RegionSelection::iterator r = rs.begin(); r != rs.end(); ++r) {
7063 (*r)->region()->add_transient(where);
7066 commit_reversible_command ();
7070 Editor::remove_transient(ArdourCanvas::Item* item)
7076 ArdourCanvas::Line* _line = reinterpret_cast<ArdourCanvas::Line*> (item);
7079 AudioRegionView* _arv = reinterpret_cast<AudioRegionView*> (item->get_data ("regionview"));
7080 _arv->remove_transient (*(float*) _line->get_data ("position"));
7084 Editor::snap_regions_to_grid ()
7086 list <boost::shared_ptr<Playlist > > used_playlists;
7088 RegionSelection rs = get_regions_from_selection_and_entered ();
7090 if (!_session || rs.empty()) {
7094 begin_reversible_command (_("snap regions to grid"));
7096 for (RegionSelection::iterator r = rs.begin(); r != rs.end(); ++r) {
7098 boost::shared_ptr<Playlist> pl = (*r)->region()->playlist();
7100 if (!pl->frozen()) {
7101 /* we haven't seen this playlist before */
7103 /* remember used playlists so we can thaw them later */
7104 used_playlists.push_back(pl);
7107 (*r)->region()->clear_changes ();
7109 MusicSample start ((*r)->region()->first_sample (), 0);
7110 snap_to (start, RoundNearest, SnapToGrid);
7111 (*r)->region()->set_position (start.sample, start.division);
7112 _session->add_command(new StatefulDiffCommand ((*r)->region()));
7115 while (used_playlists.size() > 0) {
7116 list <boost::shared_ptr<Playlist > >::iterator i = used_playlists.begin();
7118 used_playlists.pop_front();
7121 commit_reversible_command ();
7125 Editor::close_region_gaps ()
7127 list <boost::shared_ptr<Playlist > > used_playlists;
7129 RegionSelection rs = get_regions_from_selection_and_entered ();
7131 if (!_session || rs.empty()) {
7135 Dialog dialog (_("Close Region Gaps"));
7138 table.set_spacings (12);
7139 table.set_border_width (12);
7140 Label* l = manage (left_aligned_label (_("Crossfade length")));
7141 table.attach (*l, 0, 1, 0, 1);
7143 SpinButton spin_crossfade (1, 0);
7144 spin_crossfade.set_range (0, 15);
7145 spin_crossfade.set_increments (1, 1);
7146 spin_crossfade.set_value (5);
7147 table.attach (spin_crossfade, 1, 2, 0, 1);
7149 table.attach (*manage (new Label (_("ms"))), 2, 3, 0, 1);
7151 l = manage (left_aligned_label (_("Pull-back length")));
7152 table.attach (*l, 0, 1, 1, 2);
7154 SpinButton spin_pullback (1, 0);
7155 spin_pullback.set_range (0, 100);
7156 spin_pullback.set_increments (1, 1);
7157 spin_pullback.set_value(30);
7158 table.attach (spin_pullback, 1, 2, 1, 2);
7160 table.attach (*manage (new Label (_("ms"))), 2, 3, 1, 2);
7162 dialog.get_vbox()->pack_start (table);
7163 dialog.add_button (Stock::CANCEL, RESPONSE_CANCEL);
7164 dialog.add_button (_("Ok"), RESPONSE_ACCEPT);
7167 if (dialog.run () == RESPONSE_CANCEL) {
7171 samplepos_t crossfade_len = spin_crossfade.get_value();
7172 samplepos_t pull_back_samples = spin_pullback.get_value();
7174 crossfade_len = lrintf (crossfade_len * _session->sample_rate()/1000);
7175 pull_back_samples = lrintf (pull_back_samples * _session->sample_rate()/1000);
7177 /* Iterate over the region list and make adjacent regions overlap by crossfade_len_ms */
7179 begin_reversible_command (_("close region gaps"));
7182 boost::shared_ptr<Region> last_region;
7184 rs.sort_by_position_and_track();
7186 for (RegionSelection::iterator r = rs.begin(); r != rs.end(); ++r) {
7188 boost::shared_ptr<Playlist> pl = (*r)->region()->playlist();
7190 if (!pl->frozen()) {
7191 /* we haven't seen this playlist before */
7193 /* remember used playlists so we can thaw them later */
7194 used_playlists.push_back(pl);
7198 samplepos_t position = (*r)->region()->position();
7200 if (idx == 0 || position < last_region->position()){
7201 last_region = (*r)->region();
7206 (*r)->region()->clear_changes ();
7207 (*r)->region()->trim_front((position - pull_back_samples));
7209 last_region->clear_changes ();
7210 last_region->trim_end ((position - pull_back_samples + crossfade_len));
7212 _session->add_command (new StatefulDiffCommand ((*r)->region()));
7213 _session->add_command (new StatefulDiffCommand (last_region));
7215 last_region = (*r)->region();
7219 while (used_playlists.size() > 0) {
7220 list <boost::shared_ptr<Playlist > >::iterator i = used_playlists.begin();
7222 used_playlists.pop_front();
7225 commit_reversible_command ();
7229 Editor::tab_to_transient (bool forward)
7231 AnalysisFeatureList positions;
7233 RegionSelection rs = get_regions_from_selection_and_entered ();
7239 samplepos_t pos = _session->audible_sample ();
7241 if (!selection->tracks.empty()) {
7243 /* don't waste time searching for transients in duplicate playlists.
7246 TrackViewList ts = selection->tracks.filter_to_unique_playlists ();
7248 for (TrackViewList::iterator t = ts.begin(); t != ts.end(); ++t) {
7250 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (*t);
7253 boost::shared_ptr<Track> tr = rtv->track();
7255 boost::shared_ptr<Playlist> pl = tr->playlist ();
7257 samplepos_t result = pl->find_next_transient (pos, forward ? 1 : -1);
7260 positions.push_back (result);
7273 for (RegionSelection::iterator r = rs.begin(); r != rs.end(); ++r) {
7274 (*r)->region()->get_transients (positions);
7278 TransientDetector::cleanup_transients (positions, _session->sample_rate(), 3.0);
7281 AnalysisFeatureList::iterator x;
7283 for (x = positions.begin(); x != positions.end(); ++x) {
7289 if (x != positions.end ()) {
7290 _session->request_locate (*x);
7294 AnalysisFeatureList::reverse_iterator x;
7296 for (x = positions.rbegin(); x != positions.rend(); ++x) {
7302 if (x != positions.rend ()) {
7303 _session->request_locate (*x);
7309 Editor::playhead_forward_to_grid ()
7315 MusicSample pos (playhead_cursor->current_sample (), 0);
7317 if (pos.sample < max_samplepos - 1) {
7319 snap_to_internal (pos, RoundUpAlways, SnapToGrid, false, true);
7320 _session->request_locate (pos.sample);
7326 Editor::playhead_backward_to_grid ()
7332 MusicSample pos (playhead_cursor->current_sample (), 0);
7334 if (pos.sample > 2) {
7336 snap_to_internal (pos, RoundDownAlways, SnapToGrid, false, true);
7337 _session->request_locate (pos.sample);
7342 Editor::set_track_height (Height h)
7344 TrackSelection& ts (selection->tracks);
7346 for (TrackSelection::iterator x = ts.begin(); x != ts.end(); ++x) {
7347 (*x)->set_height_enum (h);
7352 Editor::toggle_tracks_active ()
7354 TrackSelection& ts (selection->tracks);
7356 bool target = false;
7362 for (TrackSelection::iterator x = ts.begin(); x != ts.end(); ++x) {
7363 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*>(*x);
7367 target = !rtv->_route->active();
7370 rtv->_route->set_active (target, this);
7376 Editor::remove_tracks ()
7378 /* this will delete GUI objects that may be the subject of an event
7379 handler in which this method is called. Defer actual deletion to the
7380 next idle callback, when all event handling is finished.
7382 Glib::signal_idle().connect (sigc::mem_fun (*this, &Editor::idle_remove_tracks));
7386 Editor::idle_remove_tracks ()
7388 Session::StateProtector sp (_session);
7390 return false; /* do not call again */
7394 Editor::_remove_tracks ()
7396 TrackSelection& ts (selection->tracks);
7402 vector<string> choices;
7407 const char* trackstr;
7410 vector<boost::shared_ptr<Route> > routes;
7411 vector<boost::shared_ptr<VCA> > vcas;
7412 bool special_bus = false;
7414 for (TrackSelection::iterator x = ts.begin(); x != ts.end(); ++x) {
7415 VCATimeAxisView* vtv = dynamic_cast<VCATimeAxisView*> (*x);
7417 vcas.push_back (vtv->vca());
7421 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (*x);
7425 if (rtv->is_track()) {
7430 routes.push_back (rtv->_route);
7432 if (rtv->route()->is_master() || rtv->route()->is_monitor()) {
7437 if (special_bus && !Config->get_allow_special_bus_removal()) {
7438 MessageDialog msg (_("That would be bad news ...."),
7442 msg.set_secondary_text (string_compose (_(
7443 "Removing the master or monitor bus is such a bad idea\n\
7444 that %1 is not going to allow it.\n\
7446 If you really want to do this sort of thing\n\
7447 edit your ardour.rc file to set the\n\
7448 \"allow-special-bus-removal\" option to be \"yes\""), PROGRAM_NAME));
7455 if (ntracks + nbusses + nvcas == 0) {
7461 trackstr = P_("track", "tracks", ntracks);
7462 busstr = P_("bus", "busses", nbusses);
7463 vcastr = P_("VCA", "VCAs", nvcas);
7465 if (ntracks > 0 && nbusses > 0 && nvcas > 0) {
7466 title = _("Remove various strips");
7467 prompt = string_compose (_("Do you really want to remove %1 %2, %3 %4 and %5 %6?"),
7468 ntracks, trackstr, nbusses, busstr, nvcas, vcastr);
7470 else if (ntracks > 0 && nbusses > 0) {
7471 title = string_compose (_("Remove %1 and %2"), trackstr, busstr);
7472 prompt = string_compose (_("Do you really want to remove %1 %2 and %3 %4?"),
7473 ntracks, trackstr, nbusses, busstr);
7475 else if (ntracks > 0 && nvcas > 0) {
7476 title = string_compose (_("Remove %1 and %2"), trackstr, vcastr);
7477 prompt = string_compose (_("Do you really want to remove %1 %2 and %3 %4?"),
7478 ntracks, trackstr, nvcas, vcastr);
7480 else if (nbusses > 0 && nvcas > 0) {
7481 title = string_compose (_("Remove %1 and %2"), busstr, vcastr);
7482 prompt = string_compose (_("Do you really want to remove %1 %2 and %3 %4?"),
7483 nbusses, busstr, nvcas, vcastr);
7485 else if (ntracks > 0) {
7486 title = string_compose (_("Remove %1"), trackstr);
7487 prompt = string_compose (_("Do you really want to remove %1 %2?"),
7490 else if (nbusses > 0) {
7491 title = string_compose (_("Remove %1"), busstr);
7492 prompt = string_compose (_("Do you really want to remove %1 %2?"),
7495 else if (nvcas > 0) {
7496 title = string_compose (_("Remove %1"), vcastr);
7497 prompt = string_compose (_("Do you really want to remove %1 %2?"),
7505 prompt += "\n" + string_compose ("(You may also lose the playlists associated with the %1)", trackstr) + "\n";
7508 prompt += "\n" + string(_("This action cannot be undone, and the session file will be overwritten!"));
7510 choices.push_back (_("No, do nothing."));
7511 if (ntracks + nbusses + nvcas > 1) {
7512 choices.push_back (_("Yes, remove them."));
7514 choices.push_back (_("Yes, remove it."));
7517 Choice prompter (title, prompt, choices);
7519 if (prompter.run () != 1) {
7523 if (current_mixer_strip && routes.size () > 1 && std::find (routes.begin(), routes.end(), current_mixer_strip->route()) != routes.end ()) {
7524 /* Route deletion calls Editor::timeaxisview_deleted() iteratively (for each deleted
7525 * route). If the deleted route is currently displayed in the Editor-Mixer (highly
7526 * likely because deletion requires selection) this will call
7527 * Editor::set_selected_mixer_strip () which is expensive (MixerStrip::set_route()).
7528 * It's likewise likely that the route that has just been displayed in the
7529 * Editor-Mixer will be next in line for deletion.
7531 * So simply switch to the master-bus (if present)
7533 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
7534 if ((*i)->stripable ()->is_master ()) {
7535 set_selected_mixer_strip (*(*i));
7542 PresentationInfo::ChangeSuspender cs;
7543 DisplaySuspender ds;
7545 boost::shared_ptr<RouteList> rl (new RouteList);
7546 for (vector<boost::shared_ptr<Route> >::iterator x = routes.begin(); x != routes.end(); ++x) {
7549 _session->remove_routes (rl);
7551 for (vector<boost::shared_ptr<VCA> >::iterator x = vcas.begin(); x != vcas.end(); ++x) {
7552 _session->vca_manager().remove_vca (*x);
7556 /* TrackSelection and RouteList leave scope,
7557 * destructors are called,
7558 * diskstream drops references, save_state is called (again for every track)
7563 Editor::do_insert_time ()
7565 if (selection->tracks.empty()) {
7566 MessageDialog msg (_("You must first select some tracks to Insert Time."),
7567 true, MESSAGE_INFO, BUTTONS_OK, true);
7568 msg.set_position (WIN_POS_MOUSE);
7573 if (Config->get_edit_mode() == Lock) {
7574 MessageDialog msg (_("You cannot insert time in Lock Edit mode."),
7575 true, MESSAGE_INFO, BUTTONS_OK, true);
7576 msg.set_position (WIN_POS_MOUSE);
7581 InsertRemoveTimeDialog d (*this);
7582 int response = d.run ();
7584 if (response != RESPONSE_OK) {
7588 if (d.distance() == 0) {
7595 d.intersected_region_action (),
7599 d.move_glued_markers(),
7600 d.move_locked_markers(),
7606 Editor::insert_time (
7607 samplepos_t pos, samplecnt_t samples, InsertTimeOption opt,
7608 bool all_playlists, bool ignore_music_glue, bool markers_too, bool glued_markers_too, bool locked_markers_too, bool tempo_too
7612 if (Config->get_edit_mode() == Lock) {
7615 bool in_command = false;
7617 TrackViewList ts = selection->tracks.filter_to_unique_playlists ();
7619 for (TrackViewList::iterator x = ts.begin(); x != ts.end(); ++x) {
7623 /* don't operate on any playlist more than once, which could
7624 * happen if "all playlists" is enabled, but there is more
7625 * than 1 track using playlists "from" a given track.
7628 set<boost::shared_ptr<Playlist> > pl;
7630 if (all_playlists) {
7631 RouteTimeAxisView* rtav = dynamic_cast<RouteTimeAxisView*> (*x);
7632 if (rtav && rtav->track ()) {
7633 vector<boost::shared_ptr<Playlist> > all = _session->playlists->playlists_for_track (rtav->track ());
7634 for (vector<boost::shared_ptr<Playlist> >::iterator p = all.begin(); p != all.end(); ++p) {
7639 if ((*x)->playlist ()) {
7640 pl.insert ((*x)->playlist ());
7644 for (set<boost::shared_ptr<Playlist> >::iterator i = pl.begin(); i != pl.end(); ++i) {
7646 (*i)->clear_changes ();
7647 (*i)->clear_owned_changes ();
7650 begin_reversible_command (_("insert time"));
7654 if (opt == SplitIntersected) {
7655 /* non musical split */
7656 (*i)->split (MusicSample (pos, 0));
7659 (*i)->shift (pos, samples, (opt == MoveIntersected), ignore_music_glue);
7661 vector<Command*> cmds;
7663 _session->add_commands (cmds);
7665 _session->add_command (new StatefulDiffCommand (*i));
7669 RouteTimeAxisView* rtav = dynamic_cast<RouteTimeAxisView*> (*x);
7672 begin_reversible_command (_("insert time"));
7675 rtav->route ()->shift (pos, samples);
7682 const int32_t divisions = get_grid_music_divisions (0);
7683 XMLNode& before (_session->locations()->get_state());
7684 Locations::LocationList copy (_session->locations()->list());
7686 for (Locations::LocationList::iterator i = copy.begin(); i != copy.end(); ++i) {
7688 Locations::LocationList::const_iterator tmp;
7690 if ((*i)->position_lock_style() == AudioTime || glued_markers_too) {
7691 bool const was_locked = (*i)->locked ();
7692 if (locked_markers_too) {
7696 if ((*i)->start() >= pos) {
7697 // move end first, in case we're moving by more than the length of the range
7698 if (!(*i)->is_mark()) {
7699 (*i)->set_end ((*i)->end() + samples, false, true, divisions);
7701 (*i)->set_start ((*i)->start() + samples, false, true, divisions);
7713 begin_reversible_command (_("insert time"));
7716 XMLNode& after (_session->locations()->get_state());
7717 _session->add_command (new MementoCommand<Locations>(*_session->locations(), &before, &after));
7723 begin_reversible_command (_("insert time"));
7726 XMLNode& before (_session->tempo_map().get_state());
7727 _session->tempo_map().insert_time (pos, samples);
7728 XMLNode& after (_session->tempo_map().get_state());
7729 _session->add_command (new MementoCommand<TempoMap>(_session->tempo_map(), &before, &after));
7733 commit_reversible_command ();
7738 Editor::do_remove_time ()
7740 if (selection->tracks.empty()) {
7741 MessageDialog msg (_("You must first select some tracks to Remove Time."),
7742 true, MESSAGE_INFO, BUTTONS_OK, true);
7743 msg.set_position (WIN_POS_MOUSE);
7748 if (Config->get_edit_mode() == Lock) {
7749 MessageDialog msg (_("You cannot remove time in Lock Edit mode."),
7750 true, MESSAGE_INFO, BUTTONS_OK, true);
7751 msg.set_position (WIN_POS_MOUSE);
7756 InsertRemoveTimeDialog d (*this, true);
7758 int response = d.run ();
7760 if (response != RESPONSE_OK) {
7764 samplecnt_t distance = d.distance();
7766 if (distance == 0) {
7776 d.move_glued_markers(),
7777 d.move_locked_markers(),
7783 Editor::remove_time (samplepos_t pos, samplecnt_t samples, InsertTimeOption opt,
7784 bool ignore_music_glue, bool markers_too, bool glued_markers_too, bool locked_markers_too, bool tempo_too)
7786 if (Config->get_edit_mode() == Lock) {
7787 error << (_("Cannot insert or delete time when in Lock edit.")) << endmsg;
7790 bool in_command = false;
7792 for (TrackSelection::iterator x = selection->tracks.begin(); x != selection->tracks.end(); ++x) {
7794 boost::shared_ptr<Playlist> pl = (*x)->playlist();
7798 XMLNode &before = pl->get_state();
7801 begin_reversible_command (_("remove time"));
7805 std::list<AudioRange> rl;
7806 AudioRange ar(pos, pos+samples, 0);
7809 pl->shift (pos, -samples, true, ignore_music_glue);
7811 XMLNode &after = pl->get_state();
7813 _session->add_command (new MementoCommand<Playlist> (*pl, &before, &after));
7817 RouteTimeAxisView* rtav = dynamic_cast<RouteTimeAxisView*> (*x);
7820 begin_reversible_command (_("remove time"));
7823 rtav->route ()->shift (pos, -samples);
7827 const int32_t divisions = get_grid_music_divisions (0);
7828 std::list<Location*> loc_kill_list;
7833 XMLNode& before (_session->locations()->get_state());
7834 Locations::LocationList copy (_session->locations()->list());
7836 for (Locations::LocationList::iterator i = copy.begin(); i != copy.end(); ++i) {
7837 if ((*i)->position_lock_style() == AudioTime || glued_markers_too) {
7839 bool const was_locked = (*i)->locked ();
7840 if (locked_markers_too) {
7844 if (!(*i)->is_mark()) { // it's a range; have to handle both start and end
7845 if ((*i)->end() >= pos
7846 && (*i)->end() < pos+samples
7847 && (*i)->start() >= pos
7848 && (*i)->end() < pos+samples) { // range is completely enclosed; kill it
7850 loc_kill_list.push_back(*i);
7851 } else { // only start or end is included, try to do the right thing
7852 // move start before moving end, to avoid trying to move the end to before the start
7853 // if we're removing more time than the length of the range
7854 if ((*i)->start() >= pos && (*i)->start() < pos+samples) {
7855 // start is within cut
7856 (*i)->set_start (pos, false, true,divisions); // bring the start marker to the beginning of the cut
7858 } else if ((*i)->start() >= pos+samples) {
7859 // start (and thus entire range) lies beyond end of cut
7860 (*i)->set_start ((*i)->start() - samples, false, true, divisions); // slip the start marker back
7863 if ((*i)->end() >= pos && (*i)->end() < pos+samples) {
7864 // end is inside cut
7865 (*i)->set_end (pos, false, true, divisions); // bring the end to the cut
7867 } else if ((*i)->end() >= pos+samples) {
7868 // end is beyond end of cut
7869 (*i)->set_end ((*i)->end() - samples, false, true, divisions); // slip the end marker back
7874 } else if ((*i)->start() >= pos && (*i)->start() < pos+samples) {
7875 loc_kill_list.push_back(*i);
7877 } else if ((*i)->start() >= pos) {
7878 (*i)->set_start ((*i)->start() -samples, false, true, divisions);
7888 for (list<Location*>::iterator i = loc_kill_list.begin(); i != loc_kill_list.end(); ++i) {
7889 _session->locations()->remove (*i);
7894 begin_reversible_command (_("remove time"));
7897 XMLNode& after (_session->locations()->get_state());
7898 _session->add_command (new MementoCommand<Locations>(*_session->locations(), &before, &after));
7903 XMLNode& before (_session->tempo_map().get_state());
7905 if (_session->tempo_map().remove_time (pos, samples)) {
7907 begin_reversible_command (_("remove time"));
7910 XMLNode& after (_session->tempo_map().get_state());
7911 _session->add_command (new MementoCommand<TempoMap>(_session->tempo_map(), &before, &after));
7916 commit_reversible_command ();
7921 Editor::fit_selection ()
7923 if (!selection->tracks.empty()) {
7924 fit_tracks (selection->tracks);
7928 /* no selected tracks - use tracks with selected regions */
7930 if (!selection->regions.empty()) {
7931 for (RegionSelection::iterator r = selection->regions.begin(); r != selection->regions.end(); ++r) {
7932 tvl.push_back (&(*r)->get_time_axis_view ());
7938 } else if (internal_editing()) {
7939 /* no selected tracks, or regions, but in internal edit mode, so follow the mouse and use
7942 if (entered_track) {
7943 tvl.push_back (entered_track);
7951 Editor::fit_tracks (TrackViewList & tracks)
7953 if (tracks.empty()) {
7957 uint32_t child_heights = 0;
7958 int visible_tracks = 0;
7960 for (TrackSelection::iterator t = tracks.begin(); t != tracks.end(); ++t) {
7962 if (!(*t)->marked_for_display()) {
7966 child_heights += (*t)->effective_height() - (*t)->current_height();
7970 /* compute the per-track height from:
7972 * total canvas visible height
7973 * - height that will be taken by visible children of selected tracks
7974 * - height of the ruler/hscroll area
7976 uint32_t h = (uint32_t) floor ((trackviews_height() - child_heights) / visible_tracks);
7977 double first_y_pos = DBL_MAX;
7979 if (h < TimeAxisView::preset_height (HeightSmall)) {
7980 MessageDialog msg (_("There are too many tracks to fit in the current window"));
7981 /* too small to be displayed */
7985 undo_visual_stack.push_back (current_visual_state (true));
7986 PBD::Unwinder<bool> nsv (no_save_visual, true);
7988 /* build a list of all tracks, including children */
7991 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
7993 TimeAxisView::Children c = (*i)->get_child_list ();
7994 for (TimeAxisView::Children::iterator j = c.begin(); j != c.end(); ++j) {
7995 all.push_back (j->get());
8000 // find selection range.
8001 // if someone knows how to user TrackViewList::iterator for this
8003 int selected_top = -1;
8004 int selected_bottom = -1;
8006 for (TrackViewList::iterator t = all.begin(); t != all.end(); ++t, ++i) {
8007 if ((*t)->marked_for_display ()) {
8008 if (tracks.contains(*t)) {
8009 if (selected_top == -1) {
8012 selected_bottom = i;
8018 for (TrackViewList::iterator t = all.begin(); t != all.end(); ++t, ++i) {
8019 if ((*t)->marked_for_display ()) {
8020 if (tracks.contains(*t)) {
8021 (*t)->set_height (h);
8022 first_y_pos = std::min ((*t)->y_position (), first_y_pos);
8024 if (i > selected_top && i < selected_bottom) {
8025 hide_track_in_display (*t);
8032 set the controls_layout height now, because waiting for its size
8033 request signal handler will cause the vertical adjustment setting to fail
8036 controls_layout.property_height () = _full_canvas_height;
8037 vertical_adjustment.set_value (first_y_pos);
8039 redo_visual_stack.push_back (current_visual_state (true));
8041 visible_tracks_selector.set_text (_("Sel"));
8045 Editor::save_visual_state (uint32_t n)
8047 while (visual_states.size() <= n) {
8048 visual_states.push_back (0);
8051 if (visual_states[n] != 0) {
8052 delete visual_states[n];
8055 visual_states[n] = current_visual_state (true);
8060 Editor::goto_visual_state (uint32_t n)
8062 if (visual_states.size() <= n) {
8066 if (visual_states[n] == 0) {
8070 use_visual_state (*visual_states[n]);
8074 Editor::start_visual_state_op (uint32_t n)
8076 save_visual_state (n);
8078 PopUp* pup = new PopUp (WIN_POS_MOUSE, 1000, true);
8080 snprintf (buf, sizeof (buf), _("Saved view %u"), n+1);
8081 pup->set_text (buf);
8086 Editor::cancel_visual_state_op (uint32_t n)
8088 goto_visual_state (n);
8092 Editor::toggle_region_mute ()
8094 if (_ignore_region_action) {
8098 RegionSelection rs = get_regions_from_selection_and_entered ();
8104 if (rs.size() > 1) {
8105 begin_reversible_command (_("mute regions"));
8107 begin_reversible_command (_("mute region"));
8110 for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
8112 (*i)->region()->playlist()->clear_changes ();
8113 (*i)->region()->set_muted (!(*i)->region()->muted ());
8114 _session->add_command (new StatefulDiffCommand ((*i)->region()));
8118 commit_reversible_command ();
8122 Editor::combine_regions ()
8124 /* foreach track with selected regions, take all selected regions
8125 and join them into a new region containing the subregions (as a
8129 typedef set<RouteTimeAxisView*> RTVS;
8132 if (selection->regions.empty()) {
8136 for (RegionSelection::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
8137 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*>(&(*i)->get_time_axis_view());
8140 tracks.insert (rtv);
8144 begin_reversible_command (_("combine regions"));
8146 vector<RegionView*> new_selection;
8148 for (RTVS::iterator i = tracks.begin(); i != tracks.end(); ++i) {
8151 if ((rv = (*i)->combine_regions ()) != 0) {
8152 new_selection.push_back (rv);
8156 selection->clear_regions ();
8157 for (vector<RegionView*>::iterator i = new_selection.begin(); i != new_selection.end(); ++i) {
8158 selection->add (*i);
8161 commit_reversible_command ();
8165 Editor::uncombine_regions ()
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 (_("uncombine regions"));
8184 for (RTVS::iterator i = tracks.begin(); i != tracks.end(); ++i) {
8185 (*i)->uncombine_regions ();
8188 commit_reversible_command ();
8192 Editor::toggle_midi_input_active (bool flip_others)
8195 boost::shared_ptr<RouteList> rl (new RouteList);
8197 for (TrackSelection::iterator i = selection->tracks.begin(); i != selection->tracks.end(); ++i) {
8198 RouteTimeAxisView *rtav = dynamic_cast<RouteTimeAxisView *>(*i);
8204 boost::shared_ptr<MidiTrack> mt = rtav->midi_track();
8207 rl->push_back (rtav->route());
8208 onoff = !mt->input_active();
8212 _session->set_exclusive_input_active (rl, onoff, flip_others);
8215 static bool ok_fine (GdkEventAny*) { return true; }
8221 lock_dialog = new Gtk::Dialog (string_compose (_("%1: Locked"), PROGRAM_NAME), true);
8223 Gtk::Image* padlock = manage (new Gtk::Image (ARDOUR_UI_UTILS::get_icon ("padlock_closed")));
8224 lock_dialog->get_vbox()->pack_start (*padlock);
8225 lock_dialog->signal_delete_event ().connect (sigc::ptr_fun (ok_fine));
8227 ArdourButton* b = manage (new ArdourButton);
8228 b->set_name ("lock button");
8229 b->set_text (_("Click to unlock"));
8230 b->signal_clicked.connect (sigc::mem_fun (*this, &Editor::unlock));
8231 lock_dialog->get_vbox()->pack_start (*b);
8233 lock_dialog->get_vbox()->show_all ();
8234 lock_dialog->set_size_request (200, 200);
8237 delete _main_menu_disabler;
8238 _main_menu_disabler = new MainMenuDisabler;
8240 lock_dialog->present ();
8242 lock_dialog->get_window()->set_decorations (Gdk::WMDecoration (0));
8248 lock_dialog->hide ();
8250 delete _main_menu_disabler;
8251 _main_menu_disabler = 0;
8253 if (UIConfiguration::instance().get_lock_gui_after_seconds()) {
8254 start_lock_event_timing ();
8259 Editor::bring_in_callback (Gtk::Label* label, uint32_t n, uint32_t total, string name)
8261 Gtkmm2ext::UI::instance()->call_slot (invalidator (*this), boost::bind (&Editor::update_bring_in_message, this, label, n, total, name));
8265 Editor::update_bring_in_message (Gtk::Label* label, uint32_t n, uint32_t total, string name)
8267 Timers::TimerSuspender t;
8268 label->set_text (string_compose ("Copying %1, %2 of %3", name, n, total));
8269 Gtkmm2ext::UI::instance()->flush_pending (1);
8273 Editor::bring_all_sources_into_session ()
8280 ArdourDialog w (_("Moving embedded files into session folder"));
8281 w.get_vbox()->pack_start (msg);
8284 /* flush all pending GUI events because we're about to start copying
8288 Timers::TimerSuspender t;
8289 Gtkmm2ext::UI::instance()->flush_pending (3);
8293 _session->bring_all_sources_into_session (boost::bind (&Editor::bring_in_callback, this, &msg, _1, _2, _3));