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 /* if no snap boundaries were set, just bail out here with an empty region cache */
738 if ( interesting_points.empty() ) {
739 _region_boundary_cache_dirty = false;
743 TimeAxisView *ontrack = 0;
746 tlist = track_views.filter_to_unique_playlists ();
748 if (maybe_first_sample) {
749 TrackViewList::const_iterator i;
750 for (i = tlist.begin(); i != tlist.end(); ++i) {
751 boost::shared_ptr<Playlist> pl = (*i)->playlist();
752 if (pl && pl->count_regions_at (0)) {
753 region_boundary_cache.push_back (0);
759 //allow regions to snap to the video start (if any) as if it were a "region"
760 if (ARDOUR_UI::instance()->video_timeline) {
761 region_boundary_cache.push_back (ARDOUR_UI::instance()->video_timeline->get_video_start_offset());
764 std::pair<samplepos_t, samplepos_t> ext = session_gui_extents (false);
765 samplepos_t session_end = ext.second;
767 while (pos < session_end && !at_end) {
770 samplepos_t lpos = session_end;
772 for (vector<RegionPoint>::iterator p = interesting_points.begin(); p != interesting_points.end(); ++p) {
774 if ((r = find_next_region (pos, *p, 1, tlist, &ontrack)) == 0) {
775 if (*p == interesting_points.back()) {
778 /* move to next point type */
784 rpos = r->first_sample();
788 rpos = r->last_sample();
792 rpos = r->sync_position ();
803 /* prevent duplicates, but we don't use set<> because we want to be able
807 vector<samplepos_t>::iterator ri;
809 for (ri = region_boundary_cache.begin(); ri != region_boundary_cache.end(); ++ri) {
815 if (ri == region_boundary_cache.end()) {
816 region_boundary_cache.push_back (rpos);
823 /* finally sort to be sure that the order is correct */
825 sort (region_boundary_cache.begin(), region_boundary_cache.end());
827 _region_boundary_cache_dirty = false;
830 boost::shared_ptr<Region>
831 Editor::find_next_region (samplepos_t sample, RegionPoint point, int32_t dir, TrackViewList& tracks, TimeAxisView **ontrack)
833 TrackViewList::iterator i;
834 samplepos_t closest = max_samplepos;
835 boost::shared_ptr<Region> ret;
836 samplepos_t rpos = 0;
838 samplepos_t track_sample;
840 for (i = tracks.begin(); i != tracks.end(); ++i) {
842 samplecnt_t distance;
843 boost::shared_ptr<Region> r;
845 track_sample = sample;
847 if ((r = (*i)->find_next_region (track_sample, point, dir)) == 0) {
853 rpos = r->first_sample ();
857 rpos = r->last_sample ();
861 rpos = r->sync_position ();
866 distance = rpos - sample;
868 distance = sample - rpos;
871 if (distance < closest) {
883 Editor::find_next_region_boundary (samplepos_t pos, int32_t dir, const TrackViewList& tracks)
885 samplecnt_t distance = max_samplepos;
886 samplepos_t current_nearest = -1;
888 for (TrackViewList::const_iterator i = tracks.begin(); i != tracks.end(); ++i) {
889 samplepos_t contender;
892 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (*i);
898 if ((contender = rtv->find_next_region_boundary (pos, dir)) < 0) {
902 d = ::llabs (pos - contender);
905 current_nearest = contender;
910 return current_nearest;
914 Editor::get_region_boundary (samplepos_t pos, int32_t dir, bool with_selection, bool only_onscreen)
919 if (with_selection && Config->get_region_boundaries_from_selected_tracks()) {
921 if (!selection->tracks.empty()) {
923 target = find_next_region_boundary (pos, dir, selection->tracks);
927 if (only_onscreen || Config->get_region_boundaries_from_onscreen_tracks()) {
928 get_onscreen_tracks (tvl);
929 target = find_next_region_boundary (pos, dir, tvl);
931 target = find_next_region_boundary (pos, dir, track_views);
937 if (only_onscreen || Config->get_region_boundaries_from_onscreen_tracks()) {
938 get_onscreen_tracks (tvl);
939 target = find_next_region_boundary (pos, dir, tvl);
941 target = find_next_region_boundary (pos, dir, track_views);
949 Editor::cursor_to_region_boundary (bool with_selection, int32_t dir)
951 samplepos_t pos = playhead_cursor->current_sample ();
958 // so we don't find the current region again..
959 if (dir > 0 || pos > 0) {
963 if ((target = get_region_boundary (pos, dir, with_selection, false)) < 0) {
967 _session->request_locate (target);
971 Editor::cursor_to_next_region_boundary (bool with_selection)
973 cursor_to_region_boundary (with_selection, 1);
977 Editor::cursor_to_previous_region_boundary (bool with_selection)
979 cursor_to_region_boundary (with_selection, -1);
983 Editor::cursor_to_region_point (EditorCursor* cursor, RegionPoint point, int32_t dir)
985 boost::shared_ptr<Region> r;
986 samplepos_t pos = cursor->current_sample ();
992 TimeAxisView *ontrack = 0;
994 // so we don't find the current region again..
998 if (!selection->tracks.empty()) {
1000 r = find_next_region (pos, point, dir, selection->tracks, &ontrack);
1002 } else if (clicked_axisview) {
1005 t.push_back (clicked_axisview);
1007 r = find_next_region (pos, point, dir, t, &ontrack);
1011 r = find_next_region (pos, point, dir, track_views, &ontrack);
1020 pos = r->first_sample ();
1024 pos = r->last_sample ();
1028 pos = r->sync_position ();
1032 if (cursor == playhead_cursor) {
1033 _session->request_locate (pos);
1035 cursor->set_position (pos);
1040 Editor::cursor_to_next_region_point (EditorCursor* cursor, RegionPoint point)
1042 cursor_to_region_point (cursor, point, 1);
1046 Editor::cursor_to_previous_region_point (EditorCursor* cursor, RegionPoint point)
1048 cursor_to_region_point (cursor, point, -1);
1052 Editor::cursor_to_selection_start (EditorCursor *cursor)
1054 samplepos_t pos = 0;
1056 switch (mouse_mode) {
1058 if (!selection->regions.empty()) {
1059 pos = selection->regions.start();
1064 if (!selection->time.empty()) {
1065 pos = selection->time.start ();
1073 if (cursor == playhead_cursor) {
1074 _session->request_locate (pos);
1076 cursor->set_position (pos);
1081 Editor::cursor_to_selection_end (EditorCursor *cursor)
1083 samplepos_t pos = 0;
1085 switch (mouse_mode) {
1087 if (!selection->regions.empty()) {
1088 pos = selection->regions.end_sample();
1093 if (!selection->time.empty()) {
1094 pos = selection->time.end_sample ();
1102 if (cursor == playhead_cursor) {
1103 _session->request_locate (pos);
1105 cursor->set_position (pos);
1110 Editor::selected_marker_to_region_boundary (bool with_selection, int32_t dir)
1120 if (selection->markers.empty()) {
1124 if (!mouse_sample (mouse, ignored)) {
1128 add_location_mark (mouse);
1131 if ((loc = find_location_from_marker (selection->markers.front(), ignored)) == 0) {
1135 samplepos_t pos = loc->start();
1137 // so we don't find the current region again..
1138 if (dir > 0 || pos > 0) {
1142 if ((target = get_region_boundary (pos, dir, with_selection, false)) < 0) {
1146 loc->move_to (target, 0);
1150 Editor::selected_marker_to_next_region_boundary (bool with_selection)
1152 selected_marker_to_region_boundary (with_selection, 1);
1156 Editor::selected_marker_to_previous_region_boundary (bool with_selection)
1158 selected_marker_to_region_boundary (with_selection, -1);
1162 Editor::selected_marker_to_region_point (RegionPoint point, int32_t dir)
1164 boost::shared_ptr<Region> r;
1169 if (!_session || selection->markers.empty()) {
1173 if ((loc = find_location_from_marker (selection->markers.front(), ignored)) == 0) {
1177 TimeAxisView *ontrack = 0;
1181 // so we don't find the current region again..
1185 if (!selection->tracks.empty()) {
1187 r = find_next_region (pos, point, dir, selection->tracks, &ontrack);
1191 r = find_next_region (pos, point, dir, track_views, &ontrack);
1200 pos = r->first_sample ();
1204 pos = r->last_sample ();
1208 pos = r->adjust_to_sync (r->first_sample());
1212 loc->move_to (pos, 0);
1216 Editor::selected_marker_to_next_region_point (RegionPoint point)
1218 selected_marker_to_region_point (point, 1);
1222 Editor::selected_marker_to_previous_region_point (RegionPoint point)
1224 selected_marker_to_region_point (point, -1);
1228 Editor::selected_marker_to_selection_start ()
1230 samplepos_t pos = 0;
1234 if (!_session || selection->markers.empty()) {
1238 if ((loc = find_location_from_marker (selection->markers.front(), ignored)) == 0) {
1242 switch (mouse_mode) {
1244 if (!selection->regions.empty()) {
1245 pos = selection->regions.start();
1250 if (!selection->time.empty()) {
1251 pos = selection->time.start ();
1259 loc->move_to (pos, 0);
1263 Editor::selected_marker_to_selection_end ()
1265 samplepos_t pos = 0;
1269 if (!_session || selection->markers.empty()) {
1273 if ((loc = find_location_from_marker (selection->markers.front(), ignored)) == 0) {
1277 switch (mouse_mode) {
1279 if (!selection->regions.empty()) {
1280 pos = selection->regions.end_sample();
1285 if (!selection->time.empty()) {
1286 pos = selection->time.end_sample ();
1294 loc->move_to (pos, 0);
1298 Editor::scroll_playhead (bool forward)
1300 samplepos_t pos = playhead_cursor->current_sample ();
1301 samplecnt_t delta = (samplecnt_t) floor (current_page_samples() / 0.8);
1304 if (pos == max_samplepos) {
1308 if (pos < max_samplepos - delta) {
1311 pos = max_samplepos;
1327 _session->request_locate (pos);
1331 Editor::cursor_align (bool playhead_to_edit)
1337 if (playhead_to_edit) {
1339 if (selection->markers.empty()) {
1343 _session->request_locate (selection->markers.front()->position(), _session->transport_rolling());
1346 const int32_t divisions = get_grid_music_divisions (0);
1347 /* move selected markers to playhead */
1349 for (MarkerSelection::iterator i = selection->markers.begin(); i != selection->markers.end(); ++i) {
1352 Location* loc = find_location_from_marker (*i, ignored);
1354 if (loc->is_mark()) {
1355 loc->set_start (playhead_cursor->current_sample (), false, true, divisions);
1357 loc->set (playhead_cursor->current_sample (),
1358 playhead_cursor->current_sample () + loc->length(), true, divisions);
1365 Editor::scroll_backward (float pages)
1367 samplepos_t const one_page = (samplepos_t) rint (_visible_canvas_width * samples_per_pixel);
1368 samplepos_t const cnt = (samplepos_t) floor (pages * one_page);
1371 if (_leftmost_sample < cnt) {
1374 sample = _leftmost_sample - cnt;
1377 reset_x_origin (sample);
1381 Editor::scroll_forward (float pages)
1383 samplepos_t const one_page = (samplepos_t) rint (_visible_canvas_width * samples_per_pixel);
1384 samplepos_t const cnt = (samplepos_t) floor (pages * one_page);
1387 if (max_samplepos - cnt < _leftmost_sample) {
1388 sample = max_samplepos - cnt;
1390 sample = _leftmost_sample + cnt;
1393 reset_x_origin (sample);
1397 Editor::scroll_tracks_down ()
1399 double vert_value = vertical_adjustment.get_value() + vertical_adjustment.get_page_size();
1400 if (vert_value > vertical_adjustment.get_upper() - _visible_canvas_height) {
1401 vert_value = vertical_adjustment.get_upper() - _visible_canvas_height;
1404 vertical_adjustment.set_value (vert_value);
1408 Editor::scroll_tracks_up ()
1410 vertical_adjustment.set_value (vertical_adjustment.get_value() - vertical_adjustment.get_page_size());
1414 Editor::scroll_tracks_down_line ()
1416 double vert_value = vertical_adjustment.get_value() + 60;
1418 if (vert_value > vertical_adjustment.get_upper() - _visible_canvas_height) {
1419 vert_value = vertical_adjustment.get_upper() - _visible_canvas_height;
1422 vertical_adjustment.set_value (vert_value);
1426 Editor::scroll_tracks_up_line ()
1428 reset_y_origin (vertical_adjustment.get_value() - 60);
1432 Editor::select_topmost_track ()
1434 const double top_of_trackviews = vertical_adjustment.get_value();
1435 for (TrackViewList::iterator t = track_views.begin(); t != track_views.end(); ++t) {
1436 if ((*t)->hidden()) {
1439 std::pair<TimeAxisView*,double> res = (*t)->covers_y_position (top_of_trackviews);
1441 selection->set (*t);
1448 Editor::scroll_down_one_track (bool skip_child_views)
1450 TrackViewList::reverse_iterator next = track_views.rend();
1451 const double top_of_trackviews = vertical_adjustment.get_value();
1453 for (TrackViewList::reverse_iterator t = track_views.rbegin(); t != track_views.rend(); ++t) {
1454 if ((*t)->hidden()) {
1458 /* If this is the upper-most visible trackview, we want to display
1459 * the one above it (next)
1461 * Note that covers_y_position() is recursive and includes child views
1463 std::pair<TimeAxisView*,double> res = (*t)->covers_y_position (top_of_trackviews);
1466 if (skip_child_views) {
1469 /* automation lane (one level, non-recursive)
1471 * - if no automation lane exists -> move to next tack
1472 * - if the first (here: bottom-most) matches -> move to next tack
1473 * - if no y-axis match is found -> the current track is at the top
1474 * -> move to last (here: top-most) automation lane
1476 TimeAxisView::Children kids = (*t)->get_child_list();
1477 TimeAxisView::Children::reverse_iterator nkid = kids.rend();
1479 for (TimeAxisView::Children::reverse_iterator ci = kids.rbegin(); ci != kids.rend(); ++ci) {
1480 if ((*ci)->hidden()) {
1484 std::pair<TimeAxisView*,double> dev;
1485 dev = (*ci)->covers_y_position (top_of_trackviews);
1487 /* some automation lane is currently at the top */
1488 if (ci == kids.rbegin()) {
1489 /* first (bottom-most) autmation lane is at the top.
1490 * -> move to next track
1499 if (nkid != kids.rend()) {
1500 ensure_time_axis_view_is_visible (**nkid, true);
1508 /* move to the track below the first one that covers the */
1510 if (next != track_views.rend()) {
1511 ensure_time_axis_view_is_visible (**next, true);
1519 Editor::scroll_up_one_track (bool skip_child_views)
1521 TrackViewList::iterator prev = track_views.end();
1522 double top_of_trackviews = vertical_adjustment.get_value ();
1524 for (TrackViewList::iterator t = track_views.begin(); t != track_views.end(); ++t) {
1526 if ((*t)->hidden()) {
1530 /* find the trackview at the top of the trackview group
1532 * Note that covers_y_position() is recursive and includes child views
1534 std::pair<TimeAxisView*,double> res = (*t)->covers_y_position (top_of_trackviews);
1537 if (skip_child_views) {
1540 /* automation lane (one level, non-recursive)
1542 * - if no automation lane exists -> move to prev tack
1543 * - if no y-axis match is found -> the current track is at the top -> move to prev track
1544 * (actually last automation lane of previous track, see below)
1545 * - if first (top-most) lane is at the top -> move to this track
1546 * - else move up one lane
1548 TimeAxisView::Children kids = (*t)->get_child_list();
1549 TimeAxisView::Children::iterator pkid = kids.end();
1551 for (TimeAxisView::Children::iterator ci = kids.begin(); ci != kids.end(); ++ci) {
1552 if ((*ci)->hidden()) {
1556 std::pair<TimeAxisView*,double> dev;
1557 dev = (*ci)->covers_y_position (top_of_trackviews);
1559 /* some automation lane is currently at the top */
1560 if (ci == kids.begin()) {
1561 /* first (top-most) autmation lane is at the top.
1562 * jump directly to this track's top
1564 ensure_time_axis_view_is_visible (**t, true);
1567 else if (pkid != kids.end()) {
1568 /* some other automation lane is at the top.
1569 * move up to prev automation lane.
1571 ensure_time_axis_view_is_visible (**pkid, true);
1574 assert(0); // not reached
1585 if (prev != track_views.end()) {
1586 // move to bottom-most automation-lane of the previous track
1587 TimeAxisView::Children kids = (*prev)->get_child_list();
1588 TimeAxisView::Children::reverse_iterator pkid = kids.rend();
1589 if (!skip_child_views) {
1590 // find the last visible lane
1591 for (TimeAxisView::Children::reverse_iterator ci = kids.rbegin(); ci != kids.rend(); ++ci) {
1592 if (!(*ci)->hidden()) {
1598 if (pkid != kids.rend()) {
1599 ensure_time_axis_view_is_visible (**pkid, true);
1601 ensure_time_axis_view_is_visible (**prev, true);
1610 Editor::scroll_left_step ()
1612 samplepos_t xdelta = (current_page_samples() / 8);
1614 if (_leftmost_sample > xdelta) {
1615 reset_x_origin (_leftmost_sample - xdelta);
1623 Editor::scroll_right_step ()
1625 samplepos_t xdelta = (current_page_samples() / 8);
1627 if (max_samplepos - xdelta > _leftmost_sample) {
1628 reset_x_origin (_leftmost_sample + xdelta);
1630 reset_x_origin (max_samplepos - current_page_samples());
1635 Editor::scroll_left_half_page ()
1637 samplepos_t xdelta = (current_page_samples() / 2);
1638 if (_leftmost_sample > xdelta) {
1639 reset_x_origin (_leftmost_sample - xdelta);
1646 Editor::scroll_right_half_page ()
1648 samplepos_t xdelta = (current_page_samples() / 2);
1649 if (max_samplepos - xdelta > _leftmost_sample) {
1650 reset_x_origin (_leftmost_sample + xdelta);
1652 reset_x_origin (max_samplepos - current_page_samples());
1659 Editor::tav_zoom_step (bool coarser)
1661 DisplaySuspender ds;
1665 if (selection->tracks.empty()) {
1668 ts = &selection->tracks;
1671 for (TrackViewList::iterator i = ts->begin(); i != ts->end(); ++i) {
1672 TimeAxisView *tv = (static_cast<TimeAxisView*>(*i));
1673 tv->step_height (coarser);
1678 Editor::tav_zoom_smooth (bool coarser, bool force_all)
1680 DisplaySuspender ds;
1684 if (selection->tracks.empty() || force_all) {
1687 ts = &selection->tracks;
1690 for (TrackViewList::iterator i = ts->begin(); i != ts->end(); ++i) {
1691 TimeAxisView *tv = (static_cast<TimeAxisView*>(*i));
1692 uint32_t h = tv->current_height ();
1697 if (h >= TimeAxisView::preset_height (HeightSmall)) {
1702 tv->set_height (h + 5);
1708 Editor::temporal_zoom_step_mouse_focus_scale (bool zoom_out, double scale)
1710 Editing::ZoomFocus temp_focus = zoom_focus;
1711 zoom_focus = Editing::ZoomFocusMouse;
1712 temporal_zoom_step_scale (zoom_out, scale);
1713 zoom_focus = temp_focus;
1717 Editor::temporal_zoom_step_mouse_focus (bool zoom_out)
1719 temporal_zoom_step_mouse_focus_scale (zoom_out, 2.0);
1723 Editor::temporal_zoom_step (bool zoom_out)
1725 temporal_zoom_step_scale (zoom_out, 2.0);
1729 Editor::temporal_zoom_step_scale (bool zoom_out, double scale)
1731 ENSURE_GUI_THREAD (*this, &Editor::temporal_zoom_step, zoom_out, scale)
1733 samplecnt_t nspp = samples_per_pixel;
1737 if (nspp == samples_per_pixel) {
1742 if (nspp == samples_per_pixel) {
1747 //zoom-behavior-tweaks
1748 //limit our maximum zoom to the session gui extents value
1749 std::pair<samplepos_t, samplepos_t> ext = session_gui_extents();
1750 samplecnt_t session_extents_pp = (ext.second - ext.first) / _visible_canvas_width;
1751 if (nspp > session_extents_pp)
1752 nspp = session_extents_pp;
1754 temporal_zoom (nspp);
1758 Editor::temporal_zoom (samplecnt_t fpp)
1764 samplepos_t current_page = current_page_samples();
1765 samplepos_t current_leftmost = _leftmost_sample;
1766 samplepos_t current_rightmost;
1767 samplepos_t current_center;
1768 samplepos_t new_page_size;
1769 samplepos_t half_page_size;
1770 samplepos_t leftmost_after_zoom = 0;
1772 bool in_track_canvas;
1773 bool use_mouse_sample = true;
1777 if (fpp == samples_per_pixel) {
1781 // Imposing an arbitrary limit to zoom out as too much zoom out produces
1782 // segfaults for lack of memory. If somebody decides this is not high enough I
1783 // believe it can be raisen to higher values but some limit must be in place.
1785 // This constant represents 1 day @ 48kHz on a 1600 pixel wide display
1786 // all of which is used for the editor track displays. The whole day
1787 // would be 4147200000 samples, so 2592000 samples per pixel.
1789 nfpp = min (fpp, (samplecnt_t) 2592000);
1790 nfpp = max ((samplecnt_t) 1, nfpp);
1792 new_page_size = (samplepos_t) floor (_visible_canvas_width * nfpp);
1793 half_page_size = new_page_size / 2;
1795 switch (zoom_focus) {
1797 leftmost_after_zoom = current_leftmost;
1800 case ZoomFocusRight:
1801 current_rightmost = _leftmost_sample + current_page;
1802 if (current_rightmost < new_page_size) {
1803 leftmost_after_zoom = 0;
1805 leftmost_after_zoom = current_rightmost - new_page_size;
1809 case ZoomFocusCenter:
1810 current_center = current_leftmost + (current_page/2);
1811 if (current_center < half_page_size) {
1812 leftmost_after_zoom = 0;
1814 leftmost_after_zoom = current_center - half_page_size;
1818 case ZoomFocusPlayhead:
1819 /* centre playhead */
1820 l = playhead_cursor->current_sample () - (new_page_size * 0.5);
1823 leftmost_after_zoom = 0;
1824 } else if (l > max_samplepos) {
1825 leftmost_after_zoom = max_samplepos - new_page_size;
1827 leftmost_after_zoom = (samplepos_t) l;
1831 case ZoomFocusMouse:
1832 /* try to keep the mouse over the same point in the display */
1834 if (_drags->active()) {
1835 where = _drags->current_pointer_sample ();
1836 } else if (!mouse_sample (where, in_track_canvas)) {
1837 use_mouse_sample = false;
1840 if (use_mouse_sample) {
1841 l = - ((new_page_size * ((where - current_leftmost)/(double)current_page)) - where);
1844 leftmost_after_zoom = 0;
1845 } else if (l > max_samplepos) {
1846 leftmost_after_zoom = max_samplepos - new_page_size;
1848 leftmost_after_zoom = (samplepos_t) l;
1851 /* use playhead instead */
1852 where = playhead_cursor->current_sample ();
1854 if (where < half_page_size) {
1855 leftmost_after_zoom = 0;
1857 leftmost_after_zoom = where - half_page_size;
1863 /* try to keep the edit point in the same place */
1864 where = get_preferred_edit_position ();
1868 double l = - ((new_page_size * ((where - current_leftmost)/(double)current_page)) - where);
1871 leftmost_after_zoom = 0;
1872 } else if (l > max_samplepos) {
1873 leftmost_after_zoom = max_samplepos - new_page_size;
1875 leftmost_after_zoom = (samplepos_t) l;
1879 /* edit point not defined */
1886 // leftmost_after_zoom = min (leftmost_after_zoom, _session->current_end_sample());
1888 reposition_and_zoom (leftmost_after_zoom, nfpp);
1892 Editor::calc_extra_zoom_edges(samplepos_t &start, samplepos_t &end)
1894 /* this func helps make sure we leave a little space
1895 at each end of the editor so that the zoom doesn't fit the region
1896 precisely to the screen.
1899 GdkScreen* screen = gdk_screen_get_default ();
1900 const gint pixwidth = gdk_screen_get_width (screen);
1901 const gint mmwidth = gdk_screen_get_width_mm (screen);
1902 const double pix_per_mm = (double) pixwidth/ (double) mmwidth;
1903 const double one_centimeter_in_pixels = pix_per_mm * 10.0;
1905 const samplepos_t range = end - start;
1906 const samplecnt_t new_fpp = (samplecnt_t) ceil ((double) range / (double) _visible_canvas_width);
1907 const samplepos_t extra_samples = (samplepos_t) floor (one_centimeter_in_pixels * new_fpp);
1909 if (start > extra_samples) {
1910 start -= extra_samples;
1915 if (max_samplepos - extra_samples > end) {
1916 end += extra_samples;
1918 end = max_samplepos;
1923 Editor::get_selection_extents (samplepos_t &start, samplepos_t &end) const
1925 start = max_samplepos;
1929 //ToDo: if notes are selected, set extents to that selection
1931 //ToDo: if control points are selected, set extents to that selection
1933 if (!selection->regions.empty()) {
1934 RegionSelection rs = get_regions_from_selection_and_entered ();
1936 for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
1938 if ((*i)->region()->position() < start) {
1939 start = (*i)->region()->position();
1942 if ((*i)->region()->last_sample() + 1 > end) {
1943 end = (*i)->region()->last_sample() + 1;
1947 } else if (!selection->time.empty()) {
1948 start = selection->time.start();
1949 end = selection->time.end_sample();
1951 ret = false; //no selection found
1954 if ((start == 0 && end == 0) || end < start) {
1963 Editor::temporal_zoom_selection (Editing::ZoomAxis axes)
1965 if (!selection) return;
1967 if (selection->regions.empty() && selection->time.empty()) {
1968 if (axes == Horizontal || axes == Both) {
1969 temporal_zoom_step(true);
1971 if (axes == Vertical || axes == Both) {
1972 if (!track_views.empty()) {
1976 //implicit hack: by extending the top & bottom check outside the current view limits, we include the trackviews immediately above & below what is visible
1977 const double top = vertical_adjustment.get_value() - 10;
1978 const double btm = top + _visible_canvas_height + 10;
1980 for (TrackViewList::iterator iter = track_views.begin(); iter != track_views.end(); ++iter) {
1981 if ((*iter)->covered_by_y_range (top, btm)) {
1982 tvl.push_back(*iter);
1992 //ToDo: if notes are selected, zoom to that
1994 //ToDo: if control points are selected, zoom to that
1996 if (axes == Horizontal || axes == Both) {
1998 samplepos_t start, end;
1999 if (get_selection_extents (start, end)) {
2000 calc_extra_zoom_edges (start, end);
2001 temporal_zoom_by_sample (start, end);
2005 if (axes == Vertical || axes == Both) {
2009 //normally, we don't do anything "automatic" to the user's selection.
2010 //but in this case, we will clear the selection after a zoom-to-selection.
2015 Editor::temporal_zoom_session ()
2017 ENSURE_GUI_THREAD (*this, &Editor::temporal_zoom_session)
2020 samplecnt_t start = _session->current_start_sample();
2021 samplecnt_t end = _session->current_end_sample();
2023 if (_session->actively_recording ()) {
2024 samplepos_t cur = playhead_cursor->current_sample ();
2026 /* recording beyond the end marker; zoom out
2027 * by 5 seconds more so that if 'follow
2028 * playhead' is active we don't immediately
2031 end = cur + _session->sample_rate() * 5;
2035 if ((start == 0 && end == 0) || end < start) {
2039 calc_extra_zoom_edges(start, end);
2041 temporal_zoom_by_sample (start, end);
2046 Editor::temporal_zoom_extents ()
2048 ENSURE_GUI_THREAD (*this, &Editor::temporal_zoom_extents)
2051 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
2053 samplecnt_t start = ext.first;
2054 samplecnt_t end = ext.second;
2056 if (_session->actively_recording ()) {
2057 samplepos_t cur = playhead_cursor->current_sample ();
2059 /* recording beyond the end marker; zoom out
2060 * by 5 seconds more so that if 'follow
2061 * playhead' is active we don't immediately
2064 end = cur + _session->sample_rate() * 5;
2068 if ((start == 0 && end == 0) || end < start) {
2072 calc_extra_zoom_edges(start, end);
2074 temporal_zoom_by_sample (start, end);
2079 Editor::temporal_zoom_by_sample (samplepos_t start, samplepos_t end)
2081 if (!_session) return;
2083 if ((start == 0 && end == 0) || end < start) {
2087 samplepos_t range = end - start;
2089 const samplecnt_t new_fpp = (samplecnt_t) ceil ((double) range / (double) _visible_canvas_width);
2091 samplepos_t new_page = range;
2092 samplepos_t middle = (samplepos_t) floor ((double) start + ((double) range / 2.0f));
2093 samplepos_t new_leftmost = (samplepos_t) floor ((double) middle - ((double) new_page / 2.0f));
2095 if (new_leftmost > middle) {
2099 if (new_leftmost < 0) {
2103 reposition_and_zoom (new_leftmost, new_fpp);
2107 Editor::temporal_zoom_to_sample (bool coarser, samplepos_t sample)
2113 samplecnt_t range_before = sample - _leftmost_sample;
2114 samplecnt_t new_spp;
2117 if (samples_per_pixel <= 1) {
2120 new_spp = samples_per_pixel + (samples_per_pixel/2);
2122 range_before += range_before/2;
2124 if (samples_per_pixel >= 1) {
2125 new_spp = samples_per_pixel - (samples_per_pixel/2);
2127 /* could bail out here since we cannot zoom any finer,
2128 but leave that to the equality test below
2130 new_spp = samples_per_pixel;
2133 range_before -= range_before/2;
2136 if (new_spp == samples_per_pixel) {
2140 /* zoom focus is automatically taken as @param sample when this
2144 samplepos_t new_leftmost = sample - (samplepos_t)range_before;
2146 if (new_leftmost > sample) {
2150 if (new_leftmost < 0) {
2154 reposition_and_zoom (new_leftmost, new_spp);
2159 Editor::choose_new_marker_name(string &name) {
2161 if (!UIConfiguration::instance().get_name_new_markers()) {
2162 /* don't prompt user for a new name */
2166 Prompter dialog (true);
2168 dialog.set_prompt (_("New Name:"));
2170 dialog.set_title (_("New Location Marker"));
2172 dialog.set_name ("MarkNameWindow");
2173 dialog.set_size_request (250, -1);
2174 dialog.set_position (Gtk::WIN_POS_MOUSE);
2176 dialog.add_button (Stock::OK, RESPONSE_ACCEPT);
2177 dialog.set_initial_text (name);
2181 switch (dialog.run ()) {
2182 case RESPONSE_ACCEPT:
2188 dialog.get_result(name);
2195 Editor::add_location_from_selection ()
2199 if (selection->time.empty()) {
2203 if (_session == 0 || clicked_axisview == 0) {
2207 samplepos_t start = selection->time[clicked_selection].start;
2208 samplepos_t end = selection->time[clicked_selection].end;
2210 _session->locations()->next_available_name(rangename,"selection");
2211 Location *location = new Location (*_session, start, end, rangename, Location::IsRangeMarker, get_grid_music_divisions(0));
2213 begin_reversible_command (_("add marker"));
2215 XMLNode &before = _session->locations()->get_state();
2216 _session->locations()->add (location, true);
2217 XMLNode &after = _session->locations()->get_state();
2218 _session->add_command(new MementoCommand<Locations>(*(_session->locations()), &before, &after));
2220 commit_reversible_command ();
2224 Editor::add_location_mark (samplepos_t where)
2228 select_new_marker = true;
2230 _session->locations()->next_available_name(markername,"mark");
2231 if (!choose_new_marker_name(markername)) {
2234 Location *location = new Location (*_session, where, where, markername, Location::IsMark, get_grid_music_divisions (0));
2235 begin_reversible_command (_("add marker"));
2237 XMLNode &before = _session->locations()->get_state();
2238 _session->locations()->add (location, true);
2239 XMLNode &after = _session->locations()->get_state();
2240 _session->add_command(new MementoCommand<Locations>(*(_session->locations()), &before, &after));
2242 commit_reversible_command ();
2246 Editor::set_session_start_from_playhead ()
2252 if ((loc = _session->locations()->session_range_location()) == 0) { //should never happen
2253 _session->set_session_extents (_session->audible_sample(), _session->audible_sample());
2255 XMLNode &before = loc->get_state();
2257 _session->set_session_extents (_session->audible_sample(), loc->end());
2259 XMLNode &after = loc->get_state();
2261 begin_reversible_command (_("Set session start"));
2263 _session->add_command (new MementoCommand<Location>(*loc, &before, &after));
2265 commit_reversible_command ();
2270 Editor::set_session_end_from_playhead ()
2276 if ((loc = _session->locations()->session_range_location()) == 0) { //should never happen
2277 _session->set_session_extents (_session->audible_sample(), _session->audible_sample());
2279 XMLNode &before = loc->get_state();
2281 _session->set_session_extents (loc->start(), _session->audible_sample());
2283 XMLNode &after = loc->get_state();
2285 begin_reversible_command (_("Set session start"));
2287 _session->add_command (new MementoCommand<Location>(*loc, &before, &after));
2289 commit_reversible_command ();
2292 _session->set_end_is_free (false);
2297 Editor::toggle_location_at_playhead_cursor ()
2299 if (!do_remove_location_at_playhead_cursor())
2301 add_location_from_playhead_cursor();
2306 Editor::add_location_from_playhead_cursor ()
2308 add_location_mark (_session->audible_sample());
2312 Editor::do_remove_location_at_playhead_cursor ()
2314 bool removed = false;
2317 XMLNode &before = _session->locations()->get_state();
2319 //find location(s) at this time
2320 Locations::LocationList locs;
2321 _session->locations()->find_all_between (_session->audible_sample(), _session->audible_sample()+1, locs, Location::Flags(0));
2322 for (Locations::LocationList::iterator i = locs.begin(); i != locs.end(); ++i) {
2323 if ((*i)->is_mark()) {
2324 _session->locations()->remove (*i);
2331 begin_reversible_command (_("remove marker"));
2332 XMLNode &after = _session->locations()->get_state();
2333 _session->add_command(new MementoCommand<Locations>(*(_session->locations()), &before, &after));
2334 commit_reversible_command ();
2341 Editor::remove_location_at_playhead_cursor ()
2343 do_remove_location_at_playhead_cursor ();
2346 /** Add a range marker around each selected region */
2348 Editor::add_locations_from_region ()
2350 RegionSelection rs = get_regions_from_selection_and_entered ();
2355 bool commit = false;
2357 XMLNode &before = _session->locations()->get_state();
2359 for (RegionSelection::iterator i = rs.begin (); i != rs.end (); ++i) {
2361 boost::shared_ptr<Region> region = (*i)->region ();
2363 Location *location = new Location (*_session, region->position(), region->last_sample(), region->name(), Location::IsRangeMarker, 0);
2365 _session->locations()->add (location, true);
2370 begin_reversible_command (selection->regions.size () > 1 ? _("add markers") : _("add marker"));
2371 XMLNode &after = _session->locations()->get_state();
2372 _session->add_command (new MementoCommand<Locations>(*(_session->locations()), &before, &after));
2373 commit_reversible_command ();
2377 /** Add a single range marker around all selected regions */
2379 Editor::add_location_from_region ()
2381 RegionSelection rs = get_regions_from_selection_and_entered ();
2387 XMLNode &before = _session->locations()->get_state();
2391 if (rs.size() > 1) {
2392 _session->locations()->next_available_name(markername, "regions");
2394 RegionView* rv = *(rs.begin());
2395 boost::shared_ptr<Region> region = rv->region();
2396 markername = region->name();
2399 if (!choose_new_marker_name(markername)) {
2403 // single range spanning all selected
2404 Location *location = new Location (*_session, selection->regions.start(), selection->regions.end_sample(), markername, Location::IsRangeMarker, 0);
2405 _session->locations()->add (location, true);
2407 begin_reversible_command (_("add marker"));
2408 XMLNode &after = _session->locations()->get_state();
2409 _session->add_command (new MementoCommand<Locations>(*(_session->locations()), &before, &after));
2410 commit_reversible_command ();
2416 Editor::jump_forward_to_mark ()
2422 samplepos_t pos = _session->locations()->first_mark_after (playhead_cursor->current_sample());
2428 _session->request_locate (pos, _session->transport_rolling());
2432 Editor::jump_backward_to_mark ()
2438 samplepos_t pos = _session->locations()->first_mark_before (playhead_cursor->current_sample());
2440 //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...
2441 if (_session->transport_rolling()) {
2442 if ((playhead_cursor->current_sample() - pos) < _session->sample_rate()/2) {
2443 samplepos_t prior = _session->locations()->first_mark_before (pos);
2452 _session->request_locate (pos, _session->transport_rolling());
2458 samplepos_t const pos = _session->audible_sample ();
2461 _session->locations()->next_available_name (markername, "mark");
2463 if (!choose_new_marker_name (markername)) {
2467 _session->locations()->add (new Location (*_session, pos, 0, markername, Location::IsMark, 0), true);
2471 Editor::clear_markers ()
2474 begin_reversible_command (_("clear markers"));
2476 XMLNode &before = _session->locations()->get_state();
2477 _session->locations()->clear_markers ();
2478 XMLNode &after = _session->locations()->get_state();
2479 _session->add_command(new MementoCommand<Locations>(*(_session->locations()), &before, &after));
2481 commit_reversible_command ();
2486 Editor::clear_ranges ()
2489 begin_reversible_command (_("clear ranges"));
2491 XMLNode &before = _session->locations()->get_state();
2493 _session->locations()->clear_ranges ();
2495 XMLNode &after = _session->locations()->get_state();
2496 _session->add_command(new MementoCommand<Locations>(*(_session->locations()), &before, &after));
2498 commit_reversible_command ();
2503 Editor::clear_locations ()
2505 begin_reversible_command (_("clear locations"));
2507 XMLNode &before = _session->locations()->get_state();
2508 _session->locations()->clear ();
2509 XMLNode &after = _session->locations()->get_state();
2510 _session->add_command(new MementoCommand<Locations>(*(_session->locations()), &before, &after));
2512 commit_reversible_command ();
2516 Editor::unhide_markers ()
2518 for (LocationMarkerMap::iterator i = location_markers.begin(); i != location_markers.end(); ++i) {
2519 Location *l = (*i).first;
2520 if (l->is_hidden() && l->is_mark()) {
2521 l->set_hidden(false, this);
2527 Editor::unhide_ranges ()
2529 for (LocationMarkerMap::iterator i = location_markers.begin(); i != location_markers.end(); ++i) {
2530 Location *l = (*i).first;
2531 if (l->is_hidden() && l->is_range_marker()) {
2532 l->set_hidden(false, this);
2537 /* INSERT/REPLACE */
2540 Editor::insert_region_list_selection (float times)
2542 RouteTimeAxisView *tv = 0;
2543 boost::shared_ptr<Playlist> playlist;
2545 if (clicked_routeview != 0) {
2546 tv = clicked_routeview;
2547 } else if (!selection->tracks.empty()) {
2548 if ((tv = dynamic_cast<RouteTimeAxisView*>(selection->tracks.front())) == 0) {
2551 } else if (entered_track != 0) {
2552 if ((tv = dynamic_cast<RouteTimeAxisView*>(entered_track)) == 0) {
2559 if ((playlist = tv->playlist()) == 0) {
2563 boost::shared_ptr<Region> region = _regions->get_single_selection ();
2568 begin_reversible_command (_("insert region"));
2569 playlist->clear_changes ();
2570 playlist->add_region ((RegionFactory::create (region, true)), get_preferred_edit_position(), times);
2571 if (Config->get_edit_mode() == Ripple)
2572 playlist->ripple (get_preferred_edit_position(), region->length() * times, boost::shared_ptr<Region>());
2574 _session->add_command(new StatefulDiffCommand (playlist));
2575 commit_reversible_command ();
2578 /* BUILT-IN EFFECTS */
2581 Editor::reverse_selection ()
2586 /* GAIN ENVELOPE EDITING */
2589 Editor::edit_envelope ()
2596 Editor::transition_to_rolling (bool fwd)
2602 if (_session->config.get_external_sync()) {
2603 switch (Config->get_sync_source()) {
2607 /* transport controlled by the master */
2612 if (_session->is_auditioning()) {
2613 _session->cancel_audition ();
2617 _session->request_transport_speed (fwd ? 1.0f : -1.0f);
2621 Editor::play_from_start ()
2623 _session->request_locate (_session->current_start_sample(), true);
2627 Editor::play_from_edit_point ()
2629 _session->request_locate (get_preferred_edit_position(), true);
2633 Editor::play_from_edit_point_and_return ()
2635 samplepos_t start_sample;
2636 samplepos_t return_sample;
2638 start_sample = get_preferred_edit_position (EDIT_IGNORE_PHEAD);
2640 if (_session->transport_rolling()) {
2641 _session->request_locate (start_sample, false);
2645 /* don't reset the return sample if its already set */
2647 if ((return_sample = _session->requested_return_sample()) < 0) {
2648 return_sample = _session->audible_sample();
2651 if (start_sample >= 0) {
2652 _session->request_roll_at_and_return (start_sample, return_sample);
2657 Editor::play_selection ()
2659 samplepos_t start, end;
2660 if (!get_selection_extents (start, end))
2663 AudioRange ar (start, end, 0);
2664 list<AudioRange> lar;
2667 _session->request_play_range (&lar, true);
2672 Editor::maybe_locate_with_edit_preroll (samplepos_t location)
2674 if (_session->transport_rolling() || !UIConfiguration::instance().get_follow_edits() || _session->config.get_external_sync())
2677 location -= _session->preroll_samples (location);
2679 //don't try to locate before the beginning of time
2684 //if follow_playhead is on, keep the playhead on the screen
2685 if (_follow_playhead)
2686 if (location < _leftmost_sample)
2687 location = _leftmost_sample;
2689 _session->request_locate (location);
2693 Editor::play_with_preroll ()
2695 samplepos_t start, end;
2696 if (UIConfiguration::instance().get_follow_edits() && get_selection_extents (start, end)) {
2697 const samplepos_t preroll = _session->preroll_samples (start);
2699 samplepos_t ret = start;
2701 if (start > preroll) {
2702 start = start - preroll;
2705 end = end + preroll; //"post-roll"
2707 AudioRange ar (start, end, 0);
2708 list<AudioRange> lar;
2711 _session->request_play_range (&lar, true);
2712 _session->set_requested_return_sample (ret); //force auto-return to return to range start, without the preroll
2714 samplepos_t ph = playhead_cursor->current_sample ();
2715 const samplepos_t preroll = _session->preroll_samples (ph);
2718 start = ph - preroll;
2722 _session->request_locate (start, true);
2723 _session->set_requested_return_sample (ph); //force auto-return to return to playhead location, without the preroll
2728 Editor::rec_with_preroll ()
2730 samplepos_t ph = playhead_cursor->current_sample ();
2731 samplepos_t preroll = _session->preroll_samples (ph);
2732 _session->request_preroll_record_trim (ph, preroll);
2736 Editor::rec_with_count_in ()
2738 _session->request_count_in_record ();
2742 Editor::play_location (Location& location)
2744 if (location.start() <= location.end()) {
2748 _session->request_bounded_roll (location.start(), location.end());
2752 Editor::loop_location (Location& location)
2754 if (location.start() <= location.end()) {
2760 if ((tll = transport_loop_location()) != 0) {
2761 tll->set (location.start(), location.end());
2763 // enable looping, reposition and start rolling
2764 _session->request_locate (tll->start(), true);
2765 _session->request_play_loop (true);
2770 Editor::do_layer_operation (LayerOperation op)
2772 if (selection->regions.empty ()) {
2776 bool const multiple = selection->regions.size() > 1;
2780 begin_reversible_command (_("raise regions"));
2782 begin_reversible_command (_("raise region"));
2788 begin_reversible_command (_("raise regions to top"));
2790 begin_reversible_command (_("raise region to top"));
2796 begin_reversible_command (_("lower regions"));
2798 begin_reversible_command (_("lower region"));
2804 begin_reversible_command (_("lower regions to bottom"));
2806 begin_reversible_command (_("lower region"));
2811 set<boost::shared_ptr<Playlist> > playlists = selection->regions.playlists ();
2812 for (set<boost::shared_ptr<Playlist> >::iterator i = playlists.begin(); i != playlists.end(); ++i) {
2813 (*i)->clear_owned_changes ();
2816 for (RegionSelection::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
2817 boost::shared_ptr<Region> r = (*i)->region ();
2829 r->lower_to_bottom ();
2833 for (set<boost::shared_ptr<Playlist> >::iterator i = playlists.begin(); i != playlists.end(); ++i) {
2834 vector<Command*> cmds;
2836 _session->add_commands (cmds);
2839 commit_reversible_command ();
2843 Editor::raise_region ()
2845 do_layer_operation (Raise);
2849 Editor::raise_region_to_top ()
2851 do_layer_operation (RaiseToTop);
2855 Editor::lower_region ()
2857 do_layer_operation (Lower);
2861 Editor::lower_region_to_bottom ()
2863 do_layer_operation (LowerToBottom);
2866 /** Show the region editor for the selected regions */
2868 Editor::show_region_properties ()
2870 selection->foreach_regionview (&RegionView::show_region_editor);
2873 /** Show the midi list editor for the selected MIDI regions */
2875 Editor::show_midi_list_editor ()
2877 selection->foreach_midi_regionview (&MidiRegionView::show_list_editor);
2881 Editor::rename_region ()
2883 RegionSelection rs = get_regions_from_selection_and_entered ();
2889 ArdourDialog d (_("Rename Region"), true, false);
2891 Label label (_("New name:"));
2894 hbox.set_spacing (6);
2895 hbox.pack_start (label, false, false);
2896 hbox.pack_start (entry, true, true);
2898 d.get_vbox()->set_border_width (12);
2899 d.get_vbox()->pack_start (hbox, false, false);
2901 d.add_button(Gtk::Stock::CANCEL, Gtk::RESPONSE_CANCEL);
2902 d.add_button(Gtk::Stock::OK, Gtk::RESPONSE_OK);
2904 d.set_size_request (300, -1);
2906 entry.set_text (rs.front()->region()->name());
2907 entry.select_region (0, -1);
2909 entry.signal_activate().connect (sigc::bind (sigc::mem_fun (d, &Dialog::response), RESPONSE_OK));
2915 int const ret = d.run();
2919 if (ret != RESPONSE_OK) {
2923 std::string str = entry.get_text();
2924 strip_whitespace_edges (str);
2926 rs.front()->region()->set_name (str);
2927 _regions->redisplay ();
2931 /** Start an audition of the first selected region */
2933 Editor::play_edit_range ()
2935 samplepos_t start, end;
2937 if (get_edit_op_range (start, end)) {
2938 _session->request_bounded_roll (start, end);
2943 Editor::play_selected_region ()
2945 samplepos_t start = max_samplepos;
2946 samplepos_t end = 0;
2948 RegionSelection rs = get_regions_from_selection_and_entered ();
2954 for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
2955 if ((*i)->region()->position() < start) {
2956 start = (*i)->region()->position();
2958 if ((*i)->region()->last_sample() + 1 > end) {
2959 end = (*i)->region()->last_sample() + 1;
2963 _session->request_bounded_roll (start, end);
2967 Editor::audition_playlist_region_standalone (boost::shared_ptr<Region> region)
2969 _session->audition_region (region);
2973 Editor::region_from_selection ()
2975 if (clicked_axisview == 0) {
2979 if (selection->time.empty()) {
2983 samplepos_t start = selection->time[clicked_selection].start;
2984 samplepos_t end = selection->time[clicked_selection].end;
2986 TrackViewList tracks = get_tracks_for_range_action ();
2988 samplepos_t selection_cnt = end - start + 1;
2990 for (TrackSelection::iterator i = tracks.begin(); i != tracks.end(); ++i) {
2991 boost::shared_ptr<Region> current;
2992 boost::shared_ptr<Playlist> pl;
2993 samplepos_t internal_start;
2996 if ((pl = (*i)->playlist()) == 0) {
3000 if ((current = pl->top_region_at (start)) == 0) {
3004 internal_start = start - current->position();
3005 RegionFactory::region_name (new_name, current->name(), true);
3009 plist.add (ARDOUR::Properties::start, current->start() + internal_start);
3010 plist.add (ARDOUR::Properties::length, selection_cnt);
3011 plist.add (ARDOUR::Properties::name, new_name);
3012 plist.add (ARDOUR::Properties::layer, 0);
3014 boost::shared_ptr<Region> region (RegionFactory::create (current, plist));
3019 Editor::create_region_from_selection (vector<boost::shared_ptr<Region> >& new_regions)
3021 if (selection->time.empty() || selection->tracks.empty()) {
3025 samplepos_t start, end;
3026 if (clicked_selection) {
3027 start = selection->time[clicked_selection].start;
3028 end = selection->time[clicked_selection].end;
3030 start = selection->time.start();
3031 end = selection->time.end_sample();
3034 TrackViewList ts = selection->tracks.filter_to_unique_playlists ();
3035 sort_track_selection (ts);
3037 for (TrackSelection::iterator i = ts.begin(); i != ts.end(); ++i) {
3038 boost::shared_ptr<Region> current;
3039 boost::shared_ptr<Playlist> playlist;
3040 samplepos_t internal_start;
3043 if ((playlist = (*i)->playlist()) == 0) {
3047 if ((current = playlist->top_region_at(start)) == 0) {
3051 internal_start = start - current->position();
3052 RegionFactory::region_name (new_name, current->name(), true);
3056 plist.add (ARDOUR::Properties::start, current->start() + internal_start);
3057 plist.add (ARDOUR::Properties::length, end - start + 1);
3058 plist.add (ARDOUR::Properties::name, new_name);
3060 new_regions.push_back (RegionFactory::create (current, plist));
3065 Editor::split_multichannel_region ()
3067 RegionSelection rs = get_regions_from_selection_and_entered ();
3073 vector< boost::shared_ptr<Region> > v;
3075 for (list<RegionView*>::iterator x = rs.begin(); x != rs.end(); ++x) {
3076 (*x)->region()->separate_by_channel (v);
3081 Editor::new_region_from_selection ()
3083 region_from_selection ();
3084 cancel_selection ();
3088 add_if_covered (RegionView* rv, const AudioRange* ar, RegionSelection* rs)
3090 switch (rv->region()->coverage (ar->start, ar->end - 1)) {
3091 // n.b. -1 because AudioRange::end is one past the end, but coverage expects inclusive ranges
3092 case Evoral::OverlapNone:
3100 * - selected tracks, or if there are none...
3101 * - tracks containing selected regions, or if there are none...
3106 Editor::get_tracks_for_range_action () const
3110 if (selection->tracks.empty()) {
3112 /* use tracks with selected regions */
3114 RegionSelection rs = selection->regions;
3116 for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
3117 TimeAxisView* tv = &(*i)->get_time_axis_view();
3119 if (!t.contains (tv)) {
3125 /* no regions and no tracks: use all tracks */
3131 t = selection->tracks;
3134 return t.filter_to_unique_playlists();
3138 Editor::separate_regions_between (const TimeSelection& ts)
3140 bool in_command = false;
3141 boost::shared_ptr<Playlist> playlist;
3142 RegionSelection new_selection;
3144 TrackViewList tmptracks = get_tracks_for_range_action ();
3145 sort_track_selection (tmptracks);
3147 for (TrackSelection::iterator i = tmptracks.begin(); i != tmptracks.end(); ++i) {
3149 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> ((*i));
3155 if (!rtv->is_track()) {
3159 /* no edits to destructive tracks */
3161 if (rtv->track()->destructive()) {
3165 if ((playlist = rtv->playlist()) != 0) {
3167 playlist->clear_changes ();
3169 /* XXX need to consider musical time selections here at some point */
3171 for (list<AudioRange>::const_iterator t = ts.begin(); t != ts.end(); ++t) {
3173 sigc::connection c = rtv->view()->RegionViewAdded.connect (
3174 sigc::mem_fun(*this, &Editor::collect_new_region_view));
3176 latest_regionviews.clear ();
3178 playlist->partition ((*t).start, (*t).end, false);
3182 if (!latest_regionviews.empty()) {
3184 rtv->view()->foreach_regionview (sigc::bind (
3185 sigc::ptr_fun (add_if_covered),
3186 &(*t), &new_selection));
3189 begin_reversible_command (_("separate"));
3193 /* pick up changes to existing regions */
3195 vector<Command*> cmds;
3196 playlist->rdiff (cmds);
3197 _session->add_commands (cmds);
3199 /* pick up changes to the playlist itself (adds/removes)
3202 _session->add_command(new StatefulDiffCommand (playlist));
3209 // selection->set (new_selection);
3211 commit_reversible_command ();
3215 struct PlaylistState {
3216 boost::shared_ptr<Playlist> playlist;
3220 /** Take tracks from get_tracks_for_range_action and cut any regions
3221 * on those tracks so that the tracks are empty over the time
3225 Editor::separate_region_from_selection ()
3227 /* preferentially use *all* ranges in the time selection if we're in range mode
3228 to allow discontiguous operation, since get_edit_op_range() currently
3229 returns a single range.
3232 if (!selection->time.empty()) {
3234 separate_regions_between (selection->time);
3241 if (get_edit_op_range (start, end)) {
3243 AudioRange ar (start, end, 1);
3247 separate_regions_between (ts);
3253 Editor::separate_region_from_punch ()
3255 Location* loc = _session->locations()->auto_punch_location();
3257 separate_regions_using_location (*loc);
3262 Editor::separate_region_from_loop ()
3264 Location* loc = _session->locations()->auto_loop_location();
3266 separate_regions_using_location (*loc);
3271 Editor::separate_regions_using_location (Location& loc)
3273 if (loc.is_mark()) {
3277 AudioRange ar (loc.start(), loc.end(), 1);
3282 separate_regions_between (ts);
3285 /** Separate regions under the selected region */
3287 Editor::separate_under_selected_regions ()
3289 vector<PlaylistState> playlists;
3293 rs = get_regions_from_selection_and_entered();
3295 if (!_session || rs.empty()) {
3299 begin_reversible_command (_("separate region under"));
3301 list<boost::shared_ptr<Region> > regions_to_remove;
3303 for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
3304 // we can't just remove the region(s) in this loop because
3305 // this removes them from the RegionSelection, and they thus
3306 // disappear from underneath the iterator, and the ++i above
3307 // SEGVs in a puzzling fashion.
3309 // so, first iterate over the regions to be removed from rs and
3310 // add them to the regions_to_remove list, and then
3311 // iterate over the list to actually remove them.
3313 regions_to_remove.push_back ((*i)->region());
3316 for (list<boost::shared_ptr<Region> >::iterator rl = regions_to_remove.begin(); rl != regions_to_remove.end(); ++rl) {
3318 boost::shared_ptr<Playlist> playlist = (*rl)->playlist();
3321 // is this check necessary?
3325 vector<PlaylistState>::iterator i;
3327 //only take state if this is a new playlist.
3328 for (i = playlists.begin(); i != playlists.end(); ++i) {
3329 if ((*i).playlist == playlist) {
3334 if (i == playlists.end()) {
3336 PlaylistState before;
3337 before.playlist = playlist;
3338 before.before = &playlist->get_state();
3339 playlist->clear_changes ();
3340 playlist->freeze ();
3341 playlists.push_back(before);
3344 //Partition on the region bounds
3345 playlist->partition ((*rl)->first_sample() - 1, (*rl)->last_sample() + 1, true);
3347 //Re-add region that was just removed due to the partition operation
3348 playlist->add_region ((*rl), (*rl)->first_sample());
3351 vector<PlaylistState>::iterator pl;
3353 for (pl = playlists.begin(); pl != playlists.end(); ++pl) {
3354 (*pl).playlist->thaw ();
3355 _session->add_command(new MementoCommand<Playlist>(*(*pl).playlist, (*pl).before, &(*pl).playlist->get_state()));
3358 commit_reversible_command ();
3362 Editor::crop_region_to_selection ()
3364 if (!selection->time.empty()) {
3366 begin_reversible_command (_("Crop Regions to Time Selection"));
3367 for (std::list<AudioRange>::iterator i = selection->time.begin(); i != selection->time.end(); ++i) {
3368 crop_region_to ((*i).start, (*i).end);
3370 commit_reversible_command();
3376 if (get_edit_op_range (start, end)) {
3377 begin_reversible_command (_("Crop Regions to Edit Range"));
3379 crop_region_to (start, end);
3381 commit_reversible_command();
3388 Editor::crop_region_to (samplepos_t start, samplepos_t end)
3390 vector<boost::shared_ptr<Playlist> > playlists;
3391 boost::shared_ptr<Playlist> playlist;
3394 if (selection->tracks.empty()) {
3395 ts = track_views.filter_to_unique_playlists();
3397 ts = selection->tracks.filter_to_unique_playlists ();
3400 sort_track_selection (ts);
3402 for (TrackSelection::iterator i = ts.begin(); i != ts.end(); ++i) {
3404 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> ((*i));
3410 boost::shared_ptr<Track> t = rtv->track();
3412 if (t != 0 && ! t->destructive()) {
3414 if ((playlist = rtv->playlist()) != 0) {
3415 playlists.push_back (playlist);
3420 if (playlists.empty()) {
3425 samplepos_t new_start;
3426 samplepos_t new_end;
3427 samplecnt_t new_length;
3429 for (vector<boost::shared_ptr<Playlist> >::iterator i = playlists.begin(); i != playlists.end(); ++i) {
3431 /* Only the top regions at start and end have to be cropped */
3432 boost::shared_ptr<Region> region_at_start = (*i)->top_region_at(start);
3433 boost::shared_ptr<Region> region_at_end = (*i)->top_region_at(end);
3435 vector<boost::shared_ptr<Region> > regions;
3437 if (region_at_start != 0) {
3438 regions.push_back (region_at_start);
3440 if (region_at_end != 0) {
3441 regions.push_back (region_at_end);
3444 /* now adjust lengths */
3445 for (vector<boost::shared_ptr<Region> >::iterator i = regions.begin(); i != regions.end(); ++i) {
3447 pos = (*i)->position();
3448 new_start = max (start, pos);
3449 if (max_samplepos - pos > (*i)->length()) {
3450 new_end = pos + (*i)->length() - 1;
3452 new_end = max_samplepos;
3454 new_end = min (end, new_end);
3455 new_length = new_end - new_start + 1;
3457 (*i)->clear_changes ();
3458 (*i)->trim_to (new_start, new_length);
3459 _session->add_command (new StatefulDiffCommand (*i));
3465 Editor::region_fill_track ()
3467 boost::shared_ptr<Playlist> playlist;
3468 RegionSelection regions = get_regions_from_selection_and_entered ();
3469 RegionSelection foo;
3471 samplepos_t const end = _session->current_end_sample ();
3473 if (regions.empty () || regions.end_sample () + 1 >= end) {
3477 samplepos_t const start_sample = regions.start ();
3478 samplepos_t const end_sample = regions.end_sample ();
3479 samplecnt_t const gap = end_sample - start_sample + 1;
3481 begin_reversible_command (Operations::region_fill);
3483 selection->clear_regions ();
3485 for (RegionSelection::iterator i = regions.begin(); i != regions.end(); ++i) {
3487 boost::shared_ptr<Region> r ((*i)->region());
3489 TimeAxisView& tv = (*i)->get_time_axis_view();
3490 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (&tv);
3491 latest_regionviews.clear ();
3492 sigc::connection c = rtv->view()->RegionViewAdded.connect (sigc::mem_fun(*this, &Editor::collect_new_region_view));
3494 samplepos_t const position = end_sample + (r->first_sample() - start_sample + 1);
3495 playlist = (*i)->region()->playlist();
3496 playlist->clear_changes ();
3497 playlist->duplicate_until (r, position, gap, end);
3498 _session->add_command(new StatefulDiffCommand (playlist));
3502 foo.insert (foo.end(), latest_regionviews.begin(), latest_regionviews.end());
3506 selection->set (foo);
3509 commit_reversible_command ();
3513 Editor::set_region_sync_position ()
3515 set_sync_point (get_preferred_edit_position (), get_regions_from_selection_and_edit_point ());
3519 Editor::set_sync_point (samplepos_t where, const RegionSelection& rs)
3521 bool in_command = false;
3523 for (RegionSelection::const_iterator r = rs.begin(); r != rs.end(); ++r) {
3525 if (!(*r)->region()->covers (where)) {
3529 boost::shared_ptr<Region> region ((*r)->region());
3532 begin_reversible_command (_("set sync point"));
3536 region->clear_changes ();
3537 region->set_sync_position (where);
3538 _session->add_command(new StatefulDiffCommand (region));
3542 commit_reversible_command ();
3546 /** Remove the sync positions of the selection */
3548 Editor::remove_region_sync ()
3550 RegionSelection rs = get_regions_from_selection_and_entered ();
3556 begin_reversible_command (_("remove region sync"));
3558 for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
3560 (*i)->region()->clear_changes ();
3561 (*i)->region()->clear_sync_position ();
3562 _session->add_command(new StatefulDiffCommand ((*i)->region()));
3565 commit_reversible_command ();
3569 Editor::naturalize_region ()
3571 RegionSelection rs = get_regions_from_selection_and_entered ();
3577 if (rs.size() > 1) {
3578 begin_reversible_command (_("move regions to original position"));
3580 begin_reversible_command (_("move region to original position"));
3583 for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
3584 (*i)->region()->clear_changes ();
3585 (*i)->region()->move_to_natural_position ();
3586 _session->add_command (new StatefulDiffCommand ((*i)->region()));
3589 commit_reversible_command ();
3593 Editor::align_regions (RegionPoint what)
3595 RegionSelection const rs = get_regions_from_selection_and_edit_point ();
3601 begin_reversible_command (_("align selection"));
3603 samplepos_t const position = get_preferred_edit_position ();
3605 for (RegionSelection::const_iterator i = rs.begin(); i != rs.end(); ++i) {
3606 align_region_internal ((*i)->region(), what, position);
3609 commit_reversible_command ();
3612 struct RegionSortByTime {
3613 bool operator() (const RegionView* a, const RegionView* b) {
3614 return a->region()->position() < b->region()->position();
3619 Editor::align_regions_relative (RegionPoint point)
3621 RegionSelection const rs = get_regions_from_selection_and_edit_point ();
3627 samplepos_t const position = get_preferred_edit_position ();
3629 samplepos_t distance = 0;
3630 samplepos_t pos = 0;
3633 list<RegionView*> sorted;
3634 rs.by_position (sorted);
3636 boost::shared_ptr<Region> r ((*sorted.begin())->region());
3641 if (position > r->position()) {
3642 distance = position - r->position();
3644 distance = r->position() - position;
3650 if (position > r->last_sample()) {
3651 distance = position - r->last_sample();
3652 pos = r->position() + distance;
3654 distance = r->last_sample() - position;
3655 pos = r->position() - distance;
3661 pos = r->adjust_to_sync (position);
3662 if (pos > r->position()) {
3663 distance = pos - r->position();
3665 distance = r->position() - pos;
3671 if (pos == r->position()) {
3675 begin_reversible_command (_("align selection (relative)"));
3677 /* move first one specially */
3679 r->clear_changes ();
3680 r->set_position (pos);
3681 _session->add_command(new StatefulDiffCommand (r));
3683 /* move rest by the same amount */
3687 for (list<RegionView*>::iterator i = sorted.begin(); i != sorted.end(); ++i) {
3689 boost::shared_ptr<Region> region ((*i)->region());
3691 region->clear_changes ();
3694 region->set_position (region->position() + distance);
3696 region->set_position (region->position() - distance);
3699 _session->add_command(new StatefulDiffCommand (region));
3703 commit_reversible_command ();
3707 Editor::align_region (boost::shared_ptr<Region> region, RegionPoint point, samplepos_t position)
3709 begin_reversible_command (_("align region"));
3710 align_region_internal (region, point, position);
3711 commit_reversible_command ();
3715 Editor::align_region_internal (boost::shared_ptr<Region> region, RegionPoint point, samplepos_t position)
3717 region->clear_changes ();
3721 region->set_position (region->adjust_to_sync (position));
3725 if (position > region->length()) {
3726 region->set_position (position - region->length());
3731 region->set_position (position);
3735 _session->add_command(new StatefulDiffCommand (region));
3739 Editor::trim_region_front ()
3745 Editor::trim_region_back ()
3747 trim_region (false);
3751 Editor::trim_region (bool front)
3753 samplepos_t where = get_preferred_edit_position();
3754 RegionSelection rs = get_regions_from_selection_and_edit_point ();
3760 begin_reversible_command (front ? _("trim front") : _("trim back"));
3762 for (list<RegionView*>::const_iterator i = rs.by_layer().begin(); i != rs.by_layer().end(); ++i) {
3763 if (!(*i)->region()->locked()) {
3765 (*i)->region()->clear_changes ();
3768 (*i)->region()->trim_front (where);
3770 (*i)->region()->trim_end (where);
3773 _session->add_command (new StatefulDiffCommand ((*i)->region()));
3777 commit_reversible_command ();
3780 /** Trim the end of the selected regions to the position of the edit cursor */
3782 Editor::trim_region_to_loop ()
3784 Location* loc = _session->locations()->auto_loop_location();
3788 trim_region_to_location (*loc, _("trim to loop"));
3792 Editor::trim_region_to_punch ()
3794 Location* loc = _session->locations()->auto_punch_location();
3798 trim_region_to_location (*loc, _("trim to punch"));
3802 Editor::trim_region_to_location (const Location& loc, const char* str)
3804 RegionSelection rs = get_regions_from_selection_and_entered ();
3805 bool in_command = false;
3807 for (RegionSelection::iterator x = rs.begin(); x != rs.end(); ++x) {
3808 RegionView* rv = (*x);
3810 /* require region to span proposed trim */
3811 switch (rv->region()->coverage (loc.start(), loc.end())) {
3812 case Evoral::OverlapInternal:
3818 RouteTimeAxisView* tav = dynamic_cast<RouteTimeAxisView*> (&rv->get_time_axis_view());
3826 start = loc.start();
3829 rv->region()->clear_changes ();
3830 rv->region()->trim_to (start, (end - start));
3833 begin_reversible_command (str);
3836 _session->add_command(new StatefulDiffCommand (rv->region()));
3840 commit_reversible_command ();
3845 Editor::trim_region_to_previous_region_end ()
3847 return trim_to_region(false);
3851 Editor::trim_region_to_next_region_start ()
3853 return trim_to_region(true);
3857 Editor::trim_to_region(bool forward)
3859 RegionSelection rs = get_regions_from_selection_and_entered ();
3860 bool in_command = false;
3862 boost::shared_ptr<Region> next_region;
3864 for (RegionSelection::iterator x = rs.begin(); x != rs.end(); ++x) {
3866 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (*x);
3872 AudioTimeAxisView* atav = dynamic_cast<AudioTimeAxisView*> (&arv->get_time_axis_view());
3878 boost::shared_ptr<Region> region = arv->region();
3879 boost::shared_ptr<Playlist> playlist (region->playlist());
3881 region->clear_changes ();
3885 next_region = playlist->find_next_region (region->first_sample(), Start, 1);
3891 region->trim_end (next_region->first_sample() - 1);
3892 arv->region_changed (PropertyChange (ARDOUR::Properties::length));
3896 next_region = playlist->find_next_region (region->first_sample(), Start, 0);
3902 region->trim_front (next_region->last_sample() + 1);
3903 arv->region_changed (ARDOUR::bounds_change);
3907 begin_reversible_command (_("trim to region"));
3910 _session->add_command(new StatefulDiffCommand (region));
3914 commit_reversible_command ();
3919 Editor::unfreeze_route ()
3921 if (clicked_routeview == 0 || !clicked_routeview->is_track()) {
3925 clicked_routeview->track()->unfreeze ();
3929 Editor::_freeze_thread (void* arg)
3931 return static_cast<Editor*>(arg)->freeze_thread ();
3935 Editor::freeze_thread ()
3937 /* create event pool because we may need to talk to the session */
3938 SessionEvent::create_per_thread_pool ("freeze events", 64);
3939 /* create per-thread buffers for process() tree to use */
3940 clicked_routeview->audio_track()->freeze_me (*current_interthread_info);
3941 current_interthread_info->done = true;
3946 Editor::freeze_route ()
3952 /* stop transport before we start. this is important */
3954 _session->request_transport_speed (0.0);
3956 /* wait for just a little while, because the above call is asynchronous */
3958 Glib::usleep (250000);
3960 if (clicked_routeview == 0 || !clicked_routeview->is_audio_track()) {
3964 if (!clicked_routeview->track()->bounceable (clicked_routeview->track()->main_outs(), true)) {
3966 _("This track/bus cannot be frozen because the signal adds or loses channels before reaching the outputs.\n"
3967 "This is typically caused by plugins that generate stereo output from mono input or vice versa.")
3969 d.set_title (_("Cannot freeze"));
3974 if (clicked_routeview->track()->has_external_redirects()) {
3975 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"
3976 "Freezing will only process the signal as far as the first send/insert/return."),
3977 clicked_routeview->track()->name()), true, MESSAGE_INFO, BUTTONS_NONE, true);
3979 d.add_button (_("Freeze anyway"), Gtk::RESPONSE_OK);
3980 d.add_button (_("Don't freeze"), Gtk::RESPONSE_CANCEL);
3981 d.set_title (_("Freeze Limits"));
3983 int response = d.run ();
3986 case Gtk::RESPONSE_CANCEL:
3993 InterThreadInfo itt;
3994 current_interthread_info = &itt;
3996 InterthreadProgressWindow ipw (current_interthread_info, _("Freeze"), _("Cancel Freeze"));
3998 pthread_create_and_store (X_("freezer"), &itt.thread, _freeze_thread, this);
4000 CursorContext::Handle cursor_ctx = CursorContext::create(*this, _cursors->wait);
4002 while (!itt.done && !itt.cancel) {
4003 gtk_main_iteration ();
4006 pthread_join (itt.thread, 0);
4007 current_interthread_info = 0;
4011 Editor::bounce_range_selection (bool replace, bool enable_processing)
4013 if (selection->time.empty()) {
4017 TrackSelection views = selection->tracks;
4019 for (TrackViewList::iterator i = views.begin(); i != views.end(); ++i) {
4021 if (enable_processing) {
4023 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (*i);
4025 if (rtv && rtv->track() && replace && enable_processing && !rtv->track()->bounceable (rtv->track()->main_outs(), false)) {
4027 _("You can't perform this operation because the processing of the signal "
4028 "will cause one or more of the tracks to end up with a region with more channels than this track has inputs.\n\n"
4029 "You can do this without processing, which is a different operation.")
4031 d.set_title (_("Cannot bounce"));
4038 samplepos_t start = selection->time[clicked_selection].start;
4039 samplepos_t end = selection->time[clicked_selection].end;
4040 samplepos_t cnt = end - start + 1;
4041 bool in_command = false;
4043 for (TrackViewList::iterator i = views.begin(); i != views.end(); ++i) {
4045 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (*i);
4051 boost::shared_ptr<Playlist> playlist;
4053 if ((playlist = rtv->playlist()) == 0) {
4057 InterThreadInfo itt;
4059 playlist->clear_changes ();
4060 playlist->clear_owned_changes ();
4062 boost::shared_ptr<Region> r;
4064 if (enable_processing) {
4065 r = rtv->track()->bounce_range (start, start+cnt, itt, rtv->track()->main_outs(), false);
4067 r = rtv->track()->bounce_range (start, start+cnt, itt, boost::shared_ptr<Processor>(), false);
4075 list<AudioRange> ranges;
4076 ranges.push_back (AudioRange (start, start+cnt, 0));
4077 playlist->cut (ranges); // discard result
4078 playlist->add_region (r, start);
4082 begin_reversible_command (_("bounce range"));
4085 vector<Command*> cmds;
4086 playlist->rdiff (cmds);
4087 _session->add_commands (cmds);
4089 _session->add_command (new StatefulDiffCommand (playlist));
4093 commit_reversible_command ();
4097 /** Delete selected regions, automation points or a time range */
4101 //special case: if the user is pointing in the editor/mixer strip, they may be trying to delete a plugin.
4102 //we need this because the editor-mixer strip is in the editor window, so it doesn't get the bindings from the mix window
4103 bool deleted = false;
4104 if (current_mixer_strip && current_mixer_strip == MixerStrip::entered_mixer_strip())
4105 deleted = current_mixer_strip->delete_processors ();
4111 /** Cut selected regions, automation points or a time range */
4118 /** Copy selected regions, automation points or a time range */
4126 /** @return true if a Cut, Copy or Clear is possible */
4128 Editor::can_cut_copy () const
4130 if (!selection->time.empty() || !selection->regions.empty() || !selection->points.empty())
4137 /** Cut, copy or clear selected regions, automation points or a time range.
4138 * @param op Operation (Delete, Cut, Copy or Clear)
4141 Editor::cut_copy (CutCopyOp op)
4143 /* only cancel selection if cut/copy is successful.*/
4149 opname = _("delete");
4158 opname = _("clear");
4162 /* if we're deleting something, and the mouse is still pressed,
4163 the thing we started a drag for will be gone when we release
4164 the mouse button(s). avoid this. see part 2 at the end of
4168 if (op == Delete || op == Cut || op == Clear) {
4169 if (_drags->active ()) {
4174 if (op != Delete) { //"Delete" doesn't change copy/paste buf
4175 cut_buffer->clear ();
4178 if (entered_marker) {
4180 /* cut/delete op while pointing at a marker */
4183 Location* loc = find_location_from_marker (entered_marker, ignored);
4185 if (_session && loc) {
4186 entered_marker = NULL;
4187 Glib::signal_idle().connect (sigc::bind (sigc::mem_fun(*this, &Editor::really_remove_marker), loc));
4194 switch (mouse_mode) {
4197 begin_reversible_command (opname + ' ' + X_("MIDI"));
4199 commit_reversible_command ();
4205 bool did_edit = false;
4207 if (!selection->regions.empty() || !selection->points.empty()) {
4208 begin_reversible_command (opname + ' ' + _("objects"));
4211 if (!selection->regions.empty()) {
4212 cut_copy_regions (op, selection->regions);
4214 if (op == Cut || op == Delete) {
4215 selection->clear_regions ();
4219 if (!selection->points.empty()) {
4220 cut_copy_points (op);
4222 if (op == Cut || op == Delete) {
4223 selection->clear_points ();
4226 } else if (selection->time.empty()) {
4227 samplepos_t start, end;
4228 /* no time selection, see if we can get an edit range
4231 if (get_edit_op_range (start, end)) {
4232 selection->set (start, end);
4234 } else if (!selection->time.empty()) {
4235 begin_reversible_command (opname + ' ' + _("range"));
4238 cut_copy_ranges (op);
4240 if (op == Cut || op == Delete) {
4241 selection->clear_time ();
4246 /* reset repeated paste state */
4248 last_paste_pos = -1;
4249 commit_reversible_command ();
4252 if (op == Delete || op == Cut || op == Clear) {
4258 struct AutomationRecord {
4259 AutomationRecord () : state (0) , line(NULL) {}
4260 AutomationRecord (XMLNode* s, const AutomationLine* l) : state (s) , line (l) {}
4262 XMLNode* state; ///< state before any operation
4263 const AutomationLine* line; ///< line this came from
4264 boost::shared_ptr<Evoral::ControlList> copy; ///< copied events for the cut buffer
4267 struct PointsSelectionPositionSorter {
4268 bool operator() (ControlPoint* a, ControlPoint* b) {
4269 return (*(a->model()))->when < (*(b->model()))->when;
4273 /** Cut, copy or clear selected automation points.
4274 * @param op Operation (Cut, Copy or Clear)
4277 Editor::cut_copy_points (Editing::CutCopyOp op, Temporal::Beats earliest, bool midi)
4279 if (selection->points.empty ()) {
4283 /* XXX: not ideal, as there may be more than one track involved in the point selection */
4284 _last_cut_copy_source_track = &selection->points.front()->line().trackview;
4286 /* Keep a record of the AutomationLists that we end up using in this operation */
4287 typedef std::map<boost::shared_ptr<AutomationList>, AutomationRecord> Lists;
4290 /* user could select points in any order */
4291 selection->points.sort(PointsSelectionPositionSorter ());
4293 /* Go through all selected points, making an AutomationRecord for each distinct AutomationList */
4294 for (PointSelection::iterator sel_point = selection->points.begin(); sel_point != selection->points.end(); ++sel_point) {
4295 const AutomationLine& line = (*sel_point)->line();
4296 const boost::shared_ptr<AutomationList> al = line.the_list();
4297 if (lists.find (al) == lists.end ()) {
4298 /* We haven't seen this list yet, so make a record for it. This includes
4299 taking a copy of its current state, in case this is needed for undo later.
4301 lists[al] = AutomationRecord (&al->get_state (), &line);
4305 if (op == Cut || op == Copy) {
4306 /* This operation will involve putting things in the cut buffer, so create an empty
4307 ControlList for each of our source lists to put the cut buffer data in.
4309 for (Lists::iterator i = lists.begin(); i != lists.end(); ++i) {
4310 i->second.copy = i->first->create (i->first->parameter (), i->first->descriptor());
4313 /* Add all selected points to the relevant copy ControlLists */
4314 MusicSample start (std::numeric_limits<samplepos_t>::max(), 0);
4315 for (PointSelection::iterator sel_point = selection->points.begin(); sel_point != selection->points.end(); ++sel_point) {
4316 boost::shared_ptr<AutomationList> al = (*sel_point)->line().the_list();
4317 AutomationList::const_iterator ctrl_evt = (*sel_point)->model ();
4319 lists[al].copy->fast_simple_add ((*ctrl_evt)->when, (*ctrl_evt)->value);
4321 /* Update earliest MIDI start time in beats */
4322 earliest = std::min(earliest, Temporal::Beats((*ctrl_evt)->when));
4324 /* Update earliest session start time in samples */
4325 start.sample = std::min(start.sample, (*sel_point)->line().session_position(ctrl_evt));
4329 /* Snap start time backwards, so copy/paste is snap aligned. */
4331 if (earliest == std::numeric_limits<Temporal::Beats>::max()) {
4332 earliest = Temporal::Beats(); // Weird... don't offset
4334 earliest.round_down_to_beat();
4336 if (start.sample == std::numeric_limits<double>::max()) {
4337 start.sample = 0; // Weird... don't offset
4339 snap_to(start, RoundDownMaybe);
4342 const double line_offset = midi ? earliest.to_double() : start.sample;
4343 for (Lists::iterator i = lists.begin(); i != lists.end(); ++i) {
4344 /* Correct this copy list so that it is relative to the earliest
4345 start time, so relative ordering between points is preserved
4346 when copying from several lists and the paste starts at the
4347 earliest copied piece of data. */
4348 boost::shared_ptr<Evoral::ControlList> &al_cpy = i->second.copy;
4349 for (AutomationList::iterator ctrl_evt = al_cpy->begin(); ctrl_evt != al_cpy->end(); ++ctrl_evt) {
4350 (*ctrl_evt)->when -= line_offset;
4353 /* And add it to the cut buffer */
4354 cut_buffer->add (al_cpy);
4358 if (op == Delete || op == Cut) {
4359 /* This operation needs to remove things from the main AutomationList, so do that now */
4361 for (Lists::iterator i = lists.begin(); i != lists.end(); ++i) {
4362 i->first->freeze ();
4365 /* Remove each selected point from its AutomationList */
4366 for (PointSelection::iterator sel_point = selection->points.begin(); sel_point != selection->points.end(); ++sel_point) {
4367 AutomationLine& line = (*sel_point)->line ();
4368 boost::shared_ptr<AutomationList> al = line.the_list();
4372 if (dynamic_cast<AudioRegionGainLine*> (&line)) {
4373 /* removing of first and last gain point in region gain lines is prohibited*/
4374 if (line.is_last_point (*(*sel_point)) || line.is_first_point (*(*sel_point))) {
4380 al->erase ((*sel_point)->model ());
4384 /* Thaw the lists and add undo records for them */
4385 for (Lists::iterator i = lists.begin(); i != lists.end(); ++i) {
4386 boost::shared_ptr<AutomationList> al = i->first;
4388 _session->add_command (new MementoCommand<AutomationList> (*al.get(), i->second.state, &(al->get_state ())));
4393 /** Cut, copy or clear selected automation points.
4394 * @param op Operation (Cut, Copy or Clear)
4397 Editor::cut_copy_midi (CutCopyOp op)
4399 Temporal::Beats earliest = std::numeric_limits<Temporal::Beats>::max();
4400 for (MidiRegionSelection::iterator i = selection->midi_regions.begin(); i != selection->midi_regions.end(); ++i) {
4401 MidiRegionView* mrv = dynamic_cast<MidiRegionView*>(*i);
4403 if (!mrv->selection().empty()) {
4404 earliest = std::min(earliest, (*mrv->selection().begin())->note()->time());
4406 mrv->cut_copy_clear (op);
4408 /* XXX: not ideal, as there may be more than one track involved in the selection */
4409 _last_cut_copy_source_track = &mrv->get_time_axis_view();
4413 if (!selection->points.empty()) {
4414 cut_copy_points (op, earliest, true);
4415 if (op == Cut || op == Delete) {
4416 selection->clear_points ();
4421 struct lt_playlist {
4422 bool operator () (const PlaylistState& a, const PlaylistState& b) {
4423 return a.playlist < b.playlist;
4427 struct PlaylistMapping {
4429 boost::shared_ptr<Playlist> pl;
4431 PlaylistMapping (TimeAxisView* tvp) : tv (tvp) {}
4434 /** Remove `clicked_regionview' */
4436 Editor::remove_clicked_region ()
4438 if (clicked_routeview == 0 || clicked_regionview == 0) {
4442 begin_reversible_command (_("remove region"));
4444 boost::shared_ptr<Playlist> playlist = clicked_routeview->playlist();
4446 playlist->clear_changes ();
4447 playlist->clear_owned_changes ();
4448 playlist->remove_region (clicked_regionview->region());
4449 if (Config->get_edit_mode() == Ripple)
4450 playlist->ripple (clicked_regionview->region()->position(), -clicked_regionview->region()->length(), boost::shared_ptr<Region>());
4452 /* We might have removed regions, which alters other regions' layering_index,
4453 so we need to do a recursive diff here.
4455 vector<Command*> cmds;
4456 playlist->rdiff (cmds);
4457 _session->add_commands (cmds);
4459 _session->add_command(new StatefulDiffCommand (playlist));
4460 commit_reversible_command ();
4464 /** Remove the selected regions */
4466 Editor::remove_selected_regions ()
4468 RegionSelection rs = get_regions_from_selection_and_entered ();
4470 if (!_session || rs.empty()) {
4474 list<boost::shared_ptr<Region> > regions_to_remove;
4476 for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
4477 // we can't just remove the region(s) in this loop because
4478 // this removes them from the RegionSelection, and they thus
4479 // disappear from underneath the iterator, and the ++i above
4480 // SEGVs in a puzzling fashion.
4482 // so, first iterate over the regions to be removed from rs and
4483 // add them to the regions_to_remove list, and then
4484 // iterate over the list to actually remove them.
4486 regions_to_remove.push_back ((*i)->region());
4489 vector<boost::shared_ptr<Playlist> > playlists;
4491 for (list<boost::shared_ptr<Region> >::iterator rl = regions_to_remove.begin(); rl != regions_to_remove.end(); ++rl) {
4493 boost::shared_ptr<Playlist> playlist = (*rl)->playlist();
4496 // is this check necessary?
4500 /* get_regions_from_selection_and_entered() guarantees that
4501 the playlists involved are unique, so there is no need
4505 playlists.push_back (playlist);
4507 playlist->clear_changes ();
4508 playlist->clear_owned_changes ();
4509 playlist->freeze ();
4510 playlist->remove_region (*rl);
4511 if (Config->get_edit_mode() == Ripple)
4512 playlist->ripple ((*rl)->position(), -(*rl)->length(), boost::shared_ptr<Region>());
4516 vector<boost::shared_ptr<Playlist> >::iterator pl;
4517 bool in_command = false;
4519 for (pl = playlists.begin(); pl != playlists.end(); ++pl) {
4522 /* We might have removed regions, which alters other regions' layering_index,
4523 so we need to do a recursive diff here.
4527 begin_reversible_command (_("remove region"));
4530 vector<Command*> cmds;
4531 (*pl)->rdiff (cmds);
4532 _session->add_commands (cmds);
4534 _session->add_command(new StatefulDiffCommand (*pl));
4538 commit_reversible_command ();
4542 /** Cut, copy or clear selected regions.
4543 * @param op Operation (Cut, Copy or Clear)
4546 Editor::cut_copy_regions (CutCopyOp op, RegionSelection& rs)
4548 /* we can't use a std::map here because the ordering is important, and we can't trivially sort
4549 a map when we want ordered access to both elements. i think.
4552 vector<PlaylistMapping> pmap;
4554 samplepos_t first_position = max_samplepos;
4556 typedef set<boost::shared_ptr<Playlist> > FreezeList;
4557 FreezeList freezelist;
4559 /* get ordering correct before we cut/copy */
4561 rs.sort_by_position_and_track ();
4563 for (RegionSelection::iterator x = rs.begin(); x != rs.end(); ++x) {
4565 first_position = min ((samplepos_t) (*x)->region()->position(), first_position);
4567 if (op == Cut || op == Clear || op == Delete) {
4568 boost::shared_ptr<Playlist> pl = (*x)->region()->playlist();
4571 FreezeList::iterator fl;
4573 // only take state if this is a new playlist.
4574 for (fl = freezelist.begin(); fl != freezelist.end(); ++fl) {
4580 if (fl == freezelist.end()) {
4581 pl->clear_changes();
4582 pl->clear_owned_changes ();
4584 freezelist.insert (pl);
4589 TimeAxisView* tv = &(*x)->get_time_axis_view();
4590 vector<PlaylistMapping>::iterator z;
4592 for (z = pmap.begin(); z != pmap.end(); ++z) {
4593 if ((*z).tv == tv) {
4598 if (z == pmap.end()) {
4599 pmap.push_back (PlaylistMapping (tv));
4603 for (RegionSelection::iterator x = rs.begin(); x != rs.end(); ) {
4605 boost::shared_ptr<Playlist> pl = (*x)->region()->playlist();
4608 /* region not yet associated with a playlist (e.g. unfinished
4615 TimeAxisView& tv = (*x)->get_time_axis_view();
4616 boost::shared_ptr<Playlist> npl;
4617 RegionSelection::iterator tmp;
4624 vector<PlaylistMapping>::iterator z;
4626 for (z = pmap.begin(); z != pmap.end(); ++z) {
4627 if ((*z).tv == &tv) {
4632 assert (z != pmap.end());
4635 npl = PlaylistFactory::create (pl->data_type(), *_session, "cutlist", true);
4643 boost::shared_ptr<Region> r = (*x)->region();
4644 boost::shared_ptr<Region> _xx;
4650 pl->remove_region (r);
4651 if (Config->get_edit_mode() == Ripple)
4652 pl->ripple (r->position(), -r->length(), boost::shared_ptr<Region>());
4656 _xx = RegionFactory::create (r);
4657 npl->add_region (_xx, r->position() - first_position);
4658 pl->remove_region (r);
4659 if (Config->get_edit_mode() == Ripple)
4660 pl->ripple (r->position(), -r->length(), boost::shared_ptr<Region>());
4664 /* copy region before adding, so we're not putting same object into two different playlists */
4665 npl->add_region (RegionFactory::create (r), r->position() - first_position);
4669 pl->remove_region (r);
4670 if (Config->get_edit_mode() == Ripple)
4671 pl->ripple (r->position(), -r->length(), boost::shared_ptr<Region>());
4680 list<boost::shared_ptr<Playlist> > foo;
4682 /* the pmap is in the same order as the tracks in which selected regions occurred */
4684 for (vector<PlaylistMapping>::iterator i = pmap.begin(); i != pmap.end(); ++i) {
4687 foo.push_back ((*i).pl);
4692 cut_buffer->set (foo);
4696 _last_cut_copy_source_track = 0;
4698 _last_cut_copy_source_track = pmap.front().tv;
4702 for (FreezeList::iterator pl = freezelist.begin(); pl != freezelist.end(); ++pl) {
4705 /* We might have removed regions, which alters other regions' layering_index,
4706 so we need to do a recursive diff here.
4708 vector<Command*> cmds;
4709 (*pl)->rdiff (cmds);
4710 _session->add_commands (cmds);
4712 _session->add_command (new StatefulDiffCommand (*pl));
4717 Editor::cut_copy_ranges (CutCopyOp op)
4719 TrackViewList ts = selection->tracks.filter_to_unique_playlists ();
4721 /* Sort the track selection now, so that it if is used, the playlists
4722 selected by the calls below to cut_copy_clear are in the order that
4723 their tracks appear in the editor. This makes things like paste
4724 of ranges work properly.
4727 sort_track_selection (ts);
4730 if (!entered_track) {
4733 ts.push_back (entered_track);
4736 for (TrackViewList::iterator i = ts.begin(); i != ts.end(); ++i) {
4737 (*i)->cut_copy_clear (*selection, op);
4742 Editor::paste (float times, bool from_context)
4744 DEBUG_TRACE (DEBUG::CutNPaste, "paste to preferred edit pos\n");
4745 MusicSample where (get_preferred_edit_position (EDIT_IGNORE_NONE, from_context), 0);
4746 paste_internal (where.sample, times, 0);
4750 Editor::mouse_paste ()
4752 MusicSample where (0, 0);
4754 if (!mouse_sample (where.sample, ignored)) {
4759 paste_internal (where.sample, 1, where.division);
4763 Editor::paste_internal (samplepos_t position, float times, const int32_t sub_num)
4765 DEBUG_TRACE (DEBUG::CutNPaste, string_compose ("apparent paste position is %1\n", position));
4767 if (cut_buffer->empty(internal_editing())) {
4771 if (position == max_samplepos) {
4772 position = get_preferred_edit_position();
4773 DEBUG_TRACE (DEBUG::CutNPaste, string_compose ("preferred edit position is %1\n", position));
4776 if (position != last_paste_pos) {
4777 /* paste in new location, reset repeated paste state */
4779 last_paste_pos = position;
4782 /* get everything in the correct order */
4785 if (!selection->tracks.empty()) {
4786 /* If there is a track selection, paste into exactly those tracks and
4787 * only those tracks. This allows the user to be explicit and override
4788 * the below "do the reasonable thing" logic. */
4789 ts = selection->tracks.filter_to_unique_playlists ();
4790 sort_track_selection (ts);
4792 /* Figure out which track to base the paste at. */
4793 TimeAxisView* base_track = NULL;
4794 if (_edit_point == Editing::EditAtMouse && entered_track) {
4795 /* With the mouse edit point, paste onto the track under the mouse. */
4796 base_track = entered_track;
4797 } else if (_edit_point == Editing::EditAtMouse && entered_regionview) {
4798 /* With the mouse edit point, paste onto the track of the region under the mouse. */
4799 base_track = &entered_regionview->get_time_axis_view();
4800 } else if (_last_cut_copy_source_track) {
4801 /* Paste to the track that the cut/copy came from (see mantis #333). */
4802 base_track = _last_cut_copy_source_track;
4804 /* This is "impossible" since we've copied... well, do nothing. */
4808 /* Walk up to parent if necessary, so base track is a route. */
4809 while (base_track->get_parent()) {
4810 base_track = base_track->get_parent();
4813 /* Add base track and all tracks below it. The paste logic will select
4814 the appropriate object types from the cut buffer in relative order. */
4815 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
4816 if ((*i)->order() >= base_track->order()) {
4821 /* Sort tracks so the nth track of type T will pick the nth object of type T. */
4822 sort_track_selection (ts);
4824 /* Add automation children of each track in order, for pasting several lines. */
4825 for (TrackViewList::iterator i = ts.begin(); i != ts.end();) {
4826 /* Add any automation children for pasting several lines */
4827 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*>(*i++);
4832 typedef RouteTimeAxisView::AutomationTracks ATracks;
4833 const ATracks& atracks = rtv->automation_tracks();
4834 for (ATracks::const_iterator a = atracks.begin(); a != atracks.end(); ++a) {
4835 i = ts.insert(i, a->second.get());
4840 /* We now have a list of trackviews starting at base_track, including
4841 automation children, in the order shown in the editor, e.g. R1,
4842 R1.A1, R1.A2, R2, R2.A1, ... */
4845 begin_reversible_command (Operations::paste);
4847 if (ts.size() == 1 && cut_buffer->lines.size() == 1 &&
4848 dynamic_cast<AutomationTimeAxisView*>(ts.front())) {
4849 /* Only one line copied, and one automation track selected. Do a
4850 "greedy" paste from one automation type to another. */
4852 PasteContext ctx(paste_count, times, ItemCounts(), true);
4853 ts.front()->paste (position, *cut_buffer, ctx, sub_num);
4857 /* Paste into tracks */
4859 PasteContext ctx(paste_count, times, ItemCounts(), false);
4860 for (TrackViewList::iterator i = ts.begin(); i != ts.end(); ++i) {
4861 (*i)->paste (position, *cut_buffer, ctx, sub_num);
4867 commit_reversible_command ();
4871 Editor::duplicate_regions (float times)
4873 RegionSelection rs (get_regions_from_selection_and_entered());
4874 duplicate_some_regions (rs, times);
4878 Editor::duplicate_some_regions (RegionSelection& regions, float times)
4880 if (regions.empty ()) {
4884 boost::shared_ptr<Playlist> playlist;
4885 std::set<boost::shared_ptr<Playlist> > playlists; // list of unique playlists affected by duplication
4886 RegionSelection sel = regions; // clear (below) may clear the argument list if its the current region selection
4887 RegionSelection foo;
4889 samplepos_t const start_sample = regions.start ();
4890 samplepos_t const end_sample = regions.end_sample ();
4891 samplecnt_t const span = end_sample - start_sample + 1;
4893 begin_reversible_command (Operations::duplicate_region);
4895 selection->clear_regions ();
4897 /* ripple first so that we don't move the duplicates that will be added */
4899 if (Config->get_edit_mode() == Ripple) {
4901 /* convert RegionSelection into RegionList so that we can pass it to ripple and exclude the regions we will duplicate */
4905 for (RegionSelection::iterator i = sel.begin(); i != sel.end(); ++i) {
4906 exclude.push_back ((*i)->region());
4907 playlist = (*i)->region()->playlist();
4908 if (playlists.insert (playlist).second) {
4909 /* successfully inserted into set, so it's the first time we've seen this playlist */
4910 playlist->clear_changes ();
4914 for (set<boost::shared_ptr<Playlist> >::iterator p = playlists.begin(); p != playlists.end(); ++p) {
4915 (*p)->ripple (start_sample, span * times, &exclude);
4919 for (RegionSelection::iterator i = sel.begin(); i != sel.end(); ++i) {
4921 boost::shared_ptr<Region> r ((*i)->region());
4923 TimeAxisView& tv = (*i)->get_time_axis_view();
4924 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (&tv);
4925 latest_regionviews.clear ();
4926 sigc::connection c = rtv->view()->RegionViewAdded.connect (sigc::mem_fun(*this, &Editor::collect_new_region_view));
4928 samplepos_t const position = end_sample + (r->first_sample() - start_sample + 1);
4929 playlist = (*i)->region()->playlist();
4931 if (Config->get_edit_mode() != Ripple) {
4932 if (playlists.insert (playlist).second) {
4933 playlist->clear_changes ();
4937 playlist->duplicate (r, position, span, times);
4941 foo.insert (foo.end(), latest_regionviews.begin(), latest_regionviews.end());
4944 for (set<boost::shared_ptr<Playlist> >::iterator p = playlists.begin(); p != playlists.end(); ++p) {
4945 _session->add_command (new StatefulDiffCommand (*p));
4946 vector<Command*> cmds;
4948 _session->add_commands (cmds);
4952 selection->set (foo);
4955 commit_reversible_command ();
4959 Editor::duplicate_selection (float times)
4961 if (selection->time.empty() || selection->tracks.empty()) {
4965 boost::shared_ptr<Playlist> playlist;
4967 TrackViewList ts = selection->tracks.filter_to_unique_playlists ();
4969 bool in_command = false;
4971 for (TrackViewList::iterator i = ts.begin(); i != ts.end(); ++i) {
4972 if ((playlist = (*i)->playlist()) == 0) {
4975 playlist->clear_changes ();
4977 if (clicked_selection) {
4978 playlist->duplicate_range (selection->time[clicked_selection], times);
4980 playlist->duplicate_ranges (selection->time, times);
4984 begin_reversible_command (_("duplicate range selection"));
4987 _session->add_command (new StatefulDiffCommand (playlist));
4992 if (times == 1.0f) {
4993 // now "move" range selection to after the current range selection
4994 samplecnt_t distance = 0;
4996 if (clicked_selection) {
4998 selection->time[clicked_selection].end - selection->time[clicked_selection].start;
5000 distance = selection->time.end_sample () - selection->time.start ();
5003 selection->move_time (distance);
5005 commit_reversible_command ();
5009 /** Reset all selected points to the relevant default value */
5011 Editor::reset_point_selection ()
5013 for (PointSelection::iterator i = selection->points.begin(); i != selection->points.end(); ++i) {
5014 ARDOUR::AutomationList::iterator j = (*i)->model ();
5015 (*j)->value = (*i)->line().the_list()->descriptor ().normal;
5020 Editor::center_playhead ()
5022 float const page = _visible_canvas_width * samples_per_pixel;
5023 center_screen_internal (playhead_cursor->current_sample (), page);
5027 Editor::center_edit_point ()
5029 float const page = _visible_canvas_width * samples_per_pixel;
5030 center_screen_internal (get_preferred_edit_position(), page);
5033 /** Caller must begin and commit a reversible command */
5035 Editor::clear_playlist (boost::shared_ptr<Playlist> playlist)
5037 playlist->clear_changes ();
5039 _session->add_command (new StatefulDiffCommand (playlist));
5043 Editor::nudge_track (bool use_edit, bool forwards)
5045 boost::shared_ptr<Playlist> playlist;
5046 samplepos_t distance;
5047 samplepos_t next_distance;
5051 start = get_preferred_edit_position();
5056 if ((distance = get_nudge_distance (start, next_distance)) == 0) {
5060 if (selection->tracks.empty()) {
5064 TrackViewList ts = selection->tracks.filter_to_unique_playlists ();
5065 bool in_command = false;
5067 for (TrackViewList::iterator i = ts.begin(); i != ts.end(); ++i) {
5069 if ((playlist = (*i)->playlist()) == 0) {
5073 playlist->clear_changes ();
5074 playlist->clear_owned_changes ();
5076 playlist->nudge_after (start, distance, forwards);
5079 begin_reversible_command (_("nudge track"));
5082 vector<Command*> cmds;
5084 playlist->rdiff (cmds);
5085 _session->add_commands (cmds);
5087 _session->add_command (new StatefulDiffCommand (playlist));
5091 commit_reversible_command ();
5096 Editor::remove_last_capture ()
5098 vector<string> choices;
5105 if (Config->get_verify_remove_last_capture()) {
5106 prompt = _("Do you really want to destroy the last capture?"
5107 "\n(This is destructive and cannot be undone)");
5109 choices.push_back (_("No, do nothing."));
5110 choices.push_back (_("Yes, destroy it."));
5112 Choice prompter (_("Destroy last capture"), prompt, choices);
5114 if (prompter.run () == 1) {
5115 _session->remove_last_capture ();
5116 _regions->redisplay ();
5120 _session->remove_last_capture();
5121 _regions->redisplay ();
5126 Editor::normalize_region ()
5132 RegionSelection rs = get_regions_from_selection_and_entered ();
5138 NormalizeDialog dialog (rs.size() > 1);
5140 if (dialog.run () != RESPONSE_ACCEPT) {
5144 CursorContext::Handle cursor_ctx = CursorContext::create(*this, _cursors->wait);
5147 /* XXX: should really only count audio regions here */
5148 int const regions = rs.size ();
5150 /* Make a list of the selected audio regions' maximum amplitudes, and also
5151 obtain the maximum amplitude of them all.
5153 list<double> max_amps;
5154 list<double> rms_vals;
5157 bool use_rms = dialog.constrain_rms ();
5159 for (RegionSelection::const_iterator i = rs.begin(); i != rs.end(); ++i) {
5160 AudioRegionView const * arv = dynamic_cast<AudioRegionView const *> (*i);
5164 dialog.descend (1.0 / regions);
5165 double const a = arv->audio_region()->maximum_amplitude (&dialog);
5167 double r = arv->audio_region()->rms (&dialog);
5168 max_rms = max (max_rms, r);
5169 rms_vals.push_back (r);
5173 /* the user cancelled the operation */
5177 max_amps.push_back (a);
5178 max_amp = max (max_amp, a);
5182 list<double>::const_iterator a = max_amps.begin ();
5183 list<double>::const_iterator l = rms_vals.begin ();
5184 bool in_command = false;
5186 for (RegionSelection::iterator r = rs.begin(); r != rs.end(); ++r) {
5187 AudioRegionView* const arv = dynamic_cast<AudioRegionView*> (*r);
5192 arv->region()->clear_changes ();
5194 double amp = dialog.normalize_individually() ? *a : max_amp;
5195 double target = dialog.target_peak (); // dB
5198 double const amp_rms = dialog.normalize_individually() ? *l : max_rms;
5199 const double t_rms = dialog.target_rms ();
5200 const gain_t c_peak = dB_to_coefficient (target);
5201 const gain_t c_rms = dB_to_coefficient (t_rms);
5202 if ((amp_rms / c_rms) > (amp / c_peak)) {
5208 arv->audio_region()->normalize (amp, target);
5211 begin_reversible_command (_("normalize"));
5214 _session->add_command (new StatefulDiffCommand (arv->region()));
5221 commit_reversible_command ();
5227 Editor::reset_region_scale_amplitude ()
5233 RegionSelection rs = get_regions_from_selection_and_entered ();
5239 bool in_command = false;
5241 for (RegionSelection::iterator r = rs.begin(); r != rs.end(); ++r) {
5242 AudioRegionView* const arv = dynamic_cast<AudioRegionView*>(*r);
5245 arv->region()->clear_changes ();
5246 arv->audio_region()->set_scale_amplitude (1.0f);
5249 begin_reversible_command ("reset gain");
5252 _session->add_command (new StatefulDiffCommand (arv->region()));
5256 commit_reversible_command ();
5261 Editor::adjust_region_gain (bool up)
5263 RegionSelection rs = get_regions_from_selection_and_entered ();
5265 if (!_session || rs.empty()) {
5269 bool in_command = false;
5271 for (RegionSelection::iterator r = rs.begin(); r != rs.end(); ++r) {
5272 AudioRegionView* const arv = dynamic_cast<AudioRegionView*>(*r);
5277 arv->region()->clear_changes ();
5279 double dB = accurate_coefficient_to_dB (arv->audio_region()->scale_amplitude ());
5287 arv->audio_region()->set_scale_amplitude (dB_to_coefficient (dB));
5290 begin_reversible_command ("adjust region gain");
5293 _session->add_command (new StatefulDiffCommand (arv->region()));
5297 commit_reversible_command ();
5302 Editor::reset_region_gain ()
5304 RegionSelection rs = get_regions_from_selection_and_entered ();
5306 if (!_session || rs.empty()) {
5310 bool in_command = false;
5312 for (RegionSelection::iterator r = rs.begin(); r != rs.end(); ++r) {
5313 AudioRegionView* const arv = dynamic_cast<AudioRegionView*>(*r);
5318 arv->region()->clear_changes ();
5320 arv->audio_region()->set_scale_amplitude (1.0f);
5323 begin_reversible_command ("reset region gain");
5326 _session->add_command (new StatefulDiffCommand (arv->region()));
5330 commit_reversible_command ();
5335 Editor::reverse_region ()
5341 Reverse rev (*_session);
5342 apply_filter (rev, _("reverse regions"));
5346 Editor::strip_region_silence ()
5352 RegionSelection rs = get_regions_from_selection_and_entered ();
5358 std::list<RegionView*> audio_only;
5360 for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
5361 AudioRegionView* const arv = dynamic_cast<AudioRegionView*> (*i);
5363 audio_only.push_back (arv);
5367 assert (!audio_only.empty());
5369 StripSilenceDialog d (_session, audio_only);
5370 int const r = d.run ();
5374 if (r == Gtk::RESPONSE_OK) {
5375 ARDOUR::AudioIntervalMap silences;
5376 d.silences (silences);
5377 StripSilence s (*_session, silences, d.fade_length());
5379 apply_filter (s, _("strip silence"), &d);
5384 Editor::apply_midi_note_edit_op_to_region (MidiOperator& op, MidiRegionView& mrv)
5386 Evoral::Sequence<Temporal::Beats>::Notes selected;
5387 mrv.selection_as_notelist (selected, true);
5389 vector<Evoral::Sequence<Temporal::Beats>::Notes> v;
5390 v.push_back (selected);
5392 Temporal::Beats pos_beats = Temporal::Beats (mrv.midi_region()->beat()) - mrv.midi_region()->start_beats();
5394 return op (mrv.midi_region()->model(), pos_beats, v);
5398 Editor::apply_midi_note_edit_op (MidiOperator& op, const RegionSelection& rs)
5404 bool in_command = false;
5406 for (RegionSelection::const_iterator r = rs.begin(); r != rs.end(); ) {
5407 RegionSelection::const_iterator tmp = r;
5410 MidiRegionView* const mrv = dynamic_cast<MidiRegionView*> (*r);
5413 Command* cmd = apply_midi_note_edit_op_to_region (op, *mrv);
5416 begin_reversible_command (op.name ());
5420 _session->add_command (cmd);
5428 commit_reversible_command ();
5429 _session->set_dirty ();
5434 Editor::fork_region ()
5436 RegionSelection rs = get_regions_from_selection_and_entered ();
5442 CursorContext::Handle cursor_ctx = CursorContext::create(*this, _cursors->wait);
5443 bool in_command = false;
5447 for (RegionSelection::iterator r = rs.begin(); r != rs.end(); ) {
5448 RegionSelection::iterator tmp = r;
5451 MidiRegionView* const mrv = dynamic_cast<MidiRegionView*>(*r);
5455 boost::shared_ptr<Playlist> playlist = mrv->region()->playlist();
5456 boost::shared_ptr<MidiSource> new_source = _session->create_midi_source_by_stealing_name (mrv->midi_view()->track());
5457 boost::shared_ptr<MidiRegion> newregion = mrv->midi_region()->clone (new_source);
5460 begin_reversible_command (_("Fork Region(s)"));
5463 playlist->clear_changes ();
5464 playlist->replace_region (mrv->region(), newregion, mrv->region()->position());
5465 _session->add_command(new StatefulDiffCommand (playlist));
5467 error << string_compose (_("Could not unlink %1"), mrv->region()->name()) << endmsg;
5475 commit_reversible_command ();
5480 Editor::quantize_region ()
5483 quantize_regions(get_regions_from_selection_and_entered ());
5488 Editor::quantize_regions (const RegionSelection& rs)
5490 if (rs.n_midi_regions() == 0) {
5494 if (!quantize_dialog) {
5495 quantize_dialog = new QuantizeDialog (*this);
5498 if (quantize_dialog->is_mapped()) {
5499 /* in progress already */
5503 quantize_dialog->present ();
5504 const int r = quantize_dialog->run ();
5505 quantize_dialog->hide ();
5507 if (r == Gtk::RESPONSE_OK) {
5508 Quantize quant (quantize_dialog->snap_start(),
5509 quantize_dialog->snap_end(),
5510 quantize_dialog->start_grid_size(),
5511 quantize_dialog->end_grid_size(),
5512 quantize_dialog->strength(),
5513 quantize_dialog->swing(),
5514 quantize_dialog->threshold());
5516 apply_midi_note_edit_op (quant, rs);
5521 Editor::legatize_region (bool shrink_only)
5524 legatize_regions(get_regions_from_selection_and_entered (), shrink_only);
5529 Editor::legatize_regions (const RegionSelection& rs, bool shrink_only)
5531 if (rs.n_midi_regions() == 0) {
5535 Legatize legatize(shrink_only);
5536 apply_midi_note_edit_op (legatize, rs);
5540 Editor::transform_region ()
5543 transform_regions(get_regions_from_selection_and_entered ());
5548 Editor::transform_regions (const RegionSelection& rs)
5550 if (rs.n_midi_regions() == 0) {
5557 const int r = td.run();
5560 if (r == Gtk::RESPONSE_OK) {
5561 Transform transform(td.get());
5562 apply_midi_note_edit_op(transform, rs);
5567 Editor::transpose_region ()
5570 transpose_regions(get_regions_from_selection_and_entered ());
5575 Editor::transpose_regions (const RegionSelection& rs)
5577 if (rs.n_midi_regions() == 0) {
5582 int const r = d.run ();
5584 if (r == RESPONSE_ACCEPT) {
5585 Transpose transpose(d.semitones ());
5586 apply_midi_note_edit_op (transpose, rs);
5591 Editor::insert_patch_change (bool from_context)
5593 RegionSelection rs = get_regions_from_selection_and_entered ();
5599 const samplepos_t p = get_preferred_edit_position (EDIT_IGNORE_NONE, from_context);
5601 /* XXX: bit of a hack; use the MIDNAM from the first selected region;
5602 there may be more than one, but the PatchChangeDialog can only offer
5603 one set of patch menus.
5605 MidiRegionView* first = dynamic_cast<MidiRegionView*> (rs.front ());
5607 Evoral::PatchChange<Temporal::Beats> empty (Temporal::Beats(), 0, 0, 0);
5608 PatchChangeDialog d (0, _session, empty, first->instrument_info(), Gtk::Stock::ADD);
5610 if (d.run() == RESPONSE_CANCEL) {
5614 for (RegionSelection::iterator i = rs.begin (); i != rs.end(); ++i) {
5615 MidiRegionView* const mrv = dynamic_cast<MidiRegionView*> (*i);
5617 if (p >= mrv->region()->first_sample() && p <= mrv->region()->last_sample()) {
5618 mrv->add_patch_change (p - mrv->region()->position(), d.patch ());
5625 Editor::apply_filter (Filter& filter, string command, ProgressReporter* progress)
5627 RegionSelection rs = get_regions_from_selection_and_entered ();
5633 CursorContext::Handle cursor_ctx = CursorContext::create(*this, _cursors->wait);
5634 bool in_command = false;
5639 int const N = rs.size ();
5641 for (RegionSelection::iterator r = rs.begin(); r != rs.end(); ) {
5642 RegionSelection::iterator tmp = r;
5645 AudioRegionView* const arv = dynamic_cast<AudioRegionView*>(*r);
5647 boost::shared_ptr<Playlist> playlist = arv->region()->playlist();
5650 progress->descend (1.0 / N);
5653 if (arv->audio_region()->apply (filter, progress) == 0) {
5655 playlist->clear_changes ();
5656 playlist->clear_owned_changes ();
5659 begin_reversible_command (command);
5663 if (filter.results.empty ()) {
5665 /* no regions returned; remove the old one */
5666 playlist->remove_region (arv->region ());
5670 std::vector<boost::shared_ptr<Region> >::iterator res = filter.results.begin ();
5672 /* first region replaces the old one */
5673 playlist->replace_region (arv->region(), *res, (*res)->position());
5677 while (res != filter.results.end()) {
5678 playlist->add_region (*res, (*res)->position());
5684 /* We might have removed regions, which alters other regions' layering_index,
5685 so we need to do a recursive diff here.
5687 vector<Command*> cmds;
5688 playlist->rdiff (cmds);
5689 _session->add_commands (cmds);
5691 _session->add_command(new StatefulDiffCommand (playlist));
5695 progress->ascend ();
5704 commit_reversible_command ();
5709 Editor::external_edit_region ()
5715 Editor::reset_region_gain_envelopes ()
5717 RegionSelection rs = get_regions_from_selection_and_entered ();
5719 if (!_session || rs.empty()) {
5723 bool in_command = false;
5725 for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
5726 AudioRegionView* const arv = dynamic_cast<AudioRegionView*>(*i);
5728 boost::shared_ptr<AutomationList> alist (arv->audio_region()->envelope());
5729 XMLNode& before (alist->get_state());
5731 arv->audio_region()->set_default_envelope ();
5734 begin_reversible_command (_("reset region gain"));
5737 _session->add_command (new MementoCommand<AutomationList>(*arv->audio_region()->envelope().get(), &before, &alist->get_state()));
5742 commit_reversible_command ();
5747 Editor::set_region_gain_visibility (RegionView* rv)
5749 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (rv);
5751 arv->update_envelope_visibility();
5756 Editor::set_gain_envelope_visibility ()
5762 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
5763 AudioTimeAxisView* v = dynamic_cast<AudioTimeAxisView*>(*i);
5765 v->audio_view()->foreach_regionview (sigc::mem_fun (this, &Editor::set_region_gain_visibility));
5771 Editor::toggle_gain_envelope_active ()
5773 if (_ignore_region_action) {
5777 RegionSelection rs = get_regions_from_selection_and_entered ();
5779 if (!_session || rs.empty()) {
5783 bool in_command = false;
5785 for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
5786 AudioRegionView* const arv = dynamic_cast<AudioRegionView*>(*i);
5788 arv->region()->clear_changes ();
5789 arv->audio_region()->set_envelope_active (!arv->audio_region()->envelope_active());
5792 begin_reversible_command (_("region gain envelope active"));
5795 _session->add_command (new StatefulDiffCommand (arv->region()));
5800 commit_reversible_command ();
5805 Editor::toggle_region_lock ()
5807 if (_ignore_region_action) {
5811 RegionSelection rs = get_regions_from_selection_and_entered ();
5813 if (!_session || rs.empty()) {
5817 begin_reversible_command (_("toggle region lock"));
5819 for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
5820 (*i)->region()->clear_changes ();
5821 (*i)->region()->set_locked (!(*i)->region()->locked());
5822 _session->add_command (new StatefulDiffCommand ((*i)->region()));
5825 commit_reversible_command ();
5829 Editor::toggle_region_video_lock ()
5831 if (_ignore_region_action) {
5835 RegionSelection rs = get_regions_from_selection_and_entered ();
5837 if (!_session || rs.empty()) {
5841 begin_reversible_command (_("Toggle Video Lock"));
5843 for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
5844 (*i)->region()->clear_changes ();
5845 (*i)->region()->set_video_locked (!(*i)->region()->video_locked());
5846 _session->add_command (new StatefulDiffCommand ((*i)->region()));
5849 commit_reversible_command ();
5853 Editor::toggle_region_lock_style ()
5855 if (_ignore_region_action) {
5859 RegionSelection rs = get_regions_from_selection_and_entered ();
5861 if (!_session || rs.empty()) {
5865 Glib::RefPtr<ToggleAction> a = Glib::RefPtr<ToggleAction>::cast_dynamic (_region_actions->get_action("toggle-region-lock-style"));
5866 vector<Widget*> proxies = a->get_proxies();
5867 Gtk::CheckMenuItem* cmi = dynamic_cast<Gtk::CheckMenuItem*> (proxies.front());
5871 begin_reversible_command (_("toggle region lock style"));
5873 for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
5874 (*i)->region()->clear_changes ();
5875 PositionLockStyle const ns = ((*i)->region()->position_lock_style() == AudioTime && !cmi->get_inconsistent()) ? MusicTime : AudioTime;
5876 (*i)->region()->set_position_lock_style (ns);
5877 _session->add_command (new StatefulDiffCommand ((*i)->region()));
5880 commit_reversible_command ();
5884 Editor::toggle_opaque_region ()
5886 if (_ignore_region_action) {
5890 RegionSelection rs = get_regions_from_selection_and_entered ();
5892 if (!_session || rs.empty()) {
5896 begin_reversible_command (_("change region opacity"));
5898 for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
5899 (*i)->region()->clear_changes ();
5900 (*i)->region()->set_opaque (!(*i)->region()->opaque());
5901 _session->add_command (new StatefulDiffCommand ((*i)->region()));
5904 commit_reversible_command ();
5908 Editor::toggle_record_enable ()
5910 bool new_state = false;
5912 for (TrackSelection::iterator i = selection->tracks.begin(); i != selection->tracks.end(); ++i) {
5913 RouteTimeAxisView *rtav = dynamic_cast<RouteTimeAxisView *>(*i);
5916 if (!rtav->is_track())
5920 new_state = !rtav->track()->rec_enable_control()->get_value();
5924 rtav->track()->rec_enable_control()->set_value (new_state, Controllable::UseGroup);
5929 tracklist_to_stripables (TrackViewList list)
5933 for (TrackSelection::iterator i = list.begin(); i != list.end(); ++i) {
5934 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> ((*i));
5936 if (rtv && rtv->is_track()) {
5937 ret.push_back (rtv->track());
5945 Editor::play_solo_selection (bool restart)
5947 //note: session::solo_selection takes care of invalidating the region playlist
5949 if ((!selection->tracks.empty()) && selection->time.length() > 0) { //a range is selected; solo the tracks and roll
5951 StripableList sl = tracklist_to_stripables (selection->tracks);
5952 _session->solo_selection (sl, true);
5955 samplepos_t start = selection->time.start();
5956 samplepos_t end = selection->time.end_sample();
5957 _session->request_bounded_roll (start, end);
5959 } else if (! selection->tracks.empty()) { //no range is selected, but tracks are selected; solo the tracks and roll
5960 StripableList sl = tracklist_to_stripables (selection->tracks);
5961 _session->solo_selection (sl, true);
5962 _session->request_cancel_play_range();
5963 transition_to_rolling (true);
5965 } else if (! selection->regions.empty()) { //solo any tracks with selected regions, and roll
5966 StripableList sl = tracklist_to_stripables (get_tracks_for_range_action());
5967 _session->solo_selection (sl, true);
5968 _session->request_cancel_play_range();
5969 transition_to_rolling (true);
5971 _session->request_cancel_play_range();
5972 transition_to_rolling (true); //no selection. just roll.
5977 Editor::toggle_solo ()
5979 bool new_state = false;
5981 boost::shared_ptr<ControlList> cl (new ControlList);
5983 for (TrackSelection::iterator i = selection->tracks.begin(); i != selection->tracks.end(); ++i) {
5984 StripableTimeAxisView *stav = dynamic_cast<StripableTimeAxisView *>(*i);
5986 if (!stav || !stav->stripable()->solo_control()) {
5991 new_state = !stav->stripable()->solo_control()->soloed ();
5995 cl->push_back (stav->stripable()->solo_control());
5998 _session->set_controls (cl, new_state ? 1.0 : 0.0, Controllable::UseGroup);
6002 Editor::toggle_mute ()
6004 bool new_state = false;
6006 boost::shared_ptr<ControlList> cl (new ControlList);
6008 for (TrackSelection::iterator i = selection->tracks.begin(); i != selection->tracks.end(); ++i) {
6009 StripableTimeAxisView *stav = dynamic_cast<StripableTimeAxisView *>(*i);
6011 if (!stav || !stav->stripable()->mute_control()) {
6016 new_state = !stav->stripable()->mute_control()->muted();
6020 cl->push_back (stav->stripable()->mute_control());
6023 _session->set_controls (cl, new_state, Controllable::UseGroup);
6027 Editor::toggle_solo_isolate ()
6033 Editor::fade_range ()
6035 TrackViewList ts = selection->tracks.filter_to_unique_playlists ();
6037 begin_reversible_command (_("fade range"));
6039 for (TrackViewList::iterator i = ts.begin(); i != ts.end(); ++i) {
6040 (*i)->fade_range (selection->time);
6043 commit_reversible_command ();
6048 Editor::set_fade_length (bool in)
6050 RegionSelection rs = get_regions_from_selection_and_entered ();
6056 /* we need a region to measure the offset from the start */
6058 RegionView* rv = rs.front ();
6060 samplepos_t pos = get_preferred_edit_position();
6064 if (pos > rv->region()->last_sample() || pos < rv->region()->first_sample()) {
6065 /* edit point is outside the relevant region */
6070 if (pos <= rv->region()->position()) {
6074 len = pos - rv->region()->position();
6075 cmd = _("set fade in length");
6077 if (pos >= rv->region()->last_sample()) {
6081 len = rv->region()->last_sample() - pos;
6082 cmd = _("set fade out length");
6085 bool in_command = false;
6087 for (RegionSelection::iterator x = rs.begin(); x != rs.end(); ++x) {
6088 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (*x);
6094 boost::shared_ptr<AutomationList> alist;
6096 alist = tmp->audio_region()->fade_in();
6098 alist = tmp->audio_region()->fade_out();
6101 XMLNode &before = alist->get_state();
6104 tmp->audio_region()->set_fade_in_length (len);
6105 tmp->audio_region()->set_fade_in_active (true);
6107 tmp->audio_region()->set_fade_out_length (len);
6108 tmp->audio_region()->set_fade_out_active (true);
6112 begin_reversible_command (cmd);
6115 XMLNode &after = alist->get_state();
6116 _session->add_command(new MementoCommand<AutomationList>(*alist, &before, &after));
6120 commit_reversible_command ();
6125 Editor::set_fade_in_shape (FadeShape shape)
6127 RegionSelection rs = get_regions_from_selection_and_entered ();
6132 bool in_command = false;
6134 for (RegionSelection::iterator x = rs.begin(); x != rs.end(); ++x) {
6135 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (*x);
6141 boost::shared_ptr<AutomationList> alist = tmp->audio_region()->fade_in();
6142 XMLNode &before = alist->get_state();
6144 tmp->audio_region()->set_fade_in_shape (shape);
6147 begin_reversible_command (_("set fade in shape"));
6150 XMLNode &after = alist->get_state();
6151 _session->add_command(new MementoCommand<AutomationList>(*alist.get(), &before, &after));
6155 commit_reversible_command ();
6160 Editor::set_fade_out_shape (FadeShape shape)
6162 RegionSelection rs = get_regions_from_selection_and_entered ();
6167 bool in_command = false;
6169 for (RegionSelection::iterator x = rs.begin(); x != rs.end(); ++x) {
6170 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (*x);
6176 boost::shared_ptr<AutomationList> alist = tmp->audio_region()->fade_out();
6177 XMLNode &before = alist->get_state();
6179 tmp->audio_region()->set_fade_out_shape (shape);
6182 begin_reversible_command (_("set fade out shape"));
6185 XMLNode &after = alist->get_state();
6186 _session->add_command(new MementoCommand<AutomationList>(*alist.get(), &before, &after));
6190 commit_reversible_command ();
6195 Editor::set_fade_in_active (bool yn)
6197 RegionSelection rs = get_regions_from_selection_and_entered ();
6202 bool in_command = false;
6204 for (RegionSelection::iterator x = rs.begin(); x != rs.end(); ++x) {
6205 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (*x);
6212 boost::shared_ptr<AudioRegion> ar (tmp->audio_region());
6214 ar->clear_changes ();
6215 ar->set_fade_in_active (yn);
6218 begin_reversible_command (_("set fade in active"));
6221 _session->add_command (new StatefulDiffCommand (ar));
6225 commit_reversible_command ();
6230 Editor::set_fade_out_active (bool yn)
6232 RegionSelection rs = get_regions_from_selection_and_entered ();
6237 bool in_command = false;
6239 for (RegionSelection::iterator x = rs.begin(); x != rs.end(); ++x) {
6240 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (*x);
6246 boost::shared_ptr<AudioRegion> ar (tmp->audio_region());
6248 ar->clear_changes ();
6249 ar->set_fade_out_active (yn);
6252 begin_reversible_command (_("set fade out active"));
6255 _session->add_command(new StatefulDiffCommand (ar));
6259 commit_reversible_command ();
6264 Editor::toggle_region_fades (int dir)
6266 if (_ignore_region_action) {
6270 boost::shared_ptr<AudioRegion> ar;
6273 RegionSelection rs = get_regions_from_selection_and_entered ();
6279 RegionSelection::iterator i;
6280 for (i = rs.begin(); i != rs.end(); ++i) {
6281 if ((ar = boost::dynamic_pointer_cast<AudioRegion>((*i)->region())) != 0) {
6283 yn = ar->fade_out_active ();
6285 yn = ar->fade_in_active ();
6291 if (i == rs.end()) {
6295 /* XXX should this undo-able? */
6296 bool in_command = false;
6298 for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
6299 if ((ar = boost::dynamic_pointer_cast<AudioRegion>((*i)->region())) == 0) {
6302 ar->clear_changes ();
6304 if (dir == 1 || dir == 0) {
6305 ar->set_fade_in_active (!yn);
6308 if (dir == -1 || dir == 0) {
6309 ar->set_fade_out_active (!yn);
6312 begin_reversible_command (_("toggle fade active"));
6315 _session->add_command(new StatefulDiffCommand (ar));
6319 commit_reversible_command ();
6324 /** Update region fade visibility after its configuration has been changed */
6326 Editor::update_region_fade_visibility ()
6328 bool _fade_visibility = _session->config.get_show_region_fades ();
6330 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
6331 AudioTimeAxisView* v = dynamic_cast<AudioTimeAxisView*>(*i);
6333 if (_fade_visibility) {
6334 v->audio_view()->show_all_fades ();
6336 v->audio_view()->hide_all_fades ();
6343 Editor::set_edit_point ()
6346 MusicSample where (0, 0);
6348 if (!mouse_sample (where.sample, ignored)) {
6354 if (selection->markers.empty()) {
6356 mouse_add_new_marker (where.sample);
6361 Location* loc = find_location_from_marker (selection->markers.front(), ignored);
6364 loc->move_to (where.sample, where.division);
6370 Editor::set_playhead_cursor ()
6372 if (entered_marker) {
6373 _session->request_locate (entered_marker->position(), _session->transport_rolling());
6375 MusicSample where (0, 0);
6378 if (!mouse_sample (where.sample, ignored)) {
6385 _session->request_locate (where.sample, _session->transport_rolling());
6389 //not sure what this was for; remove it for now.
6390 // if (UIConfiguration::instance().get_follow_edits() && (!_session || !_session->config.get_external_sync())) {
6391 // cancel_time_selection();
6397 Editor::split_region ()
6399 if (_dragging_playhead) {
6401 } else if (_drags->active ()) {
6402 /*any other kind of drag, bail out so we avoid Undo snafu*/
6406 //if a range is selected, separate it
6407 if (!selection->time.empty()) {
6408 separate_regions_between (selection->time);
6412 //if no range was selected, try to find some regions to split
6413 if (current_mouse_mode() == MouseObject || current_mouse_mode() == MouseRange ) { //don't try this for Internal Edit, Stretch, Draw, etc.
6415 RegionSelection rs = get_regions_from_selection_and_edit_point ();
6416 const samplepos_t pos = get_preferred_edit_position();
6417 const int32_t division = get_grid_music_divisions (0);
6418 MusicSample where (pos, division);
6424 split_regions_at (where, rs);
6430 Editor::select_next_stripable (bool routes_only)
6432 _session->selection().select_next_stripable (false, routes_only);
6436 Editor::select_prev_stripable (bool routes_only)
6438 _session->selection().select_prev_stripable (false, routes_only);
6442 Editor::set_loop_from_selection (bool play)
6444 if (_session == 0) {
6448 samplepos_t start, end;
6449 if (!get_selection_extents (start, end))
6452 set_loop_range (start, end, _("set loop range from selection"));
6455 _session->request_play_loop (true, true);
6460 Editor::set_loop_from_region (bool play)
6462 samplepos_t start, end;
6463 if (!get_selection_extents (start, end))
6466 set_loop_range (start, end, _("set loop range from region"));
6469 _session->request_locate (start, true);
6470 _session->request_play_loop (true);
6475 Editor::set_punch_from_selection ()
6477 if (_session == 0) {
6481 samplepos_t start, end;
6482 if (!get_selection_extents (start, end))
6485 set_punch_range (start, end, _("set punch range from selection"));
6489 Editor::set_auto_punch_range ()
6491 // auto punch in/out button from a single button
6492 // If Punch In is unset, set punch range from playhead to end, enable punch in
6493 // If Punch In is set, the next punch sets Punch Out, unless the playhead has been
6494 // rewound beyond the Punch In marker, in which case that marker will be moved back
6495 // to the current playhead position.
6496 // If punch out is set, it clears the punch range and Punch In/Out buttons
6498 if (_session == 0) {
6502 Location* tpl = transport_punch_location();
6503 samplepos_t now = playhead_cursor->current_sample();
6504 samplepos_t begin = now;
6505 samplepos_t end = _session->current_end_sample();
6507 if (!_session->config.get_punch_in()) {
6508 // First Press - set punch in and create range from here to eternity
6509 set_punch_range (begin, end, _("Auto Punch In"));
6510 _session->config.set_punch_in(true);
6511 } else if (tpl && !_session->config.get_punch_out()) {
6512 // Second press - update end range marker and set punch_out
6513 if (now < tpl->start()) {
6514 // playhead has been rewound - move start back and pretend nothing happened
6516 set_punch_range (begin, end, _("Auto Punch In/Out"));
6518 // normal case for 2nd press - set the punch out
6519 end = playhead_cursor->current_sample ();
6520 set_punch_range (tpl->start(), now, _("Auto Punch In/Out"));
6521 _session->config.set_punch_out(true);
6524 if (_session->config.get_punch_out()) {
6525 _session->config.set_punch_out(false);
6528 if (_session->config.get_punch_in()) {
6529 _session->config.set_punch_in(false);
6534 // third press - unset punch in/out and remove range
6535 _session->locations()->remove(tpl);
6542 Editor::set_session_extents_from_selection ()
6544 if (_session == 0) {
6548 samplepos_t start, end;
6549 if (!get_selection_extents (start, end))
6553 if ((loc = _session->locations()->session_range_location()) == 0) {
6554 _session->set_session_extents (start, end); // this will create a new session range; no need for UNDO
6556 XMLNode &before = loc->get_state();
6558 _session->set_session_extents (start, end);
6560 XMLNode &after = loc->get_state();
6562 begin_reversible_command (_("set session start/end from selection"));
6564 _session->add_command (new MementoCommand<Location>(*loc, &before, &after));
6566 commit_reversible_command ();
6569 _session->set_end_is_free (false);
6573 Editor::set_punch_start_from_edit_point ()
6577 MusicSample start (0, 0);
6578 samplepos_t end = max_samplepos;
6580 //use the existing punch end, if any
6581 Location* tpl = transport_punch_location();
6586 if ((_edit_point == EditAtPlayhead) && _session->transport_rolling()) {
6587 start.sample = _session->audible_sample();
6589 start.sample = get_preferred_edit_position();
6592 //if there's not already a sensible selection endpoint, go "forever"
6593 if (start.sample > end) {
6594 end = max_samplepos;
6597 set_punch_range (start.sample, end, _("set punch start from EP"));
6603 Editor::set_punch_end_from_edit_point ()
6607 samplepos_t start = 0;
6608 MusicSample end (max_samplepos, 0);
6610 //use the existing punch start, if any
6611 Location* tpl = transport_punch_location();
6613 start = tpl->start();
6616 if ((_edit_point == EditAtPlayhead) && _session->transport_rolling()) {
6617 end.sample = _session->audible_sample();
6619 end.sample = get_preferred_edit_position();
6622 set_punch_range (start, end.sample, _("set punch end from EP"));
6628 Editor::set_loop_start_from_edit_point ()
6632 MusicSample start (0, 0);
6633 samplepos_t end = max_samplepos;
6635 //use the existing loop end, if any
6636 Location* tpl = transport_loop_location();
6641 if ((_edit_point == EditAtPlayhead) && _session->transport_rolling()) {
6642 start.sample = _session->audible_sample();
6644 start.sample = get_preferred_edit_position();
6647 //if there's not already a sensible selection endpoint, go "forever"
6648 if (start.sample > end) {
6649 end = max_samplepos;
6652 set_loop_range (start.sample, end, _("set loop start from EP"));
6658 Editor::set_loop_end_from_edit_point ()
6662 samplepos_t start = 0;
6663 MusicSample end (max_samplepos, 0);
6665 //use the existing loop start, if any
6666 Location* tpl = transport_loop_location();
6668 start = tpl->start();
6671 if ((_edit_point == EditAtPlayhead) && _session->transport_rolling()) {
6672 end.sample = _session->audible_sample();
6674 end.sample = get_preferred_edit_position();
6677 set_loop_range (start, end.sample, _("set loop end from EP"));
6682 Editor::set_punch_from_region ()
6684 samplepos_t start, end;
6685 if (!get_selection_extents (start, end))
6688 set_punch_range (start, end, _("set punch range from region"));
6692 Editor::pitch_shift_region ()
6694 RegionSelection rs = get_regions_from_selection_and_entered ();
6696 RegionSelection audio_rs;
6697 for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
6698 if (dynamic_cast<AudioRegionView*> (*i)) {
6699 audio_rs.push_back (*i);
6703 if (audio_rs.empty()) {
6707 pitch_shift (audio_rs, 1.2);
6711 Editor::set_tempo_from_region ()
6713 RegionSelection rs = get_regions_from_selection_and_entered ();
6715 if (!_session || rs.empty()) {
6719 RegionView* rv = rs.front();
6721 define_one_bar (rv->region()->position(), rv->region()->last_sample() + 1);
6725 Editor::use_range_as_bar ()
6727 samplepos_t start, end;
6728 if (get_edit_op_range (start, end)) {
6729 define_one_bar (start, end);
6734 Editor::define_one_bar (samplepos_t start, samplepos_t end)
6736 samplepos_t length = end - start;
6738 const Meter& m (_session->tempo_map().meter_at_sample (start));
6740 /* length = 1 bar */
6742 /* We're going to deliver a constant tempo here,
6743 so we can use samples per beat to determine length.
6744 now we want samples per beat.
6745 we have samples per bar, and beats per bar, so ...
6748 /* XXXX METER MATH */
6750 double samples_per_beat = length / m.divisions_per_bar();
6752 /* beats per minute = */
6754 double beats_per_minute = (_session->sample_rate() * 60.0) / samples_per_beat;
6756 /* now decide whether to:
6758 (a) set global tempo
6759 (b) add a new tempo marker
6763 const TempoSection& t (_session->tempo_map().tempo_section_at_sample (start));
6765 bool do_global = false;
6767 if ((_session->tempo_map().n_tempos() == 1) && (_session->tempo_map().n_meters() == 1)) {
6769 /* only 1 tempo & 1 meter: ask if the user wants to set the tempo
6770 at the start, or create a new marker
6773 vector<string> options;
6774 options.push_back (_("Cancel"));
6775 options.push_back (_("Add new marker"));
6776 options.push_back (_("Set global tempo"));
6779 _("Define one bar"),
6780 _("Do you want to set the global tempo or add a new tempo marker?"),
6784 c.set_default_response (2);
6800 /* more than 1 tempo and/or meter section already, go ahead do the "usual":
6801 if the marker is at the region starter, change it, otherwise add
6806 begin_reversible_command (_("set tempo from region"));
6807 XMLNode& before (_session->tempo_map().get_state());
6810 _session->tempo_map().change_initial_tempo (beats_per_minute, t.note_type(), t.end_note_types_per_minute());
6811 } else if (t.sample() == start) {
6812 _session->tempo_map().change_existing_tempo_at (start, beats_per_minute, t.note_type(), t.end_note_types_per_minute());
6814 /* constant tempo */
6815 const Tempo tempo (beats_per_minute, t.note_type());
6816 _session->tempo_map().add_tempo (tempo, 0.0, start, AudioTime);
6819 XMLNode& after (_session->tempo_map().get_state());
6821 _session->add_command (new MementoCommand<TempoMap>(_session->tempo_map(), &before, &after));
6822 commit_reversible_command ();
6826 Editor::split_region_at_transients ()
6828 AnalysisFeatureList positions;
6830 RegionSelection rs = get_regions_from_selection_and_entered ();
6832 if (!_session || rs.empty()) {
6836 begin_reversible_command (_("split regions"));
6838 for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ) {
6840 RegionSelection::iterator tmp;
6845 boost::shared_ptr<AudioRegion> ar = boost::dynamic_pointer_cast<AudioRegion> ((*i)->region());
6848 ar->transients (positions);
6849 split_region_at_points ((*i)->region(), positions, true);
6856 commit_reversible_command ();
6861 Editor::split_region_at_points (boost::shared_ptr<Region> r, AnalysisFeatureList& positions, bool can_ferret, bool select_new)
6863 bool use_rhythmic_rodent = false;
6865 boost::shared_ptr<Playlist> pl = r->playlist();
6867 list<boost::shared_ptr<Region> > new_regions;
6873 if (positions.empty()) {
6877 if (positions.size() > 20 && can_ferret) {
6878 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);
6879 MessageDialog msg (msgstr,
6882 Gtk::BUTTONS_OK_CANCEL);
6885 msg.add_button (_("Call for the Ferret!"), RESPONSE_APPLY);
6886 msg.set_secondary_text (_("Press OK to continue with this split operation\nor ask the Ferret dialog to tune the analysis"));
6888 msg.set_secondary_text (_("Press OK to continue with this split operation"));
6891 msg.set_title (_("Excessive split?"));
6894 int response = msg.run();
6900 case RESPONSE_APPLY:
6901 use_rhythmic_rodent = true;
6908 if (use_rhythmic_rodent) {
6909 show_rhythm_ferret ();
6913 AnalysisFeatureList::const_iterator x;
6915 pl->clear_changes ();
6916 pl->clear_owned_changes ();
6918 x = positions.begin();
6920 if (x == positions.end()) {
6925 pl->remove_region (r);
6927 samplepos_t pos = 0;
6929 samplepos_t rstart = r->first_sample ();
6930 samplepos_t rend = r->last_sample ();
6932 while (x != positions.end()) {
6934 /* deal with positons that are out of scope of present region bounds */
6935 if (*x <= rstart || *x > rend) {
6940 /* file start = original start + how far we from the initial position ? */
6942 samplepos_t file_start = r->start() + pos;
6944 /* length = next position - current position */
6946 samplepos_t len = (*x) - pos - rstart;
6948 /* XXX we do we really want to allow even single-sample regions?
6949 * shouldn't we have some kind of lower limit on region size?
6958 if (RegionFactory::region_name (new_name, r->name())) {
6962 /* do NOT announce new regions 1 by one, just wait till they are all done */
6966 plist.add (ARDOUR::Properties::start, file_start);
6967 plist.add (ARDOUR::Properties::length, len);
6968 plist.add (ARDOUR::Properties::name, new_name);
6969 plist.add (ARDOUR::Properties::layer, 0);
6970 // TODO set transients_offset
6972 boost::shared_ptr<Region> nr = RegionFactory::create (r->sources(), plist, false);
6973 /* because we set annouce to false, manually add the new region to the
6976 RegionFactory::map_add (nr);
6978 pl->add_region (nr, rstart + pos);
6981 new_regions.push_front(nr);
6990 RegionFactory::region_name (new_name, r->name());
6992 /* Add the final region */
6995 plist.add (ARDOUR::Properties::start, r->start() + pos);
6996 plist.add (ARDOUR::Properties::length, r->last_sample() - (r->position() + pos) + 1);
6997 plist.add (ARDOUR::Properties::name, new_name);
6998 plist.add (ARDOUR::Properties::layer, 0);
7000 boost::shared_ptr<Region> nr = RegionFactory::create (r->sources(), plist, false);
7001 /* because we set annouce to false, manually add the new region to the
7004 RegionFactory::map_add (nr);
7005 pl->add_region (nr, r->position() + pos);
7008 new_regions.push_front(nr);
7013 /* We might have removed regions, which alters other regions' layering_index,
7014 so we need to do a recursive diff here.
7016 vector<Command*> cmds;
7018 _session->add_commands (cmds);
7020 _session->add_command (new StatefulDiffCommand (pl));
7024 for (list<boost::shared_ptr<Region> >::iterator i = new_regions.begin(); i != new_regions.end(); ++i){
7025 set_selected_regionview_from_region_list ((*i), Selection::Add);
7031 Editor::place_transient()
7037 RegionSelection rs = get_regions_from_selection_and_edit_point ();
7043 samplepos_t where = get_preferred_edit_position();
7045 begin_reversible_command (_("place transient"));
7047 for (RegionSelection::iterator r = rs.begin(); r != rs.end(); ++r) {
7048 (*r)->region()->add_transient(where);
7051 commit_reversible_command ();
7055 Editor::remove_transient(ArdourCanvas::Item* item)
7061 ArdourCanvas::Line* _line = reinterpret_cast<ArdourCanvas::Line*> (item);
7064 AudioRegionView* _arv = reinterpret_cast<AudioRegionView*> (item->get_data ("regionview"));
7065 _arv->remove_transient (*(float*) _line->get_data ("position"));
7069 Editor::snap_regions_to_grid ()
7071 list <boost::shared_ptr<Playlist > > used_playlists;
7073 RegionSelection rs = get_regions_from_selection_and_entered ();
7075 if (!_session || rs.empty()) {
7079 begin_reversible_command (_("snap regions to grid"));
7081 for (RegionSelection::iterator r = rs.begin(); r != rs.end(); ++r) {
7083 boost::shared_ptr<Playlist> pl = (*r)->region()->playlist();
7085 if (!pl->frozen()) {
7086 /* we haven't seen this playlist before */
7088 /* remember used playlists so we can thaw them later */
7089 used_playlists.push_back(pl);
7092 (*r)->region()->clear_changes ();
7094 MusicSample start ((*r)->region()->first_sample (), 0);
7095 snap_to (start, RoundNearest, SnapToGrid_Unscaled, true);
7096 (*r)->region()->set_position (start.sample, start.division);
7097 _session->add_command(new StatefulDiffCommand ((*r)->region()));
7100 while (used_playlists.size() > 0) {
7101 list <boost::shared_ptr<Playlist > >::iterator i = used_playlists.begin();
7103 used_playlists.pop_front();
7106 commit_reversible_command ();
7110 Editor::close_region_gaps ()
7112 list <boost::shared_ptr<Playlist > > used_playlists;
7114 RegionSelection rs = get_regions_from_selection_and_entered ();
7116 if (!_session || rs.empty()) {
7120 Dialog dialog (_("Close Region Gaps"));
7123 table.set_spacings (12);
7124 table.set_border_width (12);
7125 Label* l = manage (left_aligned_label (_("Crossfade length")));
7126 table.attach (*l, 0, 1, 0, 1);
7128 SpinButton spin_crossfade (1, 0);
7129 spin_crossfade.set_range (0, 15);
7130 spin_crossfade.set_increments (1, 1);
7131 spin_crossfade.set_value (5);
7132 table.attach (spin_crossfade, 1, 2, 0, 1);
7134 table.attach (*manage (new Label (_("ms"))), 2, 3, 0, 1);
7136 l = manage (left_aligned_label (_("Pull-back length")));
7137 table.attach (*l, 0, 1, 1, 2);
7139 SpinButton spin_pullback (1, 0);
7140 spin_pullback.set_range (0, 100);
7141 spin_pullback.set_increments (1, 1);
7142 spin_pullback.set_value(30);
7143 table.attach (spin_pullback, 1, 2, 1, 2);
7145 table.attach (*manage (new Label (_("ms"))), 2, 3, 1, 2);
7147 dialog.get_vbox()->pack_start (table);
7148 dialog.add_button (Stock::CANCEL, RESPONSE_CANCEL);
7149 dialog.add_button (_("Ok"), RESPONSE_ACCEPT);
7152 if (dialog.run () == RESPONSE_CANCEL) {
7156 samplepos_t crossfade_len = spin_crossfade.get_value();
7157 samplepos_t pull_back_samples = spin_pullback.get_value();
7159 crossfade_len = lrintf (crossfade_len * _session->sample_rate()/1000);
7160 pull_back_samples = lrintf (pull_back_samples * _session->sample_rate()/1000);
7162 /* Iterate over the region list and make adjacent regions overlap by crossfade_len_ms */
7164 begin_reversible_command (_("close region gaps"));
7167 boost::shared_ptr<Region> last_region;
7169 rs.sort_by_position_and_track();
7171 for (RegionSelection::iterator r = rs.begin(); r != rs.end(); ++r) {
7173 boost::shared_ptr<Playlist> pl = (*r)->region()->playlist();
7175 if (!pl->frozen()) {
7176 /* we haven't seen this playlist before */
7178 /* remember used playlists so we can thaw them later */
7179 used_playlists.push_back(pl);
7183 samplepos_t position = (*r)->region()->position();
7185 if (idx == 0 || position < last_region->position()){
7186 last_region = (*r)->region();
7191 (*r)->region()->clear_changes ();
7192 (*r)->region()->trim_front((position - pull_back_samples));
7194 last_region->clear_changes ();
7195 last_region->trim_end ((position - pull_back_samples + crossfade_len));
7197 _session->add_command (new StatefulDiffCommand ((*r)->region()));
7198 _session->add_command (new StatefulDiffCommand (last_region));
7200 last_region = (*r)->region();
7204 while (used_playlists.size() > 0) {
7205 list <boost::shared_ptr<Playlist > >::iterator i = used_playlists.begin();
7207 used_playlists.pop_front();
7210 commit_reversible_command ();
7214 Editor::tab_to_transient (bool forward)
7216 AnalysisFeatureList positions;
7218 RegionSelection rs = get_regions_from_selection_and_entered ();
7224 samplepos_t pos = _session->audible_sample ();
7226 if (!selection->tracks.empty()) {
7228 /* don't waste time searching for transients in duplicate playlists.
7231 TrackViewList ts = selection->tracks.filter_to_unique_playlists ();
7233 for (TrackViewList::iterator t = ts.begin(); t != ts.end(); ++t) {
7235 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (*t);
7238 boost::shared_ptr<Track> tr = rtv->track();
7240 boost::shared_ptr<Playlist> pl = tr->playlist ();
7242 samplepos_t result = pl->find_next_transient (pos, forward ? 1 : -1);
7245 positions.push_back (result);
7258 for (RegionSelection::iterator r = rs.begin(); r != rs.end(); ++r) {
7259 (*r)->region()->get_transients (positions);
7263 TransientDetector::cleanup_transients (positions, _session->sample_rate(), 3.0);
7266 AnalysisFeatureList::iterator x;
7268 for (x = positions.begin(); x != positions.end(); ++x) {
7274 if (x != positions.end ()) {
7275 _session->request_locate (*x);
7279 AnalysisFeatureList::reverse_iterator x;
7281 for (x = positions.rbegin(); x != positions.rend(); ++x) {
7287 if (x != positions.rend ()) {
7288 _session->request_locate (*x);
7294 Editor::playhead_forward_to_grid ()
7300 MusicSample pos (playhead_cursor->current_sample (), 0);
7302 if ( _grid_type == GridTypeNone) {
7303 if (pos.sample < max_samplepos - current_page_samples()*0.1) {
7304 pos.sample += current_page_samples()*0.1;
7305 _session->request_locate (pos.sample);
7307 _session->request_locate (0);
7311 if (pos.sample < max_samplepos - 1) {
7313 snap_to_internal (pos, RoundUpAlways, SnapToGrid_Scaled, true);
7314 _session->request_locate (pos.sample);
7319 /* keep PH visible in window */
7320 if (pos.sample > (_leftmost_sample + current_page_samples() *0.9)) {
7321 reset_x_origin (pos.sample - (current_page_samples()*0.9));
7327 Editor::playhead_backward_to_grid ()
7333 MusicSample pos (playhead_cursor->current_sample (), 0);
7335 if ( _grid_type == GridTypeNone) {
7336 if ( pos.sample > current_page_samples()*0.1 ) {
7337 pos.sample -= current_page_samples()*0.1;
7338 _session->request_locate (pos.sample);
7340 _session->request_locate (0);
7344 if (pos.sample > 2) {
7346 snap_to_internal (pos, RoundDownAlways, SnapToGrid_Scaled, true);
7349 //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...
7350 //also see: jump_backward_to_mark
7351 if (_session->transport_rolling()) {
7352 if ((playhead_cursor->current_sample() - pos.sample) < _session->sample_rate()/2) {
7353 snap_to_internal (pos, RoundDownAlways, SnapToGrid_Scaled, true);
7357 _session->request_locate (pos.sample, _session->transport_rolling());
7360 /* keep PH visible in window */
7361 if (pos.sample < (_leftmost_sample + current_page_samples() *0.1)) {
7362 reset_x_origin (pos.sample - (current_page_samples()*0.1));
7367 Editor::set_track_height (Height h)
7369 TrackSelection& ts (selection->tracks);
7371 for (TrackSelection::iterator x = ts.begin(); x != ts.end(); ++x) {
7372 (*x)->set_height_enum (h);
7377 Editor::toggle_tracks_active ()
7379 TrackSelection& ts (selection->tracks);
7381 bool target = false;
7387 for (TrackSelection::iterator x = ts.begin(); x != ts.end(); ++x) {
7388 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*>(*x);
7392 target = !rtv->_route->active();
7395 rtv->_route->set_active (target, this);
7401 Editor::remove_tracks ()
7403 /* this will delete GUI objects that may be the subject of an event
7404 handler in which this method is called. Defer actual deletion to the
7405 next idle callback, when all event handling is finished.
7407 Glib::signal_idle().connect (sigc::mem_fun (*this, &Editor::idle_remove_tracks));
7411 Editor::idle_remove_tracks ()
7413 Session::StateProtector sp (_session);
7415 return false; /* do not call again */
7419 Editor::_remove_tracks ()
7421 TrackSelection& ts (selection->tracks);
7427 vector<string> choices;
7432 const char* trackstr;
7435 vector<boost::shared_ptr<Route> > routes;
7436 vector<boost::shared_ptr<VCA> > vcas;
7437 bool special_bus = false;
7439 for (TrackSelection::iterator x = ts.begin(); x != ts.end(); ++x) {
7440 VCATimeAxisView* vtv = dynamic_cast<VCATimeAxisView*> (*x);
7442 vcas.push_back (vtv->vca());
7446 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (*x);
7450 if (rtv->is_track()) {
7455 routes.push_back (rtv->_route);
7457 if (rtv->route()->is_master() || rtv->route()->is_monitor()) {
7462 if (special_bus && !Config->get_allow_special_bus_removal()) {
7463 MessageDialog msg (_("That would be bad news ...."),
7467 msg.set_secondary_text (string_compose (_(
7468 "Removing the master or monitor bus is such a bad idea\n\
7469 that %1 is not going to allow it.\n\
7471 If you really want to do this sort of thing\n\
7472 edit your ardour.rc file to set the\n\
7473 \"allow-special-bus-removal\" option to be \"yes\""), PROGRAM_NAME));
7480 if (ntracks + nbusses + nvcas == 0) {
7486 trackstr = P_("track", "tracks", ntracks);
7487 busstr = P_("bus", "busses", nbusses);
7488 vcastr = P_("VCA", "VCAs", nvcas);
7490 if (ntracks > 0 && nbusses > 0 && nvcas > 0) {
7491 title = _("Remove various strips");
7492 prompt = string_compose (_("Do you really want to remove %1 %2, %3 %4 and %5 %6?"),
7493 ntracks, trackstr, nbusses, busstr, nvcas, vcastr);
7495 else if (ntracks > 0 && nbusses > 0) {
7496 title = string_compose (_("Remove %1 and %2"), trackstr, busstr);
7497 prompt = string_compose (_("Do you really want to remove %1 %2 and %3 %4?"),
7498 ntracks, trackstr, nbusses, busstr);
7500 else if (ntracks > 0 && nvcas > 0) {
7501 title = string_compose (_("Remove %1 and %2"), trackstr, vcastr);
7502 prompt = string_compose (_("Do you really want to remove %1 %2 and %3 %4?"),
7503 ntracks, trackstr, nvcas, vcastr);
7505 else if (nbusses > 0 && nvcas > 0) {
7506 title = string_compose (_("Remove %1 and %2"), busstr, vcastr);
7507 prompt = string_compose (_("Do you really want to remove %1 %2 and %3 %4?"),
7508 nbusses, busstr, nvcas, vcastr);
7510 else if (ntracks > 0) {
7511 title = string_compose (_("Remove %1"), trackstr);
7512 prompt = string_compose (_("Do you really want to remove %1 %2?"),
7515 else if (nbusses > 0) {
7516 title = string_compose (_("Remove %1"), busstr);
7517 prompt = string_compose (_("Do you really want to remove %1 %2?"),
7520 else if (nvcas > 0) {
7521 title = string_compose (_("Remove %1"), vcastr);
7522 prompt = string_compose (_("Do you really want to remove %1 %2?"),
7530 prompt += "\n" + string_compose ("(You may also lose the playlists associated with the %1)", trackstr) + "\n";
7533 prompt += "\n" + string(_("This action cannot be undone, and the session file will be overwritten!"));
7535 choices.push_back (_("No, do nothing."));
7536 if (ntracks + nbusses + nvcas > 1) {
7537 choices.push_back (_("Yes, remove them."));
7539 choices.push_back (_("Yes, remove it."));
7542 Choice prompter (title, prompt, choices);
7544 if (prompter.run () != 1) {
7548 if (current_mixer_strip && routes.size () > 1 && std::find (routes.begin(), routes.end(), current_mixer_strip->route()) != routes.end ()) {
7549 /* Route deletion calls Editor::timeaxisview_deleted() iteratively (for each deleted
7550 * route). If the deleted route is currently displayed in the Editor-Mixer (highly
7551 * likely because deletion requires selection) this will call
7552 * Editor::set_selected_mixer_strip () which is expensive (MixerStrip::set_route()).
7553 * It's likewise likely that the route that has just been displayed in the
7554 * Editor-Mixer will be next in line for deletion.
7556 * So simply switch to the master-bus (if present)
7558 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
7559 if ((*i)->stripable ()->is_master ()) {
7560 set_selected_mixer_strip (*(*i));
7567 PresentationInfo::ChangeSuspender cs;
7568 DisplaySuspender ds;
7570 boost::shared_ptr<RouteList> rl (new RouteList);
7571 for (vector<boost::shared_ptr<Route> >::iterator x = routes.begin(); x != routes.end(); ++x) {
7574 _session->remove_routes (rl);
7576 for (vector<boost::shared_ptr<VCA> >::iterator x = vcas.begin(); x != vcas.end(); ++x) {
7577 _session->vca_manager().remove_vca (*x);
7581 /* TrackSelection and RouteList leave scope,
7582 * destructors are called,
7583 * diskstream drops references, save_state is called (again for every track)
7588 Editor::do_insert_time ()
7590 if (selection->tracks.empty()) {
7591 MessageDialog msg (_("You must first select some tracks to Insert Time."),
7592 true, MESSAGE_INFO, BUTTONS_OK, true);
7593 msg.set_position (WIN_POS_MOUSE);
7598 if (Config->get_edit_mode() == Lock) {
7599 MessageDialog msg (_("You cannot insert time in Lock Edit mode."),
7600 true, MESSAGE_INFO, BUTTONS_OK, true);
7601 msg.set_position (WIN_POS_MOUSE);
7606 InsertRemoveTimeDialog d (*this);
7607 int response = d.run ();
7609 if (response != RESPONSE_OK) {
7613 if (d.distance() == 0) {
7620 d.intersected_region_action (),
7624 d.move_glued_markers(),
7625 d.move_locked_markers(),
7631 Editor::insert_time (
7632 samplepos_t pos, samplecnt_t samples, InsertTimeOption opt,
7633 bool all_playlists, bool ignore_music_glue, bool markers_too, bool glued_markers_too, bool locked_markers_too, bool tempo_too
7637 if (Config->get_edit_mode() == Lock) {
7640 bool in_command = false;
7642 TrackViewList ts = selection->tracks.filter_to_unique_playlists ();
7644 for (TrackViewList::iterator x = ts.begin(); x != ts.end(); ++x) {
7648 /* don't operate on any playlist more than once, which could
7649 * happen if "all playlists" is enabled, but there is more
7650 * than 1 track using playlists "from" a given track.
7653 set<boost::shared_ptr<Playlist> > pl;
7655 if (all_playlists) {
7656 RouteTimeAxisView* rtav = dynamic_cast<RouteTimeAxisView*> (*x);
7657 if (rtav && rtav->track ()) {
7658 vector<boost::shared_ptr<Playlist> > all = _session->playlists->playlists_for_track (rtav->track ());
7659 for (vector<boost::shared_ptr<Playlist> >::iterator p = all.begin(); p != all.end(); ++p) {
7664 if ((*x)->playlist ()) {
7665 pl.insert ((*x)->playlist ());
7669 for (set<boost::shared_ptr<Playlist> >::iterator i = pl.begin(); i != pl.end(); ++i) {
7671 (*i)->clear_changes ();
7672 (*i)->clear_owned_changes ();
7675 begin_reversible_command (_("insert time"));
7679 if (opt == SplitIntersected) {
7680 /* non musical split */
7681 (*i)->split (MusicSample (pos, 0));
7684 (*i)->shift (pos, samples, (opt == MoveIntersected), ignore_music_glue);
7686 vector<Command*> cmds;
7688 _session->add_commands (cmds);
7690 _session->add_command (new StatefulDiffCommand (*i));
7694 RouteTimeAxisView* rtav = dynamic_cast<RouteTimeAxisView*> (*x);
7697 begin_reversible_command (_("insert time"));
7700 rtav->route ()->shift (pos, samples);
7707 const int32_t divisions = get_grid_music_divisions (0);
7708 XMLNode& before (_session->locations()->get_state());
7709 Locations::LocationList copy (_session->locations()->list());
7711 for (Locations::LocationList::iterator i = copy.begin(); i != copy.end(); ++i) {
7713 Locations::LocationList::const_iterator tmp;
7715 if ((*i)->position_lock_style() == AudioTime || glued_markers_too) {
7716 bool const was_locked = (*i)->locked ();
7717 if (locked_markers_too) {
7721 if ((*i)->start() >= pos) {
7722 // move end first, in case we're moving by more than the length of the range
7723 if (!(*i)->is_mark()) {
7724 (*i)->set_end ((*i)->end() + samples, false, true, divisions);
7726 (*i)->set_start ((*i)->start() + samples, false, true, divisions);
7738 begin_reversible_command (_("insert time"));
7741 XMLNode& after (_session->locations()->get_state());
7742 _session->add_command (new MementoCommand<Locations>(*_session->locations(), &before, &after));
7748 begin_reversible_command (_("insert time"));
7751 XMLNode& before (_session->tempo_map().get_state());
7752 _session->tempo_map().insert_time (pos, samples);
7753 XMLNode& after (_session->tempo_map().get_state());
7754 _session->add_command (new MementoCommand<TempoMap>(_session->tempo_map(), &before, &after));
7758 commit_reversible_command ();
7763 Editor::do_remove_time ()
7765 if (selection->tracks.empty()) {
7766 MessageDialog msg (_("You must first select some tracks to Remove Time."),
7767 true, MESSAGE_INFO, BUTTONS_OK, true);
7768 msg.set_position (WIN_POS_MOUSE);
7773 if (Config->get_edit_mode() == Lock) {
7774 MessageDialog msg (_("You cannot remove time in Lock Edit mode."),
7775 true, MESSAGE_INFO, BUTTONS_OK, true);
7776 msg.set_position (WIN_POS_MOUSE);
7781 InsertRemoveTimeDialog d (*this, true);
7783 int response = d.run ();
7785 if (response != RESPONSE_OK) {
7789 samplecnt_t distance = d.distance();
7791 if (distance == 0) {
7801 d.move_glued_markers(),
7802 d.move_locked_markers(),
7808 Editor::remove_time (samplepos_t pos, samplecnt_t samples, InsertTimeOption opt,
7809 bool ignore_music_glue, bool markers_too, bool glued_markers_too, bool locked_markers_too, bool tempo_too)
7811 if (Config->get_edit_mode() == Lock) {
7812 error << (_("Cannot insert or delete time when in Lock edit.")) << endmsg;
7815 bool in_command = false;
7817 for (TrackSelection::iterator x = selection->tracks.begin(); x != selection->tracks.end(); ++x) {
7819 boost::shared_ptr<Playlist> pl = (*x)->playlist();
7823 XMLNode &before = pl->get_state();
7826 begin_reversible_command (_("remove time"));
7830 std::list<AudioRange> rl;
7831 AudioRange ar(pos, pos+samples, 0);
7834 pl->shift (pos, -samples, true, ignore_music_glue);
7836 XMLNode &after = pl->get_state();
7838 _session->add_command (new MementoCommand<Playlist> (*pl, &before, &after));
7842 RouteTimeAxisView* rtav = dynamic_cast<RouteTimeAxisView*> (*x);
7845 begin_reversible_command (_("remove time"));
7848 rtav->route ()->shift (pos, -samples);
7852 const int32_t divisions = get_grid_music_divisions (0);
7853 std::list<Location*> loc_kill_list;
7858 XMLNode& before (_session->locations()->get_state());
7859 Locations::LocationList copy (_session->locations()->list());
7861 for (Locations::LocationList::iterator i = copy.begin(); i != copy.end(); ++i) {
7862 if ((*i)->position_lock_style() == AudioTime || glued_markers_too) {
7864 bool const was_locked = (*i)->locked ();
7865 if (locked_markers_too) {
7869 if (!(*i)->is_mark()) { // it's a range; have to handle both start and end
7870 if ((*i)->end() >= pos
7871 && (*i)->end() < pos+samples
7872 && (*i)->start() >= pos
7873 && (*i)->end() < pos+samples) { // range is completely enclosed; kill it
7875 loc_kill_list.push_back(*i);
7876 } else { // only start or end is included, try to do the right thing
7877 // move start before moving end, to avoid trying to move the end to before the start
7878 // if we're removing more time than the length of the range
7879 if ((*i)->start() >= pos && (*i)->start() < pos+samples) {
7880 // start is within cut
7881 (*i)->set_start (pos, false, true,divisions); // bring the start marker to the beginning of the cut
7883 } else if ((*i)->start() >= pos+samples) {
7884 // start (and thus entire range) lies beyond end of cut
7885 (*i)->set_start ((*i)->start() - samples, false, true, divisions); // slip the start marker back
7888 if ((*i)->end() >= pos && (*i)->end() < pos+samples) {
7889 // end is inside cut
7890 (*i)->set_end (pos, false, true, divisions); // bring the end to the cut
7892 } else if ((*i)->end() >= pos+samples) {
7893 // end is beyond end of cut
7894 (*i)->set_end ((*i)->end() - samples, false, true, divisions); // slip the end marker back
7899 } else if ((*i)->start() >= pos && (*i)->start() < pos+samples) {
7900 loc_kill_list.push_back(*i);
7902 } else if ((*i)->start() >= pos) {
7903 (*i)->set_start ((*i)->start() -samples, false, true, divisions);
7913 for (list<Location*>::iterator i = loc_kill_list.begin(); i != loc_kill_list.end(); ++i) {
7914 _session->locations()->remove (*i);
7919 begin_reversible_command (_("remove time"));
7922 XMLNode& after (_session->locations()->get_state());
7923 _session->add_command (new MementoCommand<Locations>(*_session->locations(), &before, &after));
7928 XMLNode& before (_session->tempo_map().get_state());
7930 if (_session->tempo_map().remove_time (pos, samples)) {
7932 begin_reversible_command (_("remove time"));
7935 XMLNode& after (_session->tempo_map().get_state());
7936 _session->add_command (new MementoCommand<TempoMap>(_session->tempo_map(), &before, &after));
7941 commit_reversible_command ();
7946 Editor::fit_selection ()
7948 if (!selection->tracks.empty()) {
7949 fit_tracks (selection->tracks);
7953 /* no selected tracks - use tracks with selected regions */
7955 if (!selection->regions.empty()) {
7956 for (RegionSelection::iterator r = selection->regions.begin(); r != selection->regions.end(); ++r) {
7957 tvl.push_back (&(*r)->get_time_axis_view ());
7963 } else if (internal_editing()) {
7964 /* no selected tracks, or regions, but in internal edit mode, so follow the mouse and use
7967 if (entered_track) {
7968 tvl.push_back (entered_track);
7976 Editor::fit_tracks (TrackViewList & tracks)
7978 if (tracks.empty()) {
7982 uint32_t child_heights = 0;
7983 int visible_tracks = 0;
7985 for (TrackSelection::iterator t = tracks.begin(); t != tracks.end(); ++t) {
7987 if (!(*t)->marked_for_display()) {
7991 child_heights += (*t)->effective_height() - (*t)->current_height();
7995 /* compute the per-track height from:
7997 * total canvas visible height
7998 * - height that will be taken by visible children of selected tracks
7999 * - height of the ruler/hscroll area
8001 uint32_t h = (uint32_t) floor ((trackviews_height() - child_heights) / visible_tracks);
8002 double first_y_pos = DBL_MAX;
8004 if (h < TimeAxisView::preset_height (HeightSmall)) {
8005 MessageDialog msg (_("There are too many tracks to fit in the current window"));
8006 /* too small to be displayed */
8010 undo_visual_stack.push_back (current_visual_state (true));
8011 PBD::Unwinder<bool> nsv (no_save_visual, true);
8013 /* build a list of all tracks, including children */
8016 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
8018 TimeAxisView::Children c = (*i)->get_child_list ();
8019 for (TimeAxisView::Children::iterator j = c.begin(); j != c.end(); ++j) {
8020 all.push_back (j->get());
8025 // find selection range.
8026 // if someone knows how to user TrackViewList::iterator for this
8028 int selected_top = -1;
8029 int selected_bottom = -1;
8031 for (TrackViewList::iterator t = all.begin(); t != all.end(); ++t, ++i) {
8032 if ((*t)->marked_for_display ()) {
8033 if (tracks.contains(*t)) {
8034 if (selected_top == -1) {
8037 selected_bottom = i;
8043 for (TrackViewList::iterator t = all.begin(); t != all.end(); ++t, ++i) {
8044 if ((*t)->marked_for_display ()) {
8045 if (tracks.contains(*t)) {
8046 (*t)->set_height (h);
8047 first_y_pos = std::min ((*t)->y_position (), first_y_pos);
8049 if (i > selected_top && i < selected_bottom) {
8050 hide_track_in_display (*t);
8057 set the controls_layout height now, because waiting for its size
8058 request signal handler will cause the vertical adjustment setting to fail
8061 controls_layout.property_height () = _full_canvas_height;
8062 vertical_adjustment.set_value (first_y_pos);
8064 redo_visual_stack.push_back (current_visual_state (true));
8066 visible_tracks_selector.set_text (_("Sel"));
8070 Editor::save_visual_state (uint32_t n)
8072 while (visual_states.size() <= n) {
8073 visual_states.push_back (0);
8076 if (visual_states[n] != 0) {
8077 delete visual_states[n];
8080 visual_states[n] = current_visual_state (true);
8085 Editor::goto_visual_state (uint32_t n)
8087 if (visual_states.size() <= n) {
8091 if (visual_states[n] == 0) {
8095 use_visual_state (*visual_states[n]);
8099 Editor::start_visual_state_op (uint32_t n)
8101 save_visual_state (n);
8103 PopUp* pup = new PopUp (WIN_POS_MOUSE, 1000, true);
8105 snprintf (buf, sizeof (buf), _("Saved view %u"), n+1);
8106 pup->set_text (buf);
8111 Editor::cancel_visual_state_op (uint32_t n)
8113 goto_visual_state (n);
8117 Editor::toggle_region_mute ()
8119 if (_ignore_region_action) {
8123 RegionSelection rs = get_regions_from_selection_and_entered ();
8129 if (rs.size() > 1) {
8130 begin_reversible_command (_("mute regions"));
8132 begin_reversible_command (_("mute region"));
8135 for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
8137 (*i)->region()->playlist()->clear_changes ();
8138 (*i)->region()->set_muted (!(*i)->region()->muted ());
8139 _session->add_command (new StatefulDiffCommand ((*i)->region()));
8143 commit_reversible_command ();
8147 Editor::combine_regions ()
8149 /* foreach track with selected regions, take all selected regions
8150 and join them into a new region containing the subregions (as a
8154 typedef set<RouteTimeAxisView*> RTVS;
8157 if (selection->regions.empty()) {
8161 for (RegionSelection::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
8162 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*>(&(*i)->get_time_axis_view());
8165 tracks.insert (rtv);
8169 begin_reversible_command (_("combine regions"));
8171 vector<RegionView*> new_selection;
8173 for (RTVS::iterator i = tracks.begin(); i != tracks.end(); ++i) {
8176 if ((rv = (*i)->combine_regions ()) != 0) {
8177 new_selection.push_back (rv);
8181 selection->clear_regions ();
8182 for (vector<RegionView*>::iterator i = new_selection.begin(); i != new_selection.end(); ++i) {
8183 selection->add (*i);
8186 commit_reversible_command ();
8190 Editor::uncombine_regions ()
8192 typedef set<RouteTimeAxisView*> RTVS;
8195 if (selection->regions.empty()) {
8199 for (RegionSelection::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
8200 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*>(&(*i)->get_time_axis_view());
8203 tracks.insert (rtv);
8207 begin_reversible_command (_("uncombine regions"));
8209 for (RTVS::iterator i = tracks.begin(); i != tracks.end(); ++i) {
8210 (*i)->uncombine_regions ();
8213 commit_reversible_command ();
8217 Editor::toggle_midi_input_active (bool flip_others)
8220 boost::shared_ptr<RouteList> rl (new RouteList);
8222 for (TrackSelection::iterator i = selection->tracks.begin(); i != selection->tracks.end(); ++i) {
8223 RouteTimeAxisView *rtav = dynamic_cast<RouteTimeAxisView *>(*i);
8229 boost::shared_ptr<MidiTrack> mt = rtav->midi_track();
8232 rl->push_back (rtav->route());
8233 onoff = !mt->input_active();
8237 _session->set_exclusive_input_active (rl, onoff, flip_others);
8240 static bool ok_fine (GdkEventAny*) { return true; }
8246 lock_dialog = new Gtk::Dialog (string_compose (_("%1: Locked"), PROGRAM_NAME), true);
8248 Gtk::Image* padlock = manage (new Gtk::Image (ARDOUR_UI_UTILS::get_icon ("padlock_closed")));
8249 lock_dialog->get_vbox()->pack_start (*padlock);
8250 lock_dialog->signal_delete_event ().connect (sigc::ptr_fun (ok_fine));
8252 ArdourButton* b = manage (new ArdourButton);
8253 b->set_name ("lock button");
8254 b->set_text (_("Click to unlock"));
8255 b->signal_clicked.connect (sigc::mem_fun (*this, &Editor::unlock));
8256 lock_dialog->get_vbox()->pack_start (*b);
8258 lock_dialog->get_vbox()->show_all ();
8259 lock_dialog->set_size_request (200, 200);
8262 delete _main_menu_disabler;
8263 _main_menu_disabler = new MainMenuDisabler;
8265 lock_dialog->present ();
8267 lock_dialog->get_window()->set_decorations (Gdk::WMDecoration (0));
8273 lock_dialog->hide ();
8275 delete _main_menu_disabler;
8276 _main_menu_disabler = 0;
8278 if (UIConfiguration::instance().get_lock_gui_after_seconds()) {
8279 start_lock_event_timing ();
8284 Editor::bring_in_callback (Gtk::Label* label, uint32_t n, uint32_t total, string name)
8286 Gtkmm2ext::UI::instance()->call_slot (invalidator (*this), boost::bind (&Editor::update_bring_in_message, this, label, n, total, name));
8290 Editor::update_bring_in_message (Gtk::Label* label, uint32_t n, uint32_t total, string name)
8292 Timers::TimerSuspender t;
8293 label->set_text (string_compose ("Copying %1, %2 of %3", name, n, total));
8294 Gtkmm2ext::UI::instance()->flush_pending (1);
8298 Editor::bring_all_sources_into_session ()
8305 ArdourDialog w (_("Moving embedded files into session folder"));
8306 w.get_vbox()->pack_start (msg);
8309 /* flush all pending GUI events because we're about to start copying
8313 Timers::TimerSuspender t;
8314 Gtkmm2ext::UI::instance()->flush_pending (3);
8318 _session->bring_all_sources_into_session (boost::bind (&Editor::bring_in_callback, this, &msg, _1, _2, _3));