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/selection.h"
62 #include "ardour/session.h"
63 #include "ardour/session_playlists.h"
64 #include "ardour/strip_silence.h"
65 #include "ardour/transient_detector.h"
66 #include "ardour/transpose.h"
67 #include "ardour/vca_manager.h"
69 #include "canvas/canvas.h"
72 #include "ardour_ui.h"
73 #include "audio_region_view.h"
74 #include "audio_streamview.h"
75 #include "audio_time_axis.h"
76 #include "automation_region_view.h"
77 #include "automation_time_axis.h"
78 #include "control_point.h"
82 #include "editor_cursors.h"
83 #include "editor_drag.h"
84 #include "editor_regions.h"
85 #include "editor_routes.h"
86 #include "gui_thread.h"
87 #include "insert_remove_time_dialog.h"
88 #include "interthread_progress_window.h"
89 #include "item_counts.h"
91 #include "midi_region_view.h"
93 #include "mixer_strip.h"
94 #include "mouse_cursors.h"
95 #include "normalize_dialog.h"
97 #include "paste_context.h"
98 #include "patch_change_dialog.h"
99 #include "quantize_dialog.h"
100 #include "region_gain_line.h"
101 #include "rgb_macros.h"
102 #include "route_time_axis.h"
103 #include "selection.h"
104 #include "selection_templates.h"
105 #include "streamview.h"
106 #include "strip_silence_dialog.h"
107 #include "time_axis_view.h"
109 #include "transpose_dialog.h"
110 #include "transform_dialog.h"
111 #include "ui_config.h"
112 #include "vca_time_axis.h"
114 #include "pbd/i18n.h"
117 using namespace ARDOUR;
120 using namespace Gtkmm2ext;
121 using namespace ArdourWidgets;
122 using namespace Editing;
123 using Gtkmm2ext::Keyboard;
125 /***********************************************************************
127 ***********************************************************************/
130 Editor::undo (uint32_t n)
132 if (_session && _session->actively_recording()) {
133 /* no undo allowed while recording. Session will check also,
134 but we don't even want to get to that.
139 if (_drags->active ()) {
145 if (_session->undo_depth() == 0) {
146 undo_action->set_sensitive(false);
148 redo_action->set_sensitive(true);
149 begin_selection_op_history ();
154 Editor::redo (uint32_t n)
156 if (_session && _session->actively_recording()) {
157 /* no redo allowed while recording. Session will check also,
158 but we don't even want to get to that.
163 if (_drags->active ()) {
169 if (_session->redo_depth() == 0) {
170 redo_action->set_sensitive(false);
172 undo_action->set_sensitive(true);
173 begin_selection_op_history ();
178 Editor::split_regions_at (MusicSample where, RegionSelection& regions)
182 RegionSelection pre_selected_regions = selection->regions;
183 bool working_on_selection = !pre_selected_regions.empty();
185 list<boost::shared_ptr<Playlist> > used_playlists;
186 list<RouteTimeAxisView*> used_trackviews;
188 if (regions.empty()) {
192 begin_reversible_command (_("split"));
195 if (regions.size() == 1) {
196 /* TODO: if splitting a single region, and snap-to is using
197 region boundaries, mabye we shouldn't pay attention to them? */
200 EditorFreeze(); /* Emit Signal */
203 for (RegionSelection::iterator a = regions.begin(); a != regions.end(); ) {
205 RegionSelection::iterator tmp;
207 /* XXX this test needs to be more complicated, to make sure we really
208 have something to split.
211 if (!(*a)->region()->covers (where.sample)) {
219 boost::shared_ptr<Playlist> pl = (*a)->region()->playlist();
227 /* we haven't seen this playlist before */
229 /* remember used playlists so we can thaw them later */
230 used_playlists.push_back(pl);
232 TimeAxisView& tv = (*a)->get_time_axis_view();
233 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (&tv);
235 used_trackviews.push_back (rtv);
242 pl->clear_changes ();
243 pl->split_region ((*a)->region(), where);
244 _session->add_command (new StatefulDiffCommand (pl));
250 latest_regionviews.clear ();
252 vector<sigc::connection> region_added_connections;
254 for (list<RouteTimeAxisView*>::iterator i = used_trackviews.begin(); i != used_trackviews.end(); ++i) {
255 region_added_connections.push_back ((*i)->view()->RegionViewAdded.connect (sigc::mem_fun(*this, &Editor::collect_new_region_view)));
258 while (used_playlists.size() > 0) {
259 list <boost::shared_ptr<Playlist > >::iterator i = used_playlists.begin();
261 used_playlists.pop_front();
264 for (vector<sigc::connection>::iterator c = region_added_connections.begin(); c != region_added_connections.end(); ++c) {
269 EditorThaw(); /* Emit Signal */
272 if (working_on_selection) {
273 // IFF we were working on selected regions, try to reinstate the other region selections that existed before the freeze/thaw.
275 RegionSelectionAfterSplit rsas = Config->get_region_selection_after_split();
276 /* There are three classes of regions that we might want selected after
277 splitting selected regions:
278 - regions selected before the split operation, and unaffected by it
279 - newly-created regions before the split
280 - newly-created regions after the split
283 if (rsas & Existing) {
284 // region selections that existed before the split.
285 selection->add (pre_selected_regions);
288 for (RegionSelection::iterator ri = latest_regionviews.begin(); ri != latest_regionviews.end(); ri++) {
289 if ((*ri)->region()->position() < where.sample) {
290 // new regions created before the split
291 if (rsas & NewlyCreatedLeft) {
292 selection->add (*ri);
295 // new regions created after the split
296 if (rsas & NewlyCreatedRight) {
297 selection->add (*ri);
303 commit_reversible_command ();
306 /** Move one extreme of the current range selection. If more than one range is selected,
307 * the start of the earliest range or the end of the latest range is moved.
309 * @param move_end true to move the end of the current range selection, false to move
311 * @param next true to move the extreme to the next region boundary, false to move to
315 Editor::move_range_selection_start_or_end_to_region_boundary (bool move_end, bool next)
317 if (selection->time.start() == selection->time.end_sample()) {
321 samplepos_t start = selection->time.start ();
322 samplepos_t end = selection->time.end_sample ();
324 /* the position of the thing we may move */
325 samplepos_t pos = move_end ? end : start;
326 int dir = next ? 1 : -1;
328 /* so we don't find the current region again */
329 if (dir > 0 || pos > 0) {
333 samplepos_t const target = get_region_boundary (pos, dir, true, false);
348 begin_reversible_selection_op (_("alter selection"));
349 selection->set_preserving_all_ranges (start, end);
350 commit_reversible_selection_op ();
354 Editor::nudge_forward_release (GdkEventButton* ev)
356 if (ev->state & Keyboard::PrimaryModifier) {
357 nudge_forward (false, true);
359 nudge_forward (false, false);
365 Editor::nudge_backward_release (GdkEventButton* ev)
367 if (ev->state & Keyboard::PrimaryModifier) {
368 nudge_backward (false, true);
370 nudge_backward (false, false);
377 Editor::nudge_forward (bool next, bool force_playhead)
379 samplepos_t distance;
380 samplepos_t next_distance;
386 RegionSelection rs = get_regions_from_selection_and_entered ();
388 if (!force_playhead && !rs.empty()) {
390 begin_reversible_command (_("nudge regions forward"));
392 for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
393 boost::shared_ptr<Region> r ((*i)->region());
395 distance = get_nudge_distance (r->position(), next_distance);
398 distance = next_distance;
402 r->set_position (r->position() + distance);
403 _session->add_command (new StatefulDiffCommand (r));
406 commit_reversible_command ();
409 } else if (!force_playhead && !selection->markers.empty()) {
412 bool in_command = false;
413 const int32_t divisions = get_grid_music_divisions (0);
415 for (MarkerSelection::iterator i = selection->markers.begin(); i != selection->markers.end(); ++i) {
417 Location* loc = find_location_from_marker ((*i), is_start);
421 XMLNode& before (loc->get_state());
424 distance = get_nudge_distance (loc->start(), next_distance);
426 distance = next_distance;
428 if (max_samplepos - distance > loc->start() + loc->length()) {
429 loc->set_start (loc->start() + distance, false, true, divisions);
431 loc->set_start (max_samplepos - loc->length(), false, true, divisions);
434 distance = get_nudge_distance (loc->end(), next_distance);
436 distance = next_distance;
438 if (max_samplepos - distance > loc->end()) {
439 loc->set_end (loc->end() + distance, false, true, divisions);
441 loc->set_end (max_samplepos, false, true, divisions);
443 if (loc->is_session_range()) {
444 _session->set_end_is_free (false);
448 begin_reversible_command (_("nudge location forward"));
451 XMLNode& after (loc->get_state());
452 _session->add_command (new MementoCommand<Location>(*loc, &before, &after));
457 commit_reversible_command ();
460 distance = get_nudge_distance (playhead_cursor->current_sample (), next_distance);
461 _session->request_locate (playhead_cursor->current_sample () + distance);
466 Editor::nudge_backward (bool next, bool force_playhead)
468 samplepos_t distance;
469 samplepos_t next_distance;
475 RegionSelection rs = get_regions_from_selection_and_entered ();
477 if (!force_playhead && !rs.empty()) {
479 begin_reversible_command (_("nudge regions backward"));
481 for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
482 boost::shared_ptr<Region> r ((*i)->region());
484 distance = get_nudge_distance (r->position(), next_distance);
487 distance = next_distance;
492 if (r->position() > distance) {
493 r->set_position (r->position() - distance);
497 _session->add_command (new StatefulDiffCommand (r));
500 commit_reversible_command ();
502 } else if (!force_playhead && !selection->markers.empty()) {
505 bool in_command = false;
507 for (MarkerSelection::iterator i = selection->markers.begin(); i != selection->markers.end(); ++i) {
509 Location* loc = find_location_from_marker ((*i), is_start);
513 XMLNode& before (loc->get_state());
516 distance = get_nudge_distance (loc->start(), next_distance);
518 distance = next_distance;
520 if (distance < loc->start()) {
521 loc->set_start (loc->start() - distance, false, true, get_grid_music_divisions(0));
523 loc->set_start (0, false, true, get_grid_music_divisions(0));
526 distance = get_nudge_distance (loc->end(), next_distance);
529 distance = next_distance;
532 if (distance < loc->end() - loc->length()) {
533 loc->set_end (loc->end() - distance, false, true, get_grid_music_divisions(0));
535 loc->set_end (loc->length(), false, true, get_grid_music_divisions(0));
537 if (loc->is_session_range()) {
538 _session->set_end_is_free (false);
542 begin_reversible_command (_("nudge location forward"));
545 XMLNode& after (loc->get_state());
546 _session->add_command (new MementoCommand<Location>(*loc, &before, &after));
550 commit_reversible_command ();
555 distance = get_nudge_distance (playhead_cursor->current_sample (), next_distance);
557 if (playhead_cursor->current_sample () > distance) {
558 _session->request_locate (playhead_cursor->current_sample () - distance);
560 _session->goto_start();
566 Editor::nudge_forward_capture_offset ()
568 RegionSelection rs = get_regions_from_selection_and_entered ();
570 if (!_session || rs.empty()) {
574 begin_reversible_command (_("nudge forward"));
576 samplepos_t const distance = _session->worst_output_latency();
578 for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
579 boost::shared_ptr<Region> r ((*i)->region());
582 r->set_position (r->position() + distance);
583 _session->add_command(new StatefulDiffCommand (r));
586 commit_reversible_command ();
590 Editor::nudge_backward_capture_offset ()
592 RegionSelection rs = get_regions_from_selection_and_entered ();
594 if (!_session || rs.empty()) {
598 begin_reversible_command (_("nudge backward"));
600 samplepos_t const distance = _session->worst_output_latency();
602 for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
603 boost::shared_ptr<Region> r ((*i)->region());
607 if (r->position() > distance) {
608 r->set_position (r->position() - distance);
612 _session->add_command(new StatefulDiffCommand (r));
615 commit_reversible_command ();
618 struct RegionSelectionPositionSorter {
619 bool operator() (RegionView* a, RegionView* b) {
620 return a->region()->position() < b->region()->position();
625 Editor::sequence_regions ()
628 samplepos_t r_end_prev;
636 RegionSelection rs = get_regions_from_selection_and_entered ();
637 rs.sort(RegionSelectionPositionSorter());
641 bool in_command = false;
643 for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
644 boost::shared_ptr<Region> r ((*i)->region());
652 if(r->position_locked())
659 r->set_position(r_end_prev);
663 begin_reversible_command (_("sequence regions"));
666 _session->add_command (new StatefulDiffCommand (r));
668 r_end=r->position() + r->length();
674 commit_reversible_command ();
683 Editor::move_to_start ()
685 _session->goto_start ();
689 Editor::move_to_end ()
692 _session->request_locate (_session->current_end_sample());
696 Editor::build_region_boundary_cache ()
699 /* TODO: maybe set a timer so we don't recalutate when lots of changes are coming in */
700 /* TODO: maybe somehow defer this until session is fully loaded. */
702 if (!_region_boundary_cache_dirty)
706 vector<RegionPoint> interesting_points;
707 boost::shared_ptr<Region> r;
708 TrackViewList tracks;
711 region_boundary_cache.clear ();
717 bool maybe_first_sample = false;
719 if (UIConfiguration::instance().get_snap_to_region_start()) {
720 interesting_points.push_back (Start);
721 maybe_first_sample = true;
724 if (UIConfiguration::instance().get_snap_to_region_end()) {
725 interesting_points.push_back (End);
728 if (UIConfiguration::instance().get_snap_to_region_sync()) {
729 interesting_points.push_back (SyncPoint);
732 /* if no snap selections are set, boundary cache should be left empty */
733 if ( interesting_points.empty() ) {
737 TimeAxisView *ontrack = 0;
740 tlist = track_views.filter_to_unique_playlists ();
742 if (maybe_first_sample) {
743 TrackViewList::const_iterator i;
744 for (i = tlist.begin(); i != tlist.end(); ++i) {
745 boost::shared_ptr<Playlist> pl = (*i)->playlist();
746 if (pl && pl->count_regions_at (0)) {
747 region_boundary_cache.push_back (0);
753 //allow regions to snap to the video start (if any) as if it were a "region"
754 if (ARDOUR_UI::instance()->video_timeline) {
755 region_boundary_cache.push_back (ARDOUR_UI::instance()->video_timeline->get_video_start_offset());
758 std::pair<samplepos_t, samplepos_t> ext = session_gui_extents (false);
759 samplepos_t session_end = ext.second;
761 while (pos < session_end && !at_end) {
764 samplepos_t lpos = session_end;
766 for (vector<RegionPoint>::iterator p = interesting_points.begin(); p != interesting_points.end(); ++p) {
768 if ((r = find_next_region (pos, *p, 1, tlist, &ontrack)) == 0) {
769 if (*p == interesting_points.back()) {
772 /* move to next point type */
778 rpos = r->first_sample();
782 rpos = r->last_sample();
786 rpos = r->sync_position ();
797 /* prevent duplicates, but we don't use set<> because we want to be able
801 vector<samplepos_t>::iterator ri;
803 for (ri = region_boundary_cache.begin(); ri != region_boundary_cache.end(); ++ri) {
809 if (ri == region_boundary_cache.end()) {
810 region_boundary_cache.push_back (rpos);
817 /* finally sort to be sure that the order is correct */
819 sort (region_boundary_cache.begin(), region_boundary_cache.end());
821 _region_boundary_cache_dirty = false;
824 boost::shared_ptr<Region>
825 Editor::find_next_region (samplepos_t sample, RegionPoint point, int32_t dir, TrackViewList& tracks, TimeAxisView **ontrack)
827 TrackViewList::iterator i;
828 samplepos_t closest = max_samplepos;
829 boost::shared_ptr<Region> ret;
830 samplepos_t rpos = 0;
832 samplepos_t track_sample;
834 for (i = tracks.begin(); i != tracks.end(); ++i) {
836 samplecnt_t distance;
837 boost::shared_ptr<Region> r;
839 track_sample = sample;
841 if ((r = (*i)->find_next_region (track_sample, point, dir)) == 0) {
847 rpos = r->first_sample ();
851 rpos = r->last_sample ();
855 rpos = r->sync_position ();
860 distance = rpos - sample;
862 distance = sample - rpos;
865 if (distance < closest) {
877 Editor::find_next_region_boundary (samplepos_t pos, int32_t dir, const TrackViewList& tracks)
879 samplecnt_t distance = max_samplepos;
880 samplepos_t current_nearest = -1;
882 for (TrackViewList::const_iterator i = tracks.begin(); i != tracks.end(); ++i) {
883 samplepos_t contender;
886 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (*i);
892 if ((contender = rtv->find_next_region_boundary (pos, dir)) < 0) {
896 d = ::llabs (pos - contender);
899 current_nearest = contender;
904 return current_nearest;
908 Editor::get_region_boundary (samplepos_t pos, int32_t dir, bool with_selection, bool only_onscreen)
913 if (with_selection && Config->get_region_boundaries_from_selected_tracks()) {
915 if (!selection->tracks.empty()) {
917 target = find_next_region_boundary (pos, dir, selection->tracks);
921 if (only_onscreen || Config->get_region_boundaries_from_onscreen_tracks()) {
922 get_onscreen_tracks (tvl);
923 target = find_next_region_boundary (pos, dir, tvl);
925 target = find_next_region_boundary (pos, dir, track_views);
931 if (only_onscreen || Config->get_region_boundaries_from_onscreen_tracks()) {
932 get_onscreen_tracks (tvl);
933 target = find_next_region_boundary (pos, dir, tvl);
935 target = find_next_region_boundary (pos, dir, track_views);
943 Editor::cursor_to_region_boundary (bool with_selection, int32_t dir)
945 samplepos_t pos = playhead_cursor->current_sample ();
952 // so we don't find the current region again..
953 if (dir > 0 || pos > 0) {
957 if ((target = get_region_boundary (pos, dir, with_selection, false)) < 0) {
961 _session->request_locate (target);
965 Editor::cursor_to_next_region_boundary (bool with_selection)
967 cursor_to_region_boundary (with_selection, 1);
971 Editor::cursor_to_previous_region_boundary (bool with_selection)
973 cursor_to_region_boundary (with_selection, -1);
977 Editor::cursor_to_region_point (EditorCursor* cursor, RegionPoint point, int32_t dir)
979 boost::shared_ptr<Region> r;
980 samplepos_t pos = cursor->current_sample ();
986 TimeAxisView *ontrack = 0;
988 // so we don't find the current region again..
992 if (!selection->tracks.empty()) {
994 r = find_next_region (pos, point, dir, selection->tracks, &ontrack);
996 } else if (clicked_axisview) {
999 t.push_back (clicked_axisview);
1001 r = find_next_region (pos, point, dir, t, &ontrack);
1005 r = find_next_region (pos, point, dir, track_views, &ontrack);
1014 pos = r->first_sample ();
1018 pos = r->last_sample ();
1022 pos = r->sync_position ();
1026 if (cursor == playhead_cursor) {
1027 _session->request_locate (pos);
1029 cursor->set_position (pos);
1034 Editor::cursor_to_next_region_point (EditorCursor* cursor, RegionPoint point)
1036 cursor_to_region_point (cursor, point, 1);
1040 Editor::cursor_to_previous_region_point (EditorCursor* cursor, RegionPoint point)
1042 cursor_to_region_point (cursor, point, -1);
1046 Editor::cursor_to_selection_start (EditorCursor *cursor)
1048 samplepos_t pos = 0;
1050 switch (mouse_mode) {
1052 if (!selection->regions.empty()) {
1053 pos = selection->regions.start();
1058 if (!selection->time.empty()) {
1059 pos = selection->time.start ();
1067 if (cursor == playhead_cursor) {
1068 _session->request_locate (pos);
1070 cursor->set_position (pos);
1075 Editor::cursor_to_selection_end (EditorCursor *cursor)
1077 samplepos_t pos = 0;
1079 switch (mouse_mode) {
1081 if (!selection->regions.empty()) {
1082 pos = selection->regions.end_sample();
1087 if (!selection->time.empty()) {
1088 pos = selection->time.end_sample ();
1096 if (cursor == playhead_cursor) {
1097 _session->request_locate (pos);
1099 cursor->set_position (pos);
1104 Editor::selected_marker_to_region_boundary (bool with_selection, int32_t dir)
1114 if (selection->markers.empty()) {
1118 if (!mouse_sample (mouse, ignored)) {
1122 add_location_mark (mouse);
1125 if ((loc = find_location_from_marker (selection->markers.front(), ignored)) == 0) {
1129 samplepos_t pos = loc->start();
1131 // so we don't find the current region again..
1132 if (dir > 0 || pos > 0) {
1136 if ((target = get_region_boundary (pos, dir, with_selection, false)) < 0) {
1140 loc->move_to (target, 0);
1144 Editor::selected_marker_to_next_region_boundary (bool with_selection)
1146 selected_marker_to_region_boundary (with_selection, 1);
1150 Editor::selected_marker_to_previous_region_boundary (bool with_selection)
1152 selected_marker_to_region_boundary (with_selection, -1);
1156 Editor::selected_marker_to_region_point (RegionPoint point, int32_t dir)
1158 boost::shared_ptr<Region> r;
1163 if (!_session || selection->markers.empty()) {
1167 if ((loc = find_location_from_marker (selection->markers.front(), ignored)) == 0) {
1171 TimeAxisView *ontrack = 0;
1175 // so we don't find the current region again..
1179 if (!selection->tracks.empty()) {
1181 r = find_next_region (pos, point, dir, selection->tracks, &ontrack);
1185 r = find_next_region (pos, point, dir, track_views, &ontrack);
1194 pos = r->first_sample ();
1198 pos = r->last_sample ();
1202 pos = r->adjust_to_sync (r->first_sample());
1206 loc->move_to (pos, 0);
1210 Editor::selected_marker_to_next_region_point (RegionPoint point)
1212 selected_marker_to_region_point (point, 1);
1216 Editor::selected_marker_to_previous_region_point (RegionPoint point)
1218 selected_marker_to_region_point (point, -1);
1222 Editor::selected_marker_to_selection_start ()
1224 samplepos_t pos = 0;
1228 if (!_session || selection->markers.empty()) {
1232 if ((loc = find_location_from_marker (selection->markers.front(), ignored)) == 0) {
1236 switch (mouse_mode) {
1238 if (!selection->regions.empty()) {
1239 pos = selection->regions.start();
1244 if (!selection->time.empty()) {
1245 pos = selection->time.start ();
1253 loc->move_to (pos, 0);
1257 Editor::selected_marker_to_selection_end ()
1259 samplepos_t pos = 0;
1263 if (!_session || selection->markers.empty()) {
1267 if ((loc = find_location_from_marker (selection->markers.front(), ignored)) == 0) {
1271 switch (mouse_mode) {
1273 if (!selection->regions.empty()) {
1274 pos = selection->regions.end_sample();
1279 if (!selection->time.empty()) {
1280 pos = selection->time.end_sample ();
1288 loc->move_to (pos, 0);
1292 Editor::scroll_playhead (bool forward)
1294 samplepos_t pos = playhead_cursor->current_sample ();
1295 samplecnt_t delta = (samplecnt_t) floor (current_page_samples() / 0.8);
1298 if (pos == max_samplepos) {
1302 if (pos < max_samplepos - delta) {
1305 pos = max_samplepos;
1321 _session->request_locate (pos);
1325 Editor::cursor_align (bool playhead_to_edit)
1331 if (playhead_to_edit) {
1333 if (selection->markers.empty()) {
1337 _session->request_locate (selection->markers.front()->position(), _session->transport_rolling());
1340 const int32_t divisions = get_grid_music_divisions (0);
1341 /* move selected markers to playhead */
1343 for (MarkerSelection::iterator i = selection->markers.begin(); i != selection->markers.end(); ++i) {
1346 Location* loc = find_location_from_marker (*i, ignored);
1348 if (loc->is_mark()) {
1349 loc->set_start (playhead_cursor->current_sample (), false, true, divisions);
1351 loc->set (playhead_cursor->current_sample (),
1352 playhead_cursor->current_sample () + loc->length(), true, divisions);
1359 Editor::scroll_backward (float pages)
1361 samplepos_t const one_page = (samplepos_t) rint (_visible_canvas_width * samples_per_pixel);
1362 samplepos_t const cnt = (samplepos_t) floor (pages * one_page);
1365 if (_leftmost_sample < cnt) {
1368 sample = _leftmost_sample - cnt;
1371 reset_x_origin (sample);
1375 Editor::scroll_forward (float pages)
1377 samplepos_t const one_page = (samplepos_t) rint (_visible_canvas_width * samples_per_pixel);
1378 samplepos_t const cnt = (samplepos_t) floor (pages * one_page);
1381 if (max_samplepos - cnt < _leftmost_sample) {
1382 sample = max_samplepos - cnt;
1384 sample = _leftmost_sample + cnt;
1387 reset_x_origin (sample);
1391 Editor::scroll_tracks_down ()
1393 double vert_value = vertical_adjustment.get_value() + vertical_adjustment.get_page_size();
1394 if (vert_value > vertical_adjustment.get_upper() - _visible_canvas_height) {
1395 vert_value = vertical_adjustment.get_upper() - _visible_canvas_height;
1398 vertical_adjustment.set_value (vert_value);
1402 Editor::scroll_tracks_up ()
1404 vertical_adjustment.set_value (vertical_adjustment.get_value() - vertical_adjustment.get_page_size());
1408 Editor::scroll_tracks_down_line ()
1410 double vert_value = vertical_adjustment.get_value() + 60;
1412 if (vert_value > vertical_adjustment.get_upper() - _visible_canvas_height) {
1413 vert_value = vertical_adjustment.get_upper() - _visible_canvas_height;
1416 vertical_adjustment.set_value (vert_value);
1420 Editor::scroll_tracks_up_line ()
1422 reset_y_origin (vertical_adjustment.get_value() - 60);
1426 Editor::select_topmost_track ()
1428 const double top_of_trackviews = vertical_adjustment.get_value();
1429 for (TrackViewList::iterator t = track_views.begin(); t != track_views.end(); ++t) {
1430 if ((*t)->hidden()) {
1433 std::pair<TimeAxisView*,double> res = (*t)->covers_y_position (top_of_trackviews);
1435 selection->set (*t);
1442 Editor::scroll_down_one_track (bool skip_child_views)
1444 TrackViewList::reverse_iterator next = track_views.rend();
1445 const double top_of_trackviews = vertical_adjustment.get_value();
1447 for (TrackViewList::reverse_iterator t = track_views.rbegin(); t != track_views.rend(); ++t) {
1448 if ((*t)->hidden()) {
1452 /* If this is the upper-most visible trackview, we want to display
1453 * the one above it (next)
1455 * Note that covers_y_position() is recursive and includes child views
1457 std::pair<TimeAxisView*,double> res = (*t)->covers_y_position (top_of_trackviews);
1460 if (skip_child_views) {
1463 /* automation lane (one level, non-recursive)
1465 * - if no automation lane exists -> move to next tack
1466 * - if the first (here: bottom-most) matches -> move to next tack
1467 * - if no y-axis match is found -> the current track is at the top
1468 * -> move to last (here: top-most) automation lane
1470 TimeAxisView::Children kids = (*t)->get_child_list();
1471 TimeAxisView::Children::reverse_iterator nkid = kids.rend();
1473 for (TimeAxisView::Children::reverse_iterator ci = kids.rbegin(); ci != kids.rend(); ++ci) {
1474 if ((*ci)->hidden()) {
1478 std::pair<TimeAxisView*,double> dev;
1479 dev = (*ci)->covers_y_position (top_of_trackviews);
1481 /* some automation lane is currently at the top */
1482 if (ci == kids.rbegin()) {
1483 /* first (bottom-most) autmation lane is at the top.
1484 * -> move to next track
1493 if (nkid != kids.rend()) {
1494 ensure_time_axis_view_is_visible (**nkid, true);
1502 /* move to the track below the first one that covers the */
1504 if (next != track_views.rend()) {
1505 ensure_time_axis_view_is_visible (**next, true);
1513 Editor::scroll_up_one_track (bool skip_child_views)
1515 TrackViewList::iterator prev = track_views.end();
1516 double top_of_trackviews = vertical_adjustment.get_value ();
1518 for (TrackViewList::iterator t = track_views.begin(); t != track_views.end(); ++t) {
1520 if ((*t)->hidden()) {
1524 /* find the trackview at the top of the trackview group
1526 * Note that covers_y_position() is recursive and includes child views
1528 std::pair<TimeAxisView*,double> res = (*t)->covers_y_position (top_of_trackviews);
1531 if (skip_child_views) {
1534 /* automation lane (one level, non-recursive)
1536 * - if no automation lane exists -> move to prev tack
1537 * - if no y-axis match is found -> the current track is at the top -> move to prev track
1538 * (actually last automation lane of previous track, see below)
1539 * - if first (top-most) lane is at the top -> move to this track
1540 * - else move up one lane
1542 TimeAxisView::Children kids = (*t)->get_child_list();
1543 TimeAxisView::Children::iterator pkid = kids.end();
1545 for (TimeAxisView::Children::iterator ci = kids.begin(); ci != kids.end(); ++ci) {
1546 if ((*ci)->hidden()) {
1550 std::pair<TimeAxisView*,double> dev;
1551 dev = (*ci)->covers_y_position (top_of_trackviews);
1553 /* some automation lane is currently at the top */
1554 if (ci == kids.begin()) {
1555 /* first (top-most) autmation lane is at the top.
1556 * jump directly to this track's top
1558 ensure_time_axis_view_is_visible (**t, true);
1561 else if (pkid != kids.end()) {
1562 /* some other automation lane is at the top.
1563 * move up to prev automation lane.
1565 ensure_time_axis_view_is_visible (**pkid, true);
1568 assert(0); // not reached
1579 if (prev != track_views.end()) {
1580 // move to bottom-most automation-lane of the previous track
1581 TimeAxisView::Children kids = (*prev)->get_child_list();
1582 TimeAxisView::Children::reverse_iterator pkid = kids.rend();
1583 if (!skip_child_views) {
1584 // find the last visible lane
1585 for (TimeAxisView::Children::reverse_iterator ci = kids.rbegin(); ci != kids.rend(); ++ci) {
1586 if (!(*ci)->hidden()) {
1592 if (pkid != kids.rend()) {
1593 ensure_time_axis_view_is_visible (**pkid, true);
1595 ensure_time_axis_view_is_visible (**prev, true);
1604 Editor::scroll_left_step ()
1606 samplepos_t xdelta = (current_page_samples() / 8);
1608 if (_leftmost_sample > xdelta) {
1609 reset_x_origin (_leftmost_sample - xdelta);
1617 Editor::scroll_right_step ()
1619 samplepos_t xdelta = (current_page_samples() / 8);
1621 if (max_samplepos - xdelta > _leftmost_sample) {
1622 reset_x_origin (_leftmost_sample + xdelta);
1624 reset_x_origin (max_samplepos - current_page_samples());
1629 Editor::scroll_left_half_page ()
1631 samplepos_t xdelta = (current_page_samples() / 2);
1632 if (_leftmost_sample > xdelta) {
1633 reset_x_origin (_leftmost_sample - xdelta);
1640 Editor::scroll_right_half_page ()
1642 samplepos_t xdelta = (current_page_samples() / 2);
1643 if (max_samplepos - xdelta > _leftmost_sample) {
1644 reset_x_origin (_leftmost_sample + xdelta);
1646 reset_x_origin (max_samplepos - current_page_samples());
1653 Editor::tav_zoom_step (bool coarser)
1655 DisplaySuspender ds;
1659 if (selection->tracks.empty()) {
1662 ts = &selection->tracks;
1665 for (TrackViewList::iterator i = ts->begin(); i != ts->end(); ++i) {
1666 TimeAxisView *tv = (static_cast<TimeAxisView*>(*i));
1667 tv->step_height (coarser);
1672 Editor::tav_zoom_smooth (bool coarser, bool force_all)
1674 DisplaySuspender ds;
1678 if (selection->tracks.empty() || force_all) {
1681 ts = &selection->tracks;
1684 for (TrackViewList::iterator i = ts->begin(); i != ts->end(); ++i) {
1685 TimeAxisView *tv = (static_cast<TimeAxisView*>(*i));
1686 uint32_t h = tv->current_height ();
1691 if (h >= TimeAxisView::preset_height (HeightSmall)) {
1696 tv->set_height (h + 5);
1702 Editor::temporal_zoom_step_mouse_focus_scale (bool zoom_out, double scale)
1704 Editing::ZoomFocus temp_focus = zoom_focus;
1705 zoom_focus = Editing::ZoomFocusMouse;
1706 temporal_zoom_step_scale (zoom_out, scale);
1707 zoom_focus = temp_focus;
1711 Editor::temporal_zoom_step_mouse_focus (bool zoom_out)
1713 temporal_zoom_step_mouse_focus_scale (zoom_out, 2.0);
1717 Editor::temporal_zoom_step (bool zoom_out)
1719 temporal_zoom_step_scale (zoom_out, 2.0);
1723 Editor::temporal_zoom_step_scale (bool zoom_out, double scale)
1725 ENSURE_GUI_THREAD (*this, &Editor::temporal_zoom_step, zoom_out, scale)
1727 samplecnt_t nspp = samples_per_pixel;
1731 if (nspp == samples_per_pixel) {
1736 if (nspp == samples_per_pixel) {
1741 //zoom-behavior-tweaks
1742 //limit our maximum zoom to the session gui extents value
1743 std::pair<samplepos_t, samplepos_t> ext = session_gui_extents();
1744 samplecnt_t session_extents_pp = (ext.second - ext.first) / _visible_canvas_width;
1745 if (nspp > session_extents_pp)
1746 nspp = session_extents_pp;
1748 temporal_zoom (nspp);
1752 Editor::temporal_zoom (samplecnt_t fpp)
1758 samplepos_t current_page = current_page_samples();
1759 samplepos_t current_leftmost = _leftmost_sample;
1760 samplepos_t current_rightmost;
1761 samplepos_t current_center;
1762 samplepos_t new_page_size;
1763 samplepos_t half_page_size;
1764 samplepos_t leftmost_after_zoom = 0;
1766 bool in_track_canvas;
1767 bool use_mouse_sample = true;
1771 if (fpp == samples_per_pixel) {
1775 // Imposing an arbitrary limit to zoom out as too much zoom out produces
1776 // segfaults for lack of memory. If somebody decides this is not high enough I
1777 // believe it can be raisen to higher values but some limit must be in place.
1779 // This constant represents 1 day @ 48kHz on a 1600 pixel wide display
1780 // all of which is used for the editor track displays. The whole day
1781 // would be 4147200000 samples, so 2592000 samples per pixel.
1783 nfpp = min (fpp, (samplecnt_t) 2592000);
1784 nfpp = max ((samplecnt_t) 1, nfpp);
1786 new_page_size = (samplepos_t) floor (_visible_canvas_width * nfpp);
1787 half_page_size = new_page_size / 2;
1789 switch (zoom_focus) {
1791 leftmost_after_zoom = current_leftmost;
1794 case ZoomFocusRight:
1795 current_rightmost = _leftmost_sample + current_page;
1796 if (current_rightmost < new_page_size) {
1797 leftmost_after_zoom = 0;
1799 leftmost_after_zoom = current_rightmost - new_page_size;
1803 case ZoomFocusCenter:
1804 current_center = current_leftmost + (current_page/2);
1805 if (current_center < half_page_size) {
1806 leftmost_after_zoom = 0;
1808 leftmost_after_zoom = current_center - half_page_size;
1812 case ZoomFocusPlayhead:
1813 /* centre playhead */
1814 l = playhead_cursor->current_sample () - (new_page_size * 0.5);
1817 leftmost_after_zoom = 0;
1818 } else if (l > max_samplepos) {
1819 leftmost_after_zoom = max_samplepos - new_page_size;
1821 leftmost_after_zoom = (samplepos_t) l;
1825 case ZoomFocusMouse:
1826 /* try to keep the mouse over the same point in the display */
1828 if (_drags->active()) {
1829 where = _drags->current_pointer_sample ();
1830 } else if (!mouse_sample (where, in_track_canvas)) {
1831 use_mouse_sample = false;
1834 if (use_mouse_sample) {
1835 l = - ((new_page_size * ((where - current_leftmost)/(double)current_page)) - where);
1838 leftmost_after_zoom = 0;
1839 } else if (l > max_samplepos) {
1840 leftmost_after_zoom = max_samplepos - new_page_size;
1842 leftmost_after_zoom = (samplepos_t) l;
1845 /* use playhead instead */
1846 where = playhead_cursor->current_sample ();
1848 if (where < half_page_size) {
1849 leftmost_after_zoom = 0;
1851 leftmost_after_zoom = where - half_page_size;
1857 /* try to keep the edit point in the same place */
1858 where = get_preferred_edit_position ();
1862 double l = - ((new_page_size * ((where - current_leftmost)/(double)current_page)) - where);
1865 leftmost_after_zoom = 0;
1866 } else if (l > max_samplepos) {
1867 leftmost_after_zoom = max_samplepos - new_page_size;
1869 leftmost_after_zoom = (samplepos_t) l;
1873 /* edit point not defined */
1880 // leftmost_after_zoom = min (leftmost_after_zoom, _session->current_end_sample());
1882 reposition_and_zoom (leftmost_after_zoom, nfpp);
1886 Editor::calc_extra_zoom_edges(samplepos_t &start, samplepos_t &end)
1888 /* this func helps make sure we leave a little space
1889 at each end of the editor so that the zoom doesn't fit the region
1890 precisely to the screen.
1893 GdkScreen* screen = gdk_screen_get_default ();
1894 const gint pixwidth = gdk_screen_get_width (screen);
1895 const gint mmwidth = gdk_screen_get_width_mm (screen);
1896 const double pix_per_mm = (double) pixwidth/ (double) mmwidth;
1897 const double one_centimeter_in_pixels = pix_per_mm * 10.0;
1899 const samplepos_t range = end - start;
1900 const samplecnt_t new_fpp = (samplecnt_t) ceil ((double) range / (double) _visible_canvas_width);
1901 const samplepos_t extra_samples = (samplepos_t) floor (one_centimeter_in_pixels * new_fpp);
1903 if (start > extra_samples) {
1904 start -= extra_samples;
1909 if (max_samplepos - extra_samples > end) {
1910 end += extra_samples;
1912 end = max_samplepos;
1917 Editor::get_selection_extents (samplepos_t &start, samplepos_t &end) const
1919 start = max_samplepos;
1923 //ToDo: if notes are selected, set extents to that selection
1925 //ToDo: if control points are selected, set extents to that selection
1927 if (!selection->regions.empty()) {
1928 RegionSelection rs = get_regions_from_selection_and_entered ();
1930 for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
1932 if ((*i)->region()->position() < start) {
1933 start = (*i)->region()->position();
1936 if ((*i)->region()->last_sample() + 1 > end) {
1937 end = (*i)->region()->last_sample() + 1;
1941 } else if (!selection->time.empty()) {
1942 start = selection->time.start();
1943 end = selection->time.end_sample();
1945 ret = false; //no selection found
1948 if ((start == 0 && end == 0) || end < start) {
1957 Editor::temporal_zoom_selection (Editing::ZoomAxis axes)
1959 if (!selection) return;
1961 if (selection->regions.empty() && selection->time.empty()) {
1962 if (axes == Horizontal || axes == Both) {
1963 temporal_zoom_step(true);
1965 if (axes == Vertical || axes == Both) {
1966 if (!track_views.empty()) {
1970 //implicit hack: by extending the top & bottom check outside the current view limits, we include the trackviews immediately above & below what is visible
1971 const double top = vertical_adjustment.get_value() - 10;
1972 const double btm = top + _visible_canvas_height + 10;
1974 for (TrackViewList::iterator iter = track_views.begin(); iter != track_views.end(); ++iter) {
1975 if ((*iter)->covered_by_y_range (top, btm)) {
1976 tvl.push_back(*iter);
1986 //ToDo: if notes are selected, zoom to that
1988 //ToDo: if control points are selected, zoom to that
1990 if (axes == Horizontal || axes == Both) {
1992 samplepos_t start, end;
1993 if (get_selection_extents (start, end)) {
1994 calc_extra_zoom_edges (start, end);
1995 temporal_zoom_by_sample (start, end);
1999 if (axes == Vertical || axes == Both) {
2003 //normally, we don't do anything "automatic" to the user's selection.
2004 //but in this case, we will clear the selection after a zoom-to-selection.
2009 Editor::temporal_zoom_session ()
2011 ENSURE_GUI_THREAD (*this, &Editor::temporal_zoom_session)
2014 samplecnt_t start = _session->current_start_sample();
2015 samplecnt_t end = _session->current_end_sample();
2017 if (_session->actively_recording ()) {
2018 samplepos_t cur = playhead_cursor->current_sample ();
2020 /* recording beyond the end marker; zoom out
2021 * by 5 seconds more so that if 'follow
2022 * playhead' is active we don't immediately
2025 end = cur + _session->sample_rate() * 5;
2029 if ((start == 0 && end == 0) || end < start) {
2033 calc_extra_zoom_edges(start, end);
2035 temporal_zoom_by_sample (start, end);
2040 Editor::temporal_zoom_extents ()
2042 ENSURE_GUI_THREAD (*this, &Editor::temporal_zoom_extents)
2045 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
2047 samplecnt_t start = ext.first;
2048 samplecnt_t end = ext.second;
2050 if (_session->actively_recording ()) {
2051 samplepos_t cur = playhead_cursor->current_sample ();
2053 /* recording beyond the end marker; zoom out
2054 * by 5 seconds more so that if 'follow
2055 * playhead' is active we don't immediately
2058 end = cur + _session->sample_rate() * 5;
2062 if ((start == 0 && end == 0) || end < start) {
2066 calc_extra_zoom_edges(start, end);
2068 temporal_zoom_by_sample (start, end);
2073 Editor::temporal_zoom_by_sample (samplepos_t start, samplepos_t end)
2075 if (!_session) return;
2077 if ((start == 0 && end == 0) || end < start) {
2081 samplepos_t range = end - start;
2083 const samplecnt_t new_fpp = (samplecnt_t) ceil ((double) range / (double) _visible_canvas_width);
2085 samplepos_t new_page = range;
2086 samplepos_t middle = (samplepos_t) floor ((double) start + ((double) range / 2.0f));
2087 samplepos_t new_leftmost = (samplepos_t) floor ((double) middle - ((double) new_page / 2.0f));
2089 if (new_leftmost > middle) {
2093 if (new_leftmost < 0) {
2097 reposition_and_zoom (new_leftmost, new_fpp);
2101 Editor::temporal_zoom_to_sample (bool coarser, samplepos_t sample)
2107 samplecnt_t range_before = sample - _leftmost_sample;
2108 samplecnt_t new_spp;
2111 if (samples_per_pixel <= 1) {
2114 new_spp = samples_per_pixel + (samples_per_pixel/2);
2116 range_before += range_before/2;
2118 if (samples_per_pixel >= 1) {
2119 new_spp = samples_per_pixel - (samples_per_pixel/2);
2121 /* could bail out here since we cannot zoom any finer,
2122 but leave that to the equality test below
2124 new_spp = samples_per_pixel;
2127 range_before -= range_before/2;
2130 if (new_spp == samples_per_pixel) {
2134 /* zoom focus is automatically taken as @param sample when this
2138 samplepos_t new_leftmost = sample - (samplepos_t)range_before;
2140 if (new_leftmost > sample) {
2144 if (new_leftmost < 0) {
2148 reposition_and_zoom (new_leftmost, new_spp);
2153 Editor::choose_new_marker_name(string &name) {
2155 if (!UIConfiguration::instance().get_name_new_markers()) {
2156 /* don't prompt user for a new name */
2160 Prompter dialog (true);
2162 dialog.set_prompt (_("New Name:"));
2164 dialog.set_title (_("New Location Marker"));
2166 dialog.set_name ("MarkNameWindow");
2167 dialog.set_size_request (250, -1);
2168 dialog.set_position (Gtk::WIN_POS_MOUSE);
2170 dialog.add_button (Stock::OK, RESPONSE_ACCEPT);
2171 dialog.set_initial_text (name);
2175 switch (dialog.run ()) {
2176 case RESPONSE_ACCEPT:
2182 dialog.get_result(name);
2189 Editor::add_location_from_selection ()
2193 if (selection->time.empty()) {
2197 if (_session == 0 || clicked_axisview == 0) {
2201 samplepos_t start = selection->time[clicked_selection].start;
2202 samplepos_t end = selection->time[clicked_selection].end;
2204 _session->locations()->next_available_name(rangename,"selection");
2205 Location *location = new Location (*_session, start, end, rangename, Location::IsRangeMarker, get_grid_music_divisions(0));
2207 begin_reversible_command (_("add marker"));
2209 XMLNode &before = _session->locations()->get_state();
2210 _session->locations()->add (location, true);
2211 XMLNode &after = _session->locations()->get_state();
2212 _session->add_command(new MementoCommand<Locations>(*(_session->locations()), &before, &after));
2214 commit_reversible_command ();
2218 Editor::add_location_mark (samplepos_t where)
2222 select_new_marker = true;
2224 _session->locations()->next_available_name(markername,"mark");
2225 if (!choose_new_marker_name(markername)) {
2228 Location *location = new Location (*_session, where, where, markername, Location::IsMark, get_grid_music_divisions (0));
2229 begin_reversible_command (_("add marker"));
2231 XMLNode &before = _session->locations()->get_state();
2232 _session->locations()->add (location, true);
2233 XMLNode &after = _session->locations()->get_state();
2234 _session->add_command(new MementoCommand<Locations>(*(_session->locations()), &before, &after));
2236 commit_reversible_command ();
2240 Editor::set_session_start_from_playhead ()
2246 if ((loc = _session->locations()->session_range_location()) == 0) { //should never happen
2247 _session->set_session_extents (_session->audible_sample(), _session->audible_sample());
2249 XMLNode &before = loc->get_state();
2251 _session->set_session_extents (_session->audible_sample(), loc->end());
2253 XMLNode &after = loc->get_state();
2255 begin_reversible_command (_("Set session start"));
2257 _session->add_command (new MementoCommand<Location>(*loc, &before, &after));
2259 commit_reversible_command ();
2264 Editor::set_session_end_from_playhead ()
2270 if ((loc = _session->locations()->session_range_location()) == 0) { //should never happen
2271 _session->set_session_extents (_session->audible_sample(), _session->audible_sample());
2273 XMLNode &before = loc->get_state();
2275 _session->set_session_extents (loc->start(), _session->audible_sample());
2277 XMLNode &after = loc->get_state();
2279 begin_reversible_command (_("Set session start"));
2281 _session->add_command (new MementoCommand<Location>(*loc, &before, &after));
2283 commit_reversible_command ();
2286 _session->set_end_is_free (false);
2291 Editor::toggle_location_at_playhead_cursor ()
2293 if (!do_remove_location_at_playhead_cursor())
2295 add_location_from_playhead_cursor();
2300 Editor::add_location_from_playhead_cursor ()
2302 add_location_mark (_session->audible_sample());
2306 Editor::do_remove_location_at_playhead_cursor ()
2308 bool removed = false;
2311 XMLNode &before = _session->locations()->get_state();
2313 //find location(s) at this time
2314 Locations::LocationList locs;
2315 _session->locations()->find_all_between (_session->audible_sample(), _session->audible_sample()+1, locs, Location::Flags(0));
2316 for (Locations::LocationList::iterator i = locs.begin(); i != locs.end(); ++i) {
2317 if ((*i)->is_mark()) {
2318 _session->locations()->remove (*i);
2325 begin_reversible_command (_("remove marker"));
2326 XMLNode &after = _session->locations()->get_state();
2327 _session->add_command(new MementoCommand<Locations>(*(_session->locations()), &before, &after));
2328 commit_reversible_command ();
2335 Editor::remove_location_at_playhead_cursor ()
2337 do_remove_location_at_playhead_cursor ();
2340 /** Add a range marker around each selected region */
2342 Editor::add_locations_from_region ()
2344 RegionSelection rs = get_regions_from_selection_and_entered ();
2349 bool commit = false;
2351 XMLNode &before = _session->locations()->get_state();
2353 for (RegionSelection::iterator i = rs.begin (); i != rs.end (); ++i) {
2355 boost::shared_ptr<Region> region = (*i)->region ();
2357 Location *location = new Location (*_session, region->position(), region->last_sample(), region->name(), Location::IsRangeMarker, 0);
2359 _session->locations()->add (location, true);
2364 begin_reversible_command (selection->regions.size () > 1 ? _("add markers") : _("add marker"));
2365 XMLNode &after = _session->locations()->get_state();
2366 _session->add_command (new MementoCommand<Locations>(*(_session->locations()), &before, &after));
2367 commit_reversible_command ();
2371 /** Add a single range marker around all selected regions */
2373 Editor::add_location_from_region ()
2375 RegionSelection rs = get_regions_from_selection_and_entered ();
2381 XMLNode &before = _session->locations()->get_state();
2385 if (rs.size() > 1) {
2386 _session->locations()->next_available_name(markername, "regions");
2388 RegionView* rv = *(rs.begin());
2389 boost::shared_ptr<Region> region = rv->region();
2390 markername = region->name();
2393 if (!choose_new_marker_name(markername)) {
2397 // single range spanning all selected
2398 Location *location = new Location (*_session, selection->regions.start(), selection->regions.end_sample(), markername, Location::IsRangeMarker, 0);
2399 _session->locations()->add (location, true);
2401 begin_reversible_command (_("add marker"));
2402 XMLNode &after = _session->locations()->get_state();
2403 _session->add_command (new MementoCommand<Locations>(*(_session->locations()), &before, &after));
2404 commit_reversible_command ();
2410 Editor::jump_forward_to_mark ()
2416 samplepos_t pos = _session->locations()->first_mark_after (playhead_cursor->current_sample());
2422 _session->request_locate (pos, _session->transport_rolling());
2426 Editor::jump_backward_to_mark ()
2432 samplepos_t pos = _session->locations()->first_mark_before (playhead_cursor->current_sample());
2434 //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...
2435 if (_session->transport_rolling()) {
2436 if ((playhead_cursor->current_sample() - pos) < _session->sample_rate()/2) {
2437 samplepos_t prior = _session->locations()->first_mark_before (pos);
2446 _session->request_locate (pos, _session->transport_rolling());
2452 samplepos_t const pos = _session->audible_sample ();
2455 _session->locations()->next_available_name (markername, "mark");
2457 if (!choose_new_marker_name (markername)) {
2461 _session->locations()->add (new Location (*_session, pos, 0, markername, Location::IsMark, 0), true);
2465 Editor::clear_markers ()
2468 begin_reversible_command (_("clear markers"));
2470 XMLNode &before = _session->locations()->get_state();
2471 _session->locations()->clear_markers ();
2472 XMLNode &after = _session->locations()->get_state();
2473 _session->add_command(new MementoCommand<Locations>(*(_session->locations()), &before, &after));
2475 commit_reversible_command ();
2480 Editor::clear_ranges ()
2483 begin_reversible_command (_("clear ranges"));
2485 XMLNode &before = _session->locations()->get_state();
2487 _session->locations()->clear_ranges ();
2489 XMLNode &after = _session->locations()->get_state();
2490 _session->add_command(new MementoCommand<Locations>(*(_session->locations()), &before, &after));
2492 commit_reversible_command ();
2497 Editor::clear_locations ()
2499 begin_reversible_command (_("clear locations"));
2501 XMLNode &before = _session->locations()->get_state();
2502 _session->locations()->clear ();
2503 XMLNode &after = _session->locations()->get_state();
2504 _session->add_command(new MementoCommand<Locations>(*(_session->locations()), &before, &after));
2506 commit_reversible_command ();
2510 Editor::unhide_markers ()
2512 for (LocationMarkerMap::iterator i = location_markers.begin(); i != location_markers.end(); ++i) {
2513 Location *l = (*i).first;
2514 if (l->is_hidden() && l->is_mark()) {
2515 l->set_hidden(false, this);
2521 Editor::unhide_ranges ()
2523 for (LocationMarkerMap::iterator i = location_markers.begin(); i != location_markers.end(); ++i) {
2524 Location *l = (*i).first;
2525 if (l->is_hidden() && l->is_range_marker()) {
2526 l->set_hidden(false, this);
2531 /* INSERT/REPLACE */
2534 Editor::insert_region_list_selection (float times)
2536 RouteTimeAxisView *tv = 0;
2537 boost::shared_ptr<Playlist> playlist;
2539 if (clicked_routeview != 0) {
2540 tv = clicked_routeview;
2541 } else if (!selection->tracks.empty()) {
2542 if ((tv = dynamic_cast<RouteTimeAxisView*>(selection->tracks.front())) == 0) {
2545 } else if (entered_track != 0) {
2546 if ((tv = dynamic_cast<RouteTimeAxisView*>(entered_track)) == 0) {
2553 if ((playlist = tv->playlist()) == 0) {
2557 boost::shared_ptr<Region> region = _regions->get_single_selection ();
2562 begin_reversible_command (_("insert region"));
2563 playlist->clear_changes ();
2564 playlist->add_region ((RegionFactory::create (region, true)), get_preferred_edit_position(), times);
2565 if (Config->get_edit_mode() == Ripple)
2566 playlist->ripple (get_preferred_edit_position(), region->length() * times, boost::shared_ptr<Region>());
2568 _session->add_command(new StatefulDiffCommand (playlist));
2569 commit_reversible_command ();
2572 /* BUILT-IN EFFECTS */
2575 Editor::reverse_selection ()
2580 /* GAIN ENVELOPE EDITING */
2583 Editor::edit_envelope ()
2590 Editor::transition_to_rolling (bool fwd)
2596 if (_session->config.get_external_sync()) {
2597 switch (Config->get_sync_source()) {
2601 /* transport controlled by the master */
2606 if (_session->is_auditioning()) {
2607 _session->cancel_audition ();
2611 _session->request_transport_speed (fwd ? 1.0f : -1.0f);
2615 Editor::play_from_start ()
2617 _session->request_locate (_session->current_start_sample(), true);
2621 Editor::play_from_edit_point ()
2623 _session->request_locate (get_preferred_edit_position(), true);
2627 Editor::play_from_edit_point_and_return ()
2629 samplepos_t start_sample;
2630 samplepos_t return_sample;
2632 start_sample = get_preferred_edit_position (EDIT_IGNORE_PHEAD);
2634 if (_session->transport_rolling()) {
2635 _session->request_locate (start_sample, false);
2639 /* don't reset the return sample if its already set */
2641 if ((return_sample = _session->requested_return_sample()) < 0) {
2642 return_sample = _session->audible_sample();
2645 if (start_sample >= 0) {
2646 _session->request_roll_at_and_return (start_sample, return_sample);
2651 Editor::play_selection ()
2653 samplepos_t start, end;
2654 if (!get_selection_extents (start, end))
2657 AudioRange ar (start, end, 0);
2658 list<AudioRange> lar;
2661 _session->request_play_range (&lar, true);
2666 Editor::maybe_locate_with_edit_preroll (samplepos_t location)
2668 if (_session->transport_rolling() || !UIConfiguration::instance().get_follow_edits() || _session->config.get_external_sync())
2671 location -= _session->preroll_samples (location);
2673 //don't try to locate before the beginning of time
2678 //if follow_playhead is on, keep the playhead on the screen
2679 if (_follow_playhead)
2680 if (location < _leftmost_sample)
2681 location = _leftmost_sample;
2683 _session->request_locate (location);
2687 Editor::play_with_preroll ()
2689 samplepos_t start, end;
2690 if (UIConfiguration::instance().get_follow_edits() && get_selection_extents (start, end)) {
2691 const samplepos_t preroll = _session->preroll_samples (start);
2693 samplepos_t ret = start;
2695 if (start > preroll) {
2696 start = start - preroll;
2699 end = end + preroll; //"post-roll"
2701 AudioRange ar (start, end, 0);
2702 list<AudioRange> lar;
2705 _session->request_play_range (&lar, true);
2706 _session->set_requested_return_sample (ret); //force auto-return to return to range start, without the preroll
2708 samplepos_t ph = playhead_cursor->current_sample ();
2709 const samplepos_t preroll = _session->preroll_samples (ph);
2712 start = ph - preroll;
2716 _session->request_locate (start, true);
2717 _session->set_requested_return_sample (ph); //force auto-return to return to playhead location, without the preroll
2722 Editor::rec_with_preroll ()
2724 samplepos_t ph = playhead_cursor->current_sample ();
2725 samplepos_t preroll = _session->preroll_samples (ph);
2726 _session->request_preroll_record_trim (ph, preroll);
2730 Editor::rec_with_count_in ()
2732 _session->request_count_in_record ();
2736 Editor::play_location (Location& location)
2738 if (location.start() <= location.end()) {
2742 _session->request_bounded_roll (location.start(), location.end());
2746 Editor::loop_location (Location& location)
2748 if (location.start() <= location.end()) {
2754 if ((tll = transport_loop_location()) != 0) {
2755 tll->set (location.start(), location.end());
2757 // enable looping, reposition and start rolling
2758 _session->request_locate (tll->start(), true);
2759 _session->request_play_loop (true);
2764 Editor::do_layer_operation (LayerOperation op)
2766 if (selection->regions.empty ()) {
2770 bool const multiple = selection->regions.size() > 1;
2774 begin_reversible_command (_("raise regions"));
2776 begin_reversible_command (_("raise region"));
2782 begin_reversible_command (_("raise regions to top"));
2784 begin_reversible_command (_("raise region to top"));
2790 begin_reversible_command (_("lower regions"));
2792 begin_reversible_command (_("lower region"));
2798 begin_reversible_command (_("lower regions to bottom"));
2800 begin_reversible_command (_("lower region"));
2805 set<boost::shared_ptr<Playlist> > playlists = selection->regions.playlists ();
2806 for (set<boost::shared_ptr<Playlist> >::iterator i = playlists.begin(); i != playlists.end(); ++i) {
2807 (*i)->clear_owned_changes ();
2810 for (RegionSelection::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
2811 boost::shared_ptr<Region> r = (*i)->region ();
2823 r->lower_to_bottom ();
2827 for (set<boost::shared_ptr<Playlist> >::iterator i = playlists.begin(); i != playlists.end(); ++i) {
2828 vector<Command*> cmds;
2830 _session->add_commands (cmds);
2833 commit_reversible_command ();
2837 Editor::raise_region ()
2839 do_layer_operation (Raise);
2843 Editor::raise_region_to_top ()
2845 do_layer_operation (RaiseToTop);
2849 Editor::lower_region ()
2851 do_layer_operation (Lower);
2855 Editor::lower_region_to_bottom ()
2857 do_layer_operation (LowerToBottom);
2860 /** Show the region editor for the selected regions */
2862 Editor::show_region_properties ()
2864 selection->foreach_regionview (&RegionView::show_region_editor);
2867 /** Show the midi list editor for the selected MIDI regions */
2869 Editor::show_midi_list_editor ()
2871 selection->foreach_midi_regionview (&MidiRegionView::show_list_editor);
2875 Editor::rename_region ()
2877 RegionSelection rs = get_regions_from_selection_and_entered ();
2883 ArdourDialog d (_("Rename Region"), true, false);
2885 Label label (_("New name:"));
2888 hbox.set_spacing (6);
2889 hbox.pack_start (label, false, false);
2890 hbox.pack_start (entry, true, true);
2892 d.get_vbox()->set_border_width (12);
2893 d.get_vbox()->pack_start (hbox, false, false);
2895 d.add_button(Gtk::Stock::CANCEL, Gtk::RESPONSE_CANCEL);
2896 d.add_button(Gtk::Stock::OK, Gtk::RESPONSE_OK);
2898 d.set_size_request (300, -1);
2900 entry.set_text (rs.front()->region()->name());
2901 entry.select_region (0, -1);
2903 entry.signal_activate().connect (sigc::bind (sigc::mem_fun (d, &Dialog::response), RESPONSE_OK));
2909 int const ret = d.run();
2913 if (ret != RESPONSE_OK) {
2917 std::string str = entry.get_text();
2918 strip_whitespace_edges (str);
2920 rs.front()->region()->set_name (str);
2921 _regions->redisplay ();
2925 /** Start an audition of the first selected region */
2927 Editor::play_edit_range ()
2929 samplepos_t start, end;
2931 if (get_edit_op_range (start, end)) {
2932 _session->request_bounded_roll (start, end);
2937 Editor::play_selected_region ()
2939 samplepos_t start = max_samplepos;
2940 samplepos_t end = 0;
2942 RegionSelection rs = get_regions_from_selection_and_entered ();
2948 for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
2949 if ((*i)->region()->position() < start) {
2950 start = (*i)->region()->position();
2952 if ((*i)->region()->last_sample() + 1 > end) {
2953 end = (*i)->region()->last_sample() + 1;
2957 _session->request_bounded_roll (start, end);
2961 Editor::audition_playlist_region_standalone (boost::shared_ptr<Region> region)
2963 _session->audition_region (region);
2967 Editor::region_from_selection ()
2969 if (clicked_axisview == 0) {
2973 if (selection->time.empty()) {
2977 samplepos_t start = selection->time[clicked_selection].start;
2978 samplepos_t end = selection->time[clicked_selection].end;
2980 TrackViewList tracks = get_tracks_for_range_action ();
2982 samplepos_t selection_cnt = end - start + 1;
2984 for (TrackSelection::iterator i = tracks.begin(); i != tracks.end(); ++i) {
2985 boost::shared_ptr<Region> current;
2986 boost::shared_ptr<Playlist> pl;
2987 samplepos_t internal_start;
2990 if ((pl = (*i)->playlist()) == 0) {
2994 if ((current = pl->top_region_at (start)) == 0) {
2998 internal_start = start - current->position();
2999 RegionFactory::region_name (new_name, current->name(), true);
3003 plist.add (ARDOUR::Properties::start, current->start() + internal_start);
3004 plist.add (ARDOUR::Properties::length, selection_cnt);
3005 plist.add (ARDOUR::Properties::name, new_name);
3006 plist.add (ARDOUR::Properties::layer, 0);
3008 boost::shared_ptr<Region> region (RegionFactory::create (current, plist));
3013 Editor::create_region_from_selection (vector<boost::shared_ptr<Region> >& new_regions)
3015 if (selection->time.empty() || selection->tracks.empty()) {
3019 samplepos_t start, end;
3020 if (clicked_selection) {
3021 start = selection->time[clicked_selection].start;
3022 end = selection->time[clicked_selection].end;
3024 start = selection->time.start();
3025 end = selection->time.end_sample();
3028 TrackViewList ts = selection->tracks.filter_to_unique_playlists ();
3029 sort_track_selection (ts);
3031 for (TrackSelection::iterator i = ts.begin(); i != ts.end(); ++i) {
3032 boost::shared_ptr<Region> current;
3033 boost::shared_ptr<Playlist> playlist;
3034 samplepos_t internal_start;
3037 if ((playlist = (*i)->playlist()) == 0) {
3041 if ((current = playlist->top_region_at(start)) == 0) {
3045 internal_start = start - current->position();
3046 RegionFactory::region_name (new_name, current->name(), true);
3050 plist.add (ARDOUR::Properties::start, current->start() + internal_start);
3051 plist.add (ARDOUR::Properties::length, end - start + 1);
3052 plist.add (ARDOUR::Properties::name, new_name);
3054 new_regions.push_back (RegionFactory::create (current, plist));
3059 Editor::split_multichannel_region ()
3061 RegionSelection rs = get_regions_from_selection_and_entered ();
3067 vector< boost::shared_ptr<Region> > v;
3069 for (list<RegionView*>::iterator x = rs.begin(); x != rs.end(); ++x) {
3070 (*x)->region()->separate_by_channel (v);
3075 Editor::new_region_from_selection ()
3077 region_from_selection ();
3078 cancel_selection ();
3082 add_if_covered (RegionView* rv, const AudioRange* ar, RegionSelection* rs)
3084 switch (rv->region()->coverage (ar->start, ar->end - 1)) {
3085 // n.b. -1 because AudioRange::end is one past the end, but coverage expects inclusive ranges
3086 case Evoral::OverlapNone:
3094 * - selected tracks, or if there are none...
3095 * - tracks containing selected regions, or if there are none...
3100 Editor::get_tracks_for_range_action () const
3104 if (selection->tracks.empty()) {
3106 /* use tracks with selected regions */
3108 RegionSelection rs = selection->regions;
3110 for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
3111 TimeAxisView* tv = &(*i)->get_time_axis_view();
3113 if (!t.contains (tv)) {
3119 /* no regions and no tracks: use all tracks */
3125 t = selection->tracks;
3128 return t.filter_to_unique_playlists();
3132 Editor::separate_regions_between (const TimeSelection& ts)
3134 bool in_command = false;
3135 boost::shared_ptr<Playlist> playlist;
3136 RegionSelection new_selection;
3138 TrackViewList tmptracks = get_tracks_for_range_action ();
3139 sort_track_selection (tmptracks);
3141 for (TrackSelection::iterator i = tmptracks.begin(); i != tmptracks.end(); ++i) {
3143 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> ((*i));
3149 if (!rtv->is_track()) {
3153 /* no edits to destructive tracks */
3155 if (rtv->track()->destructive()) {
3159 if ((playlist = rtv->playlist()) != 0) {
3161 playlist->clear_changes ();
3163 /* XXX need to consider musical time selections here at some point */
3165 for (list<AudioRange>::const_iterator t = ts.begin(); t != ts.end(); ++t) {
3167 sigc::connection c = rtv->view()->RegionViewAdded.connect (
3168 sigc::mem_fun(*this, &Editor::collect_new_region_view));
3170 latest_regionviews.clear ();
3172 playlist->partition ((*t).start, (*t).end, false);
3176 if (!latest_regionviews.empty()) {
3178 rtv->view()->foreach_regionview (sigc::bind (
3179 sigc::ptr_fun (add_if_covered),
3180 &(*t), &new_selection));
3183 begin_reversible_command (_("separate"));
3187 /* pick up changes to existing regions */
3189 vector<Command*> cmds;
3190 playlist->rdiff (cmds);
3191 _session->add_commands (cmds);
3193 /* pick up changes to the playlist itself (adds/removes)
3196 _session->add_command(new StatefulDiffCommand (playlist));
3203 // selection->set (new_selection);
3205 commit_reversible_command ();
3209 struct PlaylistState {
3210 boost::shared_ptr<Playlist> playlist;
3214 /** Take tracks from get_tracks_for_range_action and cut any regions
3215 * on those tracks so that the tracks are empty over the time
3219 Editor::separate_region_from_selection ()
3221 /* preferentially use *all* ranges in the time selection if we're in range mode
3222 to allow discontiguous operation, since get_edit_op_range() currently
3223 returns a single range.
3226 if (!selection->time.empty()) {
3228 separate_regions_between (selection->time);
3235 if (get_edit_op_range (start, end)) {
3237 AudioRange ar (start, end, 1);
3241 separate_regions_between (ts);
3247 Editor::separate_region_from_punch ()
3249 Location* loc = _session->locations()->auto_punch_location();
3251 separate_regions_using_location (*loc);
3256 Editor::separate_region_from_loop ()
3258 Location* loc = _session->locations()->auto_loop_location();
3260 separate_regions_using_location (*loc);
3265 Editor::separate_regions_using_location (Location& loc)
3267 if (loc.is_mark()) {
3271 AudioRange ar (loc.start(), loc.end(), 1);
3276 separate_regions_between (ts);
3279 /** Separate regions under the selected region */
3281 Editor::separate_under_selected_regions ()
3283 vector<PlaylistState> playlists;
3287 rs = get_regions_from_selection_and_entered();
3289 if (!_session || rs.empty()) {
3293 begin_reversible_command (_("separate region under"));
3295 list<boost::shared_ptr<Region> > regions_to_remove;
3297 for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
3298 // we can't just remove the region(s) in this loop because
3299 // this removes them from the RegionSelection, and they thus
3300 // disappear from underneath the iterator, and the ++i above
3301 // SEGVs in a puzzling fashion.
3303 // so, first iterate over the regions to be removed from rs and
3304 // add them to the regions_to_remove list, and then
3305 // iterate over the list to actually remove them.
3307 regions_to_remove.push_back ((*i)->region());
3310 for (list<boost::shared_ptr<Region> >::iterator rl = regions_to_remove.begin(); rl != regions_to_remove.end(); ++rl) {
3312 boost::shared_ptr<Playlist> playlist = (*rl)->playlist();
3315 // is this check necessary?
3319 vector<PlaylistState>::iterator i;
3321 //only take state if this is a new playlist.
3322 for (i = playlists.begin(); i != playlists.end(); ++i) {
3323 if ((*i).playlist == playlist) {
3328 if (i == playlists.end()) {
3330 PlaylistState before;
3331 before.playlist = playlist;
3332 before.before = &playlist->get_state();
3333 playlist->clear_changes ();
3334 playlist->freeze ();
3335 playlists.push_back(before);
3338 //Partition on the region bounds
3339 playlist->partition ((*rl)->first_sample() - 1, (*rl)->last_sample() + 1, true);
3341 //Re-add region that was just removed due to the partition operation
3342 playlist->add_region ((*rl), (*rl)->first_sample());
3345 vector<PlaylistState>::iterator pl;
3347 for (pl = playlists.begin(); pl != playlists.end(); ++pl) {
3348 (*pl).playlist->thaw ();
3349 _session->add_command(new MementoCommand<Playlist>(*(*pl).playlist, (*pl).before, &(*pl).playlist->get_state()));
3352 commit_reversible_command ();
3356 Editor::crop_region_to_selection ()
3358 if (!selection->time.empty()) {
3360 begin_reversible_command (_("Crop Regions to Time Selection"));
3361 for (std::list<AudioRange>::iterator i = selection->time.begin(); i != selection->time.end(); ++i) {
3362 crop_region_to ((*i).start, (*i).end);
3364 commit_reversible_command();
3370 if (get_edit_op_range (start, end)) {
3371 begin_reversible_command (_("Crop Regions to Edit Range"));
3373 crop_region_to (start, end);
3375 commit_reversible_command();
3382 Editor::crop_region_to (samplepos_t start, samplepos_t end)
3384 vector<boost::shared_ptr<Playlist> > playlists;
3385 boost::shared_ptr<Playlist> playlist;
3388 if (selection->tracks.empty()) {
3389 ts = track_views.filter_to_unique_playlists();
3391 ts = selection->tracks.filter_to_unique_playlists ();
3394 sort_track_selection (ts);
3396 for (TrackSelection::iterator i = ts.begin(); i != ts.end(); ++i) {
3398 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> ((*i));
3404 boost::shared_ptr<Track> t = rtv->track();
3406 if (t != 0 && ! t->destructive()) {
3408 if ((playlist = rtv->playlist()) != 0) {
3409 playlists.push_back (playlist);
3414 if (playlists.empty()) {
3419 samplepos_t new_start;
3420 samplepos_t new_end;
3421 samplecnt_t new_length;
3423 for (vector<boost::shared_ptr<Playlist> >::iterator i = playlists.begin(); i != playlists.end(); ++i) {
3425 /* Only the top regions at start and end have to be cropped */
3426 boost::shared_ptr<Region> region_at_start = (*i)->top_region_at(start);
3427 boost::shared_ptr<Region> region_at_end = (*i)->top_region_at(end);
3429 vector<boost::shared_ptr<Region> > regions;
3431 if (region_at_start != 0) {
3432 regions.push_back (region_at_start);
3434 if (region_at_end != 0) {
3435 regions.push_back (region_at_end);
3438 /* now adjust lengths */
3439 for (vector<boost::shared_ptr<Region> >::iterator i = regions.begin(); i != regions.end(); ++i) {
3441 pos = (*i)->position();
3442 new_start = max (start, pos);
3443 if (max_samplepos - pos > (*i)->length()) {
3444 new_end = pos + (*i)->length() - 1;
3446 new_end = max_samplepos;
3448 new_end = min (end, new_end);
3449 new_length = new_end - new_start + 1;
3451 (*i)->clear_changes ();
3452 (*i)->trim_to (new_start, new_length);
3453 _session->add_command (new StatefulDiffCommand (*i));
3459 Editor::region_fill_track ()
3461 boost::shared_ptr<Playlist> playlist;
3462 RegionSelection regions = get_regions_from_selection_and_entered ();
3463 RegionSelection foo;
3465 samplepos_t const end = _session->current_end_sample ();
3467 if (regions.empty () || regions.end_sample () + 1 >= end) {
3471 samplepos_t const start_sample = regions.start ();
3472 samplepos_t const end_sample = regions.end_sample ();
3473 samplecnt_t const gap = end_sample - start_sample + 1;
3475 begin_reversible_command (Operations::region_fill);
3477 selection->clear_regions ();
3479 for (RegionSelection::iterator i = regions.begin(); i != regions.end(); ++i) {
3481 boost::shared_ptr<Region> r ((*i)->region());
3483 TimeAxisView& tv = (*i)->get_time_axis_view();
3484 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (&tv);
3485 latest_regionviews.clear ();
3486 sigc::connection c = rtv->view()->RegionViewAdded.connect (sigc::mem_fun(*this, &Editor::collect_new_region_view));
3488 samplepos_t const position = end_sample + (r->first_sample() - start_sample + 1);
3489 playlist = (*i)->region()->playlist();
3490 playlist->clear_changes ();
3491 playlist->duplicate_until (r, position, gap, end);
3492 _session->add_command(new StatefulDiffCommand (playlist));
3496 foo.insert (foo.end(), latest_regionviews.begin(), latest_regionviews.end());
3500 selection->set (foo);
3503 commit_reversible_command ();
3507 Editor::set_region_sync_position ()
3509 set_sync_point (get_preferred_edit_position (), get_regions_from_selection_and_edit_point ());
3513 Editor::set_sync_point (samplepos_t where, const RegionSelection& rs)
3515 bool in_command = false;
3517 for (RegionSelection::const_iterator r = rs.begin(); r != rs.end(); ++r) {
3519 if (!(*r)->region()->covers (where)) {
3523 boost::shared_ptr<Region> region ((*r)->region());
3526 begin_reversible_command (_("set sync point"));
3530 region->clear_changes ();
3531 region->set_sync_position (where);
3532 _session->add_command(new StatefulDiffCommand (region));
3536 commit_reversible_command ();
3540 /** Remove the sync positions of the selection */
3542 Editor::remove_region_sync ()
3544 RegionSelection rs = get_regions_from_selection_and_entered ();
3550 begin_reversible_command (_("remove region sync"));
3552 for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
3554 (*i)->region()->clear_changes ();
3555 (*i)->region()->clear_sync_position ();
3556 _session->add_command(new StatefulDiffCommand ((*i)->region()));
3559 commit_reversible_command ();
3563 Editor::naturalize_region ()
3565 RegionSelection rs = get_regions_from_selection_and_entered ();
3571 if (rs.size() > 1) {
3572 begin_reversible_command (_("move regions to original position"));
3574 begin_reversible_command (_("move region to original position"));
3577 for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
3578 (*i)->region()->clear_changes ();
3579 (*i)->region()->move_to_natural_position ();
3580 _session->add_command (new StatefulDiffCommand ((*i)->region()));
3583 commit_reversible_command ();
3587 Editor::align_regions (RegionPoint what)
3589 RegionSelection const rs = get_regions_from_selection_and_edit_point ();
3595 begin_reversible_command (_("align selection"));
3597 samplepos_t const position = get_preferred_edit_position ();
3599 for (RegionSelection::const_iterator i = rs.begin(); i != rs.end(); ++i) {
3600 align_region_internal ((*i)->region(), what, position);
3603 commit_reversible_command ();
3606 struct RegionSortByTime {
3607 bool operator() (const RegionView* a, const RegionView* b) {
3608 return a->region()->position() < b->region()->position();
3613 Editor::align_regions_relative (RegionPoint point)
3615 RegionSelection const rs = get_regions_from_selection_and_edit_point ();
3621 samplepos_t const position = get_preferred_edit_position ();
3623 samplepos_t distance = 0;
3624 samplepos_t pos = 0;
3627 list<RegionView*> sorted;
3628 rs.by_position (sorted);
3630 boost::shared_ptr<Region> r ((*sorted.begin())->region());
3635 if (position > r->position()) {
3636 distance = position - r->position();
3638 distance = r->position() - position;
3644 if (position > r->last_sample()) {
3645 distance = position - r->last_sample();
3646 pos = r->position() + distance;
3648 distance = r->last_sample() - position;
3649 pos = r->position() - distance;
3655 pos = r->adjust_to_sync (position);
3656 if (pos > r->position()) {
3657 distance = pos - r->position();
3659 distance = r->position() - pos;
3665 if (pos == r->position()) {
3669 begin_reversible_command (_("align selection (relative)"));
3671 /* move first one specially */
3673 r->clear_changes ();
3674 r->set_position (pos);
3675 _session->add_command(new StatefulDiffCommand (r));
3677 /* move rest by the same amount */
3681 for (list<RegionView*>::iterator i = sorted.begin(); i != sorted.end(); ++i) {
3683 boost::shared_ptr<Region> region ((*i)->region());
3685 region->clear_changes ();
3688 region->set_position (region->position() + distance);
3690 region->set_position (region->position() - distance);
3693 _session->add_command(new StatefulDiffCommand (region));
3697 commit_reversible_command ();
3701 Editor::align_region (boost::shared_ptr<Region> region, RegionPoint point, samplepos_t position)
3703 begin_reversible_command (_("align region"));
3704 align_region_internal (region, point, position);
3705 commit_reversible_command ();
3709 Editor::align_region_internal (boost::shared_ptr<Region> region, RegionPoint point, samplepos_t position)
3711 region->clear_changes ();
3715 region->set_position (region->adjust_to_sync (position));
3719 if (position > region->length()) {
3720 region->set_position (position - region->length());
3725 region->set_position (position);
3729 _session->add_command(new StatefulDiffCommand (region));
3733 Editor::trim_region_front ()
3739 Editor::trim_region_back ()
3741 trim_region (false);
3745 Editor::trim_region (bool front)
3747 samplepos_t where = get_preferred_edit_position();
3748 RegionSelection rs = get_regions_from_selection_and_edit_point ();
3754 begin_reversible_command (front ? _("trim front") : _("trim back"));
3756 for (list<RegionView*>::const_iterator i = rs.by_layer().begin(); i != rs.by_layer().end(); ++i) {
3757 if (!(*i)->region()->locked()) {
3759 (*i)->region()->clear_changes ();
3762 (*i)->region()->trim_front (where);
3764 (*i)->region()->trim_end (where);
3767 _session->add_command (new StatefulDiffCommand ((*i)->region()));
3771 commit_reversible_command ();
3774 /** Trim the end of the selected regions to the position of the edit cursor */
3776 Editor::trim_region_to_loop ()
3778 Location* loc = _session->locations()->auto_loop_location();
3782 trim_region_to_location (*loc, _("trim to loop"));
3786 Editor::trim_region_to_punch ()
3788 Location* loc = _session->locations()->auto_punch_location();
3792 trim_region_to_location (*loc, _("trim to punch"));
3796 Editor::trim_region_to_location (const Location& loc, const char* str)
3798 RegionSelection rs = get_regions_from_selection_and_entered ();
3799 bool in_command = false;
3801 for (RegionSelection::iterator x = rs.begin(); x != rs.end(); ++x) {
3802 RegionView* rv = (*x);
3804 /* require region to span proposed trim */
3805 switch (rv->region()->coverage (loc.start(), loc.end())) {
3806 case Evoral::OverlapInternal:
3812 RouteTimeAxisView* tav = dynamic_cast<RouteTimeAxisView*> (&rv->get_time_axis_view());
3820 start = loc.start();
3823 rv->region()->clear_changes ();
3824 rv->region()->trim_to (start, (end - start));
3827 begin_reversible_command (str);
3830 _session->add_command(new StatefulDiffCommand (rv->region()));
3834 commit_reversible_command ();
3839 Editor::trim_region_to_previous_region_end ()
3841 return trim_to_region(false);
3845 Editor::trim_region_to_next_region_start ()
3847 return trim_to_region(true);
3851 Editor::trim_to_region(bool forward)
3853 RegionSelection rs = get_regions_from_selection_and_entered ();
3854 bool in_command = false;
3856 boost::shared_ptr<Region> next_region;
3858 for (RegionSelection::iterator x = rs.begin(); x != rs.end(); ++x) {
3860 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (*x);
3866 AudioTimeAxisView* atav = dynamic_cast<AudioTimeAxisView*> (&arv->get_time_axis_view());
3872 boost::shared_ptr<Region> region = arv->region();
3873 boost::shared_ptr<Playlist> playlist (region->playlist());
3875 region->clear_changes ();
3879 next_region = playlist->find_next_region (region->first_sample(), Start, 1);
3885 region->trim_end (next_region->first_sample() - 1);
3886 arv->region_changed (PropertyChange (ARDOUR::Properties::length));
3890 next_region = playlist->find_next_region (region->first_sample(), Start, 0);
3896 region->trim_front (next_region->last_sample() + 1);
3897 arv->region_changed (ARDOUR::bounds_change);
3901 begin_reversible_command (_("trim to region"));
3904 _session->add_command(new StatefulDiffCommand (region));
3908 commit_reversible_command ();
3913 Editor::unfreeze_route ()
3915 if (clicked_routeview == 0 || !clicked_routeview->is_track()) {
3919 clicked_routeview->track()->unfreeze ();
3923 Editor::_freeze_thread (void* arg)
3925 return static_cast<Editor*>(arg)->freeze_thread ();
3929 Editor::freeze_thread ()
3931 /* create event pool because we may need to talk to the session */
3932 SessionEvent::create_per_thread_pool ("freeze events", 64);
3933 /* create per-thread buffers for process() tree to use */
3934 clicked_routeview->audio_track()->freeze_me (*current_interthread_info);
3935 current_interthread_info->done = true;
3940 Editor::freeze_route ()
3946 /* stop transport before we start. this is important */
3948 _session->request_transport_speed (0.0);
3950 /* wait for just a little while, because the above call is asynchronous */
3952 Glib::usleep (250000);
3954 if (clicked_routeview == 0 || !clicked_routeview->is_audio_track()) {
3958 if (!clicked_routeview->track()->bounceable (clicked_routeview->track()->main_outs(), true)) {
3960 _("This track/bus cannot be frozen because the signal adds or loses channels before reaching the outputs.\n"
3961 "This is typically caused by plugins that generate stereo output from mono input or vice versa.")
3963 d.set_title (_("Cannot freeze"));
3968 if (clicked_routeview->track()->has_external_redirects()) {
3969 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"
3970 "Freezing will only process the signal as far as the first send/insert/return."),
3971 clicked_routeview->track()->name()), true, MESSAGE_INFO, BUTTONS_NONE, true);
3973 d.add_button (_("Freeze anyway"), Gtk::RESPONSE_OK);
3974 d.add_button (_("Don't freeze"), Gtk::RESPONSE_CANCEL);
3975 d.set_title (_("Freeze Limits"));
3977 int response = d.run ();
3980 case Gtk::RESPONSE_CANCEL:
3987 InterThreadInfo itt;
3988 current_interthread_info = &itt;
3990 InterthreadProgressWindow ipw (current_interthread_info, _("Freeze"), _("Cancel Freeze"));
3992 pthread_create_and_store (X_("freezer"), &itt.thread, _freeze_thread, this);
3994 CursorContext::Handle cursor_ctx = CursorContext::create(*this, _cursors->wait);
3996 while (!itt.done && !itt.cancel) {
3997 gtk_main_iteration ();
4000 pthread_join (itt.thread, 0);
4001 current_interthread_info = 0;
4005 Editor::bounce_range_selection (bool replace, bool enable_processing)
4007 if (selection->time.empty()) {
4011 TrackSelection views = selection->tracks;
4013 for (TrackViewList::iterator i = views.begin(); i != views.end(); ++i) {
4015 if (enable_processing) {
4017 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (*i);
4019 if (rtv && rtv->track() && replace && enable_processing && !rtv->track()->bounceable (rtv->track()->main_outs(), false)) {
4021 _("You can't perform this operation because the processing of the signal "
4022 "will cause one or more of the tracks to end up with a region with more channels than this track has inputs.\n\n"
4023 "You can do this without processing, which is a different operation.")
4025 d.set_title (_("Cannot bounce"));
4032 samplepos_t start = selection->time[clicked_selection].start;
4033 samplepos_t end = selection->time[clicked_selection].end;
4034 samplepos_t cnt = end - start + 1;
4035 bool in_command = false;
4037 for (TrackViewList::iterator i = views.begin(); i != views.end(); ++i) {
4039 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (*i);
4045 boost::shared_ptr<Playlist> playlist;
4047 if ((playlist = rtv->playlist()) == 0) {
4051 InterThreadInfo itt;
4053 playlist->clear_changes ();
4054 playlist->clear_owned_changes ();
4056 boost::shared_ptr<Region> r;
4058 if (enable_processing) {
4059 r = rtv->track()->bounce_range (start, start+cnt, itt, rtv->track()->main_outs(), false);
4061 r = rtv->track()->bounce_range (start, start+cnt, itt, boost::shared_ptr<Processor>(), false);
4069 list<AudioRange> ranges;
4070 ranges.push_back (AudioRange (start, start+cnt, 0));
4071 playlist->cut (ranges); // discard result
4072 playlist->add_region (r, start);
4076 begin_reversible_command (_("bounce range"));
4079 vector<Command*> cmds;
4080 playlist->rdiff (cmds);
4081 _session->add_commands (cmds);
4083 _session->add_command (new StatefulDiffCommand (playlist));
4087 commit_reversible_command ();
4091 /** Delete selected regions, automation points or a time range */
4095 //special case: if the user is pointing in the editor/mixer strip, they may be trying to delete a plugin.
4096 //we need this because the editor-mixer strip is in the editor window, so it doesn't get the bindings from the mix window
4097 bool deleted = false;
4098 if (current_mixer_strip && current_mixer_strip == MixerStrip::entered_mixer_strip())
4099 deleted = current_mixer_strip->delete_processors ();
4105 /** Cut selected regions, automation points or a time range */
4112 /** Copy selected regions, automation points or a time range */
4120 /** @return true if a Cut, Copy or Clear is possible */
4122 Editor::can_cut_copy () const
4124 if (!selection->time.empty() || !selection->regions.empty() || !selection->points.empty())
4131 /** Cut, copy or clear selected regions, automation points or a time range.
4132 * @param op Operation (Delete, Cut, Copy or Clear)
4135 Editor::cut_copy (CutCopyOp op)
4137 /* only cancel selection if cut/copy is successful.*/
4143 opname = _("delete");
4152 opname = _("clear");
4156 /* if we're deleting something, and the mouse is still pressed,
4157 the thing we started a drag for will be gone when we release
4158 the mouse button(s). avoid this. see part 2 at the end of
4162 if (op == Delete || op == Cut || op == Clear) {
4163 if (_drags->active ()) {
4168 if (op != Delete) { //"Delete" doesn't change copy/paste buf
4169 cut_buffer->clear ();
4172 if (entered_marker) {
4174 /* cut/delete op while pointing at a marker */
4177 Location* loc = find_location_from_marker (entered_marker, ignored);
4179 if (_session && loc) {
4180 entered_marker = NULL;
4181 Glib::signal_idle().connect (sigc::bind (sigc::mem_fun(*this, &Editor::really_remove_marker), loc));
4188 switch (mouse_mode) {
4191 begin_reversible_command (opname + ' ' + X_("MIDI"));
4193 commit_reversible_command ();
4199 bool did_edit = false;
4201 if (!selection->regions.empty() || !selection->points.empty()) {
4202 begin_reversible_command (opname + ' ' + _("objects"));
4205 if (!selection->regions.empty()) {
4206 cut_copy_regions (op, selection->regions);
4208 if (op == Cut || op == Delete) {
4209 selection->clear_regions ();
4213 if (!selection->points.empty()) {
4214 cut_copy_points (op);
4216 if (op == Cut || op == Delete) {
4217 selection->clear_points ();
4220 } else if (selection->time.empty()) {
4221 samplepos_t start, end;
4222 /* no time selection, see if we can get an edit range
4225 if (get_edit_op_range (start, end)) {
4226 selection->set (start, end);
4228 } else if (!selection->time.empty()) {
4229 begin_reversible_command (opname + ' ' + _("range"));
4232 cut_copy_ranges (op);
4234 if (op == Cut || op == Delete) {
4235 selection->clear_time ();
4240 /* reset repeated paste state */
4242 last_paste_pos = -1;
4243 commit_reversible_command ();
4246 if (op == Delete || op == Cut || op == Clear) {
4252 struct AutomationRecord {
4253 AutomationRecord () : state (0) , line(NULL) {}
4254 AutomationRecord (XMLNode* s, const AutomationLine* l) : state (s) , line (l) {}
4256 XMLNode* state; ///< state before any operation
4257 const AutomationLine* line; ///< line this came from
4258 boost::shared_ptr<Evoral::ControlList> copy; ///< copied events for the cut buffer
4261 struct PointsSelectionPositionSorter {
4262 bool operator() (ControlPoint* a, ControlPoint* b) {
4263 return (*(a->model()))->when < (*(b->model()))->when;
4267 /** Cut, copy or clear selected automation points.
4268 * @param op Operation (Cut, Copy or Clear)
4271 Editor::cut_copy_points (Editing::CutCopyOp op, Temporal::Beats earliest, bool midi)
4273 if (selection->points.empty ()) {
4277 /* XXX: not ideal, as there may be more than one track involved in the point selection */
4278 _last_cut_copy_source_track = &selection->points.front()->line().trackview;
4280 /* Keep a record of the AutomationLists that we end up using in this operation */
4281 typedef std::map<boost::shared_ptr<AutomationList>, AutomationRecord> Lists;
4284 /* user could select points in any order */
4285 selection->points.sort(PointsSelectionPositionSorter ());
4287 /* Go through all selected points, making an AutomationRecord for each distinct AutomationList */
4288 for (PointSelection::iterator sel_point = selection->points.begin(); sel_point != selection->points.end(); ++sel_point) {
4289 const AutomationLine& line = (*sel_point)->line();
4290 const boost::shared_ptr<AutomationList> al = line.the_list();
4291 if (lists.find (al) == lists.end ()) {
4292 /* We haven't seen this list yet, so make a record for it. This includes
4293 taking a copy of its current state, in case this is needed for undo later.
4295 lists[al] = AutomationRecord (&al->get_state (), &line);
4299 if (op == Cut || op == Copy) {
4300 /* This operation will involve putting things in the cut buffer, so create an empty
4301 ControlList for each of our source lists to put the cut buffer data in.
4303 for (Lists::iterator i = lists.begin(); i != lists.end(); ++i) {
4304 i->second.copy = i->first->create (i->first->parameter (), i->first->descriptor());
4307 /* Add all selected points to the relevant copy ControlLists */
4308 MusicSample start (std::numeric_limits<samplepos_t>::max(), 0);
4309 for (PointSelection::iterator sel_point = selection->points.begin(); sel_point != selection->points.end(); ++sel_point) {
4310 boost::shared_ptr<AutomationList> al = (*sel_point)->line().the_list();
4311 AutomationList::const_iterator ctrl_evt = (*sel_point)->model ();
4313 lists[al].copy->fast_simple_add ((*ctrl_evt)->when, (*ctrl_evt)->value);
4315 /* Update earliest MIDI start time in beats */
4316 earliest = std::min(earliest, Temporal::Beats((*ctrl_evt)->when));
4318 /* Update earliest session start time in samples */
4319 start.sample = std::min(start.sample, (*sel_point)->line().session_position(ctrl_evt));
4323 /* Snap start time backwards, so copy/paste is snap aligned. */
4325 if (earliest == std::numeric_limits<Temporal::Beats>::max()) {
4326 earliest = Temporal::Beats(); // Weird... don't offset
4328 earliest.round_down_to_beat();
4330 if (start.sample == std::numeric_limits<double>::max()) {
4331 start.sample = 0; // Weird... don't offset
4333 snap_to(start, RoundDownMaybe);
4336 const double line_offset = midi ? earliest.to_double() : start.sample;
4337 for (Lists::iterator i = lists.begin(); i != lists.end(); ++i) {
4338 /* Correct this copy list so that it is relative to the earliest
4339 start time, so relative ordering between points is preserved
4340 when copying from several lists and the paste starts at the
4341 earliest copied piece of data. */
4342 boost::shared_ptr<Evoral::ControlList> &al_cpy = i->second.copy;
4343 for (AutomationList::iterator ctrl_evt = al_cpy->begin(); ctrl_evt != al_cpy->end(); ++ctrl_evt) {
4344 (*ctrl_evt)->when -= line_offset;
4347 /* And add it to the cut buffer */
4348 cut_buffer->add (al_cpy);
4352 if (op == Delete || op == Cut) {
4353 /* This operation needs to remove things from the main AutomationList, so do that now */
4355 for (Lists::iterator i = lists.begin(); i != lists.end(); ++i) {
4356 i->first->freeze ();
4359 /* Remove each selected point from its AutomationList */
4360 for (PointSelection::iterator sel_point = selection->points.begin(); sel_point != selection->points.end(); ++sel_point) {
4361 AutomationLine& line = (*sel_point)->line ();
4362 boost::shared_ptr<AutomationList> al = line.the_list();
4366 if (dynamic_cast<AudioRegionGainLine*> (&line)) {
4367 /* removing of first and last gain point in region gain lines is prohibited*/
4368 if (line.is_last_point (*(*sel_point)) || line.is_first_point (*(*sel_point))) {
4374 al->erase ((*sel_point)->model ());
4378 /* Thaw the lists and add undo records for them */
4379 for (Lists::iterator i = lists.begin(); i != lists.end(); ++i) {
4380 boost::shared_ptr<AutomationList> al = i->first;
4382 _session->add_command (new MementoCommand<AutomationList> (*al.get(), i->second.state, &(al->get_state ())));
4387 /** Cut, copy or clear selected automation points.
4388 * @param op Operation (Cut, Copy or Clear)
4391 Editor::cut_copy_midi (CutCopyOp op)
4393 Temporal::Beats earliest = std::numeric_limits<Temporal::Beats>::max();
4394 for (MidiRegionSelection::iterator i = selection->midi_regions.begin(); i != selection->midi_regions.end(); ++i) {
4395 MidiRegionView* mrv = dynamic_cast<MidiRegionView*>(*i);
4397 if (!mrv->selection().empty()) {
4398 earliest = std::min(earliest, (*mrv->selection().begin())->note()->time());
4400 mrv->cut_copy_clear (op);
4402 /* XXX: not ideal, as there may be more than one track involved in the selection */
4403 _last_cut_copy_source_track = &mrv->get_time_axis_view();
4407 if (!selection->points.empty()) {
4408 cut_copy_points (op, earliest, true);
4409 if (op == Cut || op == Delete) {
4410 selection->clear_points ();
4415 struct lt_playlist {
4416 bool operator () (const PlaylistState& a, const PlaylistState& b) {
4417 return a.playlist < b.playlist;
4421 struct PlaylistMapping {
4423 boost::shared_ptr<Playlist> pl;
4425 PlaylistMapping (TimeAxisView* tvp) : tv (tvp) {}
4428 /** Remove `clicked_regionview' */
4430 Editor::remove_clicked_region ()
4432 if (clicked_routeview == 0 || clicked_regionview == 0) {
4436 begin_reversible_command (_("remove region"));
4438 boost::shared_ptr<Playlist> playlist = clicked_routeview->playlist();
4440 playlist->clear_changes ();
4441 playlist->clear_owned_changes ();
4442 playlist->remove_region (clicked_regionview->region());
4443 if (Config->get_edit_mode() == Ripple)
4444 playlist->ripple (clicked_regionview->region()->position(), -clicked_regionview->region()->length(), boost::shared_ptr<Region>());
4446 /* We might have removed regions, which alters other regions' layering_index,
4447 so we need to do a recursive diff here.
4449 vector<Command*> cmds;
4450 playlist->rdiff (cmds);
4451 _session->add_commands (cmds);
4453 _session->add_command(new StatefulDiffCommand (playlist));
4454 commit_reversible_command ();
4458 /** Remove the selected regions */
4460 Editor::remove_selected_regions ()
4462 RegionSelection rs = get_regions_from_selection_and_entered ();
4464 if (!_session || rs.empty()) {
4468 list<boost::shared_ptr<Region> > regions_to_remove;
4470 for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
4471 // we can't just remove the region(s) in this loop because
4472 // this removes them from the RegionSelection, and they thus
4473 // disappear from underneath the iterator, and the ++i above
4474 // SEGVs in a puzzling fashion.
4476 // so, first iterate over the regions to be removed from rs and
4477 // add them to the regions_to_remove list, and then
4478 // iterate over the list to actually remove them.
4480 regions_to_remove.push_back ((*i)->region());
4483 vector<boost::shared_ptr<Playlist> > playlists;
4485 for (list<boost::shared_ptr<Region> >::iterator rl = regions_to_remove.begin(); rl != regions_to_remove.end(); ++rl) {
4487 boost::shared_ptr<Playlist> playlist = (*rl)->playlist();
4490 // is this check necessary?
4494 /* get_regions_from_selection_and_entered() guarantees that
4495 the playlists involved are unique, so there is no need
4499 playlists.push_back (playlist);
4501 playlist->clear_changes ();
4502 playlist->clear_owned_changes ();
4503 playlist->freeze ();
4504 playlist->remove_region (*rl);
4505 if (Config->get_edit_mode() == Ripple)
4506 playlist->ripple ((*rl)->position(), -(*rl)->length(), boost::shared_ptr<Region>());
4510 vector<boost::shared_ptr<Playlist> >::iterator pl;
4511 bool in_command = false;
4513 for (pl = playlists.begin(); pl != playlists.end(); ++pl) {
4516 /* We might have removed regions, which alters other regions' layering_index,
4517 so we need to do a recursive diff here.
4521 begin_reversible_command (_("remove region"));
4524 vector<Command*> cmds;
4525 (*pl)->rdiff (cmds);
4526 _session->add_commands (cmds);
4528 _session->add_command(new StatefulDiffCommand (*pl));
4532 commit_reversible_command ();
4536 /** Cut, copy or clear selected regions.
4537 * @param op Operation (Cut, Copy or Clear)
4540 Editor::cut_copy_regions (CutCopyOp op, RegionSelection& rs)
4542 /* we can't use a std::map here because the ordering is important, and we can't trivially sort
4543 a map when we want ordered access to both elements. i think.
4546 vector<PlaylistMapping> pmap;
4548 samplepos_t first_position = max_samplepos;
4550 typedef set<boost::shared_ptr<Playlist> > FreezeList;
4551 FreezeList freezelist;
4553 /* get ordering correct before we cut/copy */
4555 rs.sort_by_position_and_track ();
4557 for (RegionSelection::iterator x = rs.begin(); x != rs.end(); ++x) {
4559 first_position = min ((samplepos_t) (*x)->region()->position(), first_position);
4561 if (op == Cut || op == Clear || op == Delete) {
4562 boost::shared_ptr<Playlist> pl = (*x)->region()->playlist();
4565 FreezeList::iterator fl;
4567 // only take state if this is a new playlist.
4568 for (fl = freezelist.begin(); fl != freezelist.end(); ++fl) {
4574 if (fl == freezelist.end()) {
4575 pl->clear_changes();
4576 pl->clear_owned_changes ();
4578 freezelist.insert (pl);
4583 TimeAxisView* tv = &(*x)->get_time_axis_view();
4584 vector<PlaylistMapping>::iterator z;
4586 for (z = pmap.begin(); z != pmap.end(); ++z) {
4587 if ((*z).tv == tv) {
4592 if (z == pmap.end()) {
4593 pmap.push_back (PlaylistMapping (tv));
4597 for (RegionSelection::iterator x = rs.begin(); x != rs.end(); ) {
4599 boost::shared_ptr<Playlist> pl = (*x)->region()->playlist();
4602 /* region not yet associated with a playlist (e.g. unfinished
4609 TimeAxisView& tv = (*x)->get_time_axis_view();
4610 boost::shared_ptr<Playlist> npl;
4611 RegionSelection::iterator tmp;
4618 vector<PlaylistMapping>::iterator z;
4620 for (z = pmap.begin(); z != pmap.end(); ++z) {
4621 if ((*z).tv == &tv) {
4626 assert (z != pmap.end());
4629 npl = PlaylistFactory::create (pl->data_type(), *_session, "cutlist", true);
4637 boost::shared_ptr<Region> r = (*x)->region();
4638 boost::shared_ptr<Region> _xx;
4644 pl->remove_region (r);
4645 if (Config->get_edit_mode() == Ripple)
4646 pl->ripple (r->position(), -r->length(), boost::shared_ptr<Region>());
4650 _xx = RegionFactory::create (r);
4651 npl->add_region (_xx, r->position() - first_position);
4652 pl->remove_region (r);
4653 if (Config->get_edit_mode() == Ripple)
4654 pl->ripple (r->position(), -r->length(), boost::shared_ptr<Region>());
4658 /* copy region before adding, so we're not putting same object into two different playlists */
4659 npl->add_region (RegionFactory::create (r), r->position() - first_position);
4663 pl->remove_region (r);
4664 if (Config->get_edit_mode() == Ripple)
4665 pl->ripple (r->position(), -r->length(), boost::shared_ptr<Region>());
4674 list<boost::shared_ptr<Playlist> > foo;
4676 /* the pmap is in the same order as the tracks in which selected regions occurred */
4678 for (vector<PlaylistMapping>::iterator i = pmap.begin(); i != pmap.end(); ++i) {
4681 foo.push_back ((*i).pl);
4686 cut_buffer->set (foo);
4690 _last_cut_copy_source_track = 0;
4692 _last_cut_copy_source_track = pmap.front().tv;
4696 for (FreezeList::iterator pl = freezelist.begin(); pl != freezelist.end(); ++pl) {
4699 /* We might have removed regions, which alters other regions' layering_index,
4700 so we need to do a recursive diff here.
4702 vector<Command*> cmds;
4703 (*pl)->rdiff (cmds);
4704 _session->add_commands (cmds);
4706 _session->add_command (new StatefulDiffCommand (*pl));
4711 Editor::cut_copy_ranges (CutCopyOp op)
4713 TrackViewList ts = selection->tracks.filter_to_unique_playlists ();
4715 /* Sort the track selection now, so that it if is used, the playlists
4716 selected by the calls below to cut_copy_clear are in the order that
4717 their tracks appear in the editor. This makes things like paste
4718 of ranges work properly.
4721 sort_track_selection (ts);
4724 if (!entered_track) {
4727 ts.push_back (entered_track);
4730 for (TrackViewList::iterator i = ts.begin(); i != ts.end(); ++i) {
4731 (*i)->cut_copy_clear (*selection, op);
4736 Editor::paste (float times, bool from_context)
4738 DEBUG_TRACE (DEBUG::CutNPaste, "paste to preferred edit pos\n");
4739 MusicSample where (get_preferred_edit_position (EDIT_IGNORE_NONE, from_context), 0);
4740 paste_internal (where.sample, times, 0);
4744 Editor::mouse_paste ()
4746 MusicSample where (0, 0);
4748 if (!mouse_sample (where.sample, ignored)) {
4753 paste_internal (where.sample, 1, where.division);
4757 Editor::paste_internal (samplepos_t position, float times, const int32_t sub_num)
4759 DEBUG_TRACE (DEBUG::CutNPaste, string_compose ("apparent paste position is %1\n", position));
4761 if (cut_buffer->empty(internal_editing())) {
4765 if (position == max_samplepos) {
4766 position = get_preferred_edit_position();
4767 DEBUG_TRACE (DEBUG::CutNPaste, string_compose ("preferred edit position is %1\n", position));
4770 if (position != last_paste_pos) {
4771 /* paste in new location, reset repeated paste state */
4773 last_paste_pos = position;
4776 /* get everything in the correct order */
4779 if (!selection->tracks.empty()) {
4780 /* If there is a track selection, paste into exactly those tracks and
4781 * only those tracks. This allows the user to be explicit and override
4782 * the below "do the reasonable thing" logic. */
4783 ts = selection->tracks.filter_to_unique_playlists ();
4784 sort_track_selection (ts);
4786 /* Figure out which track to base the paste at. */
4787 TimeAxisView* base_track = NULL;
4788 if (_edit_point == Editing::EditAtMouse && entered_track) {
4789 /* With the mouse edit point, paste onto the track under the mouse. */
4790 base_track = entered_track;
4791 } else if (_edit_point == Editing::EditAtMouse && entered_regionview) {
4792 /* With the mouse edit point, paste onto the track of the region under the mouse. */
4793 base_track = &entered_regionview->get_time_axis_view();
4794 } else if (_last_cut_copy_source_track) {
4795 /* Paste to the track that the cut/copy came from (see mantis #333). */
4796 base_track = _last_cut_copy_source_track;
4798 /* This is "impossible" since we've copied... well, do nothing. */
4802 /* Walk up to parent if necessary, so base track is a route. */
4803 while (base_track->get_parent()) {
4804 base_track = base_track->get_parent();
4807 /* Add base track and all tracks below it. The paste logic will select
4808 the appropriate object types from the cut buffer in relative order. */
4809 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
4810 if ((*i)->order() >= base_track->order()) {
4815 /* Sort tracks so the nth track of type T will pick the nth object of type T. */
4816 sort_track_selection (ts);
4818 /* Add automation children of each track in order, for pasting several lines. */
4819 for (TrackViewList::iterator i = ts.begin(); i != ts.end();) {
4820 /* Add any automation children for pasting several lines */
4821 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*>(*i++);
4826 typedef RouteTimeAxisView::AutomationTracks ATracks;
4827 const ATracks& atracks = rtv->automation_tracks();
4828 for (ATracks::const_iterator a = atracks.begin(); a != atracks.end(); ++a) {
4829 i = ts.insert(i, a->second.get());
4834 /* We now have a list of trackviews starting at base_track, including
4835 automation children, in the order shown in the editor, e.g. R1,
4836 R1.A1, R1.A2, R2, R2.A1, ... */
4839 begin_reversible_command (Operations::paste);
4841 if (ts.size() == 1 && cut_buffer->lines.size() == 1 &&
4842 dynamic_cast<AutomationTimeAxisView*>(ts.front())) {
4843 /* Only one line copied, and one automation track selected. Do a
4844 "greedy" paste from one automation type to another. */
4846 PasteContext ctx(paste_count, times, ItemCounts(), true);
4847 ts.front()->paste (position, *cut_buffer, ctx, sub_num);
4851 /* Paste into tracks */
4853 PasteContext ctx(paste_count, times, ItemCounts(), false);
4854 for (TrackViewList::iterator i = ts.begin(); i != ts.end(); ++i) {
4855 (*i)->paste (position, *cut_buffer, ctx, sub_num);
4861 commit_reversible_command ();
4865 Editor::duplicate_regions (float times)
4867 RegionSelection rs (get_regions_from_selection_and_entered());
4868 duplicate_some_regions (rs, times);
4872 Editor::duplicate_some_regions (RegionSelection& regions, float times)
4874 if (regions.empty ()) {
4878 boost::shared_ptr<Playlist> playlist;
4879 std::set<boost::shared_ptr<Playlist> > playlists; // list of unique playlists affected by duplication
4880 RegionSelection sel = regions; // clear (below) may clear the argument list if its the current region selection
4881 RegionSelection foo;
4883 samplepos_t const start_sample = regions.start ();
4884 samplepos_t const end_sample = regions.end_sample ();
4885 samplecnt_t const span = end_sample - start_sample + 1;
4887 begin_reversible_command (Operations::duplicate_region);
4889 selection->clear_regions ();
4891 /* ripple first so that we don't move the duplicates that will be added */
4893 if (Config->get_edit_mode() == Ripple) {
4895 /* convert RegionSelection into RegionList so that we can pass it to ripple and exclude the regions we will duplicate */
4899 for (RegionSelection::iterator i = sel.begin(); i != sel.end(); ++i) {
4900 exclude.push_back ((*i)->region());
4901 playlist = (*i)->region()->playlist();
4902 if (playlists.insert (playlist).second) {
4903 /* successfully inserted into set, so it's the first time we've seen this playlist */
4904 playlist->clear_changes ();
4908 for (set<boost::shared_ptr<Playlist> >::iterator p = playlists.begin(); p != playlists.end(); ++p) {
4909 (*p)->ripple (start_sample, span * times, &exclude);
4913 for (RegionSelection::iterator i = sel.begin(); i != sel.end(); ++i) {
4915 boost::shared_ptr<Region> r ((*i)->region());
4917 TimeAxisView& tv = (*i)->get_time_axis_view();
4918 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (&tv);
4919 latest_regionviews.clear ();
4920 sigc::connection c = rtv->view()->RegionViewAdded.connect (sigc::mem_fun(*this, &Editor::collect_new_region_view));
4922 samplepos_t const position = end_sample + (r->first_sample() - start_sample + 1);
4923 playlist = (*i)->region()->playlist();
4925 if (Config->get_edit_mode() != Ripple) {
4926 if (playlists.insert (playlist).second) {
4927 playlist->clear_changes ();
4931 playlist->duplicate (r, position, span, times);
4935 foo.insert (foo.end(), latest_regionviews.begin(), latest_regionviews.end());
4938 for (set<boost::shared_ptr<Playlist> >::iterator p = playlists.begin(); p != playlists.end(); ++p) {
4939 _session->add_command (new StatefulDiffCommand (*p));
4940 vector<Command*> cmds;
4942 _session->add_commands (cmds);
4946 selection->set (foo);
4949 commit_reversible_command ();
4953 Editor::duplicate_selection (float times)
4955 if (selection->time.empty() || selection->tracks.empty()) {
4959 boost::shared_ptr<Playlist> playlist;
4961 TrackViewList ts = selection->tracks.filter_to_unique_playlists ();
4963 bool in_command = false;
4965 for (TrackViewList::iterator i = ts.begin(); i != ts.end(); ++i) {
4966 if ((playlist = (*i)->playlist()) == 0) {
4969 playlist->clear_changes ();
4971 if (clicked_selection) {
4972 playlist->duplicate_range (selection->time[clicked_selection], times);
4974 playlist->duplicate_ranges (selection->time, times);
4978 begin_reversible_command (_("duplicate range selection"));
4981 _session->add_command (new StatefulDiffCommand (playlist));
4986 if (times == 1.0f) {
4987 // now "move" range selection to after the current range selection
4988 samplecnt_t distance = 0;
4990 if (clicked_selection) {
4992 selection->time[clicked_selection].end - selection->time[clicked_selection].start;
4994 distance = selection->time.end_sample () - selection->time.start ();
4997 selection->move_time (distance);
4999 commit_reversible_command ();
5003 /** Reset all selected points to the relevant default value */
5005 Editor::reset_point_selection ()
5007 for (PointSelection::iterator i = selection->points.begin(); i != selection->points.end(); ++i) {
5008 ARDOUR::AutomationList::iterator j = (*i)->model ();
5009 (*j)->value = (*i)->line().the_list()->descriptor ().normal;
5014 Editor::center_playhead ()
5016 float const page = _visible_canvas_width * samples_per_pixel;
5017 center_screen_internal (playhead_cursor->current_sample (), page);
5021 Editor::center_edit_point ()
5023 float const page = _visible_canvas_width * samples_per_pixel;
5024 center_screen_internal (get_preferred_edit_position(), page);
5027 /** Caller must begin and commit a reversible command */
5029 Editor::clear_playlist (boost::shared_ptr<Playlist> playlist)
5031 playlist->clear_changes ();
5033 _session->add_command (new StatefulDiffCommand (playlist));
5037 Editor::nudge_track (bool use_edit, bool forwards)
5039 boost::shared_ptr<Playlist> playlist;
5040 samplepos_t distance;
5041 samplepos_t next_distance;
5045 start = get_preferred_edit_position();
5050 if ((distance = get_nudge_distance (start, next_distance)) == 0) {
5054 if (selection->tracks.empty()) {
5058 TrackViewList ts = selection->tracks.filter_to_unique_playlists ();
5059 bool in_command = false;
5061 for (TrackViewList::iterator i = ts.begin(); i != ts.end(); ++i) {
5063 if ((playlist = (*i)->playlist()) == 0) {
5067 playlist->clear_changes ();
5068 playlist->clear_owned_changes ();
5070 playlist->nudge_after (start, distance, forwards);
5073 begin_reversible_command (_("nudge track"));
5076 vector<Command*> cmds;
5078 playlist->rdiff (cmds);
5079 _session->add_commands (cmds);
5081 _session->add_command (new StatefulDiffCommand (playlist));
5085 commit_reversible_command ();
5090 Editor::remove_last_capture ()
5092 vector<string> choices;
5099 if (Config->get_verify_remove_last_capture()) {
5100 prompt = _("Do you really want to destroy the last capture?"
5101 "\n(This is destructive and cannot be undone)");
5103 choices.push_back (_("No, do nothing."));
5104 choices.push_back (_("Yes, destroy it."));
5106 Choice prompter (_("Destroy last capture"), prompt, choices);
5108 if (prompter.run () == 1) {
5109 _session->remove_last_capture ();
5110 _regions->redisplay ();
5114 _session->remove_last_capture();
5115 _regions->redisplay ();
5120 Editor::normalize_region ()
5126 RegionSelection rs = get_regions_from_selection_and_entered ();
5132 NormalizeDialog dialog (rs.size() > 1);
5134 if (dialog.run () != RESPONSE_ACCEPT) {
5138 CursorContext::Handle cursor_ctx = CursorContext::create(*this, _cursors->wait);
5141 /* XXX: should really only count audio regions here */
5142 int const regions = rs.size ();
5144 /* Make a list of the selected audio regions' maximum amplitudes, and also
5145 obtain the maximum amplitude of them all.
5147 list<double> max_amps;
5148 list<double> rms_vals;
5151 bool use_rms = dialog.constrain_rms ();
5153 for (RegionSelection::const_iterator i = rs.begin(); i != rs.end(); ++i) {
5154 AudioRegionView const * arv = dynamic_cast<AudioRegionView const *> (*i);
5158 dialog.descend (1.0 / regions);
5159 double const a = arv->audio_region()->maximum_amplitude (&dialog);
5161 double r = arv->audio_region()->rms (&dialog);
5162 max_rms = max (max_rms, r);
5163 rms_vals.push_back (r);
5167 /* the user cancelled the operation */
5171 max_amps.push_back (a);
5172 max_amp = max (max_amp, a);
5176 list<double>::const_iterator a = max_amps.begin ();
5177 list<double>::const_iterator l = rms_vals.begin ();
5178 bool in_command = false;
5180 for (RegionSelection::iterator r = rs.begin(); r != rs.end(); ++r) {
5181 AudioRegionView* const arv = dynamic_cast<AudioRegionView*> (*r);
5186 arv->region()->clear_changes ();
5188 double amp = dialog.normalize_individually() ? *a : max_amp;
5189 double target = dialog.target_peak (); // dB
5192 double const amp_rms = dialog.normalize_individually() ? *l : max_rms;
5193 const double t_rms = dialog.target_rms ();
5194 const gain_t c_peak = dB_to_coefficient (target);
5195 const gain_t c_rms = dB_to_coefficient (t_rms);
5196 if ((amp_rms / c_rms) > (amp / c_peak)) {
5202 arv->audio_region()->normalize (amp, target);
5205 begin_reversible_command (_("normalize"));
5208 _session->add_command (new StatefulDiffCommand (arv->region()));
5215 commit_reversible_command ();
5221 Editor::reset_region_scale_amplitude ()
5227 RegionSelection rs = get_regions_from_selection_and_entered ();
5233 bool in_command = false;
5235 for (RegionSelection::iterator r = rs.begin(); r != rs.end(); ++r) {
5236 AudioRegionView* const arv = dynamic_cast<AudioRegionView*>(*r);
5239 arv->region()->clear_changes ();
5240 arv->audio_region()->set_scale_amplitude (1.0f);
5243 begin_reversible_command ("reset gain");
5246 _session->add_command (new StatefulDiffCommand (arv->region()));
5250 commit_reversible_command ();
5255 Editor::adjust_region_gain (bool up)
5257 RegionSelection rs = get_regions_from_selection_and_entered ();
5259 if (!_session || rs.empty()) {
5263 bool in_command = false;
5265 for (RegionSelection::iterator r = rs.begin(); r != rs.end(); ++r) {
5266 AudioRegionView* const arv = dynamic_cast<AudioRegionView*>(*r);
5271 arv->region()->clear_changes ();
5273 double dB = accurate_coefficient_to_dB (arv->audio_region()->scale_amplitude ());
5281 arv->audio_region()->set_scale_amplitude (dB_to_coefficient (dB));
5284 begin_reversible_command ("adjust region gain");
5287 _session->add_command (new StatefulDiffCommand (arv->region()));
5291 commit_reversible_command ();
5296 Editor::reset_region_gain ()
5298 RegionSelection rs = get_regions_from_selection_and_entered ();
5300 if (!_session || rs.empty()) {
5304 bool in_command = false;
5306 for (RegionSelection::iterator r = rs.begin(); r != rs.end(); ++r) {
5307 AudioRegionView* const arv = dynamic_cast<AudioRegionView*>(*r);
5312 arv->region()->clear_changes ();
5314 arv->audio_region()->set_scale_amplitude (1.0f);
5317 begin_reversible_command ("reset region gain");
5320 _session->add_command (new StatefulDiffCommand (arv->region()));
5324 commit_reversible_command ();
5329 Editor::reverse_region ()
5335 Reverse rev (*_session);
5336 apply_filter (rev, _("reverse regions"));
5340 Editor::strip_region_silence ()
5346 RegionSelection rs = get_regions_from_selection_and_entered ();
5352 std::list<RegionView*> audio_only;
5354 for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
5355 AudioRegionView* const arv = dynamic_cast<AudioRegionView*> (*i);
5357 audio_only.push_back (arv);
5361 assert (!audio_only.empty());
5363 StripSilenceDialog d (_session, audio_only);
5364 int const r = d.run ();
5368 if (r == Gtk::RESPONSE_OK) {
5369 ARDOUR::AudioIntervalMap silences;
5370 d.silences (silences);
5371 StripSilence s (*_session, silences, d.fade_length());
5373 apply_filter (s, _("strip silence"), &d);
5378 Editor::apply_midi_note_edit_op_to_region (MidiOperator& op, MidiRegionView& mrv)
5380 Evoral::Sequence<Temporal::Beats>::Notes selected;
5381 mrv.selection_as_notelist (selected, true);
5383 vector<Evoral::Sequence<Temporal::Beats>::Notes> v;
5384 v.push_back (selected);
5386 Temporal::Beats pos_beats = Temporal::Beats (mrv.midi_region()->beat()) - mrv.midi_region()->start_beats();
5388 return op (mrv.midi_region()->model(), pos_beats, v);
5392 Editor::apply_midi_note_edit_op (MidiOperator& op, const RegionSelection& rs)
5398 bool in_command = false;
5400 for (RegionSelection::const_iterator r = rs.begin(); r != rs.end(); ) {
5401 RegionSelection::const_iterator tmp = r;
5404 MidiRegionView* const mrv = dynamic_cast<MidiRegionView*> (*r);
5407 Command* cmd = apply_midi_note_edit_op_to_region (op, *mrv);
5410 begin_reversible_command (op.name ());
5414 _session->add_command (cmd);
5422 commit_reversible_command ();
5423 _session->set_dirty ();
5428 Editor::fork_region ()
5430 RegionSelection rs = get_regions_from_selection_and_entered ();
5436 CursorContext::Handle cursor_ctx = CursorContext::create(*this, _cursors->wait);
5437 bool in_command = false;
5441 for (RegionSelection::iterator r = rs.begin(); r != rs.end(); ) {
5442 RegionSelection::iterator tmp = r;
5445 MidiRegionView* const mrv = dynamic_cast<MidiRegionView*>(*r);
5449 boost::shared_ptr<Playlist> playlist = mrv->region()->playlist();
5450 boost::shared_ptr<MidiSource> new_source = _session->create_midi_source_by_stealing_name (mrv->midi_view()->track());
5451 boost::shared_ptr<MidiRegion> newregion = mrv->midi_region()->clone (new_source);
5454 begin_reversible_command (_("Fork Region(s)"));
5457 playlist->clear_changes ();
5458 playlist->replace_region (mrv->region(), newregion, mrv->region()->position());
5459 _session->add_command(new StatefulDiffCommand (playlist));
5461 error << string_compose (_("Could not unlink %1"), mrv->region()->name()) << endmsg;
5469 commit_reversible_command ();
5474 Editor::quantize_region ()
5477 quantize_regions(get_regions_from_selection_and_entered ());
5482 Editor::quantize_regions (const RegionSelection& rs)
5484 if (rs.n_midi_regions() == 0) {
5488 if (!quantize_dialog) {
5489 quantize_dialog = new QuantizeDialog (*this);
5492 if (quantize_dialog->is_mapped()) {
5493 /* in progress already */
5497 quantize_dialog->present ();
5498 const int r = quantize_dialog->run ();
5499 quantize_dialog->hide ();
5501 if (r == Gtk::RESPONSE_OK) {
5502 Quantize quant (quantize_dialog->snap_start(),
5503 quantize_dialog->snap_end(),
5504 quantize_dialog->start_grid_size(),
5505 quantize_dialog->end_grid_size(),
5506 quantize_dialog->strength(),
5507 quantize_dialog->swing(),
5508 quantize_dialog->threshold());
5510 apply_midi_note_edit_op (quant, rs);
5515 Editor::legatize_region (bool shrink_only)
5518 legatize_regions(get_regions_from_selection_and_entered (), shrink_only);
5523 Editor::legatize_regions (const RegionSelection& rs, bool shrink_only)
5525 if (rs.n_midi_regions() == 0) {
5529 Legatize legatize(shrink_only);
5530 apply_midi_note_edit_op (legatize, rs);
5534 Editor::transform_region ()
5537 transform_regions(get_regions_from_selection_and_entered ());
5542 Editor::transform_regions (const RegionSelection& rs)
5544 if (rs.n_midi_regions() == 0) {
5551 const int r = td.run();
5554 if (r == Gtk::RESPONSE_OK) {
5555 Transform transform(td.get());
5556 apply_midi_note_edit_op(transform, rs);
5561 Editor::transpose_region ()
5564 transpose_regions(get_regions_from_selection_and_entered ());
5569 Editor::transpose_regions (const RegionSelection& rs)
5571 if (rs.n_midi_regions() == 0) {
5576 int const r = d.run ();
5578 if (r == RESPONSE_ACCEPT) {
5579 Transpose transpose(d.semitones ());
5580 apply_midi_note_edit_op (transpose, rs);
5585 Editor::insert_patch_change (bool from_context)
5587 RegionSelection rs = get_regions_from_selection_and_entered ();
5593 const samplepos_t p = get_preferred_edit_position (EDIT_IGNORE_NONE, from_context);
5595 /* XXX: bit of a hack; use the MIDNAM from the first selected region;
5596 there may be more than one, but the PatchChangeDialog can only offer
5597 one set of patch menus.
5599 MidiRegionView* first = dynamic_cast<MidiRegionView*> (rs.front ());
5601 Evoral::PatchChange<Temporal::Beats> empty (Temporal::Beats(), 0, 0, 0);
5602 PatchChangeDialog d (0, _session, empty, first->instrument_info(), Gtk::Stock::ADD);
5604 if (d.run() == RESPONSE_CANCEL) {
5608 for (RegionSelection::iterator i = rs.begin (); i != rs.end(); ++i) {
5609 MidiRegionView* const mrv = dynamic_cast<MidiRegionView*> (*i);
5611 if (p >= mrv->region()->first_sample() && p <= mrv->region()->last_sample()) {
5612 mrv->add_patch_change (p - mrv->region()->position(), d.patch ());
5619 Editor::apply_filter (Filter& filter, string command, ProgressReporter* progress)
5621 RegionSelection rs = get_regions_from_selection_and_entered ();
5627 CursorContext::Handle cursor_ctx = CursorContext::create(*this, _cursors->wait);
5628 bool in_command = false;
5633 int const N = rs.size ();
5635 for (RegionSelection::iterator r = rs.begin(); r != rs.end(); ) {
5636 RegionSelection::iterator tmp = r;
5639 AudioRegionView* const arv = dynamic_cast<AudioRegionView*>(*r);
5641 boost::shared_ptr<Playlist> playlist = arv->region()->playlist();
5644 progress->descend (1.0 / N);
5647 if (arv->audio_region()->apply (filter, progress) == 0) {
5649 playlist->clear_changes ();
5650 playlist->clear_owned_changes ();
5653 begin_reversible_command (command);
5657 if (filter.results.empty ()) {
5659 /* no regions returned; remove the old one */
5660 playlist->remove_region (arv->region ());
5664 std::vector<boost::shared_ptr<Region> >::iterator res = filter.results.begin ();
5666 /* first region replaces the old one */
5667 playlist->replace_region (arv->region(), *res, (*res)->position());
5671 while (res != filter.results.end()) {
5672 playlist->add_region (*res, (*res)->position());
5678 /* We might have removed regions, which alters other regions' layering_index,
5679 so we need to do a recursive diff here.
5681 vector<Command*> cmds;
5682 playlist->rdiff (cmds);
5683 _session->add_commands (cmds);
5685 _session->add_command(new StatefulDiffCommand (playlist));
5689 progress->ascend ();
5698 commit_reversible_command ();
5703 Editor::external_edit_region ()
5709 Editor::reset_region_gain_envelopes ()
5711 RegionSelection rs = get_regions_from_selection_and_entered ();
5713 if (!_session || rs.empty()) {
5717 bool in_command = false;
5719 for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
5720 AudioRegionView* const arv = dynamic_cast<AudioRegionView*>(*i);
5722 boost::shared_ptr<AutomationList> alist (arv->audio_region()->envelope());
5723 XMLNode& before (alist->get_state());
5725 arv->audio_region()->set_default_envelope ();
5728 begin_reversible_command (_("reset region gain"));
5731 _session->add_command (new MementoCommand<AutomationList>(*arv->audio_region()->envelope().get(), &before, &alist->get_state()));
5736 commit_reversible_command ();
5741 Editor::set_region_gain_visibility (RegionView* rv)
5743 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (rv);
5745 arv->update_envelope_visibility();
5750 Editor::set_gain_envelope_visibility ()
5756 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
5757 AudioTimeAxisView* v = dynamic_cast<AudioTimeAxisView*>(*i);
5759 v->audio_view()->foreach_regionview (sigc::mem_fun (this, &Editor::set_region_gain_visibility));
5765 Editor::toggle_gain_envelope_active ()
5767 if (_ignore_region_action) {
5771 RegionSelection rs = get_regions_from_selection_and_entered ();
5773 if (!_session || rs.empty()) {
5777 bool in_command = false;
5779 for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
5780 AudioRegionView* const arv = dynamic_cast<AudioRegionView*>(*i);
5782 arv->region()->clear_changes ();
5783 arv->audio_region()->set_envelope_active (!arv->audio_region()->envelope_active());
5786 begin_reversible_command (_("region gain envelope active"));
5789 _session->add_command (new StatefulDiffCommand (arv->region()));
5794 commit_reversible_command ();
5799 Editor::toggle_region_lock ()
5801 if (_ignore_region_action) {
5805 RegionSelection rs = get_regions_from_selection_and_entered ();
5807 if (!_session || rs.empty()) {
5811 begin_reversible_command (_("toggle region lock"));
5813 for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
5814 (*i)->region()->clear_changes ();
5815 (*i)->region()->set_locked (!(*i)->region()->locked());
5816 _session->add_command (new StatefulDiffCommand ((*i)->region()));
5819 commit_reversible_command ();
5823 Editor::toggle_region_video_lock ()
5825 if (_ignore_region_action) {
5829 RegionSelection rs = get_regions_from_selection_and_entered ();
5831 if (!_session || rs.empty()) {
5835 begin_reversible_command (_("Toggle Video Lock"));
5837 for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
5838 (*i)->region()->clear_changes ();
5839 (*i)->region()->set_video_locked (!(*i)->region()->video_locked());
5840 _session->add_command (new StatefulDiffCommand ((*i)->region()));
5843 commit_reversible_command ();
5847 Editor::toggle_region_lock_style ()
5849 if (_ignore_region_action) {
5853 RegionSelection rs = get_regions_from_selection_and_entered ();
5855 if (!_session || rs.empty()) {
5859 Glib::RefPtr<ToggleAction> a = Glib::RefPtr<ToggleAction>::cast_dynamic (_region_actions->get_action("toggle-region-lock-style"));
5860 vector<Widget*> proxies = a->get_proxies();
5861 Gtk::CheckMenuItem* cmi = dynamic_cast<Gtk::CheckMenuItem*> (proxies.front());
5865 begin_reversible_command (_("toggle region lock style"));
5867 for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
5868 (*i)->region()->clear_changes ();
5869 PositionLockStyle const ns = ((*i)->region()->position_lock_style() == AudioTime && !cmi->get_inconsistent()) ? MusicTime : AudioTime;
5870 (*i)->region()->set_position_lock_style (ns);
5871 _session->add_command (new StatefulDiffCommand ((*i)->region()));
5874 commit_reversible_command ();
5878 Editor::toggle_opaque_region ()
5880 if (_ignore_region_action) {
5884 RegionSelection rs = get_regions_from_selection_and_entered ();
5886 if (!_session || rs.empty()) {
5890 begin_reversible_command (_("change region opacity"));
5892 for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
5893 (*i)->region()->clear_changes ();
5894 (*i)->region()->set_opaque (!(*i)->region()->opaque());
5895 _session->add_command (new StatefulDiffCommand ((*i)->region()));
5898 commit_reversible_command ();
5902 Editor::toggle_record_enable ()
5904 bool new_state = false;
5906 for (TrackSelection::iterator i = selection->tracks.begin(); i != selection->tracks.end(); ++i) {
5907 RouteTimeAxisView *rtav = dynamic_cast<RouteTimeAxisView *>(*i);
5910 if (!rtav->is_track())
5914 new_state = !rtav->track()->rec_enable_control()->get_value();
5918 rtav->track()->rec_enable_control()->set_value (new_state, Controllable::UseGroup);
5923 tracklist_to_stripables (TrackViewList list)
5927 for (TrackSelection::iterator i = list.begin(); i != list.end(); ++i) {
5928 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> ((*i));
5930 if (rtv && rtv->is_track()) {
5931 ret.push_back (rtv->track());
5939 Editor::play_solo_selection (bool restart)
5941 //note: session::solo_selection takes care of invalidating the region playlist
5943 if ((!selection->tracks.empty()) && selection->time.length() > 0) { //a range is selected; solo the tracks and roll
5945 StripableList sl = tracklist_to_stripables (selection->tracks);
5946 _session->solo_selection (sl, true);
5949 samplepos_t start = selection->time.start();
5950 samplepos_t end = selection->time.end_sample();
5951 _session->request_bounded_roll (start, end);
5953 } else if (! selection->tracks.empty()) { //no range is selected, but tracks are selected; solo the tracks and roll
5954 StripableList sl = tracklist_to_stripables (selection->tracks);
5955 _session->solo_selection (sl, true);
5956 _session->request_cancel_play_range();
5957 transition_to_rolling (true);
5959 } else if (! selection->regions.empty()) { //solo any tracks with selected regions, and roll
5960 StripableList sl = tracklist_to_stripables (get_tracks_for_range_action());
5961 _session->solo_selection (sl, true);
5962 _session->request_cancel_play_range();
5963 transition_to_rolling (true);
5965 _session->request_cancel_play_range();
5966 transition_to_rolling (true); //no selection. just roll.
5971 Editor::toggle_solo ()
5973 bool new_state = false;
5975 boost::shared_ptr<ControlList> cl (new ControlList);
5977 for (TrackSelection::iterator i = selection->tracks.begin(); i != selection->tracks.end(); ++i) {
5978 StripableTimeAxisView *stav = dynamic_cast<StripableTimeAxisView *>(*i);
5980 if (!stav || !stav->stripable()->solo_control()) {
5985 new_state = !stav->stripable()->solo_control()->soloed ();
5989 cl->push_back (stav->stripable()->solo_control());
5992 _session->set_controls (cl, new_state ? 1.0 : 0.0, Controllable::UseGroup);
5996 Editor::toggle_mute ()
5998 bool new_state = false;
6000 boost::shared_ptr<ControlList> cl (new ControlList);
6002 for (TrackSelection::iterator i = selection->tracks.begin(); i != selection->tracks.end(); ++i) {
6003 StripableTimeAxisView *stav = dynamic_cast<StripableTimeAxisView *>(*i);
6005 if (!stav || !stav->stripable()->mute_control()) {
6010 new_state = !stav->stripable()->mute_control()->muted();
6014 cl->push_back (stav->stripable()->mute_control());
6017 _session->set_controls (cl, new_state, Controllable::UseGroup);
6021 Editor::toggle_solo_isolate ()
6027 Editor::fade_range ()
6029 TrackViewList ts = selection->tracks.filter_to_unique_playlists ();
6031 begin_reversible_command (_("fade range"));
6033 for (TrackViewList::iterator i = ts.begin(); i != ts.end(); ++i) {
6034 (*i)->fade_range (selection->time);
6037 commit_reversible_command ();
6042 Editor::set_fade_length (bool in)
6044 RegionSelection rs = get_regions_from_selection_and_entered ();
6050 /* we need a region to measure the offset from the start */
6052 RegionView* rv = rs.front ();
6054 samplepos_t pos = get_preferred_edit_position();
6058 if (pos > rv->region()->last_sample() || pos < rv->region()->first_sample()) {
6059 /* edit point is outside the relevant region */
6064 if (pos <= rv->region()->position()) {
6068 len = pos - rv->region()->position();
6069 cmd = _("set fade in length");
6071 if (pos >= rv->region()->last_sample()) {
6075 len = rv->region()->last_sample() - pos;
6076 cmd = _("set fade out length");
6079 bool in_command = false;
6081 for (RegionSelection::iterator x = rs.begin(); x != rs.end(); ++x) {
6082 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (*x);
6088 boost::shared_ptr<AutomationList> alist;
6090 alist = tmp->audio_region()->fade_in();
6092 alist = tmp->audio_region()->fade_out();
6095 XMLNode &before = alist->get_state();
6098 tmp->audio_region()->set_fade_in_length (len);
6099 tmp->audio_region()->set_fade_in_active (true);
6101 tmp->audio_region()->set_fade_out_length (len);
6102 tmp->audio_region()->set_fade_out_active (true);
6106 begin_reversible_command (cmd);
6109 XMLNode &after = alist->get_state();
6110 _session->add_command(new MementoCommand<AutomationList>(*alist, &before, &after));
6114 commit_reversible_command ();
6119 Editor::set_fade_in_shape (FadeShape shape)
6121 RegionSelection rs = get_regions_from_selection_and_entered ();
6126 bool in_command = false;
6128 for (RegionSelection::iterator x = rs.begin(); x != rs.end(); ++x) {
6129 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (*x);
6135 boost::shared_ptr<AutomationList> alist = tmp->audio_region()->fade_in();
6136 XMLNode &before = alist->get_state();
6138 tmp->audio_region()->set_fade_in_shape (shape);
6141 begin_reversible_command (_("set fade in shape"));
6144 XMLNode &after = alist->get_state();
6145 _session->add_command(new MementoCommand<AutomationList>(*alist.get(), &before, &after));
6149 commit_reversible_command ();
6154 Editor::set_fade_out_shape (FadeShape shape)
6156 RegionSelection rs = get_regions_from_selection_and_entered ();
6161 bool in_command = false;
6163 for (RegionSelection::iterator x = rs.begin(); x != rs.end(); ++x) {
6164 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (*x);
6170 boost::shared_ptr<AutomationList> alist = tmp->audio_region()->fade_out();
6171 XMLNode &before = alist->get_state();
6173 tmp->audio_region()->set_fade_out_shape (shape);
6176 begin_reversible_command (_("set fade out shape"));
6179 XMLNode &after = alist->get_state();
6180 _session->add_command(new MementoCommand<AutomationList>(*alist.get(), &before, &after));
6184 commit_reversible_command ();
6189 Editor::set_fade_in_active (bool yn)
6191 RegionSelection rs = get_regions_from_selection_and_entered ();
6196 bool in_command = false;
6198 for (RegionSelection::iterator x = rs.begin(); x != rs.end(); ++x) {
6199 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (*x);
6206 boost::shared_ptr<AudioRegion> ar (tmp->audio_region());
6208 ar->clear_changes ();
6209 ar->set_fade_in_active (yn);
6212 begin_reversible_command (_("set fade in active"));
6215 _session->add_command (new StatefulDiffCommand (ar));
6219 commit_reversible_command ();
6224 Editor::set_fade_out_active (bool yn)
6226 RegionSelection rs = get_regions_from_selection_and_entered ();
6231 bool in_command = false;
6233 for (RegionSelection::iterator x = rs.begin(); x != rs.end(); ++x) {
6234 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (*x);
6240 boost::shared_ptr<AudioRegion> ar (tmp->audio_region());
6242 ar->clear_changes ();
6243 ar->set_fade_out_active (yn);
6246 begin_reversible_command (_("set fade out active"));
6249 _session->add_command(new StatefulDiffCommand (ar));
6253 commit_reversible_command ();
6258 Editor::toggle_region_fades (int dir)
6260 if (_ignore_region_action) {
6264 boost::shared_ptr<AudioRegion> ar;
6267 RegionSelection rs = get_regions_from_selection_and_entered ();
6273 RegionSelection::iterator i;
6274 for (i = rs.begin(); i != rs.end(); ++i) {
6275 if ((ar = boost::dynamic_pointer_cast<AudioRegion>((*i)->region())) != 0) {
6277 yn = ar->fade_out_active ();
6279 yn = ar->fade_in_active ();
6285 if (i == rs.end()) {
6289 /* XXX should this undo-able? */
6290 bool in_command = false;
6292 for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
6293 if ((ar = boost::dynamic_pointer_cast<AudioRegion>((*i)->region())) == 0) {
6296 ar->clear_changes ();
6298 if (dir == 1 || dir == 0) {
6299 ar->set_fade_in_active (!yn);
6302 if (dir == -1 || dir == 0) {
6303 ar->set_fade_out_active (!yn);
6306 begin_reversible_command (_("toggle fade active"));
6309 _session->add_command(new StatefulDiffCommand (ar));
6313 commit_reversible_command ();
6318 /** Update region fade visibility after its configuration has been changed */
6320 Editor::update_region_fade_visibility ()
6322 bool _fade_visibility = _session->config.get_show_region_fades ();
6324 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
6325 AudioTimeAxisView* v = dynamic_cast<AudioTimeAxisView*>(*i);
6327 if (_fade_visibility) {
6328 v->audio_view()->show_all_fades ();
6330 v->audio_view()->hide_all_fades ();
6337 Editor::set_edit_point ()
6340 MusicSample where (0, 0);
6342 if (!mouse_sample (where.sample, ignored)) {
6348 if (selection->markers.empty()) {
6350 mouse_add_new_marker (where.sample);
6355 Location* loc = find_location_from_marker (selection->markers.front(), ignored);
6358 loc->move_to (where.sample, where.division);
6364 Editor::set_playhead_cursor ()
6366 if (entered_marker) {
6367 _session->request_locate (entered_marker->position(), _session->transport_rolling());
6369 MusicSample where (0, 0);
6372 if (!mouse_sample (where.sample, ignored)) {
6379 _session->request_locate (where.sample, _session->transport_rolling());
6383 //not sure what this was for; remove it for now.
6384 // if (UIConfiguration::instance().get_follow_edits() && (!_session || !_session->config.get_external_sync())) {
6385 // cancel_time_selection();
6391 Editor::split_region ()
6393 if (_dragging_playhead) {
6395 } else if (_drags->active ()) {
6396 /*any other kind of drag, bail out so we avoid Undo snafu*/
6400 //if a range is selected, separate it
6401 if (!selection->time.empty()) {
6402 separate_regions_between (selection->time);
6406 //if no range was selected, try to find some regions to split
6407 if (current_mouse_mode() == MouseObject || current_mouse_mode() == MouseRange ) { //don't try this for Internal Edit, Stretch, Draw, etc.
6409 RegionSelection rs = get_regions_from_selection_and_edit_point ();
6410 const samplepos_t pos = get_preferred_edit_position();
6411 const int32_t division = get_grid_music_divisions (0);
6412 MusicSample where (pos, division);
6418 split_regions_at (where, rs);
6424 Editor::select_next_stripable (bool routes_only)
6426 _session->selection().select_next_stripable (false, routes_only);
6430 Editor::select_prev_stripable (bool routes_only)
6432 _session->selection().select_prev_stripable (false, routes_only);
6436 Editor::set_loop_from_selection (bool play)
6438 if (_session == 0) {
6442 samplepos_t start, end;
6443 if (!get_selection_extents (start, end))
6446 set_loop_range (start, end, _("set loop range from selection"));
6449 _session->request_play_loop (true, true);
6454 Editor::set_loop_from_region (bool play)
6456 samplepos_t start, end;
6457 if (!get_selection_extents (start, end))
6460 set_loop_range (start, end, _("set loop range from region"));
6463 _session->request_locate (start, true);
6464 _session->request_play_loop (true);
6469 Editor::set_punch_from_selection ()
6471 if (_session == 0) {
6475 samplepos_t start, end;
6476 if (!get_selection_extents (start, end))
6479 set_punch_range (start, end, _("set punch range from selection"));
6483 Editor::set_auto_punch_range ()
6485 // auto punch in/out button from a single button
6486 // If Punch In is unset, set punch range from playhead to end, enable punch in
6487 // If Punch In is set, the next punch sets Punch Out, unless the playhead has been
6488 // rewound beyond the Punch In marker, in which case that marker will be moved back
6489 // to the current playhead position.
6490 // If punch out is set, it clears the punch range and Punch In/Out buttons
6492 if (_session == 0) {
6496 Location* tpl = transport_punch_location();
6497 samplepos_t now = playhead_cursor->current_sample();
6498 samplepos_t begin = now;
6499 samplepos_t end = _session->current_end_sample();
6501 if (!_session->config.get_punch_in()) {
6502 // First Press - set punch in and create range from here to eternity
6503 set_punch_range (begin, end, _("Auto Punch In"));
6504 _session->config.set_punch_in(true);
6505 } else if (tpl && !_session->config.get_punch_out()) {
6506 // Second press - update end range marker and set punch_out
6507 if (now < tpl->start()) {
6508 // playhead has been rewound - move start back and pretend nothing happened
6510 set_punch_range (begin, end, _("Auto Punch In/Out"));
6512 // normal case for 2nd press - set the punch out
6513 end = playhead_cursor->current_sample ();
6514 set_punch_range (tpl->start(), now, _("Auto Punch In/Out"));
6515 _session->config.set_punch_out(true);
6518 if (_session->config.get_punch_out()) {
6519 _session->config.set_punch_out(false);
6522 if (_session->config.get_punch_in()) {
6523 _session->config.set_punch_in(false);
6528 // third press - unset punch in/out and remove range
6529 _session->locations()->remove(tpl);
6536 Editor::set_session_extents_from_selection ()
6538 if (_session == 0) {
6542 samplepos_t start, end;
6543 if (!get_selection_extents (start, end))
6547 if ((loc = _session->locations()->session_range_location()) == 0) {
6548 _session->set_session_extents (start, end); // this will create a new session range; no need for UNDO
6550 XMLNode &before = loc->get_state();
6552 _session->set_session_extents (start, end);
6554 XMLNode &after = loc->get_state();
6556 begin_reversible_command (_("set session start/end from selection"));
6558 _session->add_command (new MementoCommand<Location>(*loc, &before, &after));
6560 commit_reversible_command ();
6563 _session->set_end_is_free (false);
6567 Editor::set_punch_start_from_edit_point ()
6571 MusicSample start (0, 0);
6572 samplepos_t end = max_samplepos;
6574 //use the existing punch end, if any
6575 Location* tpl = transport_punch_location();
6580 if ((_edit_point == EditAtPlayhead) && _session->transport_rolling()) {
6581 start.sample = _session->audible_sample();
6583 start.sample = get_preferred_edit_position();
6586 //if there's not already a sensible selection endpoint, go "forever"
6587 if (start.sample > end) {
6588 end = max_samplepos;
6591 set_punch_range (start.sample, end, _("set punch start from EP"));
6597 Editor::set_punch_end_from_edit_point ()
6601 samplepos_t start = 0;
6602 MusicSample end (max_samplepos, 0);
6604 //use the existing punch start, if any
6605 Location* tpl = transport_punch_location();
6607 start = tpl->start();
6610 if ((_edit_point == EditAtPlayhead) && _session->transport_rolling()) {
6611 end.sample = _session->audible_sample();
6613 end.sample = get_preferred_edit_position();
6616 set_punch_range (start, end.sample, _("set punch end from EP"));
6622 Editor::set_loop_start_from_edit_point ()
6626 MusicSample start (0, 0);
6627 samplepos_t end = max_samplepos;
6629 //use the existing loop end, if any
6630 Location* tpl = transport_loop_location();
6635 if ((_edit_point == EditAtPlayhead) && _session->transport_rolling()) {
6636 start.sample = _session->audible_sample();
6638 start.sample = get_preferred_edit_position();
6641 //if there's not already a sensible selection endpoint, go "forever"
6642 if (start.sample > end) {
6643 end = max_samplepos;
6646 set_loop_range (start.sample, end, _("set loop start from EP"));
6652 Editor::set_loop_end_from_edit_point ()
6656 samplepos_t start = 0;
6657 MusicSample end (max_samplepos, 0);
6659 //use the existing loop start, if any
6660 Location* tpl = transport_loop_location();
6662 start = tpl->start();
6665 if ((_edit_point == EditAtPlayhead) && _session->transport_rolling()) {
6666 end.sample = _session->audible_sample();
6668 end.sample = get_preferred_edit_position();
6671 set_loop_range (start, end.sample, _("set loop end from EP"));
6676 Editor::set_punch_from_region ()
6678 samplepos_t start, end;
6679 if (!get_selection_extents (start, end))
6682 set_punch_range (start, end, _("set punch range from region"));
6686 Editor::pitch_shift_region ()
6688 RegionSelection rs = get_regions_from_selection_and_entered ();
6690 RegionSelection audio_rs;
6691 for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
6692 if (dynamic_cast<AudioRegionView*> (*i)) {
6693 audio_rs.push_back (*i);
6697 if (audio_rs.empty()) {
6701 pitch_shift (audio_rs, 1.2);
6705 Editor::set_tempo_from_region ()
6707 RegionSelection rs = get_regions_from_selection_and_entered ();
6709 if (!_session || rs.empty()) {
6713 RegionView* rv = rs.front();
6715 define_one_bar (rv->region()->position(), rv->region()->last_sample() + 1);
6719 Editor::use_range_as_bar ()
6721 samplepos_t start, end;
6722 if (get_edit_op_range (start, end)) {
6723 define_one_bar (start, end);
6728 Editor::define_one_bar (samplepos_t start, samplepos_t end)
6730 samplepos_t length = end - start;
6732 const Meter& m (_session->tempo_map().meter_at_sample (start));
6734 /* length = 1 bar */
6736 /* We're going to deliver a constant tempo here,
6737 so we can use samples per beat to determine length.
6738 now we want samples per beat.
6739 we have samples per bar, and beats per bar, so ...
6742 /* XXXX METER MATH */
6744 double samples_per_beat = length / m.divisions_per_bar();
6746 /* beats per minute = */
6748 double beats_per_minute = (_session->sample_rate() * 60.0) / samples_per_beat;
6750 /* now decide whether to:
6752 (a) set global tempo
6753 (b) add a new tempo marker
6757 const TempoSection& t (_session->tempo_map().tempo_section_at_sample (start));
6759 bool do_global = false;
6761 if ((_session->tempo_map().n_tempos() == 1) && (_session->tempo_map().n_meters() == 1)) {
6763 /* only 1 tempo & 1 meter: ask if the user wants to set the tempo
6764 at the start, or create a new marker
6767 vector<string> options;
6768 options.push_back (_("Cancel"));
6769 options.push_back (_("Add new marker"));
6770 options.push_back (_("Set global tempo"));
6773 _("Define one bar"),
6774 _("Do you want to set the global tempo or add a new tempo marker?"),
6778 c.set_default_response (2);
6794 /* more than 1 tempo and/or meter section already, go ahead do the "usual":
6795 if the marker is at the region starter, change it, otherwise add
6800 begin_reversible_command (_("set tempo from region"));
6801 XMLNode& before (_session->tempo_map().get_state());
6804 _session->tempo_map().change_initial_tempo (beats_per_minute, t.note_type(), t.end_note_types_per_minute());
6805 } else if (t.sample() == start) {
6806 _session->tempo_map().change_existing_tempo_at (start, beats_per_minute, t.note_type(), t.end_note_types_per_minute());
6808 /* constant tempo */
6809 const Tempo tempo (beats_per_minute, t.note_type());
6810 _session->tempo_map().add_tempo (tempo, 0.0, start, AudioTime);
6813 XMLNode& after (_session->tempo_map().get_state());
6815 _session->add_command (new MementoCommand<TempoMap>(_session->tempo_map(), &before, &after));
6816 commit_reversible_command ();
6820 Editor::split_region_at_transients ()
6822 AnalysisFeatureList positions;
6824 RegionSelection rs = get_regions_from_selection_and_entered ();
6826 if (!_session || rs.empty()) {
6830 begin_reversible_command (_("split regions"));
6832 for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ) {
6834 RegionSelection::iterator tmp;
6839 boost::shared_ptr<AudioRegion> ar = boost::dynamic_pointer_cast<AudioRegion> ((*i)->region());
6842 ar->transients (positions);
6843 split_region_at_points ((*i)->region(), positions, true);
6850 commit_reversible_command ();
6855 Editor::split_region_at_points (boost::shared_ptr<Region> r, AnalysisFeatureList& positions, bool can_ferret, bool select_new)
6857 bool use_rhythmic_rodent = false;
6859 boost::shared_ptr<Playlist> pl = r->playlist();
6861 list<boost::shared_ptr<Region> > new_regions;
6867 if (positions.empty()) {
6871 if (positions.size() > 20 && can_ferret) {
6872 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);
6873 MessageDialog msg (msgstr,
6876 Gtk::BUTTONS_OK_CANCEL);
6879 msg.add_button (_("Call for the Ferret!"), RESPONSE_APPLY);
6880 msg.set_secondary_text (_("Press OK to continue with this split operation\nor ask the Ferret dialog to tune the analysis"));
6882 msg.set_secondary_text (_("Press OK to continue with this split operation"));
6885 msg.set_title (_("Excessive split?"));
6888 int response = msg.run();
6894 case RESPONSE_APPLY:
6895 use_rhythmic_rodent = true;
6902 if (use_rhythmic_rodent) {
6903 show_rhythm_ferret ();
6907 AnalysisFeatureList::const_iterator x;
6909 pl->clear_changes ();
6910 pl->clear_owned_changes ();
6912 x = positions.begin();
6914 if (x == positions.end()) {
6919 pl->remove_region (r);
6921 samplepos_t pos = 0;
6923 samplepos_t rstart = r->first_sample ();
6924 samplepos_t rend = r->last_sample ();
6926 while (x != positions.end()) {
6928 /* deal with positons that are out of scope of present region bounds */
6929 if (*x <= rstart || *x > rend) {
6934 /* file start = original start + how far we from the initial position ? */
6936 samplepos_t file_start = r->start() + pos;
6938 /* length = next position - current position */
6940 samplepos_t len = (*x) - pos - rstart;
6942 /* XXX we do we really want to allow even single-sample regions?
6943 * shouldn't we have some kind of lower limit on region size?
6952 if (RegionFactory::region_name (new_name, r->name())) {
6956 /* do NOT announce new regions 1 by one, just wait till they are all done */
6960 plist.add (ARDOUR::Properties::start, file_start);
6961 plist.add (ARDOUR::Properties::length, len);
6962 plist.add (ARDOUR::Properties::name, new_name);
6963 plist.add (ARDOUR::Properties::layer, 0);
6964 // TODO set transients_offset
6966 boost::shared_ptr<Region> nr = RegionFactory::create (r->sources(), plist, false);
6967 /* because we set annouce to false, manually add the new region to the
6970 RegionFactory::map_add (nr);
6972 pl->add_region (nr, rstart + pos);
6975 new_regions.push_front(nr);
6984 RegionFactory::region_name (new_name, r->name());
6986 /* Add the final region */
6989 plist.add (ARDOUR::Properties::start, r->start() + pos);
6990 plist.add (ARDOUR::Properties::length, r->last_sample() - (r->position() + pos) + 1);
6991 plist.add (ARDOUR::Properties::name, new_name);
6992 plist.add (ARDOUR::Properties::layer, 0);
6994 boost::shared_ptr<Region> nr = RegionFactory::create (r->sources(), plist, false);
6995 /* because we set annouce to false, manually add the new region to the
6998 RegionFactory::map_add (nr);
6999 pl->add_region (nr, r->position() + pos);
7002 new_regions.push_front(nr);
7007 /* We might have removed regions, which alters other regions' layering_index,
7008 so we need to do a recursive diff here.
7010 vector<Command*> cmds;
7012 _session->add_commands (cmds);
7014 _session->add_command (new StatefulDiffCommand (pl));
7018 for (list<boost::shared_ptr<Region> >::iterator i = new_regions.begin(); i != new_regions.end(); ++i){
7019 set_selected_regionview_from_region_list ((*i), Selection::Add);
7025 Editor::place_transient()
7031 RegionSelection rs = get_regions_from_selection_and_edit_point ();
7037 samplepos_t where = get_preferred_edit_position();
7039 begin_reversible_command (_("place transient"));
7041 for (RegionSelection::iterator r = rs.begin(); r != rs.end(); ++r) {
7042 (*r)->region()->add_transient(where);
7045 commit_reversible_command ();
7049 Editor::remove_transient(ArdourCanvas::Item* item)
7055 ArdourCanvas::Line* _line = reinterpret_cast<ArdourCanvas::Line*> (item);
7058 AudioRegionView* _arv = reinterpret_cast<AudioRegionView*> (item->get_data ("regionview"));
7059 _arv->remove_transient (*(float*) _line->get_data ("position"));
7063 Editor::snap_regions_to_grid ()
7065 list <boost::shared_ptr<Playlist > > used_playlists;
7067 RegionSelection rs = get_regions_from_selection_and_entered ();
7069 if (!_session || rs.empty()) {
7073 begin_reversible_command (_("snap regions to grid"));
7075 for (RegionSelection::iterator r = rs.begin(); r != rs.end(); ++r) {
7077 boost::shared_ptr<Playlist> pl = (*r)->region()->playlist();
7079 if (!pl->frozen()) {
7080 /* we haven't seen this playlist before */
7082 /* remember used playlists so we can thaw them later */
7083 used_playlists.push_back(pl);
7086 (*r)->region()->clear_changes ();
7088 MusicSample start ((*r)->region()->first_sample (), 0);
7089 snap_to (start, RoundNearest, SnapToGrid_Unscaled, true);
7090 (*r)->region()->set_position (start.sample, start.division);
7091 _session->add_command(new StatefulDiffCommand ((*r)->region()));
7094 while (used_playlists.size() > 0) {
7095 list <boost::shared_ptr<Playlist > >::iterator i = used_playlists.begin();
7097 used_playlists.pop_front();
7100 commit_reversible_command ();
7104 Editor::close_region_gaps ()
7106 list <boost::shared_ptr<Playlist > > used_playlists;
7108 RegionSelection rs = get_regions_from_selection_and_entered ();
7110 if (!_session || rs.empty()) {
7114 Dialog dialog (_("Close Region Gaps"));
7117 table.set_spacings (12);
7118 table.set_border_width (12);
7119 Label* l = manage (left_aligned_label (_("Crossfade length")));
7120 table.attach (*l, 0, 1, 0, 1);
7122 SpinButton spin_crossfade (1, 0);
7123 spin_crossfade.set_range (0, 15);
7124 spin_crossfade.set_increments (1, 1);
7125 spin_crossfade.set_value (5);
7126 table.attach (spin_crossfade, 1, 2, 0, 1);
7128 table.attach (*manage (new Label (_("ms"))), 2, 3, 0, 1);
7130 l = manage (left_aligned_label (_("Pull-back length")));
7131 table.attach (*l, 0, 1, 1, 2);
7133 SpinButton spin_pullback (1, 0);
7134 spin_pullback.set_range (0, 100);
7135 spin_pullback.set_increments (1, 1);
7136 spin_pullback.set_value(30);
7137 table.attach (spin_pullback, 1, 2, 1, 2);
7139 table.attach (*manage (new Label (_("ms"))), 2, 3, 1, 2);
7141 dialog.get_vbox()->pack_start (table);
7142 dialog.add_button (Stock::CANCEL, RESPONSE_CANCEL);
7143 dialog.add_button (_("Ok"), RESPONSE_ACCEPT);
7146 if (dialog.run () == RESPONSE_CANCEL) {
7150 samplepos_t crossfade_len = spin_crossfade.get_value();
7151 samplepos_t pull_back_samples = spin_pullback.get_value();
7153 crossfade_len = lrintf (crossfade_len * _session->sample_rate()/1000);
7154 pull_back_samples = lrintf (pull_back_samples * _session->sample_rate()/1000);
7156 /* Iterate over the region list and make adjacent regions overlap by crossfade_len_ms */
7158 begin_reversible_command (_("close region gaps"));
7161 boost::shared_ptr<Region> last_region;
7163 rs.sort_by_position_and_track();
7165 for (RegionSelection::iterator r = rs.begin(); r != rs.end(); ++r) {
7167 boost::shared_ptr<Playlist> pl = (*r)->region()->playlist();
7169 if (!pl->frozen()) {
7170 /* we haven't seen this playlist before */
7172 /* remember used playlists so we can thaw them later */
7173 used_playlists.push_back(pl);
7177 samplepos_t position = (*r)->region()->position();
7179 if (idx == 0 || position < last_region->position()){
7180 last_region = (*r)->region();
7185 (*r)->region()->clear_changes ();
7186 (*r)->region()->trim_front((position - pull_back_samples));
7188 last_region->clear_changes ();
7189 last_region->trim_end ((position - pull_back_samples + crossfade_len));
7191 _session->add_command (new StatefulDiffCommand ((*r)->region()));
7192 _session->add_command (new StatefulDiffCommand (last_region));
7194 last_region = (*r)->region();
7198 while (used_playlists.size() > 0) {
7199 list <boost::shared_ptr<Playlist > >::iterator i = used_playlists.begin();
7201 used_playlists.pop_front();
7204 commit_reversible_command ();
7208 Editor::tab_to_transient (bool forward)
7210 AnalysisFeatureList positions;
7212 RegionSelection rs = get_regions_from_selection_and_entered ();
7218 samplepos_t pos = _session->audible_sample ();
7220 if (!selection->tracks.empty()) {
7222 /* don't waste time searching for transients in duplicate playlists.
7225 TrackViewList ts = selection->tracks.filter_to_unique_playlists ();
7227 for (TrackViewList::iterator t = ts.begin(); t != ts.end(); ++t) {
7229 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (*t);
7232 boost::shared_ptr<Track> tr = rtv->track();
7234 boost::shared_ptr<Playlist> pl = tr->playlist ();
7236 samplepos_t result = pl->find_next_transient (pos, forward ? 1 : -1);
7239 positions.push_back (result);
7252 for (RegionSelection::iterator r = rs.begin(); r != rs.end(); ++r) {
7253 (*r)->region()->get_transients (positions);
7257 TransientDetector::cleanup_transients (positions, _session->sample_rate(), 3.0);
7260 AnalysisFeatureList::iterator x;
7262 for (x = positions.begin(); x != positions.end(); ++x) {
7268 if (x != positions.end ()) {
7269 _session->request_locate (*x);
7273 AnalysisFeatureList::reverse_iterator x;
7275 for (x = positions.rbegin(); x != positions.rend(); ++x) {
7281 if (x != positions.rend ()) {
7282 _session->request_locate (*x);
7288 Editor::playhead_forward_to_grid ()
7294 MusicSample pos (playhead_cursor->current_sample (), 0);
7296 if ( _grid_type == GridTypeNone) {
7297 if (pos.sample < max_samplepos - current_page_samples()*0.1) {
7298 pos.sample += current_page_samples()*0.1;
7299 _session->request_locate (pos.sample);
7301 _session->request_locate (0);
7305 if (pos.sample < max_samplepos - 1) {
7307 snap_to_internal (pos, RoundUpAlways, SnapToGrid_Scaled, true);
7308 _session->request_locate (pos.sample);
7313 /* keep PH visible in window */
7314 if (pos.sample > (_leftmost_sample + current_page_samples() *0.9)) {
7315 reset_x_origin (pos.sample - (current_page_samples()*0.9));
7321 Editor::playhead_backward_to_grid ()
7327 MusicSample pos (playhead_cursor->current_sample (), 0);
7329 if ( _grid_type == GridTypeNone) {
7330 if ( pos.sample > current_page_samples()*0.1 ) {
7331 pos.sample -= current_page_samples()*0.1;
7332 _session->request_locate (pos.sample);
7334 _session->request_locate (0);
7338 if (pos.sample > 2) {
7340 snap_to_internal (pos, RoundDownAlways, SnapToGrid_Scaled, true);
7343 //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...
7344 //also see: jump_backward_to_mark
7345 if (_session->transport_rolling()) {
7346 if ((playhead_cursor->current_sample() - pos.sample) < _session->sample_rate()/2) {
7347 snap_to_internal (pos, RoundDownAlways, SnapToGrid_Scaled, true);
7351 _session->request_locate (pos.sample, _session->transport_rolling());
7354 /* keep PH visible in window */
7355 if (pos.sample < (_leftmost_sample + current_page_samples() *0.1)) {
7356 reset_x_origin (pos.sample - (current_page_samples()*0.1));
7361 Editor::set_track_height (Height h)
7363 TrackSelection& ts (selection->tracks);
7365 for (TrackSelection::iterator x = ts.begin(); x != ts.end(); ++x) {
7366 (*x)->set_height_enum (h);
7371 Editor::toggle_tracks_active ()
7373 TrackSelection& ts (selection->tracks);
7375 bool target = false;
7381 for (TrackSelection::iterator x = ts.begin(); x != ts.end(); ++x) {
7382 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*>(*x);
7386 target = !rtv->_route->active();
7389 rtv->_route->set_active (target, this);
7395 Editor::remove_tracks ()
7397 /* this will delete GUI objects that may be the subject of an event
7398 handler in which this method is called. Defer actual deletion to the
7399 next idle callback, when all event handling is finished.
7401 Glib::signal_idle().connect (sigc::mem_fun (*this, &Editor::idle_remove_tracks));
7405 Editor::idle_remove_tracks ()
7407 Session::StateProtector sp (_session);
7409 return false; /* do not call again */
7413 Editor::_remove_tracks ()
7415 TrackSelection& ts (selection->tracks);
7421 vector<string> choices;
7426 const char* trackstr;
7429 vector<boost::shared_ptr<Route> > routes;
7430 vector<boost::shared_ptr<VCA> > vcas;
7431 bool special_bus = false;
7433 for (TrackSelection::iterator x = ts.begin(); x != ts.end(); ++x) {
7434 VCATimeAxisView* vtv = dynamic_cast<VCATimeAxisView*> (*x);
7436 vcas.push_back (vtv->vca());
7440 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (*x);
7444 if (rtv->is_track()) {
7449 routes.push_back (rtv->_route);
7451 if (rtv->route()->is_master() || rtv->route()->is_monitor()) {
7456 if (special_bus && !Config->get_allow_special_bus_removal()) {
7457 MessageDialog msg (_("That would be bad news ...."),
7461 msg.set_secondary_text (string_compose (_(
7462 "Removing the master or monitor bus is such a bad idea\n\
7463 that %1 is not going to allow it.\n\
7465 If you really want to do this sort of thing\n\
7466 edit your ardour.rc file to set the\n\
7467 \"allow-special-bus-removal\" option to be \"yes\""), PROGRAM_NAME));
7474 if (ntracks + nbusses + nvcas == 0) {
7480 trackstr = P_("track", "tracks", ntracks);
7481 busstr = P_("bus", "busses", nbusses);
7482 vcastr = P_("VCA", "VCAs", nvcas);
7484 if (ntracks > 0 && nbusses > 0 && nvcas > 0) {
7485 title = _("Remove various strips");
7486 prompt = string_compose (_("Do you really want to remove %1 %2, %3 %4 and %5 %6?"),
7487 ntracks, trackstr, nbusses, busstr, nvcas, vcastr);
7489 else if (ntracks > 0 && nbusses > 0) {
7490 title = string_compose (_("Remove %1 and %2"), trackstr, busstr);
7491 prompt = string_compose (_("Do you really want to remove %1 %2 and %3 %4?"),
7492 ntracks, trackstr, nbusses, busstr);
7494 else if (ntracks > 0 && nvcas > 0) {
7495 title = string_compose (_("Remove %1 and %2"), trackstr, vcastr);
7496 prompt = string_compose (_("Do you really want to remove %1 %2 and %3 %4?"),
7497 ntracks, trackstr, nvcas, vcastr);
7499 else if (nbusses > 0 && nvcas > 0) {
7500 title = string_compose (_("Remove %1 and %2"), busstr, vcastr);
7501 prompt = string_compose (_("Do you really want to remove %1 %2 and %3 %4?"),
7502 nbusses, busstr, nvcas, vcastr);
7504 else if (ntracks > 0) {
7505 title = string_compose (_("Remove %1"), trackstr);
7506 prompt = string_compose (_("Do you really want to remove %1 %2?"),
7509 else if (nbusses > 0) {
7510 title = string_compose (_("Remove %1"), busstr);
7511 prompt = string_compose (_("Do you really want to remove %1 %2?"),
7514 else if (nvcas > 0) {
7515 title = string_compose (_("Remove %1"), vcastr);
7516 prompt = string_compose (_("Do you really want to remove %1 %2?"),
7524 prompt += "\n" + string_compose ("(You may also lose the playlists associated with the %1)", trackstr) + "\n";
7527 prompt += "\n" + string(_("This action cannot be undone, and the session file will be overwritten!"));
7529 choices.push_back (_("No, do nothing."));
7530 if (ntracks + nbusses + nvcas > 1) {
7531 choices.push_back (_("Yes, remove them."));
7533 choices.push_back (_("Yes, remove it."));
7536 Choice prompter (title, prompt, choices);
7538 if (prompter.run () != 1) {
7542 if (current_mixer_strip && routes.size () > 1 && std::find (routes.begin(), routes.end(), current_mixer_strip->route()) != routes.end ()) {
7543 /* Route deletion calls Editor::timeaxisview_deleted() iteratively (for each deleted
7544 * route). If the deleted route is currently displayed in the Editor-Mixer (highly
7545 * likely because deletion requires selection) this will call
7546 * Editor::set_selected_mixer_strip () which is expensive (MixerStrip::set_route()).
7547 * It's likewise likely that the route that has just been displayed in the
7548 * Editor-Mixer will be next in line for deletion.
7550 * So simply switch to the master-bus (if present)
7552 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
7553 if ((*i)->stripable ()->is_master ()) {
7554 set_selected_mixer_strip (*(*i));
7561 PresentationInfo::ChangeSuspender cs;
7562 DisplaySuspender ds;
7564 boost::shared_ptr<RouteList> rl (new RouteList);
7565 for (vector<boost::shared_ptr<Route> >::iterator x = routes.begin(); x != routes.end(); ++x) {
7568 _session->remove_routes (rl);
7570 for (vector<boost::shared_ptr<VCA> >::iterator x = vcas.begin(); x != vcas.end(); ++x) {
7571 _session->vca_manager().remove_vca (*x);
7575 /* TrackSelection and RouteList leave scope,
7576 * destructors are called,
7577 * diskstream drops references, save_state is called (again for every track)
7582 Editor::do_insert_time ()
7584 if (selection->tracks.empty()) {
7585 MessageDialog msg (_("You must first select some tracks to Insert Time."),
7586 true, MESSAGE_INFO, BUTTONS_OK, true);
7587 msg.set_position (WIN_POS_MOUSE);
7592 if (Config->get_edit_mode() == Lock) {
7593 MessageDialog msg (_("You cannot insert time in Lock Edit mode."),
7594 true, MESSAGE_INFO, BUTTONS_OK, true);
7595 msg.set_position (WIN_POS_MOUSE);
7600 InsertRemoveTimeDialog d (*this);
7601 int response = d.run ();
7603 if (response != RESPONSE_OK) {
7607 if (d.distance() == 0) {
7614 d.intersected_region_action (),
7618 d.move_glued_markers(),
7619 d.move_locked_markers(),
7625 Editor::insert_time (
7626 samplepos_t pos, samplecnt_t samples, InsertTimeOption opt,
7627 bool all_playlists, bool ignore_music_glue, bool markers_too, bool glued_markers_too, bool locked_markers_too, bool tempo_too
7631 if (Config->get_edit_mode() == Lock) {
7634 bool in_command = false;
7636 TrackViewList ts = selection->tracks.filter_to_unique_playlists ();
7638 for (TrackViewList::iterator x = ts.begin(); x != ts.end(); ++x) {
7642 /* don't operate on any playlist more than once, which could
7643 * happen if "all playlists" is enabled, but there is more
7644 * than 1 track using playlists "from" a given track.
7647 set<boost::shared_ptr<Playlist> > pl;
7649 if (all_playlists) {
7650 RouteTimeAxisView* rtav = dynamic_cast<RouteTimeAxisView*> (*x);
7651 if (rtav && rtav->track ()) {
7652 vector<boost::shared_ptr<Playlist> > all = _session->playlists->playlists_for_track (rtav->track ());
7653 for (vector<boost::shared_ptr<Playlist> >::iterator p = all.begin(); p != all.end(); ++p) {
7658 if ((*x)->playlist ()) {
7659 pl.insert ((*x)->playlist ());
7663 for (set<boost::shared_ptr<Playlist> >::iterator i = pl.begin(); i != pl.end(); ++i) {
7665 (*i)->clear_changes ();
7666 (*i)->clear_owned_changes ();
7669 begin_reversible_command (_("insert time"));
7673 if (opt == SplitIntersected) {
7674 /* non musical split */
7675 (*i)->split (MusicSample (pos, 0));
7678 (*i)->shift (pos, samples, (opt == MoveIntersected), ignore_music_glue);
7680 vector<Command*> cmds;
7682 _session->add_commands (cmds);
7684 _session->add_command (new StatefulDiffCommand (*i));
7688 RouteTimeAxisView* rtav = dynamic_cast<RouteTimeAxisView*> (*x);
7691 begin_reversible_command (_("insert time"));
7694 rtav->route ()->shift (pos, samples);
7701 const int32_t divisions = get_grid_music_divisions (0);
7702 XMLNode& before (_session->locations()->get_state());
7703 Locations::LocationList copy (_session->locations()->list());
7705 for (Locations::LocationList::iterator i = copy.begin(); i != copy.end(); ++i) {
7707 Locations::LocationList::const_iterator tmp;
7709 if ((*i)->position_lock_style() == AudioTime || glued_markers_too) {
7710 bool const was_locked = (*i)->locked ();
7711 if (locked_markers_too) {
7715 if ((*i)->start() >= pos) {
7716 // move end first, in case we're moving by more than the length of the range
7717 if (!(*i)->is_mark()) {
7718 (*i)->set_end ((*i)->end() + samples, false, true, divisions);
7720 (*i)->set_start ((*i)->start() + samples, false, true, divisions);
7732 begin_reversible_command (_("insert time"));
7735 XMLNode& after (_session->locations()->get_state());
7736 _session->add_command (new MementoCommand<Locations>(*_session->locations(), &before, &after));
7742 begin_reversible_command (_("insert time"));
7745 XMLNode& before (_session->tempo_map().get_state());
7746 _session->tempo_map().insert_time (pos, samples);
7747 XMLNode& after (_session->tempo_map().get_state());
7748 _session->add_command (new MementoCommand<TempoMap>(_session->tempo_map(), &before, &after));
7752 commit_reversible_command ();
7757 Editor::do_remove_time ()
7759 if (selection->tracks.empty()) {
7760 MessageDialog msg (_("You must first select some tracks to Remove Time."),
7761 true, MESSAGE_INFO, BUTTONS_OK, true);
7762 msg.set_position (WIN_POS_MOUSE);
7767 if (Config->get_edit_mode() == Lock) {
7768 MessageDialog msg (_("You cannot remove time in Lock Edit mode."),
7769 true, MESSAGE_INFO, BUTTONS_OK, true);
7770 msg.set_position (WIN_POS_MOUSE);
7775 InsertRemoveTimeDialog d (*this, true);
7777 int response = d.run ();
7779 if (response != RESPONSE_OK) {
7783 samplecnt_t distance = d.distance();
7785 if (distance == 0) {
7795 d.move_glued_markers(),
7796 d.move_locked_markers(),
7802 Editor::remove_time (samplepos_t pos, samplecnt_t samples, InsertTimeOption opt,
7803 bool ignore_music_glue, bool markers_too, bool glued_markers_too, bool locked_markers_too, bool tempo_too)
7805 if (Config->get_edit_mode() == Lock) {
7806 error << (_("Cannot insert or delete time when in Lock edit.")) << endmsg;
7809 bool in_command = false;
7811 for (TrackSelection::iterator x = selection->tracks.begin(); x != selection->tracks.end(); ++x) {
7813 boost::shared_ptr<Playlist> pl = (*x)->playlist();
7817 XMLNode &before = pl->get_state();
7820 begin_reversible_command (_("remove time"));
7824 std::list<AudioRange> rl;
7825 AudioRange ar(pos, pos+samples, 0);
7828 pl->shift (pos, -samples, true, ignore_music_glue);
7830 XMLNode &after = pl->get_state();
7832 _session->add_command (new MementoCommand<Playlist> (*pl, &before, &after));
7836 RouteTimeAxisView* rtav = dynamic_cast<RouteTimeAxisView*> (*x);
7839 begin_reversible_command (_("remove time"));
7842 rtav->route ()->shift (pos, -samples);
7846 const int32_t divisions = get_grid_music_divisions (0);
7847 std::list<Location*> loc_kill_list;
7852 XMLNode& before (_session->locations()->get_state());
7853 Locations::LocationList copy (_session->locations()->list());
7855 for (Locations::LocationList::iterator i = copy.begin(); i != copy.end(); ++i) {
7856 if ((*i)->position_lock_style() == AudioTime || glued_markers_too) {
7858 bool const was_locked = (*i)->locked ();
7859 if (locked_markers_too) {
7863 if (!(*i)->is_mark()) { // it's a range; have to handle both start and end
7864 if ((*i)->end() >= pos
7865 && (*i)->end() < pos+samples
7866 && (*i)->start() >= pos
7867 && (*i)->end() < pos+samples) { // range is completely enclosed; kill it
7869 loc_kill_list.push_back(*i);
7870 } else { // only start or end is included, try to do the right thing
7871 // move start before moving end, to avoid trying to move the end to before the start
7872 // if we're removing more time than the length of the range
7873 if ((*i)->start() >= pos && (*i)->start() < pos+samples) {
7874 // start is within cut
7875 (*i)->set_start (pos, false, true,divisions); // bring the start marker to the beginning of the cut
7877 } else if ((*i)->start() >= pos+samples) {
7878 // start (and thus entire range) lies beyond end of cut
7879 (*i)->set_start ((*i)->start() - samples, false, true, divisions); // slip the start marker back
7882 if ((*i)->end() >= pos && (*i)->end() < pos+samples) {
7883 // end is inside cut
7884 (*i)->set_end (pos, false, true, divisions); // bring the end to the cut
7886 } else if ((*i)->end() >= pos+samples) {
7887 // end is beyond end of cut
7888 (*i)->set_end ((*i)->end() - samples, false, true, divisions); // slip the end marker back
7893 } else if ((*i)->start() >= pos && (*i)->start() < pos+samples) {
7894 loc_kill_list.push_back(*i);
7896 } else if ((*i)->start() >= pos) {
7897 (*i)->set_start ((*i)->start() -samples, false, true, divisions);
7907 for (list<Location*>::iterator i = loc_kill_list.begin(); i != loc_kill_list.end(); ++i) {
7908 _session->locations()->remove (*i);
7913 begin_reversible_command (_("remove time"));
7916 XMLNode& after (_session->locations()->get_state());
7917 _session->add_command (new MementoCommand<Locations>(*_session->locations(), &before, &after));
7922 XMLNode& before (_session->tempo_map().get_state());
7924 if (_session->tempo_map().remove_time (pos, samples)) {
7926 begin_reversible_command (_("remove time"));
7929 XMLNode& after (_session->tempo_map().get_state());
7930 _session->add_command (new MementoCommand<TempoMap>(_session->tempo_map(), &before, &after));
7935 commit_reversible_command ();
7940 Editor::fit_selection ()
7942 if (!selection->tracks.empty()) {
7943 fit_tracks (selection->tracks);
7947 /* no selected tracks - use tracks with selected regions */
7949 if (!selection->regions.empty()) {
7950 for (RegionSelection::iterator r = selection->regions.begin(); r != selection->regions.end(); ++r) {
7951 tvl.push_back (&(*r)->get_time_axis_view ());
7957 } else if (internal_editing()) {
7958 /* no selected tracks, or regions, but in internal edit mode, so follow the mouse and use
7961 if (entered_track) {
7962 tvl.push_back (entered_track);
7970 Editor::fit_tracks (TrackViewList & tracks)
7972 if (tracks.empty()) {
7976 uint32_t child_heights = 0;
7977 int visible_tracks = 0;
7979 for (TrackSelection::iterator t = tracks.begin(); t != tracks.end(); ++t) {
7981 if (!(*t)->marked_for_display()) {
7985 child_heights += (*t)->effective_height() - (*t)->current_height();
7989 /* compute the per-track height from:
7991 * total canvas visible height
7992 * - height that will be taken by visible children of selected tracks
7993 * - height of the ruler/hscroll area
7995 uint32_t h = (uint32_t) floor ((trackviews_height() - child_heights) / visible_tracks);
7996 double first_y_pos = DBL_MAX;
7998 if (h < TimeAxisView::preset_height (HeightSmall)) {
7999 MessageDialog msg (_("There are too many tracks to fit in the current window"));
8000 /* too small to be displayed */
8004 undo_visual_stack.push_back (current_visual_state (true));
8005 PBD::Unwinder<bool> nsv (no_save_visual, true);
8007 /* build a list of all tracks, including children */
8010 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
8012 TimeAxisView::Children c = (*i)->get_child_list ();
8013 for (TimeAxisView::Children::iterator j = c.begin(); j != c.end(); ++j) {
8014 all.push_back (j->get());
8019 // find selection range.
8020 // if someone knows how to user TrackViewList::iterator for this
8022 int selected_top = -1;
8023 int selected_bottom = -1;
8025 for (TrackViewList::iterator t = all.begin(); t != all.end(); ++t, ++i) {
8026 if ((*t)->marked_for_display ()) {
8027 if (tracks.contains(*t)) {
8028 if (selected_top == -1) {
8031 selected_bottom = i;
8037 for (TrackViewList::iterator t = all.begin(); t != all.end(); ++t, ++i) {
8038 if ((*t)->marked_for_display ()) {
8039 if (tracks.contains(*t)) {
8040 (*t)->set_height (h);
8041 first_y_pos = std::min ((*t)->y_position (), first_y_pos);
8043 if (i > selected_top && i < selected_bottom) {
8044 hide_track_in_display (*t);
8051 set the controls_layout height now, because waiting for its size
8052 request signal handler will cause the vertical adjustment setting to fail
8055 controls_layout.property_height () = _full_canvas_height;
8056 vertical_adjustment.set_value (first_y_pos);
8058 redo_visual_stack.push_back (current_visual_state (true));
8060 visible_tracks_selector.set_text (_("Sel"));
8064 Editor::save_visual_state (uint32_t n)
8066 while (visual_states.size() <= n) {
8067 visual_states.push_back (0);
8070 if (visual_states[n] != 0) {
8071 delete visual_states[n];
8074 visual_states[n] = current_visual_state (true);
8079 Editor::goto_visual_state (uint32_t n)
8081 if (visual_states.size() <= n) {
8085 if (visual_states[n] == 0) {
8089 use_visual_state (*visual_states[n]);
8093 Editor::start_visual_state_op (uint32_t n)
8095 save_visual_state (n);
8097 PopUp* pup = new PopUp (WIN_POS_MOUSE, 1000, true);
8099 snprintf (buf, sizeof (buf), _("Saved view %u"), n+1);
8100 pup->set_text (buf);
8105 Editor::cancel_visual_state_op (uint32_t n)
8107 goto_visual_state (n);
8111 Editor::toggle_region_mute ()
8113 if (_ignore_region_action) {
8117 RegionSelection rs = get_regions_from_selection_and_entered ();
8123 if (rs.size() > 1) {
8124 begin_reversible_command (_("mute regions"));
8126 begin_reversible_command (_("mute region"));
8129 for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
8131 (*i)->region()->playlist()->clear_changes ();
8132 (*i)->region()->set_muted (!(*i)->region()->muted ());
8133 _session->add_command (new StatefulDiffCommand ((*i)->region()));
8137 commit_reversible_command ();
8141 Editor::combine_regions ()
8143 /* foreach track with selected regions, take all selected regions
8144 and join them into a new region containing the subregions (as a
8148 typedef set<RouteTimeAxisView*> RTVS;
8151 if (selection->regions.empty()) {
8155 for (RegionSelection::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
8156 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*>(&(*i)->get_time_axis_view());
8159 tracks.insert (rtv);
8163 begin_reversible_command (_("combine regions"));
8165 vector<RegionView*> new_selection;
8167 for (RTVS::iterator i = tracks.begin(); i != tracks.end(); ++i) {
8170 if ((rv = (*i)->combine_regions ()) != 0) {
8171 new_selection.push_back (rv);
8175 selection->clear_regions ();
8176 for (vector<RegionView*>::iterator i = new_selection.begin(); i != new_selection.end(); ++i) {
8177 selection->add (*i);
8180 commit_reversible_command ();
8184 Editor::uncombine_regions ()
8186 typedef set<RouteTimeAxisView*> RTVS;
8189 if (selection->regions.empty()) {
8193 for (RegionSelection::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
8194 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*>(&(*i)->get_time_axis_view());
8197 tracks.insert (rtv);
8201 begin_reversible_command (_("uncombine regions"));
8203 for (RTVS::iterator i = tracks.begin(); i != tracks.end(); ++i) {
8204 (*i)->uncombine_regions ();
8207 commit_reversible_command ();
8211 Editor::toggle_midi_input_active (bool flip_others)
8214 boost::shared_ptr<RouteList> rl (new RouteList);
8216 for (TrackSelection::iterator i = selection->tracks.begin(); i != selection->tracks.end(); ++i) {
8217 RouteTimeAxisView *rtav = dynamic_cast<RouteTimeAxisView *>(*i);
8223 boost::shared_ptr<MidiTrack> mt = rtav->midi_track();
8226 rl->push_back (rtav->route());
8227 onoff = !mt->input_active();
8231 _session->set_exclusive_input_active (rl, onoff, flip_others);
8234 static bool ok_fine (GdkEventAny*) { return true; }
8240 lock_dialog = new Gtk::Dialog (string_compose (_("%1: Locked"), PROGRAM_NAME), true);
8242 Gtk::Image* padlock = manage (new Gtk::Image (ARDOUR_UI_UTILS::get_icon ("padlock_closed")));
8243 lock_dialog->get_vbox()->pack_start (*padlock);
8244 lock_dialog->signal_delete_event ().connect (sigc::ptr_fun (ok_fine));
8246 ArdourButton* b = manage (new ArdourButton);
8247 b->set_name ("lock button");
8248 b->set_text (_("Click to unlock"));
8249 b->signal_clicked.connect (sigc::mem_fun (*this, &Editor::unlock));
8250 lock_dialog->get_vbox()->pack_start (*b);
8252 lock_dialog->get_vbox()->show_all ();
8253 lock_dialog->set_size_request (200, 200);
8256 delete _main_menu_disabler;
8257 _main_menu_disabler = new MainMenuDisabler;
8259 lock_dialog->present ();
8261 lock_dialog->get_window()->set_decorations (Gdk::WMDecoration (0));
8267 lock_dialog->hide ();
8269 delete _main_menu_disabler;
8270 _main_menu_disabler = 0;
8272 if (UIConfiguration::instance().get_lock_gui_after_seconds()) {
8273 start_lock_event_timing ();
8278 Editor::bring_in_callback (Gtk::Label* label, uint32_t n, uint32_t total, string name)
8280 Gtkmm2ext::UI::instance()->call_slot (invalidator (*this), boost::bind (&Editor::update_bring_in_message, this, label, n, total, name));
8284 Editor::update_bring_in_message (Gtk::Label* label, uint32_t n, uint32_t total, string name)
8286 Timers::TimerSuspender t;
8287 label->set_text (string_compose ("Copying %1, %2 of %3", name, n, total));
8288 Gtkmm2ext::UI::instance()->flush_pending (1);
8292 Editor::bring_all_sources_into_session ()
8299 ArdourDialog w (_("Moving embedded files into session folder"));
8300 w.get_vbox()->pack_start (msg);
8303 /* flush all pending GUI events because we're about to start copying
8307 Timers::TimerSuspender t;
8308 Gtkmm2ext::UI::instance()->flush_pending (3);
8312 _session->bring_all_sources_into_session (boost::bind (&Editor::bring_in_callback, this, &msg, _1, _2, _3));