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 (_dragging_playhead) {
6353 } else if (_drags->active ()) {
6354 /*any other kind of drag, bail out so we avoid Undo snafu*/
6358 //if a range is selected, separate it
6359 if (!selection->time.empty()) {
6360 separate_regions_between (selection->time);
6364 //if no range was selected, try to find some regions to split
6365 if (current_mouse_mode() == MouseObject || current_mouse_mode() == MouseRange ) { //don't try this for Internal Edit, Stretch, Draw, etc.
6367 RegionSelection rs = get_regions_from_selection_and_edit_point ();
6368 const samplepos_t pos = get_preferred_edit_position();
6369 const int32_t division = get_grid_music_divisions (0);
6370 MusicSample where (pos, division);
6376 split_regions_at (where, rs);
6382 Editor::select_next_stripable (bool routes_only)
6384 if (selection->tracks.empty()) {
6385 selection->set (track_views.front());
6389 TimeAxisView* current = selection->tracks.front();
6393 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
6395 if (*i == current) {
6397 if (i != track_views.end()) {
6400 current = (*(track_views.begin()));
6401 //selection->set (*(track_views.begin()));
6408 RouteUI* rui = dynamic_cast<RouteUI *>(current);
6409 valid = rui && rui->route()->active();
6411 valid = 0 != current->stripable ().get();
6414 } while (current->hidden() || !valid);
6416 selection->set (current);
6418 ensure_time_axis_view_is_visible (*current, false);
6422 Editor::select_prev_stripable (bool routes_only)
6424 if (selection->tracks.empty()) {
6425 selection->set (track_views.front());
6429 TimeAxisView* current = selection->tracks.front();
6433 for (TrackViewList::reverse_iterator i = track_views.rbegin(); i != track_views.rend(); ++i) {
6435 if (*i == current) {
6437 if (i != track_views.rend()) {
6440 current = *(track_views.rbegin());
6446 RouteUI* rui = dynamic_cast<RouteUI *>(current);
6447 valid = rui && rui->route()->active();
6449 valid = 0 != current->stripable ().get();
6452 } while (current->hidden() || !valid);
6454 selection->set (current);
6456 ensure_time_axis_view_is_visible (*current, false);
6460 Editor::set_loop_from_selection (bool play)
6462 if (_session == 0) {
6466 samplepos_t start, end;
6467 if (!get_selection_extents (start, end))
6470 set_loop_range (start, end, _("set loop range from selection"));
6473 _session->request_play_loop (true, true);
6478 Editor::set_loop_from_region (bool play)
6480 samplepos_t start, end;
6481 if (!get_selection_extents (start, end))
6484 set_loop_range (start, end, _("set loop range from region"));
6487 _session->request_locate (start, true);
6488 _session->request_play_loop (true);
6493 Editor::set_punch_from_selection ()
6495 if (_session == 0) {
6499 samplepos_t start, end;
6500 if (!get_selection_extents (start, end))
6503 set_punch_range (start, end, _("set punch range from selection"));
6507 Editor::set_auto_punch_range ()
6509 // auto punch in/out button from a single button
6510 // If Punch In is unset, set punch range from playhead to end, enable punch in
6511 // If Punch In is set, the next punch sets Punch Out, unless the playhead has been
6512 // rewound beyond the Punch In marker, in which case that marker will be moved back
6513 // to the current playhead position.
6514 // If punch out is set, it clears the punch range and Punch In/Out buttons
6516 if (_session == 0) {
6520 Location* tpl = transport_punch_location();
6521 samplepos_t now = playhead_cursor->current_sample();
6522 samplepos_t begin = now;
6523 samplepos_t end = _session->current_end_sample();
6525 if (!_session->config.get_punch_in()) {
6526 // First Press - set punch in and create range from here to eternity
6527 set_punch_range (begin, end, _("Auto Punch In"));
6528 _session->config.set_punch_in(true);
6529 } else if (tpl && !_session->config.get_punch_out()) {
6530 // Second press - update end range marker and set punch_out
6531 if (now < tpl->start()) {
6532 // playhead has been rewound - move start back and pretend nothing happened
6534 set_punch_range (begin, end, _("Auto Punch In/Out"));
6536 // normal case for 2nd press - set the punch out
6537 end = playhead_cursor->current_sample ();
6538 set_punch_range (tpl->start(), now, _("Auto Punch In/Out"));
6539 _session->config.set_punch_out(true);
6542 if (_session->config.get_punch_out()) {
6543 _session->config.set_punch_out(false);
6546 if (_session->config.get_punch_in()) {
6547 _session->config.set_punch_in(false);
6552 // third press - unset punch in/out and remove range
6553 _session->locations()->remove(tpl);
6560 Editor::set_session_extents_from_selection ()
6562 if (_session == 0) {
6566 samplepos_t start, end;
6567 if (!get_selection_extents (start, end))
6571 if ((loc = _session->locations()->session_range_location()) == 0) {
6572 _session->set_session_extents (start, end); // this will create a new session range; no need for UNDO
6574 XMLNode &before = loc->get_state();
6576 _session->set_session_extents (start, end);
6578 XMLNode &after = loc->get_state();
6580 begin_reversible_command (_("set session start/end from selection"));
6582 _session->add_command (new MementoCommand<Location>(*loc, &before, &after));
6584 commit_reversible_command ();
6587 _session->set_end_is_free (false);
6591 Editor::set_punch_start_from_edit_point ()
6595 MusicSample start (0, 0);
6596 samplepos_t end = max_samplepos;
6598 //use the existing punch end, if any
6599 Location* tpl = transport_punch_location();
6604 if ((_edit_point == EditAtPlayhead) && _session->transport_rolling()) {
6605 start.sample = _session->audible_sample();
6607 start.sample = get_preferred_edit_position();
6610 //if there's not already a sensible selection endpoint, go "forever"
6611 if (start.sample > end) {
6612 end = max_samplepos;
6615 set_punch_range (start.sample, end, _("set punch start from EP"));
6621 Editor::set_punch_end_from_edit_point ()
6625 samplepos_t start = 0;
6626 MusicSample end (max_samplepos, 0);
6628 //use the existing punch start, if any
6629 Location* tpl = transport_punch_location();
6631 start = tpl->start();
6634 if ((_edit_point == EditAtPlayhead) && _session->transport_rolling()) {
6635 end.sample = _session->audible_sample();
6637 end.sample = get_preferred_edit_position();
6640 set_punch_range (start, end.sample, _("set punch end from EP"));
6646 Editor::set_loop_start_from_edit_point ()
6650 MusicSample start (0, 0);
6651 samplepos_t end = max_samplepos;
6653 //use the existing loop end, if any
6654 Location* tpl = transport_loop_location();
6659 if ((_edit_point == EditAtPlayhead) && _session->transport_rolling()) {
6660 start.sample = _session->audible_sample();
6662 start.sample = get_preferred_edit_position();
6665 //if there's not already a sensible selection endpoint, go "forever"
6666 if (start.sample > end) {
6667 end = max_samplepos;
6670 set_loop_range (start.sample, end, _("set loop start from EP"));
6676 Editor::set_loop_end_from_edit_point ()
6680 samplepos_t start = 0;
6681 MusicSample end (max_samplepos, 0);
6683 //use the existing loop start, if any
6684 Location* tpl = transport_loop_location();
6686 start = tpl->start();
6689 if ((_edit_point == EditAtPlayhead) && _session->transport_rolling()) {
6690 end.sample = _session->audible_sample();
6692 end.sample = get_preferred_edit_position();
6695 set_loop_range (start, end.sample, _("set loop end from EP"));
6700 Editor::set_punch_from_region ()
6702 samplepos_t start, end;
6703 if (!get_selection_extents (start, end))
6706 set_punch_range (start, end, _("set punch range from region"));
6710 Editor::pitch_shift_region ()
6712 RegionSelection rs = get_regions_from_selection_and_entered ();
6714 RegionSelection audio_rs;
6715 for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
6716 if (dynamic_cast<AudioRegionView*> (*i)) {
6717 audio_rs.push_back (*i);
6721 if (audio_rs.empty()) {
6725 pitch_shift (audio_rs, 1.2);
6729 Editor::set_tempo_from_region ()
6731 RegionSelection rs = get_regions_from_selection_and_entered ();
6733 if (!_session || rs.empty()) {
6737 RegionView* rv = rs.front();
6739 define_one_bar (rv->region()->position(), rv->region()->last_sample() + 1);
6743 Editor::use_range_as_bar ()
6745 samplepos_t start, end;
6746 if (get_edit_op_range (start, end)) {
6747 define_one_bar (start, end);
6752 Editor::define_one_bar (samplepos_t start, samplepos_t end)
6754 samplepos_t length = end - start;
6756 const Meter& m (_session->tempo_map().meter_at_sample (start));
6758 /* length = 1 bar */
6760 /* We're going to deliver a constant tempo here,
6761 so we can use samples per beat to determine length.
6762 now we want samples per beat.
6763 we have samples per bar, and beats per bar, so ...
6766 /* XXXX METER MATH */
6768 double samples_per_beat = length / m.divisions_per_bar();
6770 /* beats per minute = */
6772 double beats_per_minute = (_session->sample_rate() * 60.0) / samples_per_beat;
6774 /* now decide whether to:
6776 (a) set global tempo
6777 (b) add a new tempo marker
6781 const TempoSection& t (_session->tempo_map().tempo_section_at_sample (start));
6783 bool do_global = false;
6785 if ((_session->tempo_map().n_tempos() == 1) && (_session->tempo_map().n_meters() == 1)) {
6787 /* only 1 tempo & 1 meter: ask if the user wants to set the tempo
6788 at the start, or create a new marker
6791 vector<string> options;
6792 options.push_back (_("Cancel"));
6793 options.push_back (_("Add new marker"));
6794 options.push_back (_("Set global tempo"));
6797 _("Define one bar"),
6798 _("Do you want to set the global tempo or add a new tempo marker?"),
6802 c.set_default_response (2);
6818 /* more than 1 tempo and/or meter section already, go ahead do the "usual":
6819 if the marker is at the region starter, change it, otherwise add
6824 begin_reversible_command (_("set tempo from region"));
6825 XMLNode& before (_session->tempo_map().get_state());
6828 _session->tempo_map().change_initial_tempo (beats_per_minute, t.note_type(), t.end_note_types_per_minute());
6829 } else if (t.sample() == start) {
6830 _session->tempo_map().change_existing_tempo_at (start, beats_per_minute, t.note_type(), t.end_note_types_per_minute());
6832 /* constant tempo */
6833 const Tempo tempo (beats_per_minute, t.note_type());
6834 _session->tempo_map().add_tempo (tempo, 0.0, start, AudioTime);
6837 XMLNode& after (_session->tempo_map().get_state());
6839 _session->add_command (new MementoCommand<TempoMap>(_session->tempo_map(), &before, &after));
6840 commit_reversible_command ();
6844 Editor::split_region_at_transients ()
6846 AnalysisFeatureList positions;
6848 RegionSelection rs = get_regions_from_selection_and_entered ();
6850 if (!_session || rs.empty()) {
6854 begin_reversible_command (_("split regions"));
6856 for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ) {
6858 RegionSelection::iterator tmp;
6863 boost::shared_ptr<AudioRegion> ar = boost::dynamic_pointer_cast<AudioRegion> ((*i)->region());
6866 ar->transients (positions);
6867 split_region_at_points ((*i)->region(), positions, true);
6874 commit_reversible_command ();
6879 Editor::split_region_at_points (boost::shared_ptr<Region> r, AnalysisFeatureList& positions, bool can_ferret, bool select_new)
6881 bool use_rhythmic_rodent = false;
6883 boost::shared_ptr<Playlist> pl = r->playlist();
6885 list<boost::shared_ptr<Region> > new_regions;
6891 if (positions.empty()) {
6895 if (positions.size() > 20 && can_ferret) {
6896 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);
6897 MessageDialog msg (msgstr,
6900 Gtk::BUTTONS_OK_CANCEL);
6903 msg.add_button (_("Call for the Ferret!"), RESPONSE_APPLY);
6904 msg.set_secondary_text (_("Press OK to continue with this split operation\nor ask the Ferret dialog to tune the analysis"));
6906 msg.set_secondary_text (_("Press OK to continue with this split operation"));
6909 msg.set_title (_("Excessive split?"));
6912 int response = msg.run();
6918 case RESPONSE_APPLY:
6919 use_rhythmic_rodent = true;
6926 if (use_rhythmic_rodent) {
6927 show_rhythm_ferret ();
6931 AnalysisFeatureList::const_iterator x;
6933 pl->clear_changes ();
6934 pl->clear_owned_changes ();
6936 x = positions.begin();
6938 if (x == positions.end()) {
6943 pl->remove_region (r);
6945 samplepos_t pos = 0;
6947 samplepos_t rstart = r->first_sample ();
6948 samplepos_t rend = r->last_sample ();
6950 while (x != positions.end()) {
6952 /* deal with positons that are out of scope of present region bounds */
6953 if (*x <= rstart || *x > rend) {
6958 /* file start = original start + how far we from the initial position ? */
6960 samplepos_t file_start = r->start() + pos;
6962 /* length = next position - current position */
6964 samplepos_t len = (*x) - pos - rstart;
6966 /* XXX we do we really want to allow even single-sample regions?
6967 * shouldn't we have some kind of lower limit on region size?
6976 if (RegionFactory::region_name (new_name, r->name())) {
6980 /* do NOT announce new regions 1 by one, just wait till they are all done */
6984 plist.add (ARDOUR::Properties::start, file_start);
6985 plist.add (ARDOUR::Properties::length, len);
6986 plist.add (ARDOUR::Properties::name, new_name);
6987 plist.add (ARDOUR::Properties::layer, 0);
6988 // TODO set transients_offset
6990 boost::shared_ptr<Region> nr = RegionFactory::create (r->sources(), plist, false);
6991 /* because we set annouce to false, manually add the new region to the
6994 RegionFactory::map_add (nr);
6996 pl->add_region (nr, rstart + pos);
6999 new_regions.push_front(nr);
7008 RegionFactory::region_name (new_name, r->name());
7010 /* Add the final region */
7013 plist.add (ARDOUR::Properties::start, r->start() + pos);
7014 plist.add (ARDOUR::Properties::length, r->last_sample() - (r->position() + pos) + 1);
7015 plist.add (ARDOUR::Properties::name, new_name);
7016 plist.add (ARDOUR::Properties::layer, 0);
7018 boost::shared_ptr<Region> nr = RegionFactory::create (r->sources(), plist, false);
7019 /* because we set annouce to false, manually add the new region to the
7022 RegionFactory::map_add (nr);
7023 pl->add_region (nr, r->position() + pos);
7026 new_regions.push_front(nr);
7031 /* We might have removed regions, which alters other regions' layering_index,
7032 so we need to do a recursive diff here.
7034 vector<Command*> cmds;
7036 _session->add_commands (cmds);
7038 _session->add_command (new StatefulDiffCommand (pl));
7042 for (list<boost::shared_ptr<Region> >::iterator i = new_regions.begin(); i != new_regions.end(); ++i){
7043 set_selected_regionview_from_region_list ((*i), Selection::Add);
7049 Editor::place_transient()
7055 RegionSelection rs = get_regions_from_selection_and_edit_point ();
7061 samplepos_t where = get_preferred_edit_position();
7063 begin_reversible_command (_("place transient"));
7065 for (RegionSelection::iterator r = rs.begin(); r != rs.end(); ++r) {
7066 (*r)->region()->add_transient(where);
7069 commit_reversible_command ();
7073 Editor::remove_transient(ArdourCanvas::Item* item)
7079 ArdourCanvas::Line* _line = reinterpret_cast<ArdourCanvas::Line*> (item);
7082 AudioRegionView* _arv = reinterpret_cast<AudioRegionView*> (item->get_data ("regionview"));
7083 _arv->remove_transient (*(float*) _line->get_data ("position"));
7087 Editor::snap_regions_to_grid ()
7089 list <boost::shared_ptr<Playlist > > used_playlists;
7091 RegionSelection rs = get_regions_from_selection_and_entered ();
7093 if (!_session || rs.empty()) {
7097 begin_reversible_command (_("snap regions to grid"));
7099 for (RegionSelection::iterator r = rs.begin(); r != rs.end(); ++r) {
7101 boost::shared_ptr<Playlist> pl = (*r)->region()->playlist();
7103 if (!pl->frozen()) {
7104 /* we haven't seen this playlist before */
7106 /* remember used playlists so we can thaw them later */
7107 used_playlists.push_back(pl);
7110 (*r)->region()->clear_changes ();
7112 MusicSample start ((*r)->region()->first_sample (), 0);
7113 snap_to (start, RoundNearest, SnapToGrid);
7114 (*r)->region()->set_position (start.sample, start.division);
7115 _session->add_command(new StatefulDiffCommand ((*r)->region()));
7118 while (used_playlists.size() > 0) {
7119 list <boost::shared_ptr<Playlist > >::iterator i = used_playlists.begin();
7121 used_playlists.pop_front();
7124 commit_reversible_command ();
7128 Editor::close_region_gaps ()
7130 list <boost::shared_ptr<Playlist > > used_playlists;
7132 RegionSelection rs = get_regions_from_selection_and_entered ();
7134 if (!_session || rs.empty()) {
7138 Dialog dialog (_("Close Region Gaps"));
7141 table.set_spacings (12);
7142 table.set_border_width (12);
7143 Label* l = manage (left_aligned_label (_("Crossfade length")));
7144 table.attach (*l, 0, 1, 0, 1);
7146 SpinButton spin_crossfade (1, 0);
7147 spin_crossfade.set_range (0, 15);
7148 spin_crossfade.set_increments (1, 1);
7149 spin_crossfade.set_value (5);
7150 table.attach (spin_crossfade, 1, 2, 0, 1);
7152 table.attach (*manage (new Label (_("ms"))), 2, 3, 0, 1);
7154 l = manage (left_aligned_label (_("Pull-back length")));
7155 table.attach (*l, 0, 1, 1, 2);
7157 SpinButton spin_pullback (1, 0);
7158 spin_pullback.set_range (0, 100);
7159 spin_pullback.set_increments (1, 1);
7160 spin_pullback.set_value(30);
7161 table.attach (spin_pullback, 1, 2, 1, 2);
7163 table.attach (*manage (new Label (_("ms"))), 2, 3, 1, 2);
7165 dialog.get_vbox()->pack_start (table);
7166 dialog.add_button (Stock::CANCEL, RESPONSE_CANCEL);
7167 dialog.add_button (_("Ok"), RESPONSE_ACCEPT);
7170 if (dialog.run () == RESPONSE_CANCEL) {
7174 samplepos_t crossfade_len = spin_crossfade.get_value();
7175 samplepos_t pull_back_samples = spin_pullback.get_value();
7177 crossfade_len = lrintf (crossfade_len * _session->sample_rate()/1000);
7178 pull_back_samples = lrintf (pull_back_samples * _session->sample_rate()/1000);
7180 /* Iterate over the region list and make adjacent regions overlap by crossfade_len_ms */
7182 begin_reversible_command (_("close region gaps"));
7185 boost::shared_ptr<Region> last_region;
7187 rs.sort_by_position_and_track();
7189 for (RegionSelection::iterator r = rs.begin(); r != rs.end(); ++r) {
7191 boost::shared_ptr<Playlist> pl = (*r)->region()->playlist();
7193 if (!pl->frozen()) {
7194 /* we haven't seen this playlist before */
7196 /* remember used playlists so we can thaw them later */
7197 used_playlists.push_back(pl);
7201 samplepos_t position = (*r)->region()->position();
7203 if (idx == 0 || position < last_region->position()){
7204 last_region = (*r)->region();
7209 (*r)->region()->clear_changes ();
7210 (*r)->region()->trim_front((position - pull_back_samples));
7212 last_region->clear_changes ();
7213 last_region->trim_end ((position - pull_back_samples + crossfade_len));
7215 _session->add_command (new StatefulDiffCommand ((*r)->region()));
7216 _session->add_command (new StatefulDiffCommand (last_region));
7218 last_region = (*r)->region();
7222 while (used_playlists.size() > 0) {
7223 list <boost::shared_ptr<Playlist > >::iterator i = used_playlists.begin();
7225 used_playlists.pop_front();
7228 commit_reversible_command ();
7232 Editor::tab_to_transient (bool forward)
7234 AnalysisFeatureList positions;
7236 RegionSelection rs = get_regions_from_selection_and_entered ();
7242 samplepos_t pos = _session->audible_sample ();
7244 if (!selection->tracks.empty()) {
7246 /* don't waste time searching for transients in duplicate playlists.
7249 TrackViewList ts = selection->tracks.filter_to_unique_playlists ();
7251 for (TrackViewList::iterator t = ts.begin(); t != ts.end(); ++t) {
7253 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (*t);
7256 boost::shared_ptr<Track> tr = rtv->track();
7258 boost::shared_ptr<Playlist> pl = tr->playlist ();
7260 samplepos_t result = pl->find_next_transient (pos, forward ? 1 : -1);
7263 positions.push_back (result);
7276 for (RegionSelection::iterator r = rs.begin(); r != rs.end(); ++r) {
7277 (*r)->region()->get_transients (positions);
7281 TransientDetector::cleanup_transients (positions, _session->sample_rate(), 3.0);
7284 AnalysisFeatureList::iterator x;
7286 for (x = positions.begin(); x != positions.end(); ++x) {
7292 if (x != positions.end ()) {
7293 _session->request_locate (*x);
7297 AnalysisFeatureList::reverse_iterator x;
7299 for (x = positions.rbegin(); x != positions.rend(); ++x) {
7305 if (x != positions.rend ()) {
7306 _session->request_locate (*x);
7312 Editor::playhead_forward_to_grid ()
7318 MusicSample pos (playhead_cursor->current_sample (), 0);
7320 if (pos.sample < max_samplepos - 1) {
7322 snap_to_internal (pos, RoundUpAlways, SnapToGrid, false, true);
7323 _session->request_locate (pos.sample);
7329 Editor::playhead_backward_to_grid ()
7335 MusicSample pos (playhead_cursor->current_sample (), 0);
7337 if (pos.sample > 2) {
7339 snap_to_internal (pos, RoundDownAlways, SnapToGrid, false, true);
7340 _session->request_locate (pos.sample);
7345 Editor::set_track_height (Height h)
7347 TrackSelection& ts (selection->tracks);
7349 for (TrackSelection::iterator x = ts.begin(); x != ts.end(); ++x) {
7350 (*x)->set_height_enum (h);
7355 Editor::toggle_tracks_active ()
7357 TrackSelection& ts (selection->tracks);
7359 bool target = false;
7365 for (TrackSelection::iterator x = ts.begin(); x != ts.end(); ++x) {
7366 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*>(*x);
7370 target = !rtv->_route->active();
7373 rtv->_route->set_active (target, this);
7379 Editor::remove_tracks ()
7381 /* this will delete GUI objects that may be the subject of an event
7382 handler in which this method is called. Defer actual deletion to the
7383 next idle callback, when all event handling is finished.
7385 Glib::signal_idle().connect (sigc::mem_fun (*this, &Editor::idle_remove_tracks));
7389 Editor::idle_remove_tracks ()
7391 Session::StateProtector sp (_session);
7393 return false; /* do not call again */
7397 Editor::_remove_tracks ()
7399 TrackSelection& ts (selection->tracks);
7405 vector<string> choices;
7410 const char* trackstr;
7413 vector<boost::shared_ptr<Route> > routes;
7414 vector<boost::shared_ptr<VCA> > vcas;
7415 bool special_bus = false;
7417 for (TrackSelection::iterator x = ts.begin(); x != ts.end(); ++x) {
7418 VCATimeAxisView* vtv = dynamic_cast<VCATimeAxisView*> (*x);
7420 vcas.push_back (vtv->vca());
7424 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (*x);
7428 if (rtv->is_track()) {
7433 routes.push_back (rtv->_route);
7435 if (rtv->route()->is_master() || rtv->route()->is_monitor()) {
7440 if (special_bus && !Config->get_allow_special_bus_removal()) {
7441 MessageDialog msg (_("That would be bad news ...."),
7445 msg.set_secondary_text (string_compose (_(
7446 "Removing the master or monitor bus is such a bad idea\n\
7447 that %1 is not going to allow it.\n\
7449 If you really want to do this sort of thing\n\
7450 edit your ardour.rc file to set the\n\
7451 \"allow-special-bus-removal\" option to be \"yes\""), PROGRAM_NAME));
7458 if (ntracks + nbusses + nvcas == 0) {
7464 trackstr = P_("track", "tracks", ntracks);
7465 busstr = P_("bus", "busses", nbusses);
7466 vcastr = P_("VCA", "VCAs", nvcas);
7468 if (ntracks > 0 && nbusses > 0 && nvcas > 0) {
7469 title = _("Remove various strips");
7470 prompt = string_compose (_("Do you really want to remove %1 %2, %3 %4 and %5 %6?"),
7471 ntracks, trackstr, nbusses, busstr, nvcas, vcastr);
7473 else if (ntracks > 0 && nbusses > 0) {
7474 title = string_compose (_("Remove %1 and %2"), trackstr, busstr);
7475 prompt = string_compose (_("Do you really want to remove %1 %2 and %3 %4?"),
7476 ntracks, trackstr, nbusses, busstr);
7478 else if (ntracks > 0 && nvcas > 0) {
7479 title = string_compose (_("Remove %1 and %2"), trackstr, vcastr);
7480 prompt = string_compose (_("Do you really want to remove %1 %2 and %3 %4?"),
7481 ntracks, trackstr, nvcas, vcastr);
7483 else if (nbusses > 0 && nvcas > 0) {
7484 title = string_compose (_("Remove %1 and %2"), busstr, vcastr);
7485 prompt = string_compose (_("Do you really want to remove %1 %2 and %3 %4?"),
7486 nbusses, busstr, nvcas, vcastr);
7488 else if (ntracks > 0) {
7489 title = string_compose (_("Remove %1"), trackstr);
7490 prompt = string_compose (_("Do you really want to remove %1 %2?"),
7493 else if (nbusses > 0) {
7494 title = string_compose (_("Remove %1"), busstr);
7495 prompt = string_compose (_("Do you really want to remove %1 %2?"),
7498 else if (nvcas > 0) {
7499 title = string_compose (_("Remove %1"), vcastr);
7500 prompt = string_compose (_("Do you really want to remove %1 %2?"),
7508 prompt += "\n" + string_compose ("(You may also lose the playlists associated with the %1)", trackstr) + "\n";
7511 prompt += "\n" + string(_("This action cannot be undone, and the session file will be overwritten!"));
7513 choices.push_back (_("No, do nothing."));
7514 if (ntracks + nbusses + nvcas > 1) {
7515 choices.push_back (_("Yes, remove them."));
7517 choices.push_back (_("Yes, remove it."));
7520 Choice prompter (title, prompt, choices);
7522 if (prompter.run () != 1) {
7526 if (current_mixer_strip && routes.size () > 1 && std::find (routes.begin(), routes.end(), current_mixer_strip->route()) != routes.end ()) {
7527 /* Route deletion calls Editor::timeaxisview_deleted() iteratively (for each deleted
7528 * route). If the deleted route is currently displayed in the Editor-Mixer (highly
7529 * likely because deletion requires selection) this will call
7530 * Editor::set_selected_mixer_strip () which is expensive (MixerStrip::set_route()).
7531 * It's likewise likely that the route that has just been displayed in the
7532 * Editor-Mixer will be next in line for deletion.
7534 * So simply switch to the master-bus (if present)
7536 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
7537 if ((*i)->stripable ()->is_master ()) {
7538 set_selected_mixer_strip (*(*i));
7545 PresentationInfo::ChangeSuspender cs;
7546 DisplaySuspender ds;
7548 boost::shared_ptr<RouteList> rl (new RouteList);
7549 for (vector<boost::shared_ptr<Route> >::iterator x = routes.begin(); x != routes.end(); ++x) {
7552 _session->remove_routes (rl);
7554 for (vector<boost::shared_ptr<VCA> >::iterator x = vcas.begin(); x != vcas.end(); ++x) {
7555 _session->vca_manager().remove_vca (*x);
7559 /* TrackSelection and RouteList leave scope,
7560 * destructors are called,
7561 * diskstream drops references, save_state is called (again for every track)
7566 Editor::do_insert_time ()
7568 if (selection->tracks.empty()) {
7569 MessageDialog msg (_("You must first select some tracks to Insert Time."),
7570 true, MESSAGE_INFO, BUTTONS_OK, true);
7571 msg.set_position (WIN_POS_MOUSE);
7576 if (Config->get_edit_mode() == Lock) {
7577 MessageDialog msg (_("You cannot insert time in Lock Edit mode."),
7578 true, MESSAGE_INFO, BUTTONS_OK, true);
7579 msg.set_position (WIN_POS_MOUSE);
7584 InsertRemoveTimeDialog d (*this);
7585 int response = d.run ();
7587 if (response != RESPONSE_OK) {
7591 if (d.distance() == 0) {
7598 d.intersected_region_action (),
7602 d.move_glued_markers(),
7603 d.move_locked_markers(),
7609 Editor::insert_time (
7610 samplepos_t pos, samplecnt_t samples, InsertTimeOption opt,
7611 bool all_playlists, bool ignore_music_glue, bool markers_too, bool glued_markers_too, bool locked_markers_too, bool tempo_too
7615 if (Config->get_edit_mode() == Lock) {
7618 bool in_command = false;
7620 TrackViewList ts = selection->tracks.filter_to_unique_playlists ();
7622 for (TrackViewList::iterator x = ts.begin(); x != ts.end(); ++x) {
7626 /* don't operate on any playlist more than once, which could
7627 * happen if "all playlists" is enabled, but there is more
7628 * than 1 track using playlists "from" a given track.
7631 set<boost::shared_ptr<Playlist> > pl;
7633 if (all_playlists) {
7634 RouteTimeAxisView* rtav = dynamic_cast<RouteTimeAxisView*> (*x);
7635 if (rtav && rtav->track ()) {
7636 vector<boost::shared_ptr<Playlist> > all = _session->playlists->playlists_for_track (rtav->track ());
7637 for (vector<boost::shared_ptr<Playlist> >::iterator p = all.begin(); p != all.end(); ++p) {
7642 if ((*x)->playlist ()) {
7643 pl.insert ((*x)->playlist ());
7647 for (set<boost::shared_ptr<Playlist> >::iterator i = pl.begin(); i != pl.end(); ++i) {
7649 (*i)->clear_changes ();
7650 (*i)->clear_owned_changes ();
7653 begin_reversible_command (_("insert time"));
7657 if (opt == SplitIntersected) {
7658 /* non musical split */
7659 (*i)->split (MusicSample (pos, 0));
7662 (*i)->shift (pos, samples, (opt == MoveIntersected), ignore_music_glue);
7664 vector<Command*> cmds;
7666 _session->add_commands (cmds);
7668 _session->add_command (new StatefulDiffCommand (*i));
7672 RouteTimeAxisView* rtav = dynamic_cast<RouteTimeAxisView*> (*x);
7675 begin_reversible_command (_("insert time"));
7678 rtav->route ()->shift (pos, samples);
7685 const int32_t divisions = get_grid_music_divisions (0);
7686 XMLNode& before (_session->locations()->get_state());
7687 Locations::LocationList copy (_session->locations()->list());
7689 for (Locations::LocationList::iterator i = copy.begin(); i != copy.end(); ++i) {
7691 Locations::LocationList::const_iterator tmp;
7693 if ((*i)->position_lock_style() == AudioTime || glued_markers_too) {
7694 bool const was_locked = (*i)->locked ();
7695 if (locked_markers_too) {
7699 if ((*i)->start() >= pos) {
7700 // move end first, in case we're moving by more than the length of the range
7701 if (!(*i)->is_mark()) {
7702 (*i)->set_end ((*i)->end() + samples, false, true, divisions);
7704 (*i)->set_start ((*i)->start() + samples, false, true, divisions);
7716 begin_reversible_command (_("insert time"));
7719 XMLNode& after (_session->locations()->get_state());
7720 _session->add_command (new MementoCommand<Locations>(*_session->locations(), &before, &after));
7726 begin_reversible_command (_("insert time"));
7729 XMLNode& before (_session->tempo_map().get_state());
7730 _session->tempo_map().insert_time (pos, samples);
7731 XMLNode& after (_session->tempo_map().get_state());
7732 _session->add_command (new MementoCommand<TempoMap>(_session->tempo_map(), &before, &after));
7736 commit_reversible_command ();
7741 Editor::do_remove_time ()
7743 if (selection->tracks.empty()) {
7744 MessageDialog msg (_("You must first select some tracks to Remove Time."),
7745 true, MESSAGE_INFO, BUTTONS_OK, true);
7746 msg.set_position (WIN_POS_MOUSE);
7751 if (Config->get_edit_mode() == Lock) {
7752 MessageDialog msg (_("You cannot remove time in Lock Edit mode."),
7753 true, MESSAGE_INFO, BUTTONS_OK, true);
7754 msg.set_position (WIN_POS_MOUSE);
7759 InsertRemoveTimeDialog d (*this, true);
7761 int response = d.run ();
7763 if (response != RESPONSE_OK) {
7767 samplecnt_t distance = d.distance();
7769 if (distance == 0) {
7779 d.move_glued_markers(),
7780 d.move_locked_markers(),
7786 Editor::remove_time (samplepos_t pos, samplecnt_t samples, InsertTimeOption opt,
7787 bool ignore_music_glue, bool markers_too, bool glued_markers_too, bool locked_markers_too, bool tempo_too)
7789 if (Config->get_edit_mode() == Lock) {
7790 error << (_("Cannot insert or delete time when in Lock edit.")) << endmsg;
7793 bool in_command = false;
7795 for (TrackSelection::iterator x = selection->tracks.begin(); x != selection->tracks.end(); ++x) {
7797 boost::shared_ptr<Playlist> pl = (*x)->playlist();
7801 XMLNode &before = pl->get_state();
7804 begin_reversible_command (_("remove time"));
7808 std::list<AudioRange> rl;
7809 AudioRange ar(pos, pos+samples, 0);
7812 pl->shift (pos, -samples, true, ignore_music_glue);
7814 XMLNode &after = pl->get_state();
7816 _session->add_command (new MementoCommand<Playlist> (*pl, &before, &after));
7820 RouteTimeAxisView* rtav = dynamic_cast<RouteTimeAxisView*> (*x);
7823 begin_reversible_command (_("remove time"));
7826 rtav->route ()->shift (pos, -samples);
7830 const int32_t divisions = get_grid_music_divisions (0);
7831 std::list<Location*> loc_kill_list;
7836 XMLNode& before (_session->locations()->get_state());
7837 Locations::LocationList copy (_session->locations()->list());
7839 for (Locations::LocationList::iterator i = copy.begin(); i != copy.end(); ++i) {
7840 if ((*i)->position_lock_style() == AudioTime || glued_markers_too) {
7842 bool const was_locked = (*i)->locked ();
7843 if (locked_markers_too) {
7847 if (!(*i)->is_mark()) { // it's a range; have to handle both start and end
7848 if ((*i)->end() >= pos
7849 && (*i)->end() < pos+samples
7850 && (*i)->start() >= pos
7851 && (*i)->end() < pos+samples) { // range is completely enclosed; kill it
7853 loc_kill_list.push_back(*i);
7854 } else { // only start or end is included, try to do the right thing
7855 // move start before moving end, to avoid trying to move the end to before the start
7856 // if we're removing more time than the length of the range
7857 if ((*i)->start() >= pos && (*i)->start() < pos+samples) {
7858 // start is within cut
7859 (*i)->set_start (pos, false, true,divisions); // bring the start marker to the beginning of the cut
7861 } else if ((*i)->start() >= pos+samples) {
7862 // start (and thus entire range) lies beyond end of cut
7863 (*i)->set_start ((*i)->start() - samples, false, true, divisions); // slip the start marker back
7866 if ((*i)->end() >= pos && (*i)->end() < pos+samples) {
7867 // end is inside cut
7868 (*i)->set_end (pos, false, true, divisions); // bring the end to the cut
7870 } else if ((*i)->end() >= pos+samples) {
7871 // end is beyond end of cut
7872 (*i)->set_end ((*i)->end() - samples, false, true, divisions); // slip the end marker back
7877 } else if ((*i)->start() >= pos && (*i)->start() < pos+samples) {
7878 loc_kill_list.push_back(*i);
7880 } else if ((*i)->start() >= pos) {
7881 (*i)->set_start ((*i)->start() -samples, false, true, divisions);
7891 for (list<Location*>::iterator i = loc_kill_list.begin(); i != loc_kill_list.end(); ++i) {
7892 _session->locations()->remove (*i);
7897 begin_reversible_command (_("remove time"));
7900 XMLNode& after (_session->locations()->get_state());
7901 _session->add_command (new MementoCommand<Locations>(*_session->locations(), &before, &after));
7906 XMLNode& before (_session->tempo_map().get_state());
7908 if (_session->tempo_map().remove_time (pos, samples)) {
7910 begin_reversible_command (_("remove time"));
7913 XMLNode& after (_session->tempo_map().get_state());
7914 _session->add_command (new MementoCommand<TempoMap>(_session->tempo_map(), &before, &after));
7919 commit_reversible_command ();
7924 Editor::fit_selection ()
7926 if (!selection->tracks.empty()) {
7927 fit_tracks (selection->tracks);
7931 /* no selected tracks - use tracks with selected regions */
7933 if (!selection->regions.empty()) {
7934 for (RegionSelection::iterator r = selection->regions.begin(); r != selection->regions.end(); ++r) {
7935 tvl.push_back (&(*r)->get_time_axis_view ());
7941 } else if (internal_editing()) {
7942 /* no selected tracks, or regions, but in internal edit mode, so follow the mouse and use
7945 if (entered_track) {
7946 tvl.push_back (entered_track);
7954 Editor::fit_tracks (TrackViewList & tracks)
7956 if (tracks.empty()) {
7960 uint32_t child_heights = 0;
7961 int visible_tracks = 0;
7963 for (TrackSelection::iterator t = tracks.begin(); t != tracks.end(); ++t) {
7965 if (!(*t)->marked_for_display()) {
7969 child_heights += (*t)->effective_height() - (*t)->current_height();
7973 /* compute the per-track height from:
7975 * total canvas visible height
7976 * - height that will be taken by visible children of selected tracks
7977 * - height of the ruler/hscroll area
7979 uint32_t h = (uint32_t) floor ((trackviews_height() - child_heights) / visible_tracks);
7980 double first_y_pos = DBL_MAX;
7982 if (h < TimeAxisView::preset_height (HeightSmall)) {
7983 MessageDialog msg (_("There are too many tracks to fit in the current window"));
7984 /* too small to be displayed */
7988 undo_visual_stack.push_back (current_visual_state (true));
7989 PBD::Unwinder<bool> nsv (no_save_visual, true);
7991 /* build a list of all tracks, including children */
7994 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
7996 TimeAxisView::Children c = (*i)->get_child_list ();
7997 for (TimeAxisView::Children::iterator j = c.begin(); j != c.end(); ++j) {
7998 all.push_back (j->get());
8003 // find selection range.
8004 // if someone knows how to user TrackViewList::iterator for this
8006 int selected_top = -1;
8007 int selected_bottom = -1;
8009 for (TrackViewList::iterator t = all.begin(); t != all.end(); ++t, ++i) {
8010 if ((*t)->marked_for_display ()) {
8011 if (tracks.contains(*t)) {
8012 if (selected_top == -1) {
8015 selected_bottom = i;
8021 for (TrackViewList::iterator t = all.begin(); t != all.end(); ++t, ++i) {
8022 if ((*t)->marked_for_display ()) {
8023 if (tracks.contains(*t)) {
8024 (*t)->set_height (h);
8025 first_y_pos = std::min ((*t)->y_position (), first_y_pos);
8027 if (i > selected_top && i < selected_bottom) {
8028 hide_track_in_display (*t);
8035 set the controls_layout height now, because waiting for its size
8036 request signal handler will cause the vertical adjustment setting to fail
8039 controls_layout.property_height () = _full_canvas_height;
8040 vertical_adjustment.set_value (first_y_pos);
8042 redo_visual_stack.push_back (current_visual_state (true));
8044 visible_tracks_selector.set_text (_("Sel"));
8048 Editor::save_visual_state (uint32_t n)
8050 while (visual_states.size() <= n) {
8051 visual_states.push_back (0);
8054 if (visual_states[n] != 0) {
8055 delete visual_states[n];
8058 visual_states[n] = current_visual_state (true);
8063 Editor::goto_visual_state (uint32_t n)
8065 if (visual_states.size() <= n) {
8069 if (visual_states[n] == 0) {
8073 use_visual_state (*visual_states[n]);
8077 Editor::start_visual_state_op (uint32_t n)
8079 save_visual_state (n);
8081 PopUp* pup = new PopUp (WIN_POS_MOUSE, 1000, true);
8083 snprintf (buf, sizeof (buf), _("Saved view %u"), n+1);
8084 pup->set_text (buf);
8089 Editor::cancel_visual_state_op (uint32_t n)
8091 goto_visual_state (n);
8095 Editor::toggle_region_mute ()
8097 if (_ignore_region_action) {
8101 RegionSelection rs = get_regions_from_selection_and_entered ();
8107 if (rs.size() > 1) {
8108 begin_reversible_command (_("mute regions"));
8110 begin_reversible_command (_("mute region"));
8113 for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
8115 (*i)->region()->playlist()->clear_changes ();
8116 (*i)->region()->set_muted (!(*i)->region()->muted ());
8117 _session->add_command (new StatefulDiffCommand ((*i)->region()));
8121 commit_reversible_command ();
8125 Editor::combine_regions ()
8127 /* foreach track with selected regions, take all selected regions
8128 and join them into a new region containing the subregions (as a
8132 typedef set<RouteTimeAxisView*> RTVS;
8135 if (selection->regions.empty()) {
8139 for (RegionSelection::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
8140 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*>(&(*i)->get_time_axis_view());
8143 tracks.insert (rtv);
8147 begin_reversible_command (_("combine regions"));
8149 vector<RegionView*> new_selection;
8151 for (RTVS::iterator i = tracks.begin(); i != tracks.end(); ++i) {
8154 if ((rv = (*i)->combine_regions ()) != 0) {
8155 new_selection.push_back (rv);
8159 selection->clear_regions ();
8160 for (vector<RegionView*>::iterator i = new_selection.begin(); i != new_selection.end(); ++i) {
8161 selection->add (*i);
8164 commit_reversible_command ();
8168 Editor::uncombine_regions ()
8170 typedef set<RouteTimeAxisView*> RTVS;
8173 if (selection->regions.empty()) {
8177 for (RegionSelection::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
8178 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*>(&(*i)->get_time_axis_view());
8181 tracks.insert (rtv);
8185 begin_reversible_command (_("uncombine regions"));
8187 for (RTVS::iterator i = tracks.begin(); i != tracks.end(); ++i) {
8188 (*i)->uncombine_regions ();
8191 commit_reversible_command ();
8195 Editor::toggle_midi_input_active (bool flip_others)
8198 boost::shared_ptr<RouteList> rl (new RouteList);
8200 for (TrackSelection::iterator i = selection->tracks.begin(); i != selection->tracks.end(); ++i) {
8201 RouteTimeAxisView *rtav = dynamic_cast<RouteTimeAxisView *>(*i);
8207 boost::shared_ptr<MidiTrack> mt = rtav->midi_track();
8210 rl->push_back (rtav->route());
8211 onoff = !mt->input_active();
8215 _session->set_exclusive_input_active (rl, onoff, flip_others);
8218 static bool ok_fine (GdkEventAny*) { return true; }
8224 lock_dialog = new Gtk::Dialog (string_compose (_("%1: Locked"), PROGRAM_NAME), true);
8226 Gtk::Image* padlock = manage (new Gtk::Image (ARDOUR_UI_UTILS::get_icon ("padlock_closed")));
8227 lock_dialog->get_vbox()->pack_start (*padlock);
8228 lock_dialog->signal_delete_event ().connect (sigc::ptr_fun (ok_fine));
8230 ArdourButton* b = manage (new ArdourButton);
8231 b->set_name ("lock button");
8232 b->set_text (_("Click to unlock"));
8233 b->signal_clicked.connect (sigc::mem_fun (*this, &Editor::unlock));
8234 lock_dialog->get_vbox()->pack_start (*b);
8236 lock_dialog->get_vbox()->show_all ();
8237 lock_dialog->set_size_request (200, 200);
8240 delete _main_menu_disabler;
8241 _main_menu_disabler = new MainMenuDisabler;
8243 lock_dialog->present ();
8245 lock_dialog->get_window()->set_decorations (Gdk::WMDecoration (0));
8251 lock_dialog->hide ();
8253 delete _main_menu_disabler;
8254 _main_menu_disabler = 0;
8256 if (UIConfiguration::instance().get_lock_gui_after_seconds()) {
8257 start_lock_event_timing ();
8262 Editor::bring_in_callback (Gtk::Label* label, uint32_t n, uint32_t total, string name)
8264 Gtkmm2ext::UI::instance()->call_slot (invalidator (*this), boost::bind (&Editor::update_bring_in_message, this, label, n, total, name));
8268 Editor::update_bring_in_message (Gtk::Label* label, uint32_t n, uint32_t total, string name)
8270 Timers::TimerSuspender t;
8271 label->set_text (string_compose ("Copying %1, %2 of %3", name, n, total));
8272 Gtkmm2ext::UI::instance()->flush_pending (1);
8276 Editor::bring_all_sources_into_session ()
8283 ArdourDialog w (_("Moving embedded files into session folder"));
8284 w.get_vbox()->pack_start (msg);
8287 /* flush all pending GUI events because we're about to start copying
8291 Timers::TimerSuspender t;
8292 Gtkmm2ext::UI::instance()->flush_pending (3);
8296 _session->bring_all_sources_into_session (boost::bind (&Editor::bring_in_callback, this, &msg, _1, _2, _3));