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 "ardour_ui.h"
72 #include "audio_region_view.h"
73 #include "audio_streamview.h"
74 #include "audio_time_axis.h"
75 #include "automation_region_view.h"
76 #include "automation_time_axis.h"
77 #include "control_point.h"
81 #include "editor_cursors.h"
82 #include "editor_drag.h"
83 #include "editor_regions.h"
84 #include "editor_routes.h"
85 #include "gui_thread.h"
86 #include "insert_remove_time_dialog.h"
87 #include "interthread_progress_window.h"
88 #include "item_counts.h"
90 #include "midi_region_view.h"
92 #include "mixer_strip.h"
93 #include "mouse_cursors.h"
94 #include "normalize_dialog.h"
96 #include "paste_context.h"
97 #include "patch_change_dialog.h"
98 #include "quantize_dialog.h"
99 #include "region_gain_line.h"
100 #include "rgb_macros.h"
101 #include "route_time_axis.h"
102 #include "selection.h"
103 #include "selection_templates.h"
104 #include "streamview.h"
105 #include "strip_silence_dialog.h"
106 #include "time_axis_view.h"
108 #include "transpose_dialog.h"
109 #include "transform_dialog.h"
110 #include "ui_config.h"
111 #include "vca_time_axis.h"
113 #include "pbd/i18n.h"
116 using namespace ARDOUR;
119 using namespace Gtkmm2ext;
120 using namespace ArdourWidgets;
121 using namespace Editing;
122 using Gtkmm2ext::Keyboard;
124 /***********************************************************************
126 ***********************************************************************/
129 Editor::undo (uint32_t n)
131 if (_session && _session->actively_recording()) {
132 /* no undo allowed while recording. Session will check also,
133 but we don't even want to get to that.
138 if (_drags->active ()) {
144 if (_session->undo_depth() == 0) {
145 undo_action->set_sensitive(false);
147 redo_action->set_sensitive(true);
148 begin_selection_op_history ();
153 Editor::redo (uint32_t n)
155 if (_session && _session->actively_recording()) {
156 /* no redo allowed while recording. Session will check also,
157 but we don't even want to get to that.
162 if (_drags->active ()) {
168 if (_session->redo_depth() == 0) {
169 redo_action->set_sensitive(false);
171 undo_action->set_sensitive(true);
172 begin_selection_op_history ();
177 Editor::split_regions_at (MusicSample where, RegionSelection& regions)
181 RegionSelection pre_selected_regions = selection->regions;
182 bool working_on_selection = !pre_selected_regions.empty();
184 list<boost::shared_ptr<Playlist> > used_playlists;
185 list<RouteTimeAxisView*> used_trackviews;
187 if (regions.empty()) {
191 begin_reversible_command (_("split"));
194 if (regions.size() == 1) {
195 /* TODO: if splitting a single region, and snap-to is using
196 region boundaries, mabye we shouldn't pay attention to them? */
199 EditorFreeze(); /* Emit Signal */
202 for (RegionSelection::iterator a = regions.begin(); a != regions.end(); ) {
204 RegionSelection::iterator tmp;
206 /* XXX this test needs to be more complicated, to make sure we really
207 have something to split.
210 if (!(*a)->region()->covers (where.sample)) {
218 boost::shared_ptr<Playlist> pl = (*a)->region()->playlist();
226 /* we haven't seen this playlist before */
228 /* remember used playlists so we can thaw them later */
229 used_playlists.push_back(pl);
231 TimeAxisView& tv = (*a)->get_time_axis_view();
232 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (&tv);
234 used_trackviews.push_back (rtv);
241 pl->clear_changes ();
242 pl->split_region ((*a)->region(), where);
243 _session->add_command (new StatefulDiffCommand (pl));
249 latest_regionviews.clear ();
251 vector<sigc::connection> region_added_connections;
253 for (list<RouteTimeAxisView*>::iterator i = used_trackviews.begin(); i != used_trackviews.end(); ++i) {
254 region_added_connections.push_back ((*i)->view()->RegionViewAdded.connect (sigc::mem_fun(*this, &Editor::collect_new_region_view)));
257 while (used_playlists.size() > 0) {
258 list <boost::shared_ptr<Playlist > >::iterator i = used_playlists.begin();
260 used_playlists.pop_front();
263 for (vector<sigc::connection>::iterator c = region_added_connections.begin(); c != region_added_connections.end(); ++c) {
268 EditorThaw(); /* Emit Signal */
271 if (working_on_selection) {
272 // IFF we were working on selected regions, try to reinstate the other region selections that existed before the freeze/thaw.
274 RegionSelectionAfterSplit rsas = Config->get_region_selection_after_split();
275 /* There are three classes of regions that we might want selected after
276 splitting selected regions:
277 - regions selected before the split operation, and unaffected by it
278 - newly-created regions before the split
279 - newly-created regions after the split
282 if (rsas & Existing) {
283 // region selections that existed before the split.
284 selection->add (pre_selected_regions);
287 for (RegionSelection::iterator ri = latest_regionviews.begin(); ri != latest_regionviews.end(); ri++) {
288 if ((*ri)->region()->position() < where.sample) {
289 // new regions created before the split
290 if (rsas & NewlyCreatedLeft) {
291 selection->add (*ri);
294 // new regions created after the split
295 if (rsas & NewlyCreatedRight) {
296 selection->add (*ri);
302 commit_reversible_command ();
305 /** Move one extreme of the current range selection. If more than one range is selected,
306 * the start of the earliest range or the end of the latest range is moved.
308 * @param move_end true to move the end of the current range selection, false to move
310 * @param next true to move the extreme to the next region boundary, false to move to
314 Editor::move_range_selection_start_or_end_to_region_boundary (bool move_end, bool next)
316 if (selection->time.start() == selection->time.end_sample()) {
320 samplepos_t start = selection->time.start ();
321 samplepos_t end = selection->time.end_sample ();
323 /* the position of the thing we may move */
324 samplepos_t pos = move_end ? end : start;
325 int dir = next ? 1 : -1;
327 /* so we don't find the current region again */
328 if (dir > 0 || pos > 0) {
332 samplepos_t const target = get_region_boundary (pos, dir, true, false);
347 begin_reversible_selection_op (_("alter selection"));
348 selection->set_preserving_all_ranges (start, end);
349 commit_reversible_selection_op ();
353 Editor::nudge_forward_release (GdkEventButton* ev)
355 if (ev->state & Keyboard::PrimaryModifier) {
356 nudge_forward (false, true);
358 nudge_forward (false, false);
364 Editor::nudge_backward_release (GdkEventButton* ev)
366 if (ev->state & Keyboard::PrimaryModifier) {
367 nudge_backward (false, true);
369 nudge_backward (false, false);
376 Editor::nudge_forward (bool next, bool force_playhead)
378 samplepos_t distance;
379 samplepos_t next_distance;
385 RegionSelection rs = get_regions_from_selection_and_entered ();
387 if (!force_playhead && !rs.empty()) {
389 begin_reversible_command (_("nudge regions forward"));
391 for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
392 boost::shared_ptr<Region> r ((*i)->region());
394 distance = get_nudge_distance (r->position(), next_distance);
397 distance = next_distance;
401 r->set_position (r->position() + distance);
402 _session->add_command (new StatefulDiffCommand (r));
405 commit_reversible_command ();
408 } else if (!force_playhead && !selection->markers.empty()) {
411 bool in_command = false;
412 const int32_t divisions = get_grid_music_divisions (0);
414 for (MarkerSelection::iterator i = selection->markers.begin(); i != selection->markers.end(); ++i) {
416 Location* loc = find_location_from_marker ((*i), is_start);
420 XMLNode& before (loc->get_state());
423 distance = get_nudge_distance (loc->start(), next_distance);
425 distance = next_distance;
427 if (max_samplepos - distance > loc->start() + loc->length()) {
428 loc->set_start (loc->start() + distance, false, true, divisions);
430 loc->set_start (max_samplepos - loc->length(), false, true, divisions);
433 distance = get_nudge_distance (loc->end(), next_distance);
435 distance = next_distance;
437 if (max_samplepos - distance > loc->end()) {
438 loc->set_end (loc->end() + distance, false, true, divisions);
440 loc->set_end (max_samplepos, false, true, divisions);
442 if (loc->is_session_range()) {
443 _session->set_end_is_free (false);
447 begin_reversible_command (_("nudge location forward"));
450 XMLNode& after (loc->get_state());
451 _session->add_command (new MementoCommand<Location>(*loc, &before, &after));
456 commit_reversible_command ();
459 distance = get_nudge_distance (playhead_cursor->current_sample (), next_distance);
460 _session->request_locate (playhead_cursor->current_sample () + distance);
465 Editor::nudge_backward (bool next, bool force_playhead)
467 samplepos_t distance;
468 samplepos_t next_distance;
474 RegionSelection rs = get_regions_from_selection_and_entered ();
476 if (!force_playhead && !rs.empty()) {
478 begin_reversible_command (_("nudge regions backward"));
480 for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
481 boost::shared_ptr<Region> r ((*i)->region());
483 distance = get_nudge_distance (r->position(), next_distance);
486 distance = next_distance;
491 if (r->position() > distance) {
492 r->set_position (r->position() - distance);
496 _session->add_command (new StatefulDiffCommand (r));
499 commit_reversible_command ();
501 } else if (!force_playhead && !selection->markers.empty()) {
504 bool in_command = false;
506 for (MarkerSelection::iterator i = selection->markers.begin(); i != selection->markers.end(); ++i) {
508 Location* loc = find_location_from_marker ((*i), is_start);
512 XMLNode& before (loc->get_state());
515 distance = get_nudge_distance (loc->start(), next_distance);
517 distance = next_distance;
519 if (distance < loc->start()) {
520 loc->set_start (loc->start() - distance, false, true, get_grid_music_divisions(0));
522 loc->set_start (0, false, true, get_grid_music_divisions(0));
525 distance = get_nudge_distance (loc->end(), next_distance);
528 distance = next_distance;
531 if (distance < loc->end() - loc->length()) {
532 loc->set_end (loc->end() - distance, false, true, get_grid_music_divisions(0));
534 loc->set_end (loc->length(), false, true, get_grid_music_divisions(0));
536 if (loc->is_session_range()) {
537 _session->set_end_is_free (false);
541 begin_reversible_command (_("nudge location forward"));
544 XMLNode& after (loc->get_state());
545 _session->add_command (new MementoCommand<Location>(*loc, &before, &after));
549 commit_reversible_command ();
554 distance = get_nudge_distance (playhead_cursor->current_sample (), next_distance);
556 if (playhead_cursor->current_sample () > distance) {
557 _session->request_locate (playhead_cursor->current_sample () - distance);
559 _session->goto_start();
565 Editor::nudge_forward_capture_offset ()
567 RegionSelection rs = get_regions_from_selection_and_entered ();
569 if (!_session || rs.empty()) {
573 begin_reversible_command (_("nudge forward"));
575 samplepos_t const distance = _session->worst_output_latency();
577 for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
578 boost::shared_ptr<Region> r ((*i)->region());
581 r->set_position (r->position() + distance);
582 _session->add_command(new StatefulDiffCommand (r));
585 commit_reversible_command ();
589 Editor::nudge_backward_capture_offset ()
591 RegionSelection rs = get_regions_from_selection_and_entered ();
593 if (!_session || rs.empty()) {
597 begin_reversible_command (_("nudge backward"));
599 samplepos_t const distance = _session->worst_output_latency();
601 for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
602 boost::shared_ptr<Region> r ((*i)->region());
606 if (r->position() > distance) {
607 r->set_position (r->position() - distance);
611 _session->add_command(new StatefulDiffCommand (r));
614 commit_reversible_command ();
617 struct RegionSelectionPositionSorter {
618 bool operator() (RegionView* a, RegionView* b) {
619 return a->region()->position() < b->region()->position();
624 Editor::sequence_regions ()
627 samplepos_t r_end_prev;
635 RegionSelection rs = get_regions_from_selection_and_entered ();
636 rs.sort(RegionSelectionPositionSorter());
640 bool in_command = false;
642 for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
643 boost::shared_ptr<Region> r ((*i)->region());
651 if(r->position_locked())
658 r->set_position(r_end_prev);
662 begin_reversible_command (_("sequence regions"));
665 _session->add_command (new StatefulDiffCommand (r));
667 r_end=r->position() + r->length();
673 commit_reversible_command ();
682 Editor::move_to_start ()
684 _session->goto_start ();
688 Editor::move_to_end ()
691 _session->request_locate (_session->current_end_sample());
695 Editor::build_region_boundary_cache ()
698 /* TODO: maybe set a timer so we don't recalutate when lots of changes are coming in */
699 /* TODO: maybe somehow defer this until session is fully loaded. */
701 if (!_region_boundary_cache_dirty)
705 vector<RegionPoint> interesting_points;
706 boost::shared_ptr<Region> r;
707 TrackViewList tracks;
710 region_boundary_cache.clear ();
716 bool maybe_first_sample = false;
718 if (UIConfiguration::instance().get_snap_to_region_start()) {
719 interesting_points.push_back (Start);
720 maybe_first_sample = true;
723 if (UIConfiguration::instance().get_snap_to_region_end()) {
724 interesting_points.push_back (End);
727 if (UIConfiguration::instance().get_snap_to_region_sync()) {
728 interesting_points.push_back (SyncPoint);
731 /* if no snap selections are set, boundary cache should be left empty */
732 if ( interesting_points.empty() ) {
736 TimeAxisView *ontrack = 0;
739 tlist = track_views.filter_to_unique_playlists ();
741 if (maybe_first_sample) {
742 TrackViewList::const_iterator i;
743 for (i = tlist.begin(); i != tlist.end(); ++i) {
744 boost::shared_ptr<Playlist> pl = (*i)->playlist();
745 if (pl && pl->count_regions_at (0)) {
746 region_boundary_cache.push_back (0);
752 //allow regions to snap to the video start (if any) as if it were a "region"
753 if (ARDOUR_UI::instance()->video_timeline) {
754 region_boundary_cache.push_back (ARDOUR_UI::instance()->video_timeline->get_video_start_offset());
757 std::pair<samplepos_t, samplepos_t> ext = session_gui_extents (false);
758 samplepos_t session_end = ext.second;
760 while (pos < session_end && !at_end) {
763 samplepos_t lpos = session_end;
765 for (vector<RegionPoint>::iterator p = interesting_points.begin(); p != interesting_points.end(); ++p) {
767 if ((r = find_next_region (pos, *p, 1, tlist, &ontrack)) == 0) {
768 if (*p == interesting_points.back()) {
771 /* move to next point type */
777 rpos = r->first_sample();
781 rpos = r->last_sample();
785 rpos = r->sync_position ();
796 /* prevent duplicates, but we don't use set<> because we want to be able
800 vector<samplepos_t>::iterator ri;
802 for (ri = region_boundary_cache.begin(); ri != region_boundary_cache.end(); ++ri) {
808 if (ri == region_boundary_cache.end()) {
809 region_boundary_cache.push_back (rpos);
816 /* finally sort to be sure that the order is correct */
818 sort (region_boundary_cache.begin(), region_boundary_cache.end());
820 _region_boundary_cache_dirty = false;
823 boost::shared_ptr<Region>
824 Editor::find_next_region (samplepos_t sample, RegionPoint point, int32_t dir, TrackViewList& tracks, TimeAxisView **ontrack)
826 TrackViewList::iterator i;
827 samplepos_t closest = max_samplepos;
828 boost::shared_ptr<Region> ret;
829 samplepos_t rpos = 0;
831 samplepos_t track_sample;
833 for (i = tracks.begin(); i != tracks.end(); ++i) {
835 samplecnt_t distance;
836 boost::shared_ptr<Region> r;
838 track_sample = sample;
840 if ((r = (*i)->find_next_region (track_sample, point, dir)) == 0) {
846 rpos = r->first_sample ();
850 rpos = r->last_sample ();
854 rpos = r->sync_position ();
859 distance = rpos - sample;
861 distance = sample - rpos;
864 if (distance < closest) {
876 Editor::find_next_region_boundary (samplepos_t pos, int32_t dir, const TrackViewList& tracks)
878 samplecnt_t distance = max_samplepos;
879 samplepos_t current_nearest = -1;
881 for (TrackViewList::const_iterator i = tracks.begin(); i != tracks.end(); ++i) {
882 samplepos_t contender;
885 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (*i);
891 if ((contender = rtv->find_next_region_boundary (pos, dir)) < 0) {
895 d = ::llabs (pos - contender);
898 current_nearest = contender;
903 return current_nearest;
907 Editor::get_region_boundary (samplepos_t pos, int32_t dir, bool with_selection, bool only_onscreen)
912 if (with_selection && Config->get_region_boundaries_from_selected_tracks()) {
914 if (!selection->tracks.empty()) {
916 target = find_next_region_boundary (pos, dir, selection->tracks);
920 if (only_onscreen || Config->get_region_boundaries_from_onscreen_tracks()) {
921 get_onscreen_tracks (tvl);
922 target = find_next_region_boundary (pos, dir, tvl);
924 target = find_next_region_boundary (pos, dir, track_views);
930 if (only_onscreen || Config->get_region_boundaries_from_onscreen_tracks()) {
931 get_onscreen_tracks (tvl);
932 target = find_next_region_boundary (pos, dir, tvl);
934 target = find_next_region_boundary (pos, dir, track_views);
942 Editor::cursor_to_region_boundary (bool with_selection, int32_t dir)
944 samplepos_t pos = playhead_cursor->current_sample ();
951 // so we don't find the current region again..
952 if (dir > 0 || pos > 0) {
956 if ((target = get_region_boundary (pos, dir, with_selection, false)) < 0) {
960 _session->request_locate (target);
964 Editor::cursor_to_next_region_boundary (bool with_selection)
966 cursor_to_region_boundary (with_selection, 1);
970 Editor::cursor_to_previous_region_boundary (bool with_selection)
972 cursor_to_region_boundary (with_selection, -1);
976 Editor::cursor_to_region_point (EditorCursor* cursor, RegionPoint point, int32_t dir)
978 boost::shared_ptr<Region> r;
979 samplepos_t pos = cursor->current_sample ();
985 TimeAxisView *ontrack = 0;
987 // so we don't find the current region again..
991 if (!selection->tracks.empty()) {
993 r = find_next_region (pos, point, dir, selection->tracks, &ontrack);
995 } else if (clicked_axisview) {
998 t.push_back (clicked_axisview);
1000 r = find_next_region (pos, point, dir, t, &ontrack);
1004 r = find_next_region (pos, point, dir, track_views, &ontrack);
1013 pos = r->first_sample ();
1017 pos = r->last_sample ();
1021 pos = r->sync_position ();
1025 if (cursor == playhead_cursor) {
1026 _session->request_locate (pos);
1028 cursor->set_position (pos);
1033 Editor::cursor_to_next_region_point (EditorCursor* cursor, RegionPoint point)
1035 cursor_to_region_point (cursor, point, 1);
1039 Editor::cursor_to_previous_region_point (EditorCursor* cursor, RegionPoint point)
1041 cursor_to_region_point (cursor, point, -1);
1045 Editor::cursor_to_selection_start (EditorCursor *cursor)
1047 samplepos_t pos = 0;
1049 switch (mouse_mode) {
1051 if (!selection->regions.empty()) {
1052 pos = selection->regions.start();
1057 if (!selection->time.empty()) {
1058 pos = selection->time.start ();
1066 if (cursor == playhead_cursor) {
1067 _session->request_locate (pos);
1069 cursor->set_position (pos);
1074 Editor::cursor_to_selection_end (EditorCursor *cursor)
1076 samplepos_t pos = 0;
1078 switch (mouse_mode) {
1080 if (!selection->regions.empty()) {
1081 pos = selection->regions.end_sample();
1086 if (!selection->time.empty()) {
1087 pos = selection->time.end_sample ();
1095 if (cursor == playhead_cursor) {
1096 _session->request_locate (pos);
1098 cursor->set_position (pos);
1103 Editor::selected_marker_to_region_boundary (bool with_selection, int32_t dir)
1113 if (selection->markers.empty()) {
1117 if (!mouse_sample (mouse, ignored)) {
1121 add_location_mark (mouse);
1124 if ((loc = find_location_from_marker (selection->markers.front(), ignored)) == 0) {
1128 samplepos_t pos = loc->start();
1130 // so we don't find the current region again..
1131 if (dir > 0 || pos > 0) {
1135 if ((target = get_region_boundary (pos, dir, with_selection, false)) < 0) {
1139 loc->move_to (target, 0);
1143 Editor::selected_marker_to_next_region_boundary (bool with_selection)
1145 selected_marker_to_region_boundary (with_selection, 1);
1149 Editor::selected_marker_to_previous_region_boundary (bool with_selection)
1151 selected_marker_to_region_boundary (with_selection, -1);
1155 Editor::selected_marker_to_region_point (RegionPoint point, int32_t dir)
1157 boost::shared_ptr<Region> r;
1162 if (!_session || selection->markers.empty()) {
1166 if ((loc = find_location_from_marker (selection->markers.front(), ignored)) == 0) {
1170 TimeAxisView *ontrack = 0;
1174 // so we don't find the current region again..
1178 if (!selection->tracks.empty()) {
1180 r = find_next_region (pos, point, dir, selection->tracks, &ontrack);
1184 r = find_next_region (pos, point, dir, track_views, &ontrack);
1193 pos = r->first_sample ();
1197 pos = r->last_sample ();
1201 pos = r->adjust_to_sync (r->first_sample());
1205 loc->move_to (pos, 0);
1209 Editor::selected_marker_to_next_region_point (RegionPoint point)
1211 selected_marker_to_region_point (point, 1);
1215 Editor::selected_marker_to_previous_region_point (RegionPoint point)
1217 selected_marker_to_region_point (point, -1);
1221 Editor::selected_marker_to_selection_start ()
1223 samplepos_t pos = 0;
1227 if (!_session || selection->markers.empty()) {
1231 if ((loc = find_location_from_marker (selection->markers.front(), ignored)) == 0) {
1235 switch (mouse_mode) {
1237 if (!selection->regions.empty()) {
1238 pos = selection->regions.start();
1243 if (!selection->time.empty()) {
1244 pos = selection->time.start ();
1252 loc->move_to (pos, 0);
1256 Editor::selected_marker_to_selection_end ()
1258 samplepos_t pos = 0;
1262 if (!_session || selection->markers.empty()) {
1266 if ((loc = find_location_from_marker (selection->markers.front(), ignored)) == 0) {
1270 switch (mouse_mode) {
1272 if (!selection->regions.empty()) {
1273 pos = selection->regions.end_sample();
1278 if (!selection->time.empty()) {
1279 pos = selection->time.end_sample ();
1287 loc->move_to (pos, 0);
1291 Editor::scroll_playhead (bool forward)
1293 samplepos_t pos = playhead_cursor->current_sample ();
1294 samplecnt_t delta = (samplecnt_t) floor (current_page_samples() / 0.8);
1297 if (pos == max_samplepos) {
1301 if (pos < max_samplepos - delta) {
1304 pos = max_samplepos;
1320 _session->request_locate (pos);
1324 Editor::cursor_align (bool playhead_to_edit)
1330 if (playhead_to_edit) {
1332 if (selection->markers.empty()) {
1336 _session->request_locate (selection->markers.front()->position(), _session->transport_rolling());
1339 const int32_t divisions = get_grid_music_divisions (0);
1340 /* move selected markers to playhead */
1342 for (MarkerSelection::iterator i = selection->markers.begin(); i != selection->markers.end(); ++i) {
1345 Location* loc = find_location_from_marker (*i, ignored);
1347 if (loc->is_mark()) {
1348 loc->set_start (playhead_cursor->current_sample (), false, true, divisions);
1350 loc->set (playhead_cursor->current_sample (),
1351 playhead_cursor->current_sample () + loc->length(), true, divisions);
1358 Editor::scroll_backward (float pages)
1360 samplepos_t const one_page = (samplepos_t) rint (_visible_canvas_width * samples_per_pixel);
1361 samplepos_t const cnt = (samplepos_t) floor (pages * one_page);
1364 if (_leftmost_sample < cnt) {
1367 sample = _leftmost_sample - cnt;
1370 reset_x_origin (sample);
1374 Editor::scroll_forward (float pages)
1376 samplepos_t const one_page = (samplepos_t) rint (_visible_canvas_width * samples_per_pixel);
1377 samplepos_t const cnt = (samplepos_t) floor (pages * one_page);
1380 if (max_samplepos - cnt < _leftmost_sample) {
1381 sample = max_samplepos - cnt;
1383 sample = _leftmost_sample + cnt;
1386 reset_x_origin (sample);
1390 Editor::scroll_tracks_down ()
1392 double vert_value = vertical_adjustment.get_value() + vertical_adjustment.get_page_size();
1393 if (vert_value > vertical_adjustment.get_upper() - _visible_canvas_height) {
1394 vert_value = vertical_adjustment.get_upper() - _visible_canvas_height;
1397 vertical_adjustment.set_value (vert_value);
1401 Editor::scroll_tracks_up ()
1403 vertical_adjustment.set_value (vertical_adjustment.get_value() - vertical_adjustment.get_page_size());
1407 Editor::scroll_tracks_down_line ()
1409 double vert_value = vertical_adjustment.get_value() + 60;
1411 if (vert_value > vertical_adjustment.get_upper() - _visible_canvas_height) {
1412 vert_value = vertical_adjustment.get_upper() - _visible_canvas_height;
1415 vertical_adjustment.set_value (vert_value);
1419 Editor::scroll_tracks_up_line ()
1421 reset_y_origin (vertical_adjustment.get_value() - 60);
1425 Editor::select_topmost_track ()
1427 const double top_of_trackviews = vertical_adjustment.get_value();
1428 for (TrackViewList::iterator t = track_views.begin(); t != track_views.end(); ++t) {
1429 if ((*t)->hidden()) {
1432 std::pair<TimeAxisView*,double> res = (*t)->covers_y_position (top_of_trackviews);
1434 selection->set (*t);
1441 Editor::scroll_down_one_track (bool skip_child_views)
1443 TrackViewList::reverse_iterator next = track_views.rend();
1444 const double top_of_trackviews = vertical_adjustment.get_value();
1446 for (TrackViewList::reverse_iterator t = track_views.rbegin(); t != track_views.rend(); ++t) {
1447 if ((*t)->hidden()) {
1451 /* If this is the upper-most visible trackview, we want to display
1452 * the one above it (next)
1454 * Note that covers_y_position() is recursive and includes child views
1456 std::pair<TimeAxisView*,double> res = (*t)->covers_y_position (top_of_trackviews);
1459 if (skip_child_views) {
1462 /* automation lane (one level, non-recursive)
1464 * - if no automation lane exists -> move to next tack
1465 * - if the first (here: bottom-most) matches -> move to next tack
1466 * - if no y-axis match is found -> the current track is at the top
1467 * -> move to last (here: top-most) automation lane
1469 TimeAxisView::Children kids = (*t)->get_child_list();
1470 TimeAxisView::Children::reverse_iterator nkid = kids.rend();
1472 for (TimeAxisView::Children::reverse_iterator ci = kids.rbegin(); ci != kids.rend(); ++ci) {
1473 if ((*ci)->hidden()) {
1477 std::pair<TimeAxisView*,double> dev;
1478 dev = (*ci)->covers_y_position (top_of_trackviews);
1480 /* some automation lane is currently at the top */
1481 if (ci == kids.rbegin()) {
1482 /* first (bottom-most) autmation lane is at the top.
1483 * -> move to next track
1492 if (nkid != kids.rend()) {
1493 ensure_time_axis_view_is_visible (**nkid, true);
1501 /* move to the track below the first one that covers the */
1503 if (next != track_views.rend()) {
1504 ensure_time_axis_view_is_visible (**next, true);
1512 Editor::scroll_up_one_track (bool skip_child_views)
1514 TrackViewList::iterator prev = track_views.end();
1515 double top_of_trackviews = vertical_adjustment.get_value ();
1517 for (TrackViewList::iterator t = track_views.begin(); t != track_views.end(); ++t) {
1519 if ((*t)->hidden()) {
1523 /* find the trackview at the top of the trackview group
1525 * Note that covers_y_position() is recursive and includes child views
1527 std::pair<TimeAxisView*,double> res = (*t)->covers_y_position (top_of_trackviews);
1530 if (skip_child_views) {
1533 /* automation lane (one level, non-recursive)
1535 * - if no automation lane exists -> move to prev tack
1536 * - if no y-axis match is found -> the current track is at the top -> move to prev track
1537 * (actually last automation lane of previous track, see below)
1538 * - if first (top-most) lane is at the top -> move to this track
1539 * - else move up one lane
1541 TimeAxisView::Children kids = (*t)->get_child_list();
1542 TimeAxisView::Children::iterator pkid = kids.end();
1544 for (TimeAxisView::Children::iterator ci = kids.begin(); ci != kids.end(); ++ci) {
1545 if ((*ci)->hidden()) {
1549 std::pair<TimeAxisView*,double> dev;
1550 dev = (*ci)->covers_y_position (top_of_trackviews);
1552 /* some automation lane is currently at the top */
1553 if (ci == kids.begin()) {
1554 /* first (top-most) autmation lane is at the top.
1555 * jump directly to this track's top
1557 ensure_time_axis_view_is_visible (**t, true);
1560 else if (pkid != kids.end()) {
1561 /* some other automation lane is at the top.
1562 * move up to prev automation lane.
1564 ensure_time_axis_view_is_visible (**pkid, true);
1567 assert(0); // not reached
1578 if (prev != track_views.end()) {
1579 // move to bottom-most automation-lane of the previous track
1580 TimeAxisView::Children kids = (*prev)->get_child_list();
1581 TimeAxisView::Children::reverse_iterator pkid = kids.rend();
1582 if (!skip_child_views) {
1583 // find the last visible lane
1584 for (TimeAxisView::Children::reverse_iterator ci = kids.rbegin(); ci != kids.rend(); ++ci) {
1585 if (!(*ci)->hidden()) {
1591 if (pkid != kids.rend()) {
1592 ensure_time_axis_view_is_visible (**pkid, true);
1594 ensure_time_axis_view_is_visible (**prev, true);
1603 Editor::scroll_left_step ()
1605 samplepos_t xdelta = (current_page_samples() / 8);
1607 if (_leftmost_sample > xdelta) {
1608 reset_x_origin (_leftmost_sample - xdelta);
1616 Editor::scroll_right_step ()
1618 samplepos_t xdelta = (current_page_samples() / 8);
1620 if (max_samplepos - xdelta > _leftmost_sample) {
1621 reset_x_origin (_leftmost_sample + xdelta);
1623 reset_x_origin (max_samplepos - current_page_samples());
1628 Editor::scroll_left_half_page ()
1630 samplepos_t xdelta = (current_page_samples() / 2);
1631 if (_leftmost_sample > xdelta) {
1632 reset_x_origin (_leftmost_sample - xdelta);
1639 Editor::scroll_right_half_page ()
1641 samplepos_t xdelta = (current_page_samples() / 2);
1642 if (max_samplepos - xdelta > _leftmost_sample) {
1643 reset_x_origin (_leftmost_sample + xdelta);
1645 reset_x_origin (max_samplepos - current_page_samples());
1652 Editor::tav_zoom_step (bool coarser)
1654 DisplaySuspender ds;
1658 if (selection->tracks.empty()) {
1661 ts = &selection->tracks;
1664 for (TrackViewList::iterator i = ts->begin(); i != ts->end(); ++i) {
1665 TimeAxisView *tv = (static_cast<TimeAxisView*>(*i));
1666 tv->step_height (coarser);
1671 Editor::tav_zoom_smooth (bool coarser, bool force_all)
1673 DisplaySuspender ds;
1677 if (selection->tracks.empty() || force_all) {
1680 ts = &selection->tracks;
1683 for (TrackViewList::iterator i = ts->begin(); i != ts->end(); ++i) {
1684 TimeAxisView *tv = (static_cast<TimeAxisView*>(*i));
1685 uint32_t h = tv->current_height ();
1690 if (h >= TimeAxisView::preset_height (HeightSmall)) {
1695 tv->set_height (h + 5);
1701 Editor::temporal_zoom_step_mouse_focus_scale (bool zoom_out, double scale)
1703 Editing::ZoomFocus temp_focus = zoom_focus;
1704 zoom_focus = Editing::ZoomFocusMouse;
1705 temporal_zoom_step_scale (zoom_out, scale);
1706 zoom_focus = temp_focus;
1710 Editor::temporal_zoom_step_mouse_focus (bool zoom_out)
1712 temporal_zoom_step_mouse_focus_scale (zoom_out, 2.0);
1716 Editor::temporal_zoom_step (bool zoom_out)
1718 temporal_zoom_step_scale (zoom_out, 2.0);
1722 Editor::temporal_zoom_step_scale (bool zoom_out, double scale)
1724 ENSURE_GUI_THREAD (*this, &Editor::temporal_zoom_step, zoom_out, scale)
1726 samplecnt_t nspp = samples_per_pixel;
1730 if (nspp == samples_per_pixel) {
1735 if (nspp == samples_per_pixel) {
1740 //zoom-behavior-tweaks
1741 //limit our maximum zoom to the session gui extents value
1742 std::pair<samplepos_t, samplepos_t> ext = session_gui_extents();
1743 samplecnt_t session_extents_pp = (ext.second - ext.first) / _visible_canvas_width;
1744 if (nspp > session_extents_pp)
1745 nspp = session_extents_pp;
1747 temporal_zoom (nspp);
1751 Editor::temporal_zoom (samplecnt_t fpp)
1757 samplepos_t current_page = current_page_samples();
1758 samplepos_t current_leftmost = _leftmost_sample;
1759 samplepos_t current_rightmost;
1760 samplepos_t current_center;
1761 samplepos_t new_page_size;
1762 samplepos_t half_page_size;
1763 samplepos_t leftmost_after_zoom = 0;
1765 bool in_track_canvas;
1766 bool use_mouse_sample = true;
1770 if (fpp == samples_per_pixel) {
1774 // Imposing an arbitrary limit to zoom out as too much zoom out produces
1775 // segfaults for lack of memory. If somebody decides this is not high enough I
1776 // believe it can be raisen to higher values but some limit must be in place.
1778 // This constant represents 1 day @ 48kHz on a 1600 pixel wide display
1779 // all of which is used for the editor track displays. The whole day
1780 // would be 4147200000 samples, so 2592000 samples per pixel.
1782 nfpp = min (fpp, (samplecnt_t) 2592000);
1783 nfpp = max ((samplecnt_t) 1, nfpp);
1785 new_page_size = (samplepos_t) floor (_visible_canvas_width * nfpp);
1786 half_page_size = new_page_size / 2;
1788 switch (zoom_focus) {
1790 leftmost_after_zoom = current_leftmost;
1793 case ZoomFocusRight:
1794 current_rightmost = _leftmost_sample + current_page;
1795 if (current_rightmost < new_page_size) {
1796 leftmost_after_zoom = 0;
1798 leftmost_after_zoom = current_rightmost - new_page_size;
1802 case ZoomFocusCenter:
1803 current_center = current_leftmost + (current_page/2);
1804 if (current_center < half_page_size) {
1805 leftmost_after_zoom = 0;
1807 leftmost_after_zoom = current_center - half_page_size;
1811 case ZoomFocusPlayhead:
1812 /* centre playhead */
1813 l = playhead_cursor->current_sample () - (new_page_size * 0.5);
1816 leftmost_after_zoom = 0;
1817 } else if (l > max_samplepos) {
1818 leftmost_after_zoom = max_samplepos - new_page_size;
1820 leftmost_after_zoom = (samplepos_t) l;
1824 case ZoomFocusMouse:
1825 /* try to keep the mouse over the same point in the display */
1827 if (_drags->active()) {
1828 where = _drags->current_pointer_sample ();
1829 } else if (!mouse_sample (where, in_track_canvas)) {
1830 use_mouse_sample = false;
1833 if (use_mouse_sample) {
1834 l = - ((new_page_size * ((where - current_leftmost)/(double)current_page)) - where);
1837 leftmost_after_zoom = 0;
1838 } else if (l > max_samplepos) {
1839 leftmost_after_zoom = max_samplepos - new_page_size;
1841 leftmost_after_zoom = (samplepos_t) l;
1844 /* use playhead instead */
1845 where = playhead_cursor->current_sample ();
1847 if (where < half_page_size) {
1848 leftmost_after_zoom = 0;
1850 leftmost_after_zoom = where - half_page_size;
1856 /* try to keep the edit point in the same place */
1857 where = get_preferred_edit_position ();
1861 double l = - ((new_page_size * ((where - current_leftmost)/(double)current_page)) - where);
1864 leftmost_after_zoom = 0;
1865 } else if (l > max_samplepos) {
1866 leftmost_after_zoom = max_samplepos - new_page_size;
1868 leftmost_after_zoom = (samplepos_t) l;
1872 /* edit point not defined */
1879 // leftmost_after_zoom = min (leftmost_after_zoom, _session->current_end_sample());
1881 reposition_and_zoom (leftmost_after_zoom, nfpp);
1885 Editor::calc_extra_zoom_edges(samplepos_t &start, samplepos_t &end)
1887 /* this func helps make sure we leave a little space
1888 at each end of the editor so that the zoom doesn't fit the region
1889 precisely to the screen.
1892 GdkScreen* screen = gdk_screen_get_default ();
1893 const gint pixwidth = gdk_screen_get_width (screen);
1894 const gint mmwidth = gdk_screen_get_width_mm (screen);
1895 const double pix_per_mm = (double) pixwidth/ (double) mmwidth;
1896 const double one_centimeter_in_pixels = pix_per_mm * 10.0;
1898 const samplepos_t range = end - start;
1899 const samplecnt_t new_fpp = (samplecnt_t) ceil ((double) range / (double) _visible_canvas_width);
1900 const samplepos_t extra_samples = (samplepos_t) floor (one_centimeter_in_pixels * new_fpp);
1902 if (start > extra_samples) {
1903 start -= extra_samples;
1908 if (max_samplepos - extra_samples > end) {
1909 end += extra_samples;
1911 end = max_samplepos;
1916 Editor::get_selection_extents (samplepos_t &start, samplepos_t &end) const
1918 start = max_samplepos;
1922 //ToDo: if notes are selected, set extents to that selection
1924 //ToDo: if control points are selected, set extents to that selection
1926 if (!selection->regions.empty()) {
1927 RegionSelection rs = get_regions_from_selection_and_entered ();
1929 for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
1931 if ((*i)->region()->position() < start) {
1932 start = (*i)->region()->position();
1935 if ((*i)->region()->last_sample() + 1 > end) {
1936 end = (*i)->region()->last_sample() + 1;
1940 } else if (!selection->time.empty()) {
1941 start = selection->time.start();
1942 end = selection->time.end_sample();
1944 ret = false; //no selection found
1947 if ((start == 0 && end == 0) || end < start) {
1956 Editor::temporal_zoom_selection (Editing::ZoomAxis axes)
1958 if (!selection) return;
1960 if (selection->regions.empty() && selection->time.empty()) {
1961 if (axes == Horizontal || axes == Both) {
1962 temporal_zoom_step(true);
1964 if (axes == Vertical || axes == Both) {
1965 if (!track_views.empty()) {
1969 //implicit hack: by extending the top & bottom check outside the current view limits, we include the trackviews immediately above & below what is visible
1970 const double top = vertical_adjustment.get_value() - 10;
1971 const double btm = top + _visible_canvas_height + 10;
1973 for (TrackViewList::iterator iter = track_views.begin(); iter != track_views.end(); ++iter) {
1974 if ((*iter)->covered_by_y_range (top, btm)) {
1975 tvl.push_back(*iter);
1985 //ToDo: if notes are selected, zoom to that
1987 //ToDo: if control points are selected, zoom to that
1989 if (axes == Horizontal || axes == Both) {
1991 samplepos_t start, end;
1992 if (get_selection_extents (start, end)) {
1993 calc_extra_zoom_edges (start, end);
1994 temporal_zoom_by_sample (start, end);
1998 if (axes == Vertical || axes == Both) {
2002 //normally, we don't do anything "automatic" to the user's selection.
2003 //but in this case, we will clear the selection after a zoom-to-selection.
2008 Editor::temporal_zoom_session ()
2010 ENSURE_GUI_THREAD (*this, &Editor::temporal_zoom_session)
2013 samplecnt_t start = _session->current_start_sample();
2014 samplecnt_t end = _session->current_end_sample();
2016 if (_session->actively_recording ()) {
2017 samplepos_t cur = playhead_cursor->current_sample ();
2019 /* recording beyond the end marker; zoom out
2020 * by 5 seconds more so that if 'follow
2021 * playhead' is active we don't immediately
2024 end = cur + _session->sample_rate() * 5;
2028 if ((start == 0 && end == 0) || end < start) {
2032 calc_extra_zoom_edges(start, end);
2034 temporal_zoom_by_sample (start, end);
2039 Editor::temporal_zoom_extents ()
2041 ENSURE_GUI_THREAD (*this, &Editor::temporal_zoom_extents)
2044 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
2046 samplecnt_t start = ext.first;
2047 samplecnt_t end = ext.second;
2049 if (_session->actively_recording ()) {
2050 samplepos_t cur = playhead_cursor->current_sample ();
2052 /* recording beyond the end marker; zoom out
2053 * by 5 seconds more so that if 'follow
2054 * playhead' is active we don't immediately
2057 end = cur + _session->sample_rate() * 5;
2061 if ((start == 0 && end == 0) || end < start) {
2065 calc_extra_zoom_edges(start, end);
2067 temporal_zoom_by_sample (start, end);
2072 Editor::temporal_zoom_by_sample (samplepos_t start, samplepos_t end)
2074 if (!_session) return;
2076 if ((start == 0 && end == 0) || end < start) {
2080 samplepos_t range = end - start;
2082 const samplecnt_t new_fpp = (samplecnt_t) ceil ((double) range / (double) _visible_canvas_width);
2084 samplepos_t new_page = range;
2085 samplepos_t middle = (samplepos_t) floor ((double) start + ((double) range / 2.0f));
2086 samplepos_t new_leftmost = (samplepos_t) floor ((double) middle - ((double) new_page / 2.0f));
2088 if (new_leftmost > middle) {
2092 if (new_leftmost < 0) {
2096 reposition_and_zoom (new_leftmost, new_fpp);
2100 Editor::temporal_zoom_to_sample (bool coarser, samplepos_t sample)
2106 samplecnt_t range_before = sample - _leftmost_sample;
2107 samplecnt_t new_spp;
2110 if (samples_per_pixel <= 1) {
2113 new_spp = samples_per_pixel + (samples_per_pixel/2);
2115 range_before += range_before/2;
2117 if (samples_per_pixel >= 1) {
2118 new_spp = samples_per_pixel - (samples_per_pixel/2);
2120 /* could bail out here since we cannot zoom any finer,
2121 but leave that to the equality test below
2123 new_spp = samples_per_pixel;
2126 range_before -= range_before/2;
2129 if (new_spp == samples_per_pixel) {
2133 /* zoom focus is automatically taken as @param sample when this
2137 samplepos_t new_leftmost = sample - (samplepos_t)range_before;
2139 if (new_leftmost > sample) {
2143 if (new_leftmost < 0) {
2147 reposition_and_zoom (new_leftmost, new_spp);
2152 Editor::choose_new_marker_name(string &name) {
2154 if (!UIConfiguration::instance().get_name_new_markers()) {
2155 /* don't prompt user for a new name */
2159 Prompter dialog (true);
2161 dialog.set_prompt (_("New Name:"));
2163 dialog.set_title (_("New Location Marker"));
2165 dialog.set_name ("MarkNameWindow");
2166 dialog.set_size_request (250, -1);
2167 dialog.set_position (Gtk::WIN_POS_MOUSE);
2169 dialog.add_button (Stock::OK, RESPONSE_ACCEPT);
2170 dialog.set_initial_text (name);
2174 switch (dialog.run ()) {
2175 case RESPONSE_ACCEPT:
2181 dialog.get_result(name);
2188 Editor::add_location_from_selection ()
2192 if (selection->time.empty()) {
2196 if (_session == 0 || clicked_axisview == 0) {
2200 samplepos_t start = selection->time[clicked_selection].start;
2201 samplepos_t end = selection->time[clicked_selection].end;
2203 _session->locations()->next_available_name(rangename,"selection");
2204 Location *location = new Location (*_session, start, end, rangename, Location::IsRangeMarker, get_grid_music_divisions(0));
2206 begin_reversible_command (_("add marker"));
2208 XMLNode &before = _session->locations()->get_state();
2209 _session->locations()->add (location, true);
2210 XMLNode &after = _session->locations()->get_state();
2211 _session->add_command(new MementoCommand<Locations>(*(_session->locations()), &before, &after));
2213 commit_reversible_command ();
2217 Editor::add_location_mark (samplepos_t where)
2221 select_new_marker = true;
2223 _session->locations()->next_available_name(markername,"mark");
2224 if (!choose_new_marker_name(markername)) {
2227 Location *location = new Location (*_session, where, where, markername, Location::IsMark, get_grid_music_divisions (0));
2228 begin_reversible_command (_("add marker"));
2230 XMLNode &before = _session->locations()->get_state();
2231 _session->locations()->add (location, true);
2232 XMLNode &after = _session->locations()->get_state();
2233 _session->add_command(new MementoCommand<Locations>(*(_session->locations()), &before, &after));
2235 commit_reversible_command ();
2239 Editor::set_session_start_from_playhead ()
2245 if ((loc = _session->locations()->session_range_location()) == 0) { //should never happen
2246 _session->set_session_extents (_session->audible_sample(), _session->audible_sample());
2248 XMLNode &before = loc->get_state();
2250 _session->set_session_extents (_session->audible_sample(), loc->end());
2252 XMLNode &after = loc->get_state();
2254 begin_reversible_command (_("Set session start"));
2256 _session->add_command (new MementoCommand<Location>(*loc, &before, &after));
2258 commit_reversible_command ();
2263 Editor::set_session_end_from_playhead ()
2269 if ((loc = _session->locations()->session_range_location()) == 0) { //should never happen
2270 _session->set_session_extents (_session->audible_sample(), _session->audible_sample());
2272 XMLNode &before = loc->get_state();
2274 _session->set_session_extents (loc->start(), _session->audible_sample());
2276 XMLNode &after = loc->get_state();
2278 begin_reversible_command (_("Set session start"));
2280 _session->add_command (new MementoCommand<Location>(*loc, &before, &after));
2282 commit_reversible_command ();
2285 _session->set_end_is_free (false);
2290 Editor::toggle_location_at_playhead_cursor ()
2292 if (!do_remove_location_at_playhead_cursor())
2294 add_location_from_playhead_cursor();
2299 Editor::add_location_from_playhead_cursor ()
2301 add_location_mark (_session->audible_sample());
2305 Editor::do_remove_location_at_playhead_cursor ()
2307 bool removed = false;
2310 XMLNode &before = _session->locations()->get_state();
2312 //find location(s) at this time
2313 Locations::LocationList locs;
2314 _session->locations()->find_all_between (_session->audible_sample(), _session->audible_sample()+1, locs, Location::Flags(0));
2315 for (Locations::LocationList::iterator i = locs.begin(); i != locs.end(); ++i) {
2316 if ((*i)->is_mark()) {
2317 _session->locations()->remove (*i);
2324 begin_reversible_command (_("remove marker"));
2325 XMLNode &after = _session->locations()->get_state();
2326 _session->add_command(new MementoCommand<Locations>(*(_session->locations()), &before, &after));
2327 commit_reversible_command ();
2334 Editor::remove_location_at_playhead_cursor ()
2336 do_remove_location_at_playhead_cursor ();
2339 /** Add a range marker around each selected region */
2341 Editor::add_locations_from_region ()
2343 RegionSelection rs = get_regions_from_selection_and_entered ();
2348 bool commit = false;
2350 XMLNode &before = _session->locations()->get_state();
2352 for (RegionSelection::iterator i = rs.begin (); i != rs.end (); ++i) {
2354 boost::shared_ptr<Region> region = (*i)->region ();
2356 Location *location = new Location (*_session, region->position(), region->last_sample(), region->name(), Location::IsRangeMarker, 0);
2358 _session->locations()->add (location, true);
2363 begin_reversible_command (selection->regions.size () > 1 ? _("add markers") : _("add marker"));
2364 XMLNode &after = _session->locations()->get_state();
2365 _session->add_command (new MementoCommand<Locations>(*(_session->locations()), &before, &after));
2366 commit_reversible_command ();
2370 /** Add a single range marker around all selected regions */
2372 Editor::add_location_from_region ()
2374 RegionSelection rs = get_regions_from_selection_and_entered ();
2380 XMLNode &before = _session->locations()->get_state();
2384 if (rs.size() > 1) {
2385 _session->locations()->next_available_name(markername, "regions");
2387 RegionView* rv = *(rs.begin());
2388 boost::shared_ptr<Region> region = rv->region();
2389 markername = region->name();
2392 if (!choose_new_marker_name(markername)) {
2396 // single range spanning all selected
2397 Location *location = new Location (*_session, selection->regions.start(), selection->regions.end_sample(), markername, Location::IsRangeMarker, 0);
2398 _session->locations()->add (location, true);
2400 begin_reversible_command (_("add marker"));
2401 XMLNode &after = _session->locations()->get_state();
2402 _session->add_command (new MementoCommand<Locations>(*(_session->locations()), &before, &after));
2403 commit_reversible_command ();
2409 Editor::jump_forward_to_mark ()
2415 samplepos_t pos = _session->locations()->first_mark_after (playhead_cursor->current_sample());
2421 _session->request_locate (pos, _session->transport_rolling());
2425 Editor::jump_backward_to_mark ()
2431 samplepos_t pos = _session->locations()->first_mark_before (playhead_cursor->current_sample());
2433 //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...
2434 if (_session->transport_rolling()) {
2435 if ((playhead_cursor->current_sample() - pos) < _session->sample_rate()/2) {
2436 samplepos_t prior = _session->locations()->first_mark_before (pos);
2445 _session->request_locate (pos, _session->transport_rolling());
2451 samplepos_t const pos = _session->audible_sample ();
2454 _session->locations()->next_available_name (markername, "mark");
2456 if (!choose_new_marker_name (markername)) {
2460 _session->locations()->add (new Location (*_session, pos, 0, markername, Location::IsMark, 0), true);
2464 Editor::clear_markers ()
2467 begin_reversible_command (_("clear markers"));
2469 XMLNode &before = _session->locations()->get_state();
2470 _session->locations()->clear_markers ();
2471 XMLNode &after = _session->locations()->get_state();
2472 _session->add_command(new MementoCommand<Locations>(*(_session->locations()), &before, &after));
2474 commit_reversible_command ();
2479 Editor::clear_ranges ()
2482 begin_reversible_command (_("clear ranges"));
2484 XMLNode &before = _session->locations()->get_state();
2486 _session->locations()->clear_ranges ();
2488 XMLNode &after = _session->locations()->get_state();
2489 _session->add_command(new MementoCommand<Locations>(*(_session->locations()), &before, &after));
2491 commit_reversible_command ();
2496 Editor::clear_locations ()
2498 begin_reversible_command (_("clear locations"));
2500 XMLNode &before = _session->locations()->get_state();
2501 _session->locations()->clear ();
2502 XMLNode &after = _session->locations()->get_state();
2503 _session->add_command(new MementoCommand<Locations>(*(_session->locations()), &before, &after));
2505 commit_reversible_command ();
2509 Editor::unhide_markers ()
2511 for (LocationMarkerMap::iterator i = location_markers.begin(); i != location_markers.end(); ++i) {
2512 Location *l = (*i).first;
2513 if (l->is_hidden() && l->is_mark()) {
2514 l->set_hidden(false, this);
2520 Editor::unhide_ranges ()
2522 for (LocationMarkerMap::iterator i = location_markers.begin(); i != location_markers.end(); ++i) {
2523 Location *l = (*i).first;
2524 if (l->is_hidden() && l->is_range_marker()) {
2525 l->set_hidden(false, this);
2530 /* INSERT/REPLACE */
2533 Editor::insert_region_list_selection (float times)
2535 RouteTimeAxisView *tv = 0;
2536 boost::shared_ptr<Playlist> playlist;
2538 if (clicked_routeview != 0) {
2539 tv = clicked_routeview;
2540 } else if (!selection->tracks.empty()) {
2541 if ((tv = dynamic_cast<RouteTimeAxisView*>(selection->tracks.front())) == 0) {
2544 } else if (entered_track != 0) {
2545 if ((tv = dynamic_cast<RouteTimeAxisView*>(entered_track)) == 0) {
2552 if ((playlist = tv->playlist()) == 0) {
2556 boost::shared_ptr<Region> region = _regions->get_single_selection ();
2561 begin_reversible_command (_("insert region"));
2562 playlist->clear_changes ();
2563 playlist->add_region ((RegionFactory::create (region, true)), get_preferred_edit_position(), times);
2564 if (Config->get_edit_mode() == Ripple)
2565 playlist->ripple (get_preferred_edit_position(), region->length() * times, boost::shared_ptr<Region>());
2567 _session->add_command(new StatefulDiffCommand (playlist));
2568 commit_reversible_command ();
2571 /* BUILT-IN EFFECTS */
2574 Editor::reverse_selection ()
2579 /* GAIN ENVELOPE EDITING */
2582 Editor::edit_envelope ()
2589 Editor::transition_to_rolling (bool fwd)
2595 if (_session->config.get_external_sync()) {
2596 switch (Config->get_sync_source()) {
2600 /* transport controlled by the master */
2605 if (_session->is_auditioning()) {
2606 _session->cancel_audition ();
2610 _session->request_transport_speed (fwd ? 1.0f : -1.0f);
2614 Editor::play_from_start ()
2616 _session->request_locate (_session->current_start_sample(), true);
2620 Editor::play_from_edit_point ()
2622 _session->request_locate (get_preferred_edit_position(), true);
2626 Editor::play_from_edit_point_and_return ()
2628 samplepos_t start_sample;
2629 samplepos_t return_sample;
2631 start_sample = get_preferred_edit_position (EDIT_IGNORE_PHEAD);
2633 if (_session->transport_rolling()) {
2634 _session->request_locate (start_sample, false);
2638 /* don't reset the return sample if its already set */
2640 if ((return_sample = _session->requested_return_sample()) < 0) {
2641 return_sample = _session->audible_sample();
2644 if (start_sample >= 0) {
2645 _session->request_roll_at_and_return (start_sample, return_sample);
2650 Editor::play_selection ()
2652 samplepos_t start, end;
2653 if (!get_selection_extents (start, end))
2656 AudioRange ar (start, end, 0);
2657 list<AudioRange> lar;
2660 _session->request_play_range (&lar, true);
2665 Editor::maybe_locate_with_edit_preroll (samplepos_t location)
2667 if (_session->transport_rolling() || !UIConfiguration::instance().get_follow_edits() || _session->config.get_external_sync())
2670 location -= _session->preroll_samples (location);
2672 //don't try to locate before the beginning of time
2677 //if follow_playhead is on, keep the playhead on the screen
2678 if (_follow_playhead)
2679 if (location < _leftmost_sample)
2680 location = _leftmost_sample;
2682 _session->request_locate (location);
2686 Editor::play_with_preroll ()
2688 samplepos_t start, end;
2689 if (UIConfiguration::instance().get_follow_edits() && get_selection_extents (start, end)) {
2690 const samplepos_t preroll = _session->preroll_samples (start);
2692 samplepos_t ret = start;
2694 if (start > preroll) {
2695 start = start - preroll;
2698 end = end + preroll; //"post-roll"
2700 AudioRange ar (start, end, 0);
2701 list<AudioRange> lar;
2704 _session->request_play_range (&lar, true);
2705 _session->set_requested_return_sample (ret); //force auto-return to return to range start, without the preroll
2707 samplepos_t ph = playhead_cursor->current_sample ();
2708 const samplepos_t preroll = _session->preroll_samples (ph);
2711 start = ph - preroll;
2715 _session->request_locate (start, true);
2716 _session->set_requested_return_sample (ph); //force auto-return to return to playhead location, without the preroll
2721 Editor::rec_with_preroll ()
2723 samplepos_t ph = playhead_cursor->current_sample ();
2724 samplepos_t preroll = _session->preroll_samples (ph);
2725 _session->request_preroll_record_trim (ph, preroll);
2729 Editor::rec_with_count_in ()
2731 _session->request_count_in_record ();
2735 Editor::play_location (Location& location)
2737 if (location.start() <= location.end()) {
2741 _session->request_bounded_roll (location.start(), location.end());
2745 Editor::loop_location (Location& location)
2747 if (location.start() <= location.end()) {
2753 if ((tll = transport_loop_location()) != 0) {
2754 tll->set (location.start(), location.end());
2756 // enable looping, reposition and start rolling
2757 _session->request_locate (tll->start(), true);
2758 _session->request_play_loop (true);
2763 Editor::do_layer_operation (LayerOperation op)
2765 if (selection->regions.empty ()) {
2769 bool const multiple = selection->regions.size() > 1;
2773 begin_reversible_command (_("raise regions"));
2775 begin_reversible_command (_("raise region"));
2781 begin_reversible_command (_("raise regions to top"));
2783 begin_reversible_command (_("raise region to top"));
2789 begin_reversible_command (_("lower regions"));
2791 begin_reversible_command (_("lower region"));
2797 begin_reversible_command (_("lower regions to bottom"));
2799 begin_reversible_command (_("lower region"));
2804 set<boost::shared_ptr<Playlist> > playlists = selection->regions.playlists ();
2805 for (set<boost::shared_ptr<Playlist> >::iterator i = playlists.begin(); i != playlists.end(); ++i) {
2806 (*i)->clear_owned_changes ();
2809 for (RegionSelection::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
2810 boost::shared_ptr<Region> r = (*i)->region ();
2822 r->lower_to_bottom ();
2826 for (set<boost::shared_ptr<Playlist> >::iterator i = playlists.begin(); i != playlists.end(); ++i) {
2827 vector<Command*> cmds;
2829 _session->add_commands (cmds);
2832 commit_reversible_command ();
2836 Editor::raise_region ()
2838 do_layer_operation (Raise);
2842 Editor::raise_region_to_top ()
2844 do_layer_operation (RaiseToTop);
2848 Editor::lower_region ()
2850 do_layer_operation (Lower);
2854 Editor::lower_region_to_bottom ()
2856 do_layer_operation (LowerToBottom);
2859 /** Show the region editor for the selected regions */
2861 Editor::show_region_properties ()
2863 selection->foreach_regionview (&RegionView::show_region_editor);
2866 /** Show the midi list editor for the selected MIDI regions */
2868 Editor::show_midi_list_editor ()
2870 selection->foreach_midi_regionview (&MidiRegionView::show_list_editor);
2874 Editor::rename_region ()
2876 RegionSelection rs = get_regions_from_selection_and_entered ();
2882 ArdourDialog d (_("Rename Region"), true, false);
2884 Label label (_("New name:"));
2887 hbox.set_spacing (6);
2888 hbox.pack_start (label, false, false);
2889 hbox.pack_start (entry, true, true);
2891 d.get_vbox()->set_border_width (12);
2892 d.get_vbox()->pack_start (hbox, false, false);
2894 d.add_button(Gtk::Stock::CANCEL, Gtk::RESPONSE_CANCEL);
2895 d.add_button(Gtk::Stock::OK, Gtk::RESPONSE_OK);
2897 d.set_size_request (300, -1);
2899 entry.set_text (rs.front()->region()->name());
2900 entry.select_region (0, -1);
2902 entry.signal_activate().connect (sigc::bind (sigc::mem_fun (d, &Dialog::response), RESPONSE_OK));
2908 int const ret = d.run();
2912 if (ret != RESPONSE_OK) {
2916 std::string str = entry.get_text();
2917 strip_whitespace_edges (str);
2919 rs.front()->region()->set_name (str);
2920 _regions->redisplay ();
2924 /** Start an audition of the first selected region */
2926 Editor::play_edit_range ()
2928 samplepos_t start, end;
2930 if (get_edit_op_range (start, end)) {
2931 _session->request_bounded_roll (start, end);
2936 Editor::play_selected_region ()
2938 samplepos_t start = max_samplepos;
2939 samplepos_t end = 0;
2941 RegionSelection rs = get_regions_from_selection_and_entered ();
2947 for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
2948 if ((*i)->region()->position() < start) {
2949 start = (*i)->region()->position();
2951 if ((*i)->region()->last_sample() + 1 > end) {
2952 end = (*i)->region()->last_sample() + 1;
2956 _session->request_bounded_roll (start, end);
2960 Editor::audition_playlist_region_standalone (boost::shared_ptr<Region> region)
2962 _session->audition_region (region);
2966 Editor::region_from_selection ()
2968 if (clicked_axisview == 0) {
2972 if (selection->time.empty()) {
2976 samplepos_t start = selection->time[clicked_selection].start;
2977 samplepos_t end = selection->time[clicked_selection].end;
2979 TrackViewList tracks = get_tracks_for_range_action ();
2981 samplepos_t selection_cnt = end - start + 1;
2983 for (TrackSelection::iterator i = tracks.begin(); i != tracks.end(); ++i) {
2984 boost::shared_ptr<Region> current;
2985 boost::shared_ptr<Playlist> pl;
2986 samplepos_t internal_start;
2989 if ((pl = (*i)->playlist()) == 0) {
2993 if ((current = pl->top_region_at (start)) == 0) {
2997 internal_start = start - current->position();
2998 RegionFactory::region_name (new_name, current->name(), true);
3002 plist.add (ARDOUR::Properties::start, current->start() + internal_start);
3003 plist.add (ARDOUR::Properties::length, selection_cnt);
3004 plist.add (ARDOUR::Properties::name, new_name);
3005 plist.add (ARDOUR::Properties::layer, 0);
3007 boost::shared_ptr<Region> region (RegionFactory::create (current, plist));
3012 Editor::create_region_from_selection (vector<boost::shared_ptr<Region> >& new_regions)
3014 if (selection->time.empty() || selection->tracks.empty()) {
3018 samplepos_t start, end;
3019 if (clicked_selection) {
3020 start = selection->time[clicked_selection].start;
3021 end = selection->time[clicked_selection].end;
3023 start = selection->time.start();
3024 end = selection->time.end_sample();
3027 TrackViewList ts = selection->tracks.filter_to_unique_playlists ();
3028 sort_track_selection (ts);
3030 for (TrackSelection::iterator i = ts.begin(); i != ts.end(); ++i) {
3031 boost::shared_ptr<Region> current;
3032 boost::shared_ptr<Playlist> playlist;
3033 samplepos_t internal_start;
3036 if ((playlist = (*i)->playlist()) == 0) {
3040 if ((current = playlist->top_region_at(start)) == 0) {
3044 internal_start = start - current->position();
3045 RegionFactory::region_name (new_name, current->name(), true);
3049 plist.add (ARDOUR::Properties::start, current->start() + internal_start);
3050 plist.add (ARDOUR::Properties::length, end - start + 1);
3051 plist.add (ARDOUR::Properties::name, new_name);
3053 new_regions.push_back (RegionFactory::create (current, plist));
3058 Editor::split_multichannel_region ()
3060 RegionSelection rs = get_regions_from_selection_and_entered ();
3066 vector< boost::shared_ptr<Region> > v;
3068 for (list<RegionView*>::iterator x = rs.begin(); x != rs.end(); ++x) {
3069 (*x)->region()->separate_by_channel (v);
3074 Editor::new_region_from_selection ()
3076 region_from_selection ();
3077 cancel_selection ();
3081 add_if_covered (RegionView* rv, const AudioRange* ar, RegionSelection* rs)
3083 switch (rv->region()->coverage (ar->start, ar->end - 1)) {
3084 // n.b. -1 because AudioRange::end is one past the end, but coverage expects inclusive ranges
3085 case Evoral::OverlapNone:
3093 * - selected tracks, or if there are none...
3094 * - tracks containing selected regions, or if there are none...
3099 Editor::get_tracks_for_range_action () const
3103 if (selection->tracks.empty()) {
3105 /* use tracks with selected regions */
3107 RegionSelection rs = selection->regions;
3109 for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
3110 TimeAxisView* tv = &(*i)->get_time_axis_view();
3112 if (!t.contains (tv)) {
3118 /* no regions and no tracks: use all tracks */
3124 t = selection->tracks;
3127 return t.filter_to_unique_playlists();
3131 Editor::separate_regions_between (const TimeSelection& ts)
3133 bool in_command = false;
3134 boost::shared_ptr<Playlist> playlist;
3135 RegionSelection new_selection;
3137 TrackViewList tmptracks = get_tracks_for_range_action ();
3138 sort_track_selection (tmptracks);
3140 for (TrackSelection::iterator i = tmptracks.begin(); i != tmptracks.end(); ++i) {
3142 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> ((*i));
3148 if (!rtv->is_track()) {
3152 /* no edits to destructive tracks */
3154 if (rtv->track()->destructive()) {
3158 if ((playlist = rtv->playlist()) != 0) {
3160 playlist->clear_changes ();
3162 /* XXX need to consider musical time selections here at some point */
3164 for (list<AudioRange>::const_iterator t = ts.begin(); t != ts.end(); ++t) {
3166 sigc::connection c = rtv->view()->RegionViewAdded.connect (
3167 sigc::mem_fun(*this, &Editor::collect_new_region_view));
3169 latest_regionviews.clear ();
3171 playlist->partition ((*t).start, (*t).end, false);
3175 if (!latest_regionviews.empty()) {
3177 rtv->view()->foreach_regionview (sigc::bind (
3178 sigc::ptr_fun (add_if_covered),
3179 &(*t), &new_selection));
3182 begin_reversible_command (_("separate"));
3186 /* pick up changes to existing regions */
3188 vector<Command*> cmds;
3189 playlist->rdiff (cmds);
3190 _session->add_commands (cmds);
3192 /* pick up changes to the playlist itself (adds/removes)
3195 _session->add_command(new StatefulDiffCommand (playlist));
3202 // selection->set (new_selection);
3204 commit_reversible_command ();
3208 struct PlaylistState {
3209 boost::shared_ptr<Playlist> playlist;
3213 /** Take tracks from get_tracks_for_range_action and cut any regions
3214 * on those tracks so that the tracks are empty over the time
3218 Editor::separate_region_from_selection ()
3220 /* preferentially use *all* ranges in the time selection if we're in range mode
3221 to allow discontiguous operation, since get_edit_op_range() currently
3222 returns a single range.
3225 if (!selection->time.empty()) {
3227 separate_regions_between (selection->time);
3234 if (get_edit_op_range (start, end)) {
3236 AudioRange ar (start, end, 1);
3240 separate_regions_between (ts);
3246 Editor::separate_region_from_punch ()
3248 Location* loc = _session->locations()->auto_punch_location();
3250 separate_regions_using_location (*loc);
3255 Editor::separate_region_from_loop ()
3257 Location* loc = _session->locations()->auto_loop_location();
3259 separate_regions_using_location (*loc);
3264 Editor::separate_regions_using_location (Location& loc)
3266 if (loc.is_mark()) {
3270 AudioRange ar (loc.start(), loc.end(), 1);
3275 separate_regions_between (ts);
3278 /** Separate regions under the selected region */
3280 Editor::separate_under_selected_regions ()
3282 vector<PlaylistState> playlists;
3286 rs = get_regions_from_selection_and_entered();
3288 if (!_session || rs.empty()) {
3292 begin_reversible_command (_("separate region under"));
3294 list<boost::shared_ptr<Region> > regions_to_remove;
3296 for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
3297 // we can't just remove the region(s) in this loop because
3298 // this removes them from the RegionSelection, and they thus
3299 // disappear from underneath the iterator, and the ++i above
3300 // SEGVs in a puzzling fashion.
3302 // so, first iterate over the regions to be removed from rs and
3303 // add them to the regions_to_remove list, and then
3304 // iterate over the list to actually remove them.
3306 regions_to_remove.push_back ((*i)->region());
3309 for (list<boost::shared_ptr<Region> >::iterator rl = regions_to_remove.begin(); rl != regions_to_remove.end(); ++rl) {
3311 boost::shared_ptr<Playlist> playlist = (*rl)->playlist();
3314 // is this check necessary?
3318 vector<PlaylistState>::iterator i;
3320 //only take state if this is a new playlist.
3321 for (i = playlists.begin(); i != playlists.end(); ++i) {
3322 if ((*i).playlist == playlist) {
3327 if (i == playlists.end()) {
3329 PlaylistState before;
3330 before.playlist = playlist;
3331 before.before = &playlist->get_state();
3332 playlist->clear_changes ();
3333 playlist->freeze ();
3334 playlists.push_back(before);
3337 //Partition on the region bounds
3338 playlist->partition ((*rl)->first_sample() - 1, (*rl)->last_sample() + 1, true);
3340 //Re-add region that was just removed due to the partition operation
3341 playlist->add_region ((*rl), (*rl)->first_sample());
3344 vector<PlaylistState>::iterator pl;
3346 for (pl = playlists.begin(); pl != playlists.end(); ++pl) {
3347 (*pl).playlist->thaw ();
3348 _session->add_command(new MementoCommand<Playlist>(*(*pl).playlist, (*pl).before, &(*pl).playlist->get_state()));
3351 commit_reversible_command ();
3355 Editor::crop_region_to_selection ()
3357 if (!selection->time.empty()) {
3359 begin_reversible_command (_("Crop Regions to Time Selection"));
3360 for (std::list<AudioRange>::iterator i = selection->time.begin(); i != selection->time.end(); ++i) {
3361 crop_region_to ((*i).start, (*i).end);
3363 commit_reversible_command();
3369 if (get_edit_op_range (start, end)) {
3370 begin_reversible_command (_("Crop Regions to Edit Range"));
3372 crop_region_to (start, end);
3374 commit_reversible_command();
3381 Editor::crop_region_to (samplepos_t start, samplepos_t end)
3383 vector<boost::shared_ptr<Playlist> > playlists;
3384 boost::shared_ptr<Playlist> playlist;
3387 if (selection->tracks.empty()) {
3388 ts = track_views.filter_to_unique_playlists();
3390 ts = selection->tracks.filter_to_unique_playlists ();
3393 sort_track_selection (ts);
3395 for (TrackSelection::iterator i = ts.begin(); i != ts.end(); ++i) {
3397 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> ((*i));
3403 boost::shared_ptr<Track> t = rtv->track();
3405 if (t != 0 && ! t->destructive()) {
3407 if ((playlist = rtv->playlist()) != 0) {
3408 playlists.push_back (playlist);
3413 if (playlists.empty()) {
3418 samplepos_t new_start;
3419 samplepos_t new_end;
3420 samplecnt_t new_length;
3422 for (vector<boost::shared_ptr<Playlist> >::iterator i = playlists.begin(); i != playlists.end(); ++i) {
3424 /* Only the top regions at start and end have to be cropped */
3425 boost::shared_ptr<Region> region_at_start = (*i)->top_region_at(start);
3426 boost::shared_ptr<Region> region_at_end = (*i)->top_region_at(end);
3428 vector<boost::shared_ptr<Region> > regions;
3430 if (region_at_start != 0) {
3431 regions.push_back (region_at_start);
3433 if (region_at_end != 0) {
3434 regions.push_back (region_at_end);
3437 /* now adjust lengths */
3438 for (vector<boost::shared_ptr<Region> >::iterator i = regions.begin(); i != regions.end(); ++i) {
3440 pos = (*i)->position();
3441 new_start = max (start, pos);
3442 if (max_samplepos - pos > (*i)->length()) {
3443 new_end = pos + (*i)->length() - 1;
3445 new_end = max_samplepos;
3447 new_end = min (end, new_end);
3448 new_length = new_end - new_start + 1;
3450 (*i)->clear_changes ();
3451 (*i)->trim_to (new_start, new_length);
3452 _session->add_command (new StatefulDiffCommand (*i));
3458 Editor::region_fill_track ()
3460 boost::shared_ptr<Playlist> playlist;
3461 RegionSelection regions = get_regions_from_selection_and_entered ();
3462 RegionSelection foo;
3464 samplepos_t const end = _session->current_end_sample ();
3466 if (regions.empty () || regions.end_sample () + 1 >= end) {
3470 samplepos_t const start_sample = regions.start ();
3471 samplepos_t const end_sample = regions.end_sample ();
3472 samplecnt_t const gap = end_sample - start_sample + 1;
3474 begin_reversible_command (Operations::region_fill);
3476 selection->clear_regions ();
3478 for (RegionSelection::iterator i = regions.begin(); i != regions.end(); ++i) {
3480 boost::shared_ptr<Region> r ((*i)->region());
3482 TimeAxisView& tv = (*i)->get_time_axis_view();
3483 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (&tv);
3484 latest_regionviews.clear ();
3485 sigc::connection c = rtv->view()->RegionViewAdded.connect (sigc::mem_fun(*this, &Editor::collect_new_region_view));
3487 samplepos_t const position = end_sample + (r->first_sample() - start_sample + 1);
3488 playlist = (*i)->region()->playlist();
3489 playlist->clear_changes ();
3490 playlist->duplicate_until (r, position, gap, end);
3491 _session->add_command(new StatefulDiffCommand (playlist));
3495 foo.insert (foo.end(), latest_regionviews.begin(), latest_regionviews.end());
3499 selection->set (foo);
3502 commit_reversible_command ();
3506 Editor::set_region_sync_position ()
3508 set_sync_point (get_preferred_edit_position (), get_regions_from_selection_and_edit_point ());
3512 Editor::set_sync_point (samplepos_t where, const RegionSelection& rs)
3514 bool in_command = false;
3516 for (RegionSelection::const_iterator r = rs.begin(); r != rs.end(); ++r) {
3518 if (!(*r)->region()->covers (where)) {
3522 boost::shared_ptr<Region> region ((*r)->region());
3525 begin_reversible_command (_("set sync point"));
3529 region->clear_changes ();
3530 region->set_sync_position (where);
3531 _session->add_command(new StatefulDiffCommand (region));
3535 commit_reversible_command ();
3539 /** Remove the sync positions of the selection */
3541 Editor::remove_region_sync ()
3543 RegionSelection rs = get_regions_from_selection_and_entered ();
3549 begin_reversible_command (_("remove region sync"));
3551 for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
3553 (*i)->region()->clear_changes ();
3554 (*i)->region()->clear_sync_position ();
3555 _session->add_command(new StatefulDiffCommand ((*i)->region()));
3558 commit_reversible_command ();
3562 Editor::naturalize_region ()
3564 RegionSelection rs = get_regions_from_selection_and_entered ();
3570 if (rs.size() > 1) {
3571 begin_reversible_command (_("move regions to original position"));
3573 begin_reversible_command (_("move region to original position"));
3576 for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
3577 (*i)->region()->clear_changes ();
3578 (*i)->region()->move_to_natural_position ();
3579 _session->add_command (new StatefulDiffCommand ((*i)->region()));
3582 commit_reversible_command ();
3586 Editor::align_regions (RegionPoint what)
3588 RegionSelection const rs = get_regions_from_selection_and_edit_point ();
3594 begin_reversible_command (_("align selection"));
3596 samplepos_t const position = get_preferred_edit_position ();
3598 for (RegionSelection::const_iterator i = rs.begin(); i != rs.end(); ++i) {
3599 align_region_internal ((*i)->region(), what, position);
3602 commit_reversible_command ();
3605 struct RegionSortByTime {
3606 bool operator() (const RegionView* a, const RegionView* b) {
3607 return a->region()->position() < b->region()->position();
3612 Editor::align_regions_relative (RegionPoint point)
3614 RegionSelection const rs = get_regions_from_selection_and_edit_point ();
3620 samplepos_t const position = get_preferred_edit_position ();
3622 samplepos_t distance = 0;
3623 samplepos_t pos = 0;
3626 list<RegionView*> sorted;
3627 rs.by_position (sorted);
3629 boost::shared_ptr<Region> r ((*sorted.begin())->region());
3634 if (position > r->position()) {
3635 distance = position - r->position();
3637 distance = r->position() - position;
3643 if (position > r->last_sample()) {
3644 distance = position - r->last_sample();
3645 pos = r->position() + distance;
3647 distance = r->last_sample() - position;
3648 pos = r->position() - distance;
3654 pos = r->adjust_to_sync (position);
3655 if (pos > r->position()) {
3656 distance = pos - r->position();
3658 distance = r->position() - pos;
3664 if (pos == r->position()) {
3668 begin_reversible_command (_("align selection (relative)"));
3670 /* move first one specially */
3672 r->clear_changes ();
3673 r->set_position (pos);
3674 _session->add_command(new StatefulDiffCommand (r));
3676 /* move rest by the same amount */
3680 for (list<RegionView*>::iterator i = sorted.begin(); i != sorted.end(); ++i) {
3682 boost::shared_ptr<Region> region ((*i)->region());
3684 region->clear_changes ();
3687 region->set_position (region->position() + distance);
3689 region->set_position (region->position() - distance);
3692 _session->add_command(new StatefulDiffCommand (region));
3696 commit_reversible_command ();
3700 Editor::align_region (boost::shared_ptr<Region> region, RegionPoint point, samplepos_t position)
3702 begin_reversible_command (_("align region"));
3703 align_region_internal (region, point, position);
3704 commit_reversible_command ();
3708 Editor::align_region_internal (boost::shared_ptr<Region> region, RegionPoint point, samplepos_t position)
3710 region->clear_changes ();
3714 region->set_position (region->adjust_to_sync (position));
3718 if (position > region->length()) {
3719 region->set_position (position - region->length());
3724 region->set_position (position);
3728 _session->add_command(new StatefulDiffCommand (region));
3732 Editor::trim_region_front ()
3738 Editor::trim_region_back ()
3740 trim_region (false);
3744 Editor::trim_region (bool front)
3746 samplepos_t where = get_preferred_edit_position();
3747 RegionSelection rs = get_regions_from_selection_and_edit_point ();
3753 begin_reversible_command (front ? _("trim front") : _("trim back"));
3755 for (list<RegionView*>::const_iterator i = rs.by_layer().begin(); i != rs.by_layer().end(); ++i) {
3756 if (!(*i)->region()->locked()) {
3758 (*i)->region()->clear_changes ();
3761 (*i)->region()->trim_front (where);
3763 (*i)->region()->trim_end (where);
3766 _session->add_command (new StatefulDiffCommand ((*i)->region()));
3770 commit_reversible_command ();
3773 /** Trim the end of the selected regions to the position of the edit cursor */
3775 Editor::trim_region_to_loop ()
3777 Location* loc = _session->locations()->auto_loop_location();
3781 trim_region_to_location (*loc, _("trim to loop"));
3785 Editor::trim_region_to_punch ()
3787 Location* loc = _session->locations()->auto_punch_location();
3791 trim_region_to_location (*loc, _("trim to punch"));
3795 Editor::trim_region_to_location (const Location& loc, const char* str)
3797 RegionSelection rs = get_regions_from_selection_and_entered ();
3798 bool in_command = false;
3800 for (RegionSelection::iterator x = rs.begin(); x != rs.end(); ++x) {
3801 RegionView* rv = (*x);
3803 /* require region to span proposed trim */
3804 switch (rv->region()->coverage (loc.start(), loc.end())) {
3805 case Evoral::OverlapInternal:
3811 RouteTimeAxisView* tav = dynamic_cast<RouteTimeAxisView*> (&rv->get_time_axis_view());
3819 start = loc.start();
3822 rv->region()->clear_changes ();
3823 rv->region()->trim_to (start, (end - start));
3826 begin_reversible_command (str);
3829 _session->add_command(new StatefulDiffCommand (rv->region()));
3833 commit_reversible_command ();
3838 Editor::trim_region_to_previous_region_end ()
3840 return trim_to_region(false);
3844 Editor::trim_region_to_next_region_start ()
3846 return trim_to_region(true);
3850 Editor::trim_to_region(bool forward)
3852 RegionSelection rs = get_regions_from_selection_and_entered ();
3853 bool in_command = false;
3855 boost::shared_ptr<Region> next_region;
3857 for (RegionSelection::iterator x = rs.begin(); x != rs.end(); ++x) {
3859 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (*x);
3865 AudioTimeAxisView* atav = dynamic_cast<AudioTimeAxisView*> (&arv->get_time_axis_view());
3871 boost::shared_ptr<Region> region = arv->region();
3872 boost::shared_ptr<Playlist> playlist (region->playlist());
3874 region->clear_changes ();
3878 next_region = playlist->find_next_region (region->first_sample(), Start, 1);
3884 region->trim_end (next_region->first_sample() - 1);
3885 arv->region_changed (PropertyChange (ARDOUR::Properties::length));
3889 next_region = playlist->find_next_region (region->first_sample(), Start, 0);
3895 region->trim_front (next_region->last_sample() + 1);
3896 arv->region_changed (ARDOUR::bounds_change);
3900 begin_reversible_command (_("trim to region"));
3903 _session->add_command(new StatefulDiffCommand (region));
3907 commit_reversible_command ();
3912 Editor::unfreeze_route ()
3914 if (clicked_routeview == 0 || !clicked_routeview->is_track()) {
3918 clicked_routeview->track()->unfreeze ();
3922 Editor::_freeze_thread (void* arg)
3924 return static_cast<Editor*>(arg)->freeze_thread ();
3928 Editor::freeze_thread ()
3930 /* create event pool because we may need to talk to the session */
3931 SessionEvent::create_per_thread_pool ("freeze events", 64);
3932 /* create per-thread buffers for process() tree to use */
3933 clicked_routeview->audio_track()->freeze_me (*current_interthread_info);
3934 current_interthread_info->done = true;
3939 Editor::freeze_route ()
3945 /* stop transport before we start. this is important */
3947 _session->request_transport_speed (0.0);
3949 /* wait for just a little while, because the above call is asynchronous */
3951 Glib::usleep (250000);
3953 if (clicked_routeview == 0 || !clicked_routeview->is_audio_track()) {
3957 if (!clicked_routeview->track()->bounceable (clicked_routeview->track()->main_outs(), true)) {
3959 _("This track/bus cannot be frozen because the signal adds or loses channels before reaching the outputs.\n"
3960 "This is typically caused by plugins that generate stereo output from mono input or vice versa.")
3962 d.set_title (_("Cannot freeze"));
3967 if (clicked_routeview->track()->has_external_redirects()) {
3968 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"
3969 "Freezing will only process the signal as far as the first send/insert/return."),
3970 clicked_routeview->track()->name()), true, MESSAGE_INFO, BUTTONS_NONE, true);
3972 d.add_button (_("Freeze anyway"), Gtk::RESPONSE_OK);
3973 d.add_button (_("Don't freeze"), Gtk::RESPONSE_CANCEL);
3974 d.set_title (_("Freeze Limits"));
3976 int response = d.run ();
3979 case Gtk::RESPONSE_CANCEL:
3986 InterThreadInfo itt;
3987 current_interthread_info = &itt;
3989 InterthreadProgressWindow ipw (current_interthread_info, _("Freeze"), _("Cancel Freeze"));
3991 pthread_create_and_store (X_("freezer"), &itt.thread, _freeze_thread, this);
3993 CursorContext::Handle cursor_ctx = CursorContext::create(*this, _cursors->wait);
3995 while (!itt.done && !itt.cancel) {
3996 gtk_main_iteration ();
3999 pthread_join (itt.thread, 0);
4000 current_interthread_info = 0;
4004 Editor::bounce_range_selection (bool replace, bool enable_processing)
4006 if (selection->time.empty()) {
4010 TrackSelection views = selection->tracks;
4012 for (TrackViewList::iterator i = views.begin(); i != views.end(); ++i) {
4014 if (enable_processing) {
4016 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (*i);
4018 if (rtv && rtv->track() && replace && enable_processing && !rtv->track()->bounceable (rtv->track()->main_outs(), false)) {
4020 _("You can't perform this operation because the processing of the signal "
4021 "will cause one or more of the tracks to end up with a region with more channels than this track has inputs.\n\n"
4022 "You can do this without processing, which is a different operation.")
4024 d.set_title (_("Cannot bounce"));
4031 samplepos_t start = selection->time[clicked_selection].start;
4032 samplepos_t end = selection->time[clicked_selection].end;
4033 samplepos_t cnt = end - start + 1;
4034 bool in_command = false;
4036 for (TrackViewList::iterator i = views.begin(); i != views.end(); ++i) {
4038 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (*i);
4044 boost::shared_ptr<Playlist> playlist;
4046 if ((playlist = rtv->playlist()) == 0) {
4050 InterThreadInfo itt;
4052 playlist->clear_changes ();
4053 playlist->clear_owned_changes ();
4055 boost::shared_ptr<Region> r;
4057 if (enable_processing) {
4058 r = rtv->track()->bounce_range (start, start+cnt, itt, rtv->track()->main_outs(), false);
4060 r = rtv->track()->bounce_range (start, start+cnt, itt, boost::shared_ptr<Processor>(), false);
4068 list<AudioRange> ranges;
4069 ranges.push_back (AudioRange (start, start+cnt, 0));
4070 playlist->cut (ranges); // discard result
4071 playlist->add_region (r, start);
4075 begin_reversible_command (_("bounce range"));
4078 vector<Command*> cmds;
4079 playlist->rdiff (cmds);
4080 _session->add_commands (cmds);
4082 _session->add_command (new StatefulDiffCommand (playlist));
4086 commit_reversible_command ();
4090 /** Delete selected regions, automation points or a time range */
4094 //special case: if the user is pointing in the editor/mixer strip, they may be trying to delete a plugin.
4095 //we need this because the editor-mixer strip is in the editor window, so it doesn't get the bindings from the mix window
4096 bool deleted = false;
4097 if (current_mixer_strip && current_mixer_strip == MixerStrip::entered_mixer_strip())
4098 deleted = current_mixer_strip->delete_processors ();
4104 /** Cut selected regions, automation points or a time range */
4111 /** Copy selected regions, automation points or a time range */
4119 /** @return true if a Cut, Copy or Clear is possible */
4121 Editor::can_cut_copy () const
4123 if (!selection->time.empty() || !selection->regions.empty() || !selection->points.empty())
4130 /** Cut, copy or clear selected regions, automation points or a time range.
4131 * @param op Operation (Delete, Cut, Copy or Clear)
4134 Editor::cut_copy (CutCopyOp op)
4136 /* only cancel selection if cut/copy is successful.*/
4142 opname = _("delete");
4151 opname = _("clear");
4155 /* if we're deleting something, and the mouse is still pressed,
4156 the thing we started a drag for will be gone when we release
4157 the mouse button(s). avoid this. see part 2 at the end of
4161 if (op == Delete || op == Cut || op == Clear) {
4162 if (_drags->active ()) {
4167 if (op != Delete) { //"Delete" doesn't change copy/paste buf
4168 cut_buffer->clear ();
4171 if (entered_marker) {
4173 /* cut/delete op while pointing at a marker */
4176 Location* loc = find_location_from_marker (entered_marker, ignored);
4178 if (_session && loc) {
4179 entered_marker = NULL;
4180 Glib::signal_idle().connect (sigc::bind (sigc::mem_fun(*this, &Editor::really_remove_marker), loc));
4187 switch (mouse_mode) {
4190 begin_reversible_command (opname + ' ' + X_("MIDI"));
4192 commit_reversible_command ();
4198 bool did_edit = false;
4200 if (!selection->regions.empty() || !selection->points.empty()) {
4201 begin_reversible_command (opname + ' ' + _("objects"));
4204 if (!selection->regions.empty()) {
4205 cut_copy_regions (op, selection->regions);
4207 if (op == Cut || op == Delete) {
4208 selection->clear_regions ();
4212 if (!selection->points.empty()) {
4213 cut_copy_points (op);
4215 if (op == Cut || op == Delete) {
4216 selection->clear_points ();
4219 } else if (selection->time.empty()) {
4220 samplepos_t start, end;
4221 /* no time selection, see if we can get an edit range
4224 if (get_edit_op_range (start, end)) {
4225 selection->set (start, end);
4227 } else if (!selection->time.empty()) {
4228 begin_reversible_command (opname + ' ' + _("range"));
4231 cut_copy_ranges (op);
4233 if (op == Cut || op == Delete) {
4234 selection->clear_time ();
4239 /* reset repeated paste state */
4241 last_paste_pos = -1;
4242 commit_reversible_command ();
4245 if (op == Delete || op == Cut || op == Clear) {
4251 struct AutomationRecord {
4252 AutomationRecord () : state (0) , line(NULL) {}
4253 AutomationRecord (XMLNode* s, const AutomationLine* l) : state (s) , line (l) {}
4255 XMLNode* state; ///< state before any operation
4256 const AutomationLine* line; ///< line this came from
4257 boost::shared_ptr<Evoral::ControlList> copy; ///< copied events for the cut buffer
4260 struct PointsSelectionPositionSorter {
4261 bool operator() (ControlPoint* a, ControlPoint* b) {
4262 return (*(a->model()))->when < (*(b->model()))->when;
4266 /** Cut, copy or clear selected automation points.
4267 * @param op Operation (Cut, Copy or Clear)
4270 Editor::cut_copy_points (Editing::CutCopyOp op, Temporal::Beats earliest, bool midi)
4272 if (selection->points.empty ()) {
4276 /* XXX: not ideal, as there may be more than one track involved in the point selection */
4277 _last_cut_copy_source_track = &selection->points.front()->line().trackview;
4279 /* Keep a record of the AutomationLists that we end up using in this operation */
4280 typedef std::map<boost::shared_ptr<AutomationList>, AutomationRecord> Lists;
4283 /* user could select points in any order */
4284 selection->points.sort(PointsSelectionPositionSorter ());
4286 /* Go through all selected points, making an AutomationRecord for each distinct AutomationList */
4287 for (PointSelection::iterator sel_point = selection->points.begin(); sel_point != selection->points.end(); ++sel_point) {
4288 const AutomationLine& line = (*sel_point)->line();
4289 const boost::shared_ptr<AutomationList> al = line.the_list();
4290 if (lists.find (al) == lists.end ()) {
4291 /* We haven't seen this list yet, so make a record for it. This includes
4292 taking a copy of its current state, in case this is needed for undo later.
4294 lists[al] = AutomationRecord (&al->get_state (), &line);
4298 if (op == Cut || op == Copy) {
4299 /* This operation will involve putting things in the cut buffer, so create an empty
4300 ControlList for each of our source lists to put the cut buffer data in.
4302 for (Lists::iterator i = lists.begin(); i != lists.end(); ++i) {
4303 i->second.copy = i->first->create (i->first->parameter (), i->first->descriptor());
4306 /* Add all selected points to the relevant copy ControlLists */
4307 MusicSample start (std::numeric_limits<samplepos_t>::max(), 0);
4308 for (PointSelection::iterator sel_point = selection->points.begin(); sel_point != selection->points.end(); ++sel_point) {
4309 boost::shared_ptr<AutomationList> al = (*sel_point)->line().the_list();
4310 AutomationList::const_iterator ctrl_evt = (*sel_point)->model ();
4312 lists[al].copy->fast_simple_add ((*ctrl_evt)->when, (*ctrl_evt)->value);
4314 /* Update earliest MIDI start time in beats */
4315 earliest = std::min(earliest, Temporal::Beats((*ctrl_evt)->when));
4317 /* Update earliest session start time in samples */
4318 start.sample = std::min(start.sample, (*sel_point)->line().session_position(ctrl_evt));
4322 /* Snap start time backwards, so copy/paste is snap aligned. */
4324 if (earliest == std::numeric_limits<Temporal::Beats>::max()) {
4325 earliest = Temporal::Beats(); // Weird... don't offset
4327 earliest.round_down_to_beat();
4329 if (start.sample == std::numeric_limits<double>::max()) {
4330 start.sample = 0; // Weird... don't offset
4332 snap_to(start, RoundDownMaybe);
4335 const double line_offset = midi ? earliest.to_double() : start.sample;
4336 for (Lists::iterator i = lists.begin(); i != lists.end(); ++i) {
4337 /* Correct this copy list so that it is relative to the earliest
4338 start time, so relative ordering between points is preserved
4339 when copying from several lists and the paste starts at the
4340 earliest copied piece of data. */
4341 boost::shared_ptr<Evoral::ControlList> &al_cpy = i->second.copy;
4342 for (AutomationList::iterator ctrl_evt = al_cpy->begin(); ctrl_evt != al_cpy->end(); ++ctrl_evt) {
4343 (*ctrl_evt)->when -= line_offset;
4346 /* And add it to the cut buffer */
4347 cut_buffer->add (al_cpy);
4351 if (op == Delete || op == Cut) {
4352 /* This operation needs to remove things from the main AutomationList, so do that now */
4354 for (Lists::iterator i = lists.begin(); i != lists.end(); ++i) {
4355 i->first->freeze ();
4358 /* Remove each selected point from its AutomationList */
4359 for (PointSelection::iterator sel_point = selection->points.begin(); sel_point != selection->points.end(); ++sel_point) {
4360 AutomationLine& line = (*sel_point)->line ();
4361 boost::shared_ptr<AutomationList> al = line.the_list();
4365 if (dynamic_cast<AudioRegionGainLine*> (&line)) {
4366 /* removing of first and last gain point in region gain lines is prohibited*/
4367 if (line.is_last_point (*(*sel_point)) || line.is_first_point (*(*sel_point))) {
4373 al->erase ((*sel_point)->model ());
4377 /* Thaw the lists and add undo records for them */
4378 for (Lists::iterator i = lists.begin(); i != lists.end(); ++i) {
4379 boost::shared_ptr<AutomationList> al = i->first;
4381 _session->add_command (new MementoCommand<AutomationList> (*al.get(), i->second.state, &(al->get_state ())));
4386 /** Cut, copy or clear selected automation points.
4387 * @param op Operation (Cut, Copy or Clear)
4390 Editor::cut_copy_midi (CutCopyOp op)
4392 Temporal::Beats earliest = std::numeric_limits<Temporal::Beats>::max();
4393 for (MidiRegionSelection::iterator i = selection->midi_regions.begin(); i != selection->midi_regions.end(); ++i) {
4394 MidiRegionView* mrv = dynamic_cast<MidiRegionView*>(*i);
4396 if (!mrv->selection().empty()) {
4397 earliest = std::min(earliest, (*mrv->selection().begin())->note()->time());
4399 mrv->cut_copy_clear (op);
4401 /* XXX: not ideal, as there may be more than one track involved in the selection */
4402 _last_cut_copy_source_track = &mrv->get_time_axis_view();
4406 if (!selection->points.empty()) {
4407 cut_copy_points (op, earliest, true);
4408 if (op == Cut || op == Delete) {
4409 selection->clear_points ();
4414 struct lt_playlist {
4415 bool operator () (const PlaylistState& a, const PlaylistState& b) {
4416 return a.playlist < b.playlist;
4420 struct PlaylistMapping {
4422 boost::shared_ptr<Playlist> pl;
4424 PlaylistMapping (TimeAxisView* tvp) : tv (tvp) {}
4427 /** Remove `clicked_regionview' */
4429 Editor::remove_clicked_region ()
4431 if (clicked_routeview == 0 || clicked_regionview == 0) {
4435 begin_reversible_command (_("remove region"));
4437 boost::shared_ptr<Playlist> playlist = clicked_routeview->playlist();
4439 playlist->clear_changes ();
4440 playlist->clear_owned_changes ();
4441 playlist->remove_region (clicked_regionview->region());
4442 if (Config->get_edit_mode() == Ripple)
4443 playlist->ripple (clicked_regionview->region()->position(), -clicked_regionview->region()->length(), boost::shared_ptr<Region>());
4445 /* We might have removed regions, which alters other regions' layering_index,
4446 so we need to do a recursive diff here.
4448 vector<Command*> cmds;
4449 playlist->rdiff (cmds);
4450 _session->add_commands (cmds);
4452 _session->add_command(new StatefulDiffCommand (playlist));
4453 commit_reversible_command ();
4457 /** Remove the selected regions */
4459 Editor::remove_selected_regions ()
4461 RegionSelection rs = get_regions_from_selection_and_entered ();
4463 if (!_session || rs.empty()) {
4467 list<boost::shared_ptr<Region> > regions_to_remove;
4469 for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
4470 // we can't just remove the region(s) in this loop because
4471 // this removes them from the RegionSelection, and they thus
4472 // disappear from underneath the iterator, and the ++i above
4473 // SEGVs in a puzzling fashion.
4475 // so, first iterate over the regions to be removed from rs and
4476 // add them to the regions_to_remove list, and then
4477 // iterate over the list to actually remove them.
4479 regions_to_remove.push_back ((*i)->region());
4482 vector<boost::shared_ptr<Playlist> > playlists;
4484 for (list<boost::shared_ptr<Region> >::iterator rl = regions_to_remove.begin(); rl != regions_to_remove.end(); ++rl) {
4486 boost::shared_ptr<Playlist> playlist = (*rl)->playlist();
4489 // is this check necessary?
4493 /* get_regions_from_selection_and_entered() guarantees that
4494 the playlists involved are unique, so there is no need
4498 playlists.push_back (playlist);
4500 playlist->clear_changes ();
4501 playlist->clear_owned_changes ();
4502 playlist->freeze ();
4503 playlist->remove_region (*rl);
4504 if (Config->get_edit_mode() == Ripple)
4505 playlist->ripple ((*rl)->position(), -(*rl)->length(), boost::shared_ptr<Region>());
4509 vector<boost::shared_ptr<Playlist> >::iterator pl;
4510 bool in_command = false;
4512 for (pl = playlists.begin(); pl != playlists.end(); ++pl) {
4515 /* We might have removed regions, which alters other regions' layering_index,
4516 so we need to do a recursive diff here.
4520 begin_reversible_command (_("remove region"));
4523 vector<Command*> cmds;
4524 (*pl)->rdiff (cmds);
4525 _session->add_commands (cmds);
4527 _session->add_command(new StatefulDiffCommand (*pl));
4531 commit_reversible_command ();
4535 /** Cut, copy or clear selected regions.
4536 * @param op Operation (Cut, Copy or Clear)
4539 Editor::cut_copy_regions (CutCopyOp op, RegionSelection& rs)
4541 /* we can't use a std::map here because the ordering is important, and we can't trivially sort
4542 a map when we want ordered access to both elements. i think.
4545 vector<PlaylistMapping> pmap;
4547 samplepos_t first_position = max_samplepos;
4549 typedef set<boost::shared_ptr<Playlist> > FreezeList;
4550 FreezeList freezelist;
4552 /* get ordering correct before we cut/copy */
4554 rs.sort_by_position_and_track ();
4556 for (RegionSelection::iterator x = rs.begin(); x != rs.end(); ++x) {
4558 first_position = min ((samplepos_t) (*x)->region()->position(), first_position);
4560 if (op == Cut || op == Clear || op == Delete) {
4561 boost::shared_ptr<Playlist> pl = (*x)->region()->playlist();
4564 FreezeList::iterator fl;
4566 // only take state if this is a new playlist.
4567 for (fl = freezelist.begin(); fl != freezelist.end(); ++fl) {
4573 if (fl == freezelist.end()) {
4574 pl->clear_changes();
4575 pl->clear_owned_changes ();
4577 freezelist.insert (pl);
4582 TimeAxisView* tv = &(*x)->get_time_axis_view();
4583 vector<PlaylistMapping>::iterator z;
4585 for (z = pmap.begin(); z != pmap.end(); ++z) {
4586 if ((*z).tv == tv) {
4591 if (z == pmap.end()) {
4592 pmap.push_back (PlaylistMapping (tv));
4596 for (RegionSelection::iterator x = rs.begin(); x != rs.end(); ) {
4598 boost::shared_ptr<Playlist> pl = (*x)->region()->playlist();
4601 /* region not yet associated with a playlist (e.g. unfinished
4608 TimeAxisView& tv = (*x)->get_time_axis_view();
4609 boost::shared_ptr<Playlist> npl;
4610 RegionSelection::iterator tmp;
4617 vector<PlaylistMapping>::iterator z;
4619 for (z = pmap.begin(); z != pmap.end(); ++z) {
4620 if ((*z).tv == &tv) {
4625 assert (z != pmap.end());
4628 npl = PlaylistFactory::create (pl->data_type(), *_session, "cutlist", true);
4636 boost::shared_ptr<Region> r = (*x)->region();
4637 boost::shared_ptr<Region> _xx;
4643 pl->remove_region (r);
4644 if (Config->get_edit_mode() == Ripple)
4645 pl->ripple (r->position(), -r->length(), boost::shared_ptr<Region>());
4649 _xx = RegionFactory::create (r);
4650 npl->add_region (_xx, r->position() - first_position);
4651 pl->remove_region (r);
4652 if (Config->get_edit_mode() == Ripple)
4653 pl->ripple (r->position(), -r->length(), boost::shared_ptr<Region>());
4657 /* copy region before adding, so we're not putting same object into two different playlists */
4658 npl->add_region (RegionFactory::create (r), r->position() - first_position);
4662 pl->remove_region (r);
4663 if (Config->get_edit_mode() == Ripple)
4664 pl->ripple (r->position(), -r->length(), boost::shared_ptr<Region>());
4673 list<boost::shared_ptr<Playlist> > foo;
4675 /* the pmap is in the same order as the tracks in which selected regions occurred */
4677 for (vector<PlaylistMapping>::iterator i = pmap.begin(); i != pmap.end(); ++i) {
4680 foo.push_back ((*i).pl);
4685 cut_buffer->set (foo);
4689 _last_cut_copy_source_track = 0;
4691 _last_cut_copy_source_track = pmap.front().tv;
4695 for (FreezeList::iterator pl = freezelist.begin(); pl != freezelist.end(); ++pl) {
4698 /* We might have removed regions, which alters other regions' layering_index,
4699 so we need to do a recursive diff here.
4701 vector<Command*> cmds;
4702 (*pl)->rdiff (cmds);
4703 _session->add_commands (cmds);
4705 _session->add_command (new StatefulDiffCommand (*pl));
4710 Editor::cut_copy_ranges (CutCopyOp op)
4712 TrackViewList ts = selection->tracks.filter_to_unique_playlists ();
4714 /* Sort the track selection now, so that it if is used, the playlists
4715 selected by the calls below to cut_copy_clear are in the order that
4716 their tracks appear in the editor. This makes things like paste
4717 of ranges work properly.
4720 sort_track_selection (ts);
4723 if (!entered_track) {
4726 ts.push_back (entered_track);
4729 for (TrackViewList::iterator i = ts.begin(); i != ts.end(); ++i) {
4730 (*i)->cut_copy_clear (*selection, op);
4735 Editor::paste (float times, bool from_context)
4737 DEBUG_TRACE (DEBUG::CutNPaste, "paste to preferred edit pos\n");
4738 MusicSample where (get_preferred_edit_position (EDIT_IGNORE_NONE, from_context), 0);
4739 paste_internal (where.sample, times, 0);
4743 Editor::mouse_paste ()
4745 MusicSample where (0, 0);
4747 if (!mouse_sample (where.sample, ignored)) {
4752 paste_internal (where.sample, 1, where.division);
4756 Editor::paste_internal (samplepos_t position, float times, const int32_t sub_num)
4758 DEBUG_TRACE (DEBUG::CutNPaste, string_compose ("apparent paste position is %1\n", position));
4760 if (cut_buffer->empty(internal_editing())) {
4764 if (position == max_samplepos) {
4765 position = get_preferred_edit_position();
4766 DEBUG_TRACE (DEBUG::CutNPaste, string_compose ("preferred edit position is %1\n", position));
4769 if (position != last_paste_pos) {
4770 /* paste in new location, reset repeated paste state */
4772 last_paste_pos = position;
4775 /* get everything in the correct order */
4778 if (!selection->tracks.empty()) {
4779 /* If there is a track selection, paste into exactly those tracks and
4780 * only those tracks. This allows the user to be explicit and override
4781 * the below "do the reasonable thing" logic. */
4782 ts = selection->tracks.filter_to_unique_playlists ();
4783 sort_track_selection (ts);
4785 /* Figure out which track to base the paste at. */
4786 TimeAxisView* base_track = NULL;
4787 if (_edit_point == Editing::EditAtMouse && entered_track) {
4788 /* With the mouse edit point, paste onto the track under the mouse. */
4789 base_track = entered_track;
4790 } else if (_edit_point == Editing::EditAtMouse && entered_regionview) {
4791 /* With the mouse edit point, paste onto the track of the region under the mouse. */
4792 base_track = &entered_regionview->get_time_axis_view();
4793 } else if (_last_cut_copy_source_track) {
4794 /* Paste to the track that the cut/copy came from (see mantis #333). */
4795 base_track = _last_cut_copy_source_track;
4797 /* This is "impossible" since we've copied... well, do nothing. */
4801 /* Walk up to parent if necessary, so base track is a route. */
4802 while (base_track->get_parent()) {
4803 base_track = base_track->get_parent();
4806 /* Add base track and all tracks below it. The paste logic will select
4807 the appropriate object types from the cut buffer in relative order. */
4808 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
4809 if ((*i)->order() >= base_track->order()) {
4814 /* Sort tracks so the nth track of type T will pick the nth object of type T. */
4815 sort_track_selection (ts);
4817 /* Add automation children of each track in order, for pasting several lines. */
4818 for (TrackViewList::iterator i = ts.begin(); i != ts.end();) {
4819 /* Add any automation children for pasting several lines */
4820 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*>(*i++);
4825 typedef RouteTimeAxisView::AutomationTracks ATracks;
4826 const ATracks& atracks = rtv->automation_tracks();
4827 for (ATracks::const_iterator a = atracks.begin(); a != atracks.end(); ++a) {
4828 i = ts.insert(i, a->second.get());
4833 /* We now have a list of trackviews starting at base_track, including
4834 automation children, in the order shown in the editor, e.g. R1,
4835 R1.A1, R1.A2, R2, R2.A1, ... */
4838 begin_reversible_command (Operations::paste);
4840 if (ts.size() == 1 && cut_buffer->lines.size() == 1 &&
4841 dynamic_cast<AutomationTimeAxisView*>(ts.front())) {
4842 /* Only one line copied, and one automation track selected. Do a
4843 "greedy" paste from one automation type to another. */
4845 PasteContext ctx(paste_count, times, ItemCounts(), true);
4846 ts.front()->paste (position, *cut_buffer, ctx, sub_num);
4850 /* Paste into tracks */
4852 PasteContext ctx(paste_count, times, ItemCounts(), false);
4853 for (TrackViewList::iterator i = ts.begin(); i != ts.end(); ++i) {
4854 (*i)->paste (position, *cut_buffer, ctx, sub_num);
4860 commit_reversible_command ();
4864 Editor::duplicate_regions (float times)
4866 RegionSelection rs (get_regions_from_selection_and_entered());
4867 duplicate_some_regions (rs, times);
4871 Editor::duplicate_some_regions (RegionSelection& regions, float times)
4873 if (regions.empty ()) {
4877 boost::shared_ptr<Playlist> playlist;
4878 std::set<boost::shared_ptr<Playlist> > playlists; // list of unique playlists affected by duplication
4879 RegionSelection sel = regions; // clear (below) may clear the argument list if its the current region selection
4880 RegionSelection foo;
4882 samplepos_t const start_sample = regions.start ();
4883 samplepos_t const end_sample = regions.end_sample ();
4884 samplecnt_t const span = end_sample - start_sample + 1;
4886 begin_reversible_command (Operations::duplicate_region);
4888 selection->clear_regions ();
4890 /* ripple first so that we don't move the duplicates that will be added */
4892 if (Config->get_edit_mode() == Ripple) {
4894 /* convert RegionSelection into RegionList so that we can pass it to ripple and exclude the regions we will duplicate */
4898 for (RegionSelection::iterator i = sel.begin(); i != sel.end(); ++i) {
4899 exclude.push_back ((*i)->region());
4900 playlist = (*i)->region()->playlist();
4901 if (playlists.insert (playlist).second) {
4902 /* successfully inserted into set, so it's the first time we've seen this playlist */
4903 playlist->clear_changes ();
4907 for (set<boost::shared_ptr<Playlist> >::iterator p = playlists.begin(); p != playlists.end(); ++p) {
4908 (*p)->ripple (start_sample, span * times, &exclude);
4912 for (RegionSelection::iterator i = sel.begin(); i != sel.end(); ++i) {
4914 boost::shared_ptr<Region> r ((*i)->region());
4916 TimeAxisView& tv = (*i)->get_time_axis_view();
4917 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (&tv);
4918 latest_regionviews.clear ();
4919 sigc::connection c = rtv->view()->RegionViewAdded.connect (sigc::mem_fun(*this, &Editor::collect_new_region_view));
4921 samplepos_t const position = end_sample + (r->first_sample() - start_sample + 1);
4922 playlist = (*i)->region()->playlist();
4924 if (Config->get_edit_mode() != Ripple) {
4925 if (playlists.insert (playlist).second) {
4926 playlist->clear_changes ();
4930 playlist->duplicate (r, position, span, times);
4934 foo.insert (foo.end(), latest_regionviews.begin(), latest_regionviews.end());
4937 for (set<boost::shared_ptr<Playlist> >::iterator p = playlists.begin(); p != playlists.end(); ++p) {
4938 _session->add_command (new StatefulDiffCommand (*p));
4939 vector<Command*> cmds;
4941 _session->add_commands (cmds);
4945 selection->set (foo);
4948 commit_reversible_command ();
4952 Editor::duplicate_selection (float times)
4954 if (selection->time.empty() || selection->tracks.empty()) {
4958 boost::shared_ptr<Playlist> playlist;
4960 TrackViewList ts = selection->tracks.filter_to_unique_playlists ();
4962 bool in_command = false;
4964 for (TrackViewList::iterator i = ts.begin(); i != ts.end(); ++i) {
4965 if ((playlist = (*i)->playlist()) == 0) {
4968 playlist->clear_changes ();
4970 if (clicked_selection) {
4971 playlist->duplicate_range (selection->time[clicked_selection], times);
4973 playlist->duplicate_ranges (selection->time, times);
4977 begin_reversible_command (_("duplicate range selection"));
4980 _session->add_command (new StatefulDiffCommand (playlist));
4985 if (times == 1.0f) {
4986 // now "move" range selection to after the current range selection
4987 samplecnt_t distance = 0;
4989 if (clicked_selection) {
4991 selection->time[clicked_selection].end - selection->time[clicked_selection].start;
4993 distance = selection->time.end_sample () - selection->time.start ();
4996 selection->move_time (distance);
4998 commit_reversible_command ();
5002 /** Reset all selected points to the relevant default value */
5004 Editor::reset_point_selection ()
5006 for (PointSelection::iterator i = selection->points.begin(); i != selection->points.end(); ++i) {
5007 ARDOUR::AutomationList::iterator j = (*i)->model ();
5008 (*j)->value = (*i)->line().the_list()->descriptor ().normal;
5013 Editor::center_playhead ()
5015 float const page = _visible_canvas_width * samples_per_pixel;
5016 center_screen_internal (playhead_cursor->current_sample (), page);
5020 Editor::center_edit_point ()
5022 float const page = _visible_canvas_width * samples_per_pixel;
5023 center_screen_internal (get_preferred_edit_position(), page);
5026 /** Caller must begin and commit a reversible command */
5028 Editor::clear_playlist (boost::shared_ptr<Playlist> playlist)
5030 playlist->clear_changes ();
5032 _session->add_command (new StatefulDiffCommand (playlist));
5036 Editor::nudge_track (bool use_edit, bool forwards)
5038 boost::shared_ptr<Playlist> playlist;
5039 samplepos_t distance;
5040 samplepos_t next_distance;
5044 start = get_preferred_edit_position();
5049 if ((distance = get_nudge_distance (start, next_distance)) == 0) {
5053 if (selection->tracks.empty()) {
5057 TrackViewList ts = selection->tracks.filter_to_unique_playlists ();
5058 bool in_command = false;
5060 for (TrackViewList::iterator i = ts.begin(); i != ts.end(); ++i) {
5062 if ((playlist = (*i)->playlist()) == 0) {
5066 playlist->clear_changes ();
5067 playlist->clear_owned_changes ();
5069 playlist->nudge_after (start, distance, forwards);
5072 begin_reversible_command (_("nudge track"));
5075 vector<Command*> cmds;
5077 playlist->rdiff (cmds);
5078 _session->add_commands (cmds);
5080 _session->add_command (new StatefulDiffCommand (playlist));
5084 commit_reversible_command ();
5089 Editor::remove_last_capture ()
5091 vector<string> choices;
5098 if (Config->get_verify_remove_last_capture()) {
5099 prompt = _("Do you really want to destroy the last capture?"
5100 "\n(This is destructive and cannot be undone)");
5102 choices.push_back (_("No, do nothing."));
5103 choices.push_back (_("Yes, destroy it."));
5105 Choice prompter (_("Destroy last capture"), prompt, choices);
5107 if (prompter.run () == 1) {
5108 _session->remove_last_capture ();
5109 _regions->redisplay ();
5113 _session->remove_last_capture();
5114 _regions->redisplay ();
5119 Editor::normalize_region ()
5125 RegionSelection rs = get_regions_from_selection_and_entered ();
5131 NormalizeDialog dialog (rs.size() > 1);
5133 if (dialog.run () != RESPONSE_ACCEPT) {
5137 CursorContext::Handle cursor_ctx = CursorContext::create(*this, _cursors->wait);
5140 /* XXX: should really only count audio regions here */
5141 int const regions = rs.size ();
5143 /* Make a list of the selected audio regions' maximum amplitudes, and also
5144 obtain the maximum amplitude of them all.
5146 list<double> max_amps;
5147 list<double> rms_vals;
5150 bool use_rms = dialog.constrain_rms ();
5152 for (RegionSelection::const_iterator i = rs.begin(); i != rs.end(); ++i) {
5153 AudioRegionView const * arv = dynamic_cast<AudioRegionView const *> (*i);
5157 dialog.descend (1.0 / regions);
5158 double const a = arv->audio_region()->maximum_amplitude (&dialog);
5160 double r = arv->audio_region()->rms (&dialog);
5161 max_rms = max (max_rms, r);
5162 rms_vals.push_back (r);
5166 /* the user cancelled the operation */
5170 max_amps.push_back (a);
5171 max_amp = max (max_amp, a);
5175 list<double>::const_iterator a = max_amps.begin ();
5176 list<double>::const_iterator l = rms_vals.begin ();
5177 bool in_command = false;
5179 for (RegionSelection::iterator r = rs.begin(); r != rs.end(); ++r) {
5180 AudioRegionView* const arv = dynamic_cast<AudioRegionView*> (*r);
5185 arv->region()->clear_changes ();
5187 double amp = dialog.normalize_individually() ? *a : max_amp;
5188 double target = dialog.target_peak (); // dB
5191 double const amp_rms = dialog.normalize_individually() ? *l : max_rms;
5192 const double t_rms = dialog.target_rms ();
5193 const gain_t c_peak = dB_to_coefficient (target);
5194 const gain_t c_rms = dB_to_coefficient (t_rms);
5195 if ((amp_rms / c_rms) > (amp / c_peak)) {
5201 arv->audio_region()->normalize (amp, target);
5204 begin_reversible_command (_("normalize"));
5207 _session->add_command (new StatefulDiffCommand (arv->region()));
5214 commit_reversible_command ();
5220 Editor::reset_region_scale_amplitude ()
5226 RegionSelection rs = get_regions_from_selection_and_entered ();
5232 bool in_command = false;
5234 for (RegionSelection::iterator r = rs.begin(); r != rs.end(); ++r) {
5235 AudioRegionView* const arv = dynamic_cast<AudioRegionView*>(*r);
5238 arv->region()->clear_changes ();
5239 arv->audio_region()->set_scale_amplitude (1.0f);
5242 begin_reversible_command ("reset gain");
5245 _session->add_command (new StatefulDiffCommand (arv->region()));
5249 commit_reversible_command ();
5254 Editor::adjust_region_gain (bool up)
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 double dB = accurate_coefficient_to_dB (arv->audio_region()->scale_amplitude ());
5280 arv->audio_region()->set_scale_amplitude (dB_to_coefficient (dB));
5283 begin_reversible_command ("adjust region gain");
5286 _session->add_command (new StatefulDiffCommand (arv->region()));
5290 commit_reversible_command ();
5295 Editor::reset_region_gain ()
5297 RegionSelection rs = get_regions_from_selection_and_entered ();
5299 if (!_session || rs.empty()) {
5303 bool in_command = false;
5305 for (RegionSelection::iterator r = rs.begin(); r != rs.end(); ++r) {
5306 AudioRegionView* const arv = dynamic_cast<AudioRegionView*>(*r);
5311 arv->region()->clear_changes ();
5313 arv->audio_region()->set_scale_amplitude (1.0f);
5316 begin_reversible_command ("reset region gain");
5319 _session->add_command (new StatefulDiffCommand (arv->region()));
5323 commit_reversible_command ();
5328 Editor::reverse_region ()
5334 Reverse rev (*_session);
5335 apply_filter (rev, _("reverse regions"));
5339 Editor::strip_region_silence ()
5345 RegionSelection rs = get_regions_from_selection_and_entered ();
5351 std::list<RegionView*> audio_only;
5353 for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
5354 AudioRegionView* const arv = dynamic_cast<AudioRegionView*> (*i);
5356 audio_only.push_back (arv);
5360 assert (!audio_only.empty());
5362 StripSilenceDialog d (_session, audio_only);
5363 int const r = d.run ();
5367 if (r == Gtk::RESPONSE_OK) {
5368 ARDOUR::AudioIntervalMap silences;
5369 d.silences (silences);
5370 StripSilence s (*_session, silences, d.fade_length());
5372 apply_filter (s, _("strip silence"), &d);
5377 Editor::apply_midi_note_edit_op_to_region (MidiOperator& op, MidiRegionView& mrv)
5379 Evoral::Sequence<Temporal::Beats>::Notes selected;
5380 mrv.selection_as_notelist (selected, true);
5382 vector<Evoral::Sequence<Temporal::Beats>::Notes> v;
5383 v.push_back (selected);
5385 Temporal::Beats pos_beats = Temporal::Beats (mrv.midi_region()->beat()) - mrv.midi_region()->start_beats();
5387 return op (mrv.midi_region()->model(), pos_beats, v);
5391 Editor::apply_midi_note_edit_op (MidiOperator& op, const RegionSelection& rs)
5397 bool in_command = false;
5399 for (RegionSelection::const_iterator r = rs.begin(); r != rs.end(); ) {
5400 RegionSelection::const_iterator tmp = r;
5403 MidiRegionView* const mrv = dynamic_cast<MidiRegionView*> (*r);
5406 Command* cmd = apply_midi_note_edit_op_to_region (op, *mrv);
5409 begin_reversible_command (op.name ());
5413 _session->add_command (cmd);
5421 commit_reversible_command ();
5422 _session->set_dirty ();
5427 Editor::fork_region ()
5429 RegionSelection rs = get_regions_from_selection_and_entered ();
5435 CursorContext::Handle cursor_ctx = CursorContext::create(*this, _cursors->wait);
5436 bool in_command = false;
5440 for (RegionSelection::iterator r = rs.begin(); r != rs.end(); ) {
5441 RegionSelection::iterator tmp = r;
5444 MidiRegionView* const mrv = dynamic_cast<MidiRegionView*>(*r);
5448 boost::shared_ptr<Playlist> playlist = mrv->region()->playlist();
5449 boost::shared_ptr<MidiSource> new_source = _session->create_midi_source_by_stealing_name (mrv->midi_view()->track());
5450 boost::shared_ptr<MidiRegion> newregion = mrv->midi_region()->clone (new_source);
5453 begin_reversible_command (_("Fork Region(s)"));
5456 playlist->clear_changes ();
5457 playlist->replace_region (mrv->region(), newregion, mrv->region()->position());
5458 _session->add_command(new StatefulDiffCommand (playlist));
5460 error << string_compose (_("Could not unlink %1"), mrv->region()->name()) << endmsg;
5468 commit_reversible_command ();
5473 Editor::quantize_region ()
5476 quantize_regions(get_regions_from_selection_and_entered ());
5481 Editor::quantize_regions (const RegionSelection& rs)
5483 if (rs.n_midi_regions() == 0) {
5487 if (!quantize_dialog) {
5488 quantize_dialog = new QuantizeDialog (*this);
5491 if (quantize_dialog->is_mapped()) {
5492 /* in progress already */
5496 quantize_dialog->present ();
5497 const int r = quantize_dialog->run ();
5498 quantize_dialog->hide ();
5500 if (r == Gtk::RESPONSE_OK) {
5501 Quantize quant (quantize_dialog->snap_start(),
5502 quantize_dialog->snap_end(),
5503 quantize_dialog->start_grid_size(),
5504 quantize_dialog->end_grid_size(),
5505 quantize_dialog->strength(),
5506 quantize_dialog->swing(),
5507 quantize_dialog->threshold());
5509 apply_midi_note_edit_op (quant, rs);
5514 Editor::legatize_region (bool shrink_only)
5517 legatize_regions(get_regions_from_selection_and_entered (), shrink_only);
5522 Editor::legatize_regions (const RegionSelection& rs, bool shrink_only)
5524 if (rs.n_midi_regions() == 0) {
5528 Legatize legatize(shrink_only);
5529 apply_midi_note_edit_op (legatize, rs);
5533 Editor::transform_region ()
5536 transform_regions(get_regions_from_selection_and_entered ());
5541 Editor::transform_regions (const RegionSelection& rs)
5543 if (rs.n_midi_regions() == 0) {
5550 const int r = td.run();
5553 if (r == Gtk::RESPONSE_OK) {
5554 Transform transform(td.get());
5555 apply_midi_note_edit_op(transform, rs);
5560 Editor::transpose_region ()
5563 transpose_regions(get_regions_from_selection_and_entered ());
5568 Editor::transpose_regions (const RegionSelection& rs)
5570 if (rs.n_midi_regions() == 0) {
5575 int const r = d.run ();
5577 if (r == RESPONSE_ACCEPT) {
5578 Transpose transpose(d.semitones ());
5579 apply_midi_note_edit_op (transpose, rs);
5584 Editor::insert_patch_change (bool from_context)
5586 RegionSelection rs = get_regions_from_selection_and_entered ();
5592 const samplepos_t p = get_preferred_edit_position (EDIT_IGNORE_NONE, from_context);
5594 /* XXX: bit of a hack; use the MIDNAM from the first selected region;
5595 there may be more than one, but the PatchChangeDialog can only offer
5596 one set of patch menus.
5598 MidiRegionView* first = dynamic_cast<MidiRegionView*> (rs.front ());
5600 Evoral::PatchChange<Temporal::Beats> empty (Temporal::Beats(), 0, 0, 0);
5601 PatchChangeDialog d (0, _session, empty, first->instrument_info(), Gtk::Stock::ADD);
5603 if (d.run() == RESPONSE_CANCEL) {
5607 for (RegionSelection::iterator i = rs.begin (); i != rs.end(); ++i) {
5608 MidiRegionView* const mrv = dynamic_cast<MidiRegionView*> (*i);
5610 if (p >= mrv->region()->first_sample() && p <= mrv->region()->last_sample()) {
5611 mrv->add_patch_change (p - mrv->region()->position(), d.patch ());
5618 Editor::apply_filter (Filter& filter, string command, ProgressReporter* progress)
5620 RegionSelection rs = get_regions_from_selection_and_entered ();
5626 CursorContext::Handle cursor_ctx = CursorContext::create(*this, _cursors->wait);
5627 bool in_command = false;
5632 int const N = rs.size ();
5634 for (RegionSelection::iterator r = rs.begin(); r != rs.end(); ) {
5635 RegionSelection::iterator tmp = r;
5638 AudioRegionView* const arv = dynamic_cast<AudioRegionView*>(*r);
5640 boost::shared_ptr<Playlist> playlist = arv->region()->playlist();
5643 progress->descend (1.0 / N);
5646 if (arv->audio_region()->apply (filter, progress) == 0) {
5648 playlist->clear_changes ();
5649 playlist->clear_owned_changes ();
5652 begin_reversible_command (command);
5656 if (filter.results.empty ()) {
5658 /* no regions returned; remove the old one */
5659 playlist->remove_region (arv->region ());
5663 std::vector<boost::shared_ptr<Region> >::iterator res = filter.results.begin ();
5665 /* first region replaces the old one */
5666 playlist->replace_region (arv->region(), *res, (*res)->position());
5670 while (res != filter.results.end()) {
5671 playlist->add_region (*res, (*res)->position());
5677 /* We might have removed regions, which alters other regions' layering_index,
5678 so we need to do a recursive diff here.
5680 vector<Command*> cmds;
5681 playlist->rdiff (cmds);
5682 _session->add_commands (cmds);
5684 _session->add_command(new StatefulDiffCommand (playlist));
5688 progress->ascend ();
5697 commit_reversible_command ();
5702 Editor::external_edit_region ()
5708 Editor::reset_region_gain_envelopes ()
5710 RegionSelection rs = get_regions_from_selection_and_entered ();
5712 if (!_session || rs.empty()) {
5716 bool in_command = false;
5718 for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
5719 AudioRegionView* const arv = dynamic_cast<AudioRegionView*>(*i);
5721 boost::shared_ptr<AutomationList> alist (arv->audio_region()->envelope());
5722 XMLNode& before (alist->get_state());
5724 arv->audio_region()->set_default_envelope ();
5727 begin_reversible_command (_("reset region gain"));
5730 _session->add_command (new MementoCommand<AutomationList>(*arv->audio_region()->envelope().get(), &before, &alist->get_state()));
5735 commit_reversible_command ();
5740 Editor::set_region_gain_visibility (RegionView* rv)
5742 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (rv);
5744 arv->update_envelope_visibility();
5749 Editor::set_gain_envelope_visibility ()
5755 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
5756 AudioTimeAxisView* v = dynamic_cast<AudioTimeAxisView*>(*i);
5758 v->audio_view()->foreach_regionview (sigc::mem_fun (this, &Editor::set_region_gain_visibility));
5764 Editor::toggle_gain_envelope_active ()
5766 if (_ignore_region_action) {
5770 RegionSelection rs = get_regions_from_selection_and_entered ();
5772 if (!_session || rs.empty()) {
5776 bool in_command = false;
5778 for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
5779 AudioRegionView* const arv = dynamic_cast<AudioRegionView*>(*i);
5781 arv->region()->clear_changes ();
5782 arv->audio_region()->set_envelope_active (!arv->audio_region()->envelope_active());
5785 begin_reversible_command (_("region gain envelope active"));
5788 _session->add_command (new StatefulDiffCommand (arv->region()));
5793 commit_reversible_command ();
5798 Editor::toggle_region_lock ()
5800 if (_ignore_region_action) {
5804 RegionSelection rs = get_regions_from_selection_and_entered ();
5806 if (!_session || rs.empty()) {
5810 begin_reversible_command (_("toggle region lock"));
5812 for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
5813 (*i)->region()->clear_changes ();
5814 (*i)->region()->set_locked (!(*i)->region()->locked());
5815 _session->add_command (new StatefulDiffCommand ((*i)->region()));
5818 commit_reversible_command ();
5822 Editor::toggle_region_video_lock ()
5824 if (_ignore_region_action) {
5828 RegionSelection rs = get_regions_from_selection_and_entered ();
5830 if (!_session || rs.empty()) {
5834 begin_reversible_command (_("Toggle Video Lock"));
5836 for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
5837 (*i)->region()->clear_changes ();
5838 (*i)->region()->set_video_locked (!(*i)->region()->video_locked());
5839 _session->add_command (new StatefulDiffCommand ((*i)->region()));
5842 commit_reversible_command ();
5846 Editor::toggle_region_lock_style ()
5848 if (_ignore_region_action) {
5852 RegionSelection rs = get_regions_from_selection_and_entered ();
5854 if (!_session || rs.empty()) {
5858 Glib::RefPtr<ToggleAction> a = Glib::RefPtr<ToggleAction>::cast_dynamic (_region_actions->get_action("toggle-region-lock-style"));
5859 vector<Widget*> proxies = a->get_proxies();
5860 Gtk::CheckMenuItem* cmi = dynamic_cast<Gtk::CheckMenuItem*> (proxies.front());
5864 begin_reversible_command (_("toggle region lock style"));
5866 for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
5867 (*i)->region()->clear_changes ();
5868 PositionLockStyle const ns = ((*i)->region()->position_lock_style() == AudioTime && !cmi->get_inconsistent()) ? MusicTime : AudioTime;
5869 (*i)->region()->set_position_lock_style (ns);
5870 _session->add_command (new StatefulDiffCommand ((*i)->region()));
5873 commit_reversible_command ();
5877 Editor::toggle_opaque_region ()
5879 if (_ignore_region_action) {
5883 RegionSelection rs = get_regions_from_selection_and_entered ();
5885 if (!_session || rs.empty()) {
5889 begin_reversible_command (_("change region opacity"));
5891 for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
5892 (*i)->region()->clear_changes ();
5893 (*i)->region()->set_opaque (!(*i)->region()->opaque());
5894 _session->add_command (new StatefulDiffCommand ((*i)->region()));
5897 commit_reversible_command ();
5901 Editor::toggle_record_enable ()
5903 bool new_state = false;
5905 for (TrackSelection::iterator i = selection->tracks.begin(); i != selection->tracks.end(); ++i) {
5906 RouteTimeAxisView *rtav = dynamic_cast<RouteTimeAxisView *>(*i);
5909 if (!rtav->is_track())
5913 new_state = !rtav->track()->rec_enable_control()->get_value();
5917 rtav->track()->rec_enable_control()->set_value (new_state, Controllable::UseGroup);
5922 tracklist_to_stripables (TrackViewList list)
5926 for (TrackSelection::iterator i = list.begin(); i != list.end(); ++i) {
5927 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> ((*i));
5929 if (rtv && rtv->is_track()) {
5930 ret.push_back (rtv->track());
5938 Editor::play_solo_selection (bool restart)
5940 //note: session::solo_selection takes care of invalidating the region playlist
5942 if ((!selection->tracks.empty()) && selection->time.length() > 0) { //a range is selected; solo the tracks and roll
5944 StripableList sl = tracklist_to_stripables (selection->tracks);
5945 _session->solo_selection (sl, true);
5948 samplepos_t start = selection->time.start();
5949 samplepos_t end = selection->time.end_sample();
5950 _session->request_bounded_roll (start, end);
5952 } else if (! selection->tracks.empty()) { //no range is selected, but tracks are selected; solo the tracks and roll
5953 StripableList sl = tracklist_to_stripables (selection->tracks);
5954 _session->solo_selection (sl, true);
5955 _session->request_cancel_play_range();
5956 transition_to_rolling (true);
5958 } else if (! selection->regions.empty()) { //solo any tracks with selected regions, and roll
5959 StripableList sl = tracklist_to_stripables (get_tracks_for_range_action());
5960 _session->solo_selection (sl, true);
5961 _session->request_cancel_play_range();
5962 transition_to_rolling (true);
5964 _session->request_cancel_play_range();
5965 transition_to_rolling (true); //no selection. just roll.
5970 Editor::toggle_solo ()
5972 bool new_state = false;
5974 boost::shared_ptr<ControlList> cl (new ControlList);
5976 for (TrackSelection::iterator i = selection->tracks.begin(); i != selection->tracks.end(); ++i) {
5977 StripableTimeAxisView *stav = dynamic_cast<StripableTimeAxisView *>(*i);
5979 if (!stav || !stav->stripable()->solo_control()) {
5984 new_state = !stav->stripable()->solo_control()->soloed ();
5988 cl->push_back (stav->stripable()->solo_control());
5991 _session->set_controls (cl, new_state ? 1.0 : 0.0, Controllable::UseGroup);
5995 Editor::toggle_mute ()
5997 bool new_state = false;
5999 boost::shared_ptr<ControlList> cl (new ControlList);
6001 for (TrackSelection::iterator i = selection->tracks.begin(); i != selection->tracks.end(); ++i) {
6002 StripableTimeAxisView *stav = dynamic_cast<StripableTimeAxisView *>(*i);
6004 if (!stav || !stav->stripable()->mute_control()) {
6009 new_state = !stav->stripable()->mute_control()->muted();
6013 cl->push_back (stav->stripable()->mute_control());
6016 _session->set_controls (cl, new_state, Controllable::UseGroup);
6020 Editor::toggle_solo_isolate ()
6026 Editor::fade_range ()
6028 TrackViewList ts = selection->tracks.filter_to_unique_playlists ();
6030 begin_reversible_command (_("fade range"));
6032 for (TrackViewList::iterator i = ts.begin(); i != ts.end(); ++i) {
6033 (*i)->fade_range (selection->time);
6036 commit_reversible_command ();
6041 Editor::set_fade_length (bool in)
6043 RegionSelection rs = get_regions_from_selection_and_entered ();
6049 /* we need a region to measure the offset from the start */
6051 RegionView* rv = rs.front ();
6053 samplepos_t pos = get_preferred_edit_position();
6057 if (pos > rv->region()->last_sample() || pos < rv->region()->first_sample()) {
6058 /* edit point is outside the relevant region */
6063 if (pos <= rv->region()->position()) {
6067 len = pos - rv->region()->position();
6068 cmd = _("set fade in length");
6070 if (pos >= rv->region()->last_sample()) {
6074 len = rv->region()->last_sample() - pos;
6075 cmd = _("set fade out length");
6078 bool in_command = false;
6080 for (RegionSelection::iterator x = rs.begin(); x != rs.end(); ++x) {
6081 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (*x);
6087 boost::shared_ptr<AutomationList> alist;
6089 alist = tmp->audio_region()->fade_in();
6091 alist = tmp->audio_region()->fade_out();
6094 XMLNode &before = alist->get_state();
6097 tmp->audio_region()->set_fade_in_length (len);
6098 tmp->audio_region()->set_fade_in_active (true);
6100 tmp->audio_region()->set_fade_out_length (len);
6101 tmp->audio_region()->set_fade_out_active (true);
6105 begin_reversible_command (cmd);
6108 XMLNode &after = alist->get_state();
6109 _session->add_command(new MementoCommand<AutomationList>(*alist, &before, &after));
6113 commit_reversible_command ();
6118 Editor::set_fade_in_shape (FadeShape shape)
6120 RegionSelection rs = get_regions_from_selection_and_entered ();
6125 bool in_command = false;
6127 for (RegionSelection::iterator x = rs.begin(); x != rs.end(); ++x) {
6128 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (*x);
6134 boost::shared_ptr<AutomationList> alist = tmp->audio_region()->fade_in();
6135 XMLNode &before = alist->get_state();
6137 tmp->audio_region()->set_fade_in_shape (shape);
6140 begin_reversible_command (_("set fade in shape"));
6143 XMLNode &after = alist->get_state();
6144 _session->add_command(new MementoCommand<AutomationList>(*alist.get(), &before, &after));
6148 commit_reversible_command ();
6153 Editor::set_fade_out_shape (FadeShape shape)
6155 RegionSelection rs = get_regions_from_selection_and_entered ();
6160 bool in_command = false;
6162 for (RegionSelection::iterator x = rs.begin(); x != rs.end(); ++x) {
6163 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (*x);
6169 boost::shared_ptr<AutomationList> alist = tmp->audio_region()->fade_out();
6170 XMLNode &before = alist->get_state();
6172 tmp->audio_region()->set_fade_out_shape (shape);
6175 begin_reversible_command (_("set fade out shape"));
6178 XMLNode &after = alist->get_state();
6179 _session->add_command(new MementoCommand<AutomationList>(*alist.get(), &before, &after));
6183 commit_reversible_command ();
6188 Editor::set_fade_in_active (bool yn)
6190 RegionSelection rs = get_regions_from_selection_and_entered ();
6195 bool in_command = false;
6197 for (RegionSelection::iterator x = rs.begin(); x != rs.end(); ++x) {
6198 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (*x);
6205 boost::shared_ptr<AudioRegion> ar (tmp->audio_region());
6207 ar->clear_changes ();
6208 ar->set_fade_in_active (yn);
6211 begin_reversible_command (_("set fade in active"));
6214 _session->add_command (new StatefulDiffCommand (ar));
6218 commit_reversible_command ();
6223 Editor::set_fade_out_active (bool yn)
6225 RegionSelection rs = get_regions_from_selection_and_entered ();
6230 bool in_command = false;
6232 for (RegionSelection::iterator x = rs.begin(); x != rs.end(); ++x) {
6233 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (*x);
6239 boost::shared_ptr<AudioRegion> ar (tmp->audio_region());
6241 ar->clear_changes ();
6242 ar->set_fade_out_active (yn);
6245 begin_reversible_command (_("set fade out active"));
6248 _session->add_command(new StatefulDiffCommand (ar));
6252 commit_reversible_command ();
6257 Editor::toggle_region_fades (int dir)
6259 if (_ignore_region_action) {
6263 boost::shared_ptr<AudioRegion> ar;
6266 RegionSelection rs = get_regions_from_selection_and_entered ();
6272 RegionSelection::iterator i;
6273 for (i = rs.begin(); i != rs.end(); ++i) {
6274 if ((ar = boost::dynamic_pointer_cast<AudioRegion>((*i)->region())) != 0) {
6276 yn = ar->fade_out_active ();
6278 yn = ar->fade_in_active ();
6284 if (i == rs.end()) {
6288 /* XXX should this undo-able? */
6289 bool in_command = false;
6291 for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
6292 if ((ar = boost::dynamic_pointer_cast<AudioRegion>((*i)->region())) == 0) {
6295 ar->clear_changes ();
6297 if (dir == 1 || dir == 0) {
6298 ar->set_fade_in_active (!yn);
6301 if (dir == -1 || dir == 0) {
6302 ar->set_fade_out_active (!yn);
6305 begin_reversible_command (_("toggle fade active"));
6308 _session->add_command(new StatefulDiffCommand (ar));
6312 commit_reversible_command ();
6317 /** Update region fade visibility after its configuration has been changed */
6319 Editor::update_region_fade_visibility ()
6321 bool _fade_visibility = _session->config.get_show_region_fades ();
6323 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
6324 AudioTimeAxisView* v = dynamic_cast<AudioTimeAxisView*>(*i);
6326 if (_fade_visibility) {
6327 v->audio_view()->show_all_fades ();
6329 v->audio_view()->hide_all_fades ();
6336 Editor::set_edit_point ()
6339 MusicSample where (0, 0);
6341 if (!mouse_sample (where.sample, ignored)) {
6347 if (selection->markers.empty()) {
6349 mouse_add_new_marker (where.sample);
6354 Location* loc = find_location_from_marker (selection->markers.front(), ignored);
6357 loc->move_to (where.sample, where.division);
6363 Editor::set_playhead_cursor ()
6365 if (entered_marker) {
6366 _session->request_locate (entered_marker->position(), _session->transport_rolling());
6368 MusicSample where (0, 0);
6371 if (!mouse_sample (where.sample, ignored)) {
6378 _session->request_locate (where.sample, _session->transport_rolling());
6382 //not sure what this was for; remove it for now.
6383 // if (UIConfiguration::instance().get_follow_edits() && (!_session || !_session->config.get_external_sync())) {
6384 // cancel_time_selection();
6390 Editor::split_region ()
6392 if (_dragging_playhead) {
6394 } else if (_drags->active ()) {
6395 /*any other kind of drag, bail out so we avoid Undo snafu*/
6399 //if a range is selected, separate it
6400 if (!selection->time.empty()) {
6401 separate_regions_between (selection->time);
6405 //if no range was selected, try to find some regions to split
6406 if (current_mouse_mode() == MouseObject || current_mouse_mode() == MouseRange ) { //don't try this for Internal Edit, Stretch, Draw, etc.
6408 RegionSelection rs = get_regions_from_selection_and_edit_point ();
6409 const samplepos_t pos = get_preferred_edit_position();
6410 const int32_t division = get_grid_music_divisions (0);
6411 MusicSample where (pos, division);
6417 split_regions_at (where, rs);
6423 Editor::select_next_stripable (bool routes_only)
6425 if (selection->tracks.empty()) {
6426 selection->set (track_views.front());
6430 TimeAxisView* current = selection->tracks.front();
6434 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
6436 if (*i == current) {
6438 if (i != track_views.end()) {
6441 current = (*(track_views.begin()));
6442 //selection->set (*(track_views.begin()));
6449 RouteUI* rui = dynamic_cast<RouteUI *>(current);
6450 valid = rui && rui->route()->active();
6452 valid = 0 != current->stripable ().get();
6455 } while (current->hidden() || !valid);
6457 selection->set (current);
6459 ensure_time_axis_view_is_visible (*current, false);
6463 Editor::select_prev_stripable (bool routes_only)
6465 if (selection->tracks.empty()) {
6466 selection->set (track_views.front());
6470 TimeAxisView* current = selection->tracks.front();
6474 for (TrackViewList::reverse_iterator i = track_views.rbegin(); i != track_views.rend(); ++i) {
6476 if (*i == current) {
6478 if (i != track_views.rend()) {
6481 current = *(track_views.rbegin());
6487 RouteUI* rui = dynamic_cast<RouteUI *>(current);
6488 valid = rui && rui->route()->active();
6490 valid = 0 != current->stripable ().get();
6493 } while (current->hidden() || !valid);
6495 selection->set (current);
6497 ensure_time_axis_view_is_visible (*current, false);
6501 Editor::set_loop_from_selection (bool play)
6503 if (_session == 0) {
6507 samplepos_t start, end;
6508 if (!get_selection_extents (start, end))
6511 set_loop_range (start, end, _("set loop range from selection"));
6514 _session->request_play_loop (true, true);
6519 Editor::set_loop_from_region (bool play)
6521 samplepos_t start, end;
6522 if (!get_selection_extents (start, end))
6525 set_loop_range (start, end, _("set loop range from region"));
6528 _session->request_locate (start, true);
6529 _session->request_play_loop (true);
6534 Editor::set_punch_from_selection ()
6536 if (_session == 0) {
6540 samplepos_t start, end;
6541 if (!get_selection_extents (start, end))
6544 set_punch_range (start, end, _("set punch range from selection"));
6548 Editor::set_auto_punch_range ()
6550 // auto punch in/out button from a single button
6551 // If Punch In is unset, set punch range from playhead to end, enable punch in
6552 // If Punch In is set, the next punch sets Punch Out, unless the playhead has been
6553 // rewound beyond the Punch In marker, in which case that marker will be moved back
6554 // to the current playhead position.
6555 // If punch out is set, it clears the punch range and Punch In/Out buttons
6557 if (_session == 0) {
6561 Location* tpl = transport_punch_location();
6562 samplepos_t now = playhead_cursor->current_sample();
6563 samplepos_t begin = now;
6564 samplepos_t end = _session->current_end_sample();
6566 if (!_session->config.get_punch_in()) {
6567 // First Press - set punch in and create range from here to eternity
6568 set_punch_range (begin, end, _("Auto Punch In"));
6569 _session->config.set_punch_in(true);
6570 } else if (tpl && !_session->config.get_punch_out()) {
6571 // Second press - update end range marker and set punch_out
6572 if (now < tpl->start()) {
6573 // playhead has been rewound - move start back and pretend nothing happened
6575 set_punch_range (begin, end, _("Auto Punch In/Out"));
6577 // normal case for 2nd press - set the punch out
6578 end = playhead_cursor->current_sample ();
6579 set_punch_range (tpl->start(), now, _("Auto Punch In/Out"));
6580 _session->config.set_punch_out(true);
6583 if (_session->config.get_punch_out()) {
6584 _session->config.set_punch_out(false);
6587 if (_session->config.get_punch_in()) {
6588 _session->config.set_punch_in(false);
6593 // third press - unset punch in/out and remove range
6594 _session->locations()->remove(tpl);
6601 Editor::set_session_extents_from_selection ()
6603 if (_session == 0) {
6607 samplepos_t start, end;
6608 if (!get_selection_extents (start, end))
6612 if ((loc = _session->locations()->session_range_location()) == 0) {
6613 _session->set_session_extents (start, end); // this will create a new session range; no need for UNDO
6615 XMLNode &before = loc->get_state();
6617 _session->set_session_extents (start, end);
6619 XMLNode &after = loc->get_state();
6621 begin_reversible_command (_("set session start/end from selection"));
6623 _session->add_command (new MementoCommand<Location>(*loc, &before, &after));
6625 commit_reversible_command ();
6628 _session->set_end_is_free (false);
6632 Editor::set_punch_start_from_edit_point ()
6636 MusicSample start (0, 0);
6637 samplepos_t end = max_samplepos;
6639 //use the existing punch end, if any
6640 Location* tpl = transport_punch_location();
6645 if ((_edit_point == EditAtPlayhead) && _session->transport_rolling()) {
6646 start.sample = _session->audible_sample();
6648 start.sample = get_preferred_edit_position();
6651 //if there's not already a sensible selection endpoint, go "forever"
6652 if (start.sample > end) {
6653 end = max_samplepos;
6656 set_punch_range (start.sample, end, _("set punch start from EP"));
6662 Editor::set_punch_end_from_edit_point ()
6666 samplepos_t start = 0;
6667 MusicSample end (max_samplepos, 0);
6669 //use the existing punch start, if any
6670 Location* tpl = transport_punch_location();
6672 start = tpl->start();
6675 if ((_edit_point == EditAtPlayhead) && _session->transport_rolling()) {
6676 end.sample = _session->audible_sample();
6678 end.sample = get_preferred_edit_position();
6681 set_punch_range (start, end.sample, _("set punch end from EP"));
6687 Editor::set_loop_start_from_edit_point ()
6691 MusicSample start (0, 0);
6692 samplepos_t end = max_samplepos;
6694 //use the existing loop end, if any
6695 Location* tpl = transport_loop_location();
6700 if ((_edit_point == EditAtPlayhead) && _session->transport_rolling()) {
6701 start.sample = _session->audible_sample();
6703 start.sample = get_preferred_edit_position();
6706 //if there's not already a sensible selection endpoint, go "forever"
6707 if (start.sample > end) {
6708 end = max_samplepos;
6711 set_loop_range (start.sample, end, _("set loop start from EP"));
6717 Editor::set_loop_end_from_edit_point ()
6721 samplepos_t start = 0;
6722 MusicSample end (max_samplepos, 0);
6724 //use the existing loop start, if any
6725 Location* tpl = transport_loop_location();
6727 start = tpl->start();
6730 if ((_edit_point == EditAtPlayhead) && _session->transport_rolling()) {
6731 end.sample = _session->audible_sample();
6733 end.sample = get_preferred_edit_position();
6736 set_loop_range (start, end.sample, _("set loop end from EP"));
6741 Editor::set_punch_from_region ()
6743 samplepos_t start, end;
6744 if (!get_selection_extents (start, end))
6747 set_punch_range (start, end, _("set punch range from region"));
6751 Editor::pitch_shift_region ()
6753 RegionSelection rs = get_regions_from_selection_and_entered ();
6755 RegionSelection audio_rs;
6756 for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
6757 if (dynamic_cast<AudioRegionView*> (*i)) {
6758 audio_rs.push_back (*i);
6762 if (audio_rs.empty()) {
6766 pitch_shift (audio_rs, 1.2);
6770 Editor::set_tempo_from_region ()
6772 RegionSelection rs = get_regions_from_selection_and_entered ();
6774 if (!_session || rs.empty()) {
6778 RegionView* rv = rs.front();
6780 define_one_bar (rv->region()->position(), rv->region()->last_sample() + 1);
6784 Editor::use_range_as_bar ()
6786 samplepos_t start, end;
6787 if (get_edit_op_range (start, end)) {
6788 define_one_bar (start, end);
6793 Editor::define_one_bar (samplepos_t start, samplepos_t end)
6795 samplepos_t length = end - start;
6797 const Meter& m (_session->tempo_map().meter_at_sample (start));
6799 /* length = 1 bar */
6801 /* We're going to deliver a constant tempo here,
6802 so we can use samples per beat to determine length.
6803 now we want samples per beat.
6804 we have samples per bar, and beats per bar, so ...
6807 /* XXXX METER MATH */
6809 double samples_per_beat = length / m.divisions_per_bar();
6811 /* beats per minute = */
6813 double beats_per_minute = (_session->sample_rate() * 60.0) / samples_per_beat;
6815 /* now decide whether to:
6817 (a) set global tempo
6818 (b) add a new tempo marker
6822 const TempoSection& t (_session->tempo_map().tempo_section_at_sample (start));
6824 bool do_global = false;
6826 if ((_session->tempo_map().n_tempos() == 1) && (_session->tempo_map().n_meters() == 1)) {
6828 /* only 1 tempo & 1 meter: ask if the user wants to set the tempo
6829 at the start, or create a new marker
6832 vector<string> options;
6833 options.push_back (_("Cancel"));
6834 options.push_back (_("Add new marker"));
6835 options.push_back (_("Set global tempo"));
6838 _("Define one bar"),
6839 _("Do you want to set the global tempo or add a new tempo marker?"),
6843 c.set_default_response (2);
6859 /* more than 1 tempo and/or meter section already, go ahead do the "usual":
6860 if the marker is at the region starter, change it, otherwise add
6865 begin_reversible_command (_("set tempo from region"));
6866 XMLNode& before (_session->tempo_map().get_state());
6869 _session->tempo_map().change_initial_tempo (beats_per_minute, t.note_type(), t.end_note_types_per_minute());
6870 } else if (t.sample() == start) {
6871 _session->tempo_map().change_existing_tempo_at (start, beats_per_minute, t.note_type(), t.end_note_types_per_minute());
6873 /* constant tempo */
6874 const Tempo tempo (beats_per_minute, t.note_type());
6875 _session->tempo_map().add_tempo (tempo, 0.0, start, AudioTime);
6878 XMLNode& after (_session->tempo_map().get_state());
6880 _session->add_command (new MementoCommand<TempoMap>(_session->tempo_map(), &before, &after));
6881 commit_reversible_command ();
6885 Editor::split_region_at_transients ()
6887 AnalysisFeatureList positions;
6889 RegionSelection rs = get_regions_from_selection_and_entered ();
6891 if (!_session || rs.empty()) {
6895 begin_reversible_command (_("split regions"));
6897 for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ) {
6899 RegionSelection::iterator tmp;
6904 boost::shared_ptr<AudioRegion> ar = boost::dynamic_pointer_cast<AudioRegion> ((*i)->region());
6907 ar->transients (positions);
6908 split_region_at_points ((*i)->region(), positions, true);
6915 commit_reversible_command ();
6920 Editor::split_region_at_points (boost::shared_ptr<Region> r, AnalysisFeatureList& positions, bool can_ferret, bool select_new)
6922 bool use_rhythmic_rodent = false;
6924 boost::shared_ptr<Playlist> pl = r->playlist();
6926 list<boost::shared_ptr<Region> > new_regions;
6932 if (positions.empty()) {
6936 if (positions.size() > 20 && can_ferret) {
6937 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);
6938 MessageDialog msg (msgstr,
6941 Gtk::BUTTONS_OK_CANCEL);
6944 msg.add_button (_("Call for the Ferret!"), RESPONSE_APPLY);
6945 msg.set_secondary_text (_("Press OK to continue with this split operation\nor ask the Ferret dialog to tune the analysis"));
6947 msg.set_secondary_text (_("Press OK to continue with this split operation"));
6950 msg.set_title (_("Excessive split?"));
6953 int response = msg.run();
6959 case RESPONSE_APPLY:
6960 use_rhythmic_rodent = true;
6967 if (use_rhythmic_rodent) {
6968 show_rhythm_ferret ();
6972 AnalysisFeatureList::const_iterator x;
6974 pl->clear_changes ();
6975 pl->clear_owned_changes ();
6977 x = positions.begin();
6979 if (x == positions.end()) {
6984 pl->remove_region (r);
6986 samplepos_t pos = 0;
6988 samplepos_t rstart = r->first_sample ();
6989 samplepos_t rend = r->last_sample ();
6991 while (x != positions.end()) {
6993 /* deal with positons that are out of scope of present region bounds */
6994 if (*x <= rstart || *x > rend) {
6999 /* file start = original start + how far we from the initial position ? */
7001 samplepos_t file_start = r->start() + pos;
7003 /* length = next position - current position */
7005 samplepos_t len = (*x) - pos - rstart;
7007 /* XXX we do we really want to allow even single-sample regions?
7008 * shouldn't we have some kind of lower limit on region size?
7017 if (RegionFactory::region_name (new_name, r->name())) {
7021 /* do NOT announce new regions 1 by one, just wait till they are all done */
7025 plist.add (ARDOUR::Properties::start, file_start);
7026 plist.add (ARDOUR::Properties::length, len);
7027 plist.add (ARDOUR::Properties::name, new_name);
7028 plist.add (ARDOUR::Properties::layer, 0);
7029 // TODO set transients_offset
7031 boost::shared_ptr<Region> nr = RegionFactory::create (r->sources(), plist, false);
7032 /* because we set annouce to false, manually add the new region to the
7035 RegionFactory::map_add (nr);
7037 pl->add_region (nr, rstart + pos);
7040 new_regions.push_front(nr);
7049 RegionFactory::region_name (new_name, r->name());
7051 /* Add the final region */
7054 plist.add (ARDOUR::Properties::start, r->start() + pos);
7055 plist.add (ARDOUR::Properties::length, r->last_sample() - (r->position() + pos) + 1);
7056 plist.add (ARDOUR::Properties::name, new_name);
7057 plist.add (ARDOUR::Properties::layer, 0);
7059 boost::shared_ptr<Region> nr = RegionFactory::create (r->sources(), plist, false);
7060 /* because we set annouce to false, manually add the new region to the
7063 RegionFactory::map_add (nr);
7064 pl->add_region (nr, r->position() + pos);
7067 new_regions.push_front(nr);
7072 /* We might have removed regions, which alters other regions' layering_index,
7073 so we need to do a recursive diff here.
7075 vector<Command*> cmds;
7077 _session->add_commands (cmds);
7079 _session->add_command (new StatefulDiffCommand (pl));
7083 for (list<boost::shared_ptr<Region> >::iterator i = new_regions.begin(); i != new_regions.end(); ++i){
7084 set_selected_regionview_from_region_list ((*i), Selection::Add);
7090 Editor::place_transient()
7096 RegionSelection rs = get_regions_from_selection_and_edit_point ();
7102 samplepos_t where = get_preferred_edit_position();
7104 begin_reversible_command (_("place transient"));
7106 for (RegionSelection::iterator r = rs.begin(); r != rs.end(); ++r) {
7107 (*r)->region()->add_transient(where);
7110 commit_reversible_command ();
7114 Editor::remove_transient(ArdourCanvas::Item* item)
7120 ArdourCanvas::Line* _line = reinterpret_cast<ArdourCanvas::Line*> (item);
7123 AudioRegionView* _arv = reinterpret_cast<AudioRegionView*> (item->get_data ("regionview"));
7124 _arv->remove_transient (*(float*) _line->get_data ("position"));
7128 Editor::snap_regions_to_grid ()
7130 list <boost::shared_ptr<Playlist > > used_playlists;
7132 RegionSelection rs = get_regions_from_selection_and_entered ();
7134 if (!_session || rs.empty()) {
7138 begin_reversible_command (_("snap regions to grid"));
7140 for (RegionSelection::iterator r = rs.begin(); r != rs.end(); ++r) {
7142 boost::shared_ptr<Playlist> pl = (*r)->region()->playlist();
7144 if (!pl->frozen()) {
7145 /* we haven't seen this playlist before */
7147 /* remember used playlists so we can thaw them later */
7148 used_playlists.push_back(pl);
7151 (*r)->region()->clear_changes ();
7153 MusicSample start ((*r)->region()->first_sample (), 0);
7154 snap_to (start, RoundNearest, SnapToGrid);
7155 (*r)->region()->set_position (start.sample, start.division);
7156 _session->add_command(new StatefulDiffCommand ((*r)->region()));
7159 while (used_playlists.size() > 0) {
7160 list <boost::shared_ptr<Playlist > >::iterator i = used_playlists.begin();
7162 used_playlists.pop_front();
7165 commit_reversible_command ();
7169 Editor::close_region_gaps ()
7171 list <boost::shared_ptr<Playlist > > used_playlists;
7173 RegionSelection rs = get_regions_from_selection_and_entered ();
7175 if (!_session || rs.empty()) {
7179 Dialog dialog (_("Close Region Gaps"));
7182 table.set_spacings (12);
7183 table.set_border_width (12);
7184 Label* l = manage (left_aligned_label (_("Crossfade length")));
7185 table.attach (*l, 0, 1, 0, 1);
7187 SpinButton spin_crossfade (1, 0);
7188 spin_crossfade.set_range (0, 15);
7189 spin_crossfade.set_increments (1, 1);
7190 spin_crossfade.set_value (5);
7191 table.attach (spin_crossfade, 1, 2, 0, 1);
7193 table.attach (*manage (new Label (_("ms"))), 2, 3, 0, 1);
7195 l = manage (left_aligned_label (_("Pull-back length")));
7196 table.attach (*l, 0, 1, 1, 2);
7198 SpinButton spin_pullback (1, 0);
7199 spin_pullback.set_range (0, 100);
7200 spin_pullback.set_increments (1, 1);
7201 spin_pullback.set_value(30);
7202 table.attach (spin_pullback, 1, 2, 1, 2);
7204 table.attach (*manage (new Label (_("ms"))), 2, 3, 1, 2);
7206 dialog.get_vbox()->pack_start (table);
7207 dialog.add_button (Stock::CANCEL, RESPONSE_CANCEL);
7208 dialog.add_button (_("Ok"), RESPONSE_ACCEPT);
7211 if (dialog.run () == RESPONSE_CANCEL) {
7215 samplepos_t crossfade_len = spin_crossfade.get_value();
7216 samplepos_t pull_back_samples = spin_pullback.get_value();
7218 crossfade_len = lrintf (crossfade_len * _session->sample_rate()/1000);
7219 pull_back_samples = lrintf (pull_back_samples * _session->sample_rate()/1000);
7221 /* Iterate over the region list and make adjacent regions overlap by crossfade_len_ms */
7223 begin_reversible_command (_("close region gaps"));
7226 boost::shared_ptr<Region> last_region;
7228 rs.sort_by_position_and_track();
7230 for (RegionSelection::iterator r = rs.begin(); r != rs.end(); ++r) {
7232 boost::shared_ptr<Playlist> pl = (*r)->region()->playlist();
7234 if (!pl->frozen()) {
7235 /* we haven't seen this playlist before */
7237 /* remember used playlists so we can thaw them later */
7238 used_playlists.push_back(pl);
7242 samplepos_t position = (*r)->region()->position();
7244 if (idx == 0 || position < last_region->position()){
7245 last_region = (*r)->region();
7250 (*r)->region()->clear_changes ();
7251 (*r)->region()->trim_front((position - pull_back_samples));
7253 last_region->clear_changes ();
7254 last_region->trim_end ((position - pull_back_samples + crossfade_len));
7256 _session->add_command (new StatefulDiffCommand ((*r)->region()));
7257 _session->add_command (new StatefulDiffCommand (last_region));
7259 last_region = (*r)->region();
7263 while (used_playlists.size() > 0) {
7264 list <boost::shared_ptr<Playlist > >::iterator i = used_playlists.begin();
7266 used_playlists.pop_front();
7269 commit_reversible_command ();
7273 Editor::tab_to_transient (bool forward)
7275 AnalysisFeatureList positions;
7277 RegionSelection rs = get_regions_from_selection_and_entered ();
7283 samplepos_t pos = _session->audible_sample ();
7285 if (!selection->tracks.empty()) {
7287 /* don't waste time searching for transients in duplicate playlists.
7290 TrackViewList ts = selection->tracks.filter_to_unique_playlists ();
7292 for (TrackViewList::iterator t = ts.begin(); t != ts.end(); ++t) {
7294 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (*t);
7297 boost::shared_ptr<Track> tr = rtv->track();
7299 boost::shared_ptr<Playlist> pl = tr->playlist ();
7301 samplepos_t result = pl->find_next_transient (pos, forward ? 1 : -1);
7304 positions.push_back (result);
7317 for (RegionSelection::iterator r = rs.begin(); r != rs.end(); ++r) {
7318 (*r)->region()->get_transients (positions);
7322 TransientDetector::cleanup_transients (positions, _session->sample_rate(), 3.0);
7325 AnalysisFeatureList::iterator x;
7327 for (x = positions.begin(); x != positions.end(); ++x) {
7333 if (x != positions.end ()) {
7334 _session->request_locate (*x);
7338 AnalysisFeatureList::reverse_iterator x;
7340 for (x = positions.rbegin(); x != positions.rend(); ++x) {
7346 if (x != positions.rend ()) {
7347 _session->request_locate (*x);
7353 Editor::playhead_forward_to_grid ()
7359 MusicSample pos (playhead_cursor->current_sample (), 0);
7361 if ( _grid_type == GridTypeNone) {
7362 if (pos.sample < max_samplepos - current_page_samples()*0.1) {
7363 pos.sample += current_page_samples()*0.1;
7364 _session->request_locate (pos.sample);
7366 _session->request_locate (0);
7370 if (pos.sample < max_samplepos - 1) {
7372 snap_to_internal (pos, RoundUpAlways, SnapToGrid, false, true);
7373 _session->request_locate (pos.sample);
7378 /* keep PH visible in window */
7379 if (pos.sample > (_leftmost_sample + current_page_samples() *0.9)) {
7380 reset_x_origin (pos.sample - (current_page_samples()*0.9));
7386 Editor::playhead_backward_to_grid ()
7392 MusicSample pos (playhead_cursor->current_sample (), 0);
7394 if ( _grid_type == GridTypeNone) {
7395 if ( pos.sample > current_page_samples()*0.1 ) {
7396 pos.sample -= current_page_samples()*0.1;
7397 _session->request_locate (pos.sample);
7399 _session->request_locate (0);
7403 if (pos.sample > 2) {
7405 snap_to_internal (pos, RoundDownAlways, SnapToGrid, false, true);
7408 //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...
7409 //also see: jump_backward_to_mark
7410 if (_session->transport_rolling()) {
7411 if ((playhead_cursor->current_sample() - pos.sample) < _session->sample_rate()/2) {
7412 snap_to_internal (pos, RoundDownAlways, SnapToGrid, false, true);
7416 _session->request_locate (pos.sample, _session->transport_rolling());
7419 /* keep PH visible in window */
7420 if (pos.sample < (_leftmost_sample + current_page_samples() *0.1)) {
7421 reset_x_origin (pos.sample - (current_page_samples()*0.1));
7426 Editor::set_track_height (Height h)
7428 TrackSelection& ts (selection->tracks);
7430 for (TrackSelection::iterator x = ts.begin(); x != ts.end(); ++x) {
7431 (*x)->set_height_enum (h);
7436 Editor::toggle_tracks_active ()
7438 TrackSelection& ts (selection->tracks);
7440 bool target = false;
7446 for (TrackSelection::iterator x = ts.begin(); x != ts.end(); ++x) {
7447 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*>(*x);
7451 target = !rtv->_route->active();
7454 rtv->_route->set_active (target, this);
7460 Editor::remove_tracks ()
7462 /* this will delete GUI objects that may be the subject of an event
7463 handler in which this method is called. Defer actual deletion to the
7464 next idle callback, when all event handling is finished.
7466 Glib::signal_idle().connect (sigc::mem_fun (*this, &Editor::idle_remove_tracks));
7470 Editor::idle_remove_tracks ()
7472 Session::StateProtector sp (_session);
7474 return false; /* do not call again */
7478 Editor::_remove_tracks ()
7480 TrackSelection& ts (selection->tracks);
7486 vector<string> choices;
7491 const char* trackstr;
7494 vector<boost::shared_ptr<Route> > routes;
7495 vector<boost::shared_ptr<VCA> > vcas;
7496 bool special_bus = false;
7498 for (TrackSelection::iterator x = ts.begin(); x != ts.end(); ++x) {
7499 VCATimeAxisView* vtv = dynamic_cast<VCATimeAxisView*> (*x);
7501 vcas.push_back (vtv->vca());
7505 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (*x);
7509 if (rtv->is_track()) {
7514 routes.push_back (rtv->_route);
7516 if (rtv->route()->is_master() || rtv->route()->is_monitor()) {
7521 if (special_bus && !Config->get_allow_special_bus_removal()) {
7522 MessageDialog msg (_("That would be bad news ...."),
7526 msg.set_secondary_text (string_compose (_(
7527 "Removing the master or monitor bus is such a bad idea\n\
7528 that %1 is not going to allow it.\n\
7530 If you really want to do this sort of thing\n\
7531 edit your ardour.rc file to set the\n\
7532 \"allow-special-bus-removal\" option to be \"yes\""), PROGRAM_NAME));
7539 if (ntracks + nbusses + nvcas == 0) {
7545 trackstr = P_("track", "tracks", ntracks);
7546 busstr = P_("bus", "busses", nbusses);
7547 vcastr = P_("VCA", "VCAs", nvcas);
7549 if (ntracks > 0 && nbusses > 0 && nvcas > 0) {
7550 title = _("Remove various strips");
7551 prompt = string_compose (_("Do you really want to remove %1 %2, %3 %4 and %5 %6?"),
7552 ntracks, trackstr, nbusses, busstr, nvcas, vcastr);
7554 else if (ntracks > 0 && nbusses > 0) {
7555 title = string_compose (_("Remove %1 and %2"), trackstr, busstr);
7556 prompt = string_compose (_("Do you really want to remove %1 %2 and %3 %4?"),
7557 ntracks, trackstr, nbusses, busstr);
7559 else if (ntracks > 0 && nvcas > 0) {
7560 title = string_compose (_("Remove %1 and %2"), trackstr, vcastr);
7561 prompt = string_compose (_("Do you really want to remove %1 %2 and %3 %4?"),
7562 ntracks, trackstr, nvcas, vcastr);
7564 else if (nbusses > 0 && nvcas > 0) {
7565 title = string_compose (_("Remove %1 and %2"), busstr, vcastr);
7566 prompt = string_compose (_("Do you really want to remove %1 %2 and %3 %4?"),
7567 nbusses, busstr, nvcas, vcastr);
7569 else if (ntracks > 0) {
7570 title = string_compose (_("Remove %1"), trackstr);
7571 prompt = string_compose (_("Do you really want to remove %1 %2?"),
7574 else if (nbusses > 0) {
7575 title = string_compose (_("Remove %1"), busstr);
7576 prompt = string_compose (_("Do you really want to remove %1 %2?"),
7579 else if (nvcas > 0) {
7580 title = string_compose (_("Remove %1"), vcastr);
7581 prompt = string_compose (_("Do you really want to remove %1 %2?"),
7589 prompt += "\n" + string_compose ("(You may also lose the playlists associated with the %1)", trackstr) + "\n";
7592 prompt += "\n" + string(_("This action cannot be undone, and the session file will be overwritten!"));
7594 choices.push_back (_("No, do nothing."));
7595 if (ntracks + nbusses + nvcas > 1) {
7596 choices.push_back (_("Yes, remove them."));
7598 choices.push_back (_("Yes, remove it."));
7601 Choice prompter (title, prompt, choices);
7603 if (prompter.run () != 1) {
7607 if (current_mixer_strip && routes.size () > 1 && std::find (routes.begin(), routes.end(), current_mixer_strip->route()) != routes.end ()) {
7608 /* Route deletion calls Editor::timeaxisview_deleted() iteratively (for each deleted
7609 * route). If the deleted route is currently displayed in the Editor-Mixer (highly
7610 * likely because deletion requires selection) this will call
7611 * Editor::set_selected_mixer_strip () which is expensive (MixerStrip::set_route()).
7612 * It's likewise likely that the route that has just been displayed in the
7613 * Editor-Mixer will be next in line for deletion.
7615 * So simply switch to the master-bus (if present)
7617 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
7618 if ((*i)->stripable ()->is_master ()) {
7619 set_selected_mixer_strip (*(*i));
7626 PresentationInfo::ChangeSuspender cs;
7627 DisplaySuspender ds;
7629 boost::shared_ptr<RouteList> rl (new RouteList);
7630 for (vector<boost::shared_ptr<Route> >::iterator x = routes.begin(); x != routes.end(); ++x) {
7633 _session->remove_routes (rl);
7635 for (vector<boost::shared_ptr<VCA> >::iterator x = vcas.begin(); x != vcas.end(); ++x) {
7636 _session->vca_manager().remove_vca (*x);
7640 /* TrackSelection and RouteList leave scope,
7641 * destructors are called,
7642 * diskstream drops references, save_state is called (again for every track)
7647 Editor::do_insert_time ()
7649 if (selection->tracks.empty()) {
7650 MessageDialog msg (_("You must first select some tracks to Insert Time."),
7651 true, MESSAGE_INFO, BUTTONS_OK, true);
7652 msg.set_position (WIN_POS_MOUSE);
7657 if (Config->get_edit_mode() == Lock) {
7658 MessageDialog msg (_("You cannot insert time in Lock Edit mode."),
7659 true, MESSAGE_INFO, BUTTONS_OK, true);
7660 msg.set_position (WIN_POS_MOUSE);
7665 InsertRemoveTimeDialog d (*this);
7666 int response = d.run ();
7668 if (response != RESPONSE_OK) {
7672 if (d.distance() == 0) {
7679 d.intersected_region_action (),
7683 d.move_glued_markers(),
7684 d.move_locked_markers(),
7690 Editor::insert_time (
7691 samplepos_t pos, samplecnt_t samples, InsertTimeOption opt,
7692 bool all_playlists, bool ignore_music_glue, bool markers_too, bool glued_markers_too, bool locked_markers_too, bool tempo_too
7696 if (Config->get_edit_mode() == Lock) {
7699 bool in_command = false;
7701 TrackViewList ts = selection->tracks.filter_to_unique_playlists ();
7703 for (TrackViewList::iterator x = ts.begin(); x != ts.end(); ++x) {
7707 /* don't operate on any playlist more than once, which could
7708 * happen if "all playlists" is enabled, but there is more
7709 * than 1 track using playlists "from" a given track.
7712 set<boost::shared_ptr<Playlist> > pl;
7714 if (all_playlists) {
7715 RouteTimeAxisView* rtav = dynamic_cast<RouteTimeAxisView*> (*x);
7716 if (rtav && rtav->track ()) {
7717 vector<boost::shared_ptr<Playlist> > all = _session->playlists->playlists_for_track (rtav->track ());
7718 for (vector<boost::shared_ptr<Playlist> >::iterator p = all.begin(); p != all.end(); ++p) {
7723 if ((*x)->playlist ()) {
7724 pl.insert ((*x)->playlist ());
7728 for (set<boost::shared_ptr<Playlist> >::iterator i = pl.begin(); i != pl.end(); ++i) {
7730 (*i)->clear_changes ();
7731 (*i)->clear_owned_changes ();
7734 begin_reversible_command (_("insert time"));
7738 if (opt == SplitIntersected) {
7739 /* non musical split */
7740 (*i)->split (MusicSample (pos, 0));
7743 (*i)->shift (pos, samples, (opt == MoveIntersected), ignore_music_glue);
7745 vector<Command*> cmds;
7747 _session->add_commands (cmds);
7749 _session->add_command (new StatefulDiffCommand (*i));
7753 RouteTimeAxisView* rtav = dynamic_cast<RouteTimeAxisView*> (*x);
7756 begin_reversible_command (_("insert time"));
7759 rtav->route ()->shift (pos, samples);
7766 const int32_t divisions = get_grid_music_divisions (0);
7767 XMLNode& before (_session->locations()->get_state());
7768 Locations::LocationList copy (_session->locations()->list());
7770 for (Locations::LocationList::iterator i = copy.begin(); i != copy.end(); ++i) {
7772 Locations::LocationList::const_iterator tmp;
7774 if ((*i)->position_lock_style() == AudioTime || glued_markers_too) {
7775 bool const was_locked = (*i)->locked ();
7776 if (locked_markers_too) {
7780 if ((*i)->start() >= pos) {
7781 // move end first, in case we're moving by more than the length of the range
7782 if (!(*i)->is_mark()) {
7783 (*i)->set_end ((*i)->end() + samples, false, true, divisions);
7785 (*i)->set_start ((*i)->start() + samples, false, true, divisions);
7797 begin_reversible_command (_("insert time"));
7800 XMLNode& after (_session->locations()->get_state());
7801 _session->add_command (new MementoCommand<Locations>(*_session->locations(), &before, &after));
7807 begin_reversible_command (_("insert time"));
7810 XMLNode& before (_session->tempo_map().get_state());
7811 _session->tempo_map().insert_time (pos, samples);
7812 XMLNode& after (_session->tempo_map().get_state());
7813 _session->add_command (new MementoCommand<TempoMap>(_session->tempo_map(), &before, &after));
7817 commit_reversible_command ();
7822 Editor::do_remove_time ()
7824 if (selection->tracks.empty()) {
7825 MessageDialog msg (_("You must first select some tracks to Remove Time."),
7826 true, MESSAGE_INFO, BUTTONS_OK, true);
7827 msg.set_position (WIN_POS_MOUSE);
7832 if (Config->get_edit_mode() == Lock) {
7833 MessageDialog msg (_("You cannot remove time in Lock Edit mode."),
7834 true, MESSAGE_INFO, BUTTONS_OK, true);
7835 msg.set_position (WIN_POS_MOUSE);
7840 InsertRemoveTimeDialog d (*this, true);
7842 int response = d.run ();
7844 if (response != RESPONSE_OK) {
7848 samplecnt_t distance = d.distance();
7850 if (distance == 0) {
7860 d.move_glued_markers(),
7861 d.move_locked_markers(),
7867 Editor::remove_time (samplepos_t pos, samplecnt_t samples, InsertTimeOption opt,
7868 bool ignore_music_glue, bool markers_too, bool glued_markers_too, bool locked_markers_too, bool tempo_too)
7870 if (Config->get_edit_mode() == Lock) {
7871 error << (_("Cannot insert or delete time when in Lock edit.")) << endmsg;
7874 bool in_command = false;
7876 for (TrackSelection::iterator x = selection->tracks.begin(); x != selection->tracks.end(); ++x) {
7878 boost::shared_ptr<Playlist> pl = (*x)->playlist();
7882 XMLNode &before = pl->get_state();
7885 begin_reversible_command (_("remove time"));
7889 std::list<AudioRange> rl;
7890 AudioRange ar(pos, pos+samples, 0);
7893 pl->shift (pos, -samples, true, ignore_music_glue);
7895 XMLNode &after = pl->get_state();
7897 _session->add_command (new MementoCommand<Playlist> (*pl, &before, &after));
7901 RouteTimeAxisView* rtav = dynamic_cast<RouteTimeAxisView*> (*x);
7904 begin_reversible_command (_("remove time"));
7907 rtav->route ()->shift (pos, -samples);
7911 const int32_t divisions = get_grid_music_divisions (0);
7912 std::list<Location*> loc_kill_list;
7917 XMLNode& before (_session->locations()->get_state());
7918 Locations::LocationList copy (_session->locations()->list());
7920 for (Locations::LocationList::iterator i = copy.begin(); i != copy.end(); ++i) {
7921 if ((*i)->position_lock_style() == AudioTime || glued_markers_too) {
7923 bool const was_locked = (*i)->locked ();
7924 if (locked_markers_too) {
7928 if (!(*i)->is_mark()) { // it's a range; have to handle both start and end
7929 if ((*i)->end() >= pos
7930 && (*i)->end() < pos+samples
7931 && (*i)->start() >= pos
7932 && (*i)->end() < pos+samples) { // range is completely enclosed; kill it
7934 loc_kill_list.push_back(*i);
7935 } else { // only start or end is included, try to do the right thing
7936 // move start before moving end, to avoid trying to move the end to before the start
7937 // if we're removing more time than the length of the range
7938 if ((*i)->start() >= pos && (*i)->start() < pos+samples) {
7939 // start is within cut
7940 (*i)->set_start (pos, false, true,divisions); // bring the start marker to the beginning of the cut
7942 } else if ((*i)->start() >= pos+samples) {
7943 // start (and thus entire range) lies beyond end of cut
7944 (*i)->set_start ((*i)->start() - samples, false, true, divisions); // slip the start marker back
7947 if ((*i)->end() >= pos && (*i)->end() < pos+samples) {
7948 // end is inside cut
7949 (*i)->set_end (pos, false, true, divisions); // bring the end to the cut
7951 } else if ((*i)->end() >= pos+samples) {
7952 // end is beyond end of cut
7953 (*i)->set_end ((*i)->end() - samples, false, true, divisions); // slip the end marker back
7958 } else if ((*i)->start() >= pos && (*i)->start() < pos+samples) {
7959 loc_kill_list.push_back(*i);
7961 } else if ((*i)->start() >= pos) {
7962 (*i)->set_start ((*i)->start() -samples, false, true, divisions);
7972 for (list<Location*>::iterator i = loc_kill_list.begin(); i != loc_kill_list.end(); ++i) {
7973 _session->locations()->remove (*i);
7978 begin_reversible_command (_("remove time"));
7981 XMLNode& after (_session->locations()->get_state());
7982 _session->add_command (new MementoCommand<Locations>(*_session->locations(), &before, &after));
7987 XMLNode& before (_session->tempo_map().get_state());
7989 if (_session->tempo_map().remove_time (pos, samples)) {
7991 begin_reversible_command (_("remove time"));
7994 XMLNode& after (_session->tempo_map().get_state());
7995 _session->add_command (new MementoCommand<TempoMap>(_session->tempo_map(), &before, &after));
8000 commit_reversible_command ();
8005 Editor::fit_selection ()
8007 if (!selection->tracks.empty()) {
8008 fit_tracks (selection->tracks);
8012 /* no selected tracks - use tracks with selected regions */
8014 if (!selection->regions.empty()) {
8015 for (RegionSelection::iterator r = selection->regions.begin(); r != selection->regions.end(); ++r) {
8016 tvl.push_back (&(*r)->get_time_axis_view ());
8022 } else if (internal_editing()) {
8023 /* no selected tracks, or regions, but in internal edit mode, so follow the mouse and use
8026 if (entered_track) {
8027 tvl.push_back (entered_track);
8035 Editor::fit_tracks (TrackViewList & tracks)
8037 if (tracks.empty()) {
8041 uint32_t child_heights = 0;
8042 int visible_tracks = 0;
8044 for (TrackSelection::iterator t = tracks.begin(); t != tracks.end(); ++t) {
8046 if (!(*t)->marked_for_display()) {
8050 child_heights += (*t)->effective_height() - (*t)->current_height();
8054 /* compute the per-track height from:
8056 * total canvas visible height
8057 * - height that will be taken by visible children of selected tracks
8058 * - height of the ruler/hscroll area
8060 uint32_t h = (uint32_t) floor ((trackviews_height() - child_heights) / visible_tracks);
8061 double first_y_pos = DBL_MAX;
8063 if (h < TimeAxisView::preset_height (HeightSmall)) {
8064 MessageDialog msg (_("There are too many tracks to fit in the current window"));
8065 /* too small to be displayed */
8069 undo_visual_stack.push_back (current_visual_state (true));
8070 PBD::Unwinder<bool> nsv (no_save_visual, true);
8072 /* build a list of all tracks, including children */
8075 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
8077 TimeAxisView::Children c = (*i)->get_child_list ();
8078 for (TimeAxisView::Children::iterator j = c.begin(); j != c.end(); ++j) {
8079 all.push_back (j->get());
8084 // find selection range.
8085 // if someone knows how to user TrackViewList::iterator for this
8087 int selected_top = -1;
8088 int selected_bottom = -1;
8090 for (TrackViewList::iterator t = all.begin(); t != all.end(); ++t, ++i) {
8091 if ((*t)->marked_for_display ()) {
8092 if (tracks.contains(*t)) {
8093 if (selected_top == -1) {
8096 selected_bottom = i;
8102 for (TrackViewList::iterator t = all.begin(); t != all.end(); ++t, ++i) {
8103 if ((*t)->marked_for_display ()) {
8104 if (tracks.contains(*t)) {
8105 (*t)->set_height (h);
8106 first_y_pos = std::min ((*t)->y_position (), first_y_pos);
8108 if (i > selected_top && i < selected_bottom) {
8109 hide_track_in_display (*t);
8116 set the controls_layout height now, because waiting for its size
8117 request signal handler will cause the vertical adjustment setting to fail
8120 controls_layout.property_height () = _full_canvas_height;
8121 vertical_adjustment.set_value (first_y_pos);
8123 redo_visual_stack.push_back (current_visual_state (true));
8125 visible_tracks_selector.set_text (_("Sel"));
8129 Editor::save_visual_state (uint32_t n)
8131 while (visual_states.size() <= n) {
8132 visual_states.push_back (0);
8135 if (visual_states[n] != 0) {
8136 delete visual_states[n];
8139 visual_states[n] = current_visual_state (true);
8144 Editor::goto_visual_state (uint32_t n)
8146 if (visual_states.size() <= n) {
8150 if (visual_states[n] == 0) {
8154 use_visual_state (*visual_states[n]);
8158 Editor::start_visual_state_op (uint32_t n)
8160 save_visual_state (n);
8162 PopUp* pup = new PopUp (WIN_POS_MOUSE, 1000, true);
8164 snprintf (buf, sizeof (buf), _("Saved view %u"), n+1);
8165 pup->set_text (buf);
8170 Editor::cancel_visual_state_op (uint32_t n)
8172 goto_visual_state (n);
8176 Editor::toggle_region_mute ()
8178 if (_ignore_region_action) {
8182 RegionSelection rs = get_regions_from_selection_and_entered ();
8188 if (rs.size() > 1) {
8189 begin_reversible_command (_("mute regions"));
8191 begin_reversible_command (_("mute region"));
8194 for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
8196 (*i)->region()->playlist()->clear_changes ();
8197 (*i)->region()->set_muted (!(*i)->region()->muted ());
8198 _session->add_command (new StatefulDiffCommand ((*i)->region()));
8202 commit_reversible_command ();
8206 Editor::combine_regions ()
8208 /* foreach track with selected regions, take all selected regions
8209 and join them into a new region containing the subregions (as a
8213 typedef set<RouteTimeAxisView*> RTVS;
8216 if (selection->regions.empty()) {
8220 for (RegionSelection::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
8221 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*>(&(*i)->get_time_axis_view());
8224 tracks.insert (rtv);
8228 begin_reversible_command (_("combine regions"));
8230 vector<RegionView*> new_selection;
8232 for (RTVS::iterator i = tracks.begin(); i != tracks.end(); ++i) {
8235 if ((rv = (*i)->combine_regions ()) != 0) {
8236 new_selection.push_back (rv);
8240 selection->clear_regions ();
8241 for (vector<RegionView*>::iterator i = new_selection.begin(); i != new_selection.end(); ++i) {
8242 selection->add (*i);
8245 commit_reversible_command ();
8249 Editor::uncombine_regions ()
8251 typedef set<RouteTimeAxisView*> RTVS;
8254 if (selection->regions.empty()) {
8258 for (RegionSelection::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
8259 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*>(&(*i)->get_time_axis_view());
8262 tracks.insert (rtv);
8266 begin_reversible_command (_("uncombine regions"));
8268 for (RTVS::iterator i = tracks.begin(); i != tracks.end(); ++i) {
8269 (*i)->uncombine_regions ();
8272 commit_reversible_command ();
8276 Editor::toggle_midi_input_active (bool flip_others)
8279 boost::shared_ptr<RouteList> rl (new RouteList);
8281 for (TrackSelection::iterator i = selection->tracks.begin(); i != selection->tracks.end(); ++i) {
8282 RouteTimeAxisView *rtav = dynamic_cast<RouteTimeAxisView *>(*i);
8288 boost::shared_ptr<MidiTrack> mt = rtav->midi_track();
8291 rl->push_back (rtav->route());
8292 onoff = !mt->input_active();
8296 _session->set_exclusive_input_active (rl, onoff, flip_others);
8299 static bool ok_fine (GdkEventAny*) { return true; }
8305 lock_dialog = new Gtk::Dialog (string_compose (_("%1: Locked"), PROGRAM_NAME), true);
8307 Gtk::Image* padlock = manage (new Gtk::Image (ARDOUR_UI_UTILS::get_icon ("padlock_closed")));
8308 lock_dialog->get_vbox()->pack_start (*padlock);
8309 lock_dialog->signal_delete_event ().connect (sigc::ptr_fun (ok_fine));
8311 ArdourButton* b = manage (new ArdourButton);
8312 b->set_name ("lock button");
8313 b->set_text (_("Click to unlock"));
8314 b->signal_clicked.connect (sigc::mem_fun (*this, &Editor::unlock));
8315 lock_dialog->get_vbox()->pack_start (*b);
8317 lock_dialog->get_vbox()->show_all ();
8318 lock_dialog->set_size_request (200, 200);
8321 delete _main_menu_disabler;
8322 _main_menu_disabler = new MainMenuDisabler;
8324 lock_dialog->present ();
8326 lock_dialog->get_window()->set_decorations (Gdk::WMDecoration (0));
8332 lock_dialog->hide ();
8334 delete _main_menu_disabler;
8335 _main_menu_disabler = 0;
8337 if (UIConfiguration::instance().get_lock_gui_after_seconds()) {
8338 start_lock_event_timing ();
8343 Editor::bring_in_callback (Gtk::Label* label, uint32_t n, uint32_t total, string name)
8345 Gtkmm2ext::UI::instance()->call_slot (invalidator (*this), boost::bind (&Editor::update_bring_in_message, this, label, n, total, name));
8349 Editor::update_bring_in_message (Gtk::Label* label, uint32_t n, uint32_t total, string name)
8351 Timers::TimerSuspender t;
8352 label->set_text (string_compose ("Copying %1, %2 of %3", name, n, total));
8353 Gtkmm2ext::UI::instance()->flush_pending (1);
8357 Editor::bring_all_sources_into_session ()
8364 ArdourDialog w (_("Moving embedded files into session folder"));
8365 w.get_vbox()->pack_start (msg);
8368 /* flush all pending GUI events because we're about to start copying
8372 Timers::TimerSuspender t;
8373 Gtkmm2ext::UI::instance()->flush_pending (3);
8377 _session->bring_all_sources_into_session (boost::bind (&Editor::bring_in_callback, this, &msg, _1, _2, _3));