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/transport_master_manager.h"
67 #include "ardour/transpose.h"
68 #include "ardour/vca_manager.h"
70 #include "canvas/canvas.h"
73 #include "ardour_ui.h"
74 #include "audio_region_view.h"
75 #include "audio_streamview.h"
76 #include "audio_time_axis.h"
77 #include "automation_region_view.h"
78 #include "automation_time_axis.h"
79 #include "control_point.h"
83 #include "editor_cursors.h"
84 #include "editor_drag.h"
85 #include "editor_regions.h"
86 #include "editor_routes.h"
87 #include "gui_thread.h"
88 #include "insert_remove_time_dialog.h"
89 #include "interthread_progress_window.h"
90 #include "item_counts.h"
92 #include "midi_region_view.h"
94 #include "mixer_strip.h"
95 #include "mouse_cursors.h"
96 #include "normalize_dialog.h"
98 #include "paste_context.h"
99 #include "patch_change_dialog.h"
100 #include "quantize_dialog.h"
101 #include "region_gain_line.h"
102 #include "rgb_macros.h"
103 #include "route_time_axis.h"
104 #include "selection.h"
105 #include "selection_templates.h"
106 #include "streamview.h"
107 #include "strip_silence_dialog.h"
108 #include "time_axis_view.h"
110 #include "transpose_dialog.h"
111 #include "transform_dialog.h"
112 #include "ui_config.h"
114 #include "vca_time_axis.h"
116 #include "pbd/i18n.h"
119 using namespace ARDOUR;
122 using namespace Gtkmm2ext;
123 using namespace ArdourWidgets;
124 using namespace Editing;
125 using Gtkmm2ext::Keyboard;
127 /***********************************************************************
129 ***********************************************************************/
132 Editor::undo (uint32_t n)
134 if (_session && _session->actively_recording()) {
135 /* no undo allowed while recording. Session will check also,
136 but we don't even want to get to that.
141 if (_drags->active ()) {
147 if (_session->undo_depth() == 0) {
148 undo_action->set_sensitive(false);
150 redo_action->set_sensitive(true);
151 begin_selection_op_history ();
156 Editor::redo (uint32_t n)
158 if (_session && _session->actively_recording()) {
159 /* no redo allowed while recording. Session will check also,
160 but we don't even want to get to that.
165 if (_drags->active ()) {
171 if (_session->redo_depth() == 0) {
172 redo_action->set_sensitive(false);
174 undo_action->set_sensitive(true);
175 begin_selection_op_history ();
180 Editor::split_regions_at (MusicSample where, RegionSelection& regions)
184 RegionSelection pre_selected_regions = selection->regions;
185 bool working_on_selection = !pre_selected_regions.empty();
187 list<boost::shared_ptr<Playlist> > used_playlists;
188 list<RouteTimeAxisView*> used_trackviews;
190 if (regions.empty()) {
194 begin_reversible_command (_("split"));
197 if (regions.size() == 1) {
198 /* TODO: if splitting a single region, and snap-to is using
199 region boundaries, mabye we shouldn't pay attention to them? */
202 EditorFreeze(); /* Emit Signal */
205 for (RegionSelection::iterator a = regions.begin(); a != regions.end(); ) {
207 RegionSelection::iterator tmp;
209 /* XXX this test needs to be more complicated, to make sure we really
210 have something to split.
213 if (!(*a)->region()->covers (where.sample)) {
221 boost::shared_ptr<Playlist> pl = (*a)->region()->playlist();
229 /* we haven't seen this playlist before */
231 /* remember used playlists so we can thaw them later */
232 used_playlists.push_back(pl);
234 TimeAxisView& tv = (*a)->get_time_axis_view();
235 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (&tv);
237 used_trackviews.push_back (rtv);
244 pl->clear_changes ();
245 pl->split_region ((*a)->region(), where);
246 _session->add_command (new StatefulDiffCommand (pl));
252 latest_regionviews.clear ();
254 vector<sigc::connection> region_added_connections;
256 for (list<RouteTimeAxisView*>::iterator i = used_trackviews.begin(); i != used_trackviews.end(); ++i) {
257 region_added_connections.push_back ((*i)->view()->RegionViewAdded.connect (sigc::mem_fun(*this, &Editor::collect_new_region_view)));
260 while (used_playlists.size() > 0) {
261 list <boost::shared_ptr<Playlist > >::iterator i = used_playlists.begin();
263 used_playlists.pop_front();
266 for (vector<sigc::connection>::iterator c = region_added_connections.begin(); c != region_added_connections.end(); ++c) {
271 EditorThaw(); /* Emit Signal */
274 if (working_on_selection) {
275 // IFF we were working on selected regions, try to reinstate the other region selections that existed before the freeze/thaw.
277 RegionSelectionAfterSplit rsas = Config->get_region_selection_after_split();
278 /* There are three classes of regions that we might want selected after
279 splitting selected regions:
280 - regions selected before the split operation, and unaffected by it
281 - newly-created regions before the split
282 - newly-created regions after the split
285 if (rsas & Existing) {
286 // region selections that existed before the split.
287 selection->add (pre_selected_regions);
290 for (RegionSelection::iterator ri = latest_regionviews.begin(); ri != latest_regionviews.end(); ri++) {
291 if ((*ri)->region()->position() < where.sample) {
292 // new regions created before the split
293 if (rsas & NewlyCreatedLeft) {
294 selection->add (*ri);
297 // new regions created after the split
298 if (rsas & NewlyCreatedRight) {
299 selection->add (*ri);
305 commit_reversible_command ();
308 /** Move one extreme of the current range selection. If more than one range is selected,
309 * the start of the earliest range or the end of the latest range is moved.
311 * @param move_end true to move the end of the current range selection, false to move
313 * @param next true to move the extreme to the next region boundary, false to move to
317 Editor::move_range_selection_start_or_end_to_region_boundary (bool move_end, bool next)
319 if (selection->time.start() == selection->time.end_sample()) {
323 samplepos_t start = selection->time.start ();
324 samplepos_t end = selection->time.end_sample ();
326 /* the position of the thing we may move */
327 samplepos_t pos = move_end ? end : start;
328 int dir = next ? 1 : -1;
330 /* so we don't find the current region again */
331 if (dir > 0 || pos > 0) {
335 samplepos_t const target = get_region_boundary (pos, dir, true, false);
350 begin_reversible_selection_op (_("alter selection"));
351 selection->set_preserving_all_ranges (start, end);
352 commit_reversible_selection_op ();
356 Editor::nudge_forward_release (GdkEventButton* ev)
358 if (ev->state & Keyboard::PrimaryModifier) {
359 nudge_forward (false, true);
361 nudge_forward (false, false);
367 Editor::nudge_backward_release (GdkEventButton* ev)
369 if (ev->state & Keyboard::PrimaryModifier) {
370 nudge_backward (false, true);
372 nudge_backward (false, false);
379 Editor::nudge_forward (bool next, bool force_playhead)
381 samplepos_t distance;
382 samplepos_t next_distance;
388 RegionSelection rs = get_regions_from_selection_and_entered ();
390 if (!force_playhead && !rs.empty()) {
392 begin_reversible_command (_("nudge regions forward"));
394 for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
395 boost::shared_ptr<Region> r ((*i)->region());
397 distance = get_nudge_distance (r->position(), next_distance);
400 distance = next_distance;
404 r->set_position (r->position() + distance);
405 _session->add_command (new StatefulDiffCommand (r));
408 commit_reversible_command ();
411 } else if (!force_playhead && !selection->markers.empty()) {
414 bool in_command = false;
415 const int32_t divisions = get_grid_music_divisions (0);
417 for (MarkerSelection::iterator i = selection->markers.begin(); i != selection->markers.end(); ++i) {
419 Location* loc = find_location_from_marker ((*i), is_start);
423 XMLNode& before (loc->get_state());
426 distance = get_nudge_distance (loc->start(), next_distance);
428 distance = next_distance;
430 if (max_samplepos - distance > loc->start() + loc->length()) {
431 loc->set_start (loc->start() + distance, false, true, divisions);
433 loc->set_start (max_samplepos - loc->length(), false, true, divisions);
436 distance = get_nudge_distance (loc->end(), next_distance);
438 distance = next_distance;
440 if (max_samplepos - distance > loc->end()) {
441 loc->set_end (loc->end() + distance, false, true, divisions);
443 loc->set_end (max_samplepos, false, true, divisions);
445 if (loc->is_session_range()) {
446 _session->set_end_is_free (false);
450 begin_reversible_command (_("nudge location forward"));
453 XMLNode& after (loc->get_state());
454 _session->add_command (new MementoCommand<Location>(*loc, &before, &after));
459 commit_reversible_command ();
462 distance = get_nudge_distance (playhead_cursor->current_sample (), next_distance);
463 _session->request_locate (playhead_cursor->current_sample () + distance);
468 Editor::nudge_backward (bool next, bool force_playhead)
470 samplepos_t distance;
471 samplepos_t next_distance;
477 RegionSelection rs = get_regions_from_selection_and_entered ();
479 if (!force_playhead && !rs.empty()) {
481 begin_reversible_command (_("nudge regions backward"));
483 for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
484 boost::shared_ptr<Region> r ((*i)->region());
486 distance = get_nudge_distance (r->position(), next_distance);
489 distance = next_distance;
494 if (r->position() > distance) {
495 r->set_position (r->position() - distance);
499 _session->add_command (new StatefulDiffCommand (r));
502 commit_reversible_command ();
504 } else if (!force_playhead && !selection->markers.empty()) {
507 bool in_command = false;
509 for (MarkerSelection::iterator i = selection->markers.begin(); i != selection->markers.end(); ++i) {
511 Location* loc = find_location_from_marker ((*i), is_start);
515 XMLNode& before (loc->get_state());
518 distance = get_nudge_distance (loc->start(), next_distance);
520 distance = next_distance;
522 if (distance < loc->start()) {
523 loc->set_start (loc->start() - distance, false, true, get_grid_music_divisions(0));
525 loc->set_start (0, false, true, get_grid_music_divisions(0));
528 distance = get_nudge_distance (loc->end(), next_distance);
531 distance = next_distance;
534 if (distance < loc->end() - loc->length()) {
535 loc->set_end (loc->end() - distance, false, true, get_grid_music_divisions(0));
537 loc->set_end (loc->length(), false, true, get_grid_music_divisions(0));
539 if (loc->is_session_range()) {
540 _session->set_end_is_free (false);
544 begin_reversible_command (_("nudge location forward"));
547 XMLNode& after (loc->get_state());
548 _session->add_command (new MementoCommand<Location>(*loc, &before, &after));
552 commit_reversible_command ();
557 distance = get_nudge_distance (playhead_cursor->current_sample (), next_distance);
559 if (playhead_cursor->current_sample () > distance) {
560 _session->request_locate (playhead_cursor->current_sample () - distance);
562 _session->goto_start();
568 Editor::nudge_forward_capture_offset ()
570 RegionSelection rs = get_regions_from_selection_and_entered ();
572 if (!_session || rs.empty()) {
576 begin_reversible_command (_("nudge forward"));
578 samplepos_t const distance = _session->worst_output_latency();
580 for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
581 boost::shared_ptr<Region> r ((*i)->region());
584 r->set_position (r->position() + distance);
585 _session->add_command(new StatefulDiffCommand (r));
588 commit_reversible_command ();
592 Editor::nudge_backward_capture_offset ()
594 RegionSelection rs = get_regions_from_selection_and_entered ();
596 if (!_session || rs.empty()) {
600 begin_reversible_command (_("nudge backward"));
602 samplepos_t const distance = _session->worst_output_latency();
604 for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
605 boost::shared_ptr<Region> r ((*i)->region());
609 if (r->position() > distance) {
610 r->set_position (r->position() - distance);
614 _session->add_command(new StatefulDiffCommand (r));
617 commit_reversible_command ();
620 struct RegionSelectionPositionSorter {
621 bool operator() (RegionView* a, RegionView* b) {
622 return a->region()->position() < b->region()->position();
627 Editor::sequence_regions ()
630 samplepos_t r_end_prev;
638 RegionSelection rs = get_regions_from_selection_and_entered ();
639 rs.sort(RegionSelectionPositionSorter());
643 bool in_command = false;
645 for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
646 boost::shared_ptr<Region> r ((*i)->region());
654 if(r->position_locked())
661 r->set_position(r_end_prev);
665 begin_reversible_command (_("sequence regions"));
668 _session->add_command (new StatefulDiffCommand (r));
670 r_end=r->position() + r->length();
676 commit_reversible_command ();
685 Editor::move_to_start ()
687 _session->goto_start ();
691 Editor::move_to_end ()
694 _session->request_locate (_session->current_end_sample());
698 Editor::build_region_boundary_cache ()
701 /* TODO: maybe set a timer so we don't recalutate when lots of changes are coming in */
702 /* TODO: maybe somehow defer this until session is fully loaded. */
704 if (!_region_boundary_cache_dirty)
708 vector<RegionPoint> interesting_points;
709 boost::shared_ptr<Region> r;
710 TrackViewList tracks;
713 region_boundary_cache.clear ();
719 bool maybe_first_sample = false;
721 if (UIConfiguration::instance().get_snap_to_region_start()) {
722 interesting_points.push_back (Start);
723 maybe_first_sample = true;
726 if (UIConfiguration::instance().get_snap_to_region_end()) {
727 interesting_points.push_back (End);
730 if (UIConfiguration::instance().get_snap_to_region_sync()) {
731 interesting_points.push_back (SyncPoint);
734 /* if no snap selections are set, boundary cache should be left empty */
735 if ( interesting_points.empty() ) {
736 _region_boundary_cache_dirty = false;
740 TimeAxisView *ontrack = 0;
743 tlist = track_views.filter_to_unique_playlists ();
745 if (maybe_first_sample) {
746 TrackViewList::const_iterator i;
747 for (i = tlist.begin(); i != tlist.end(); ++i) {
748 boost::shared_ptr<Playlist> pl = (*i)->playlist();
749 if (pl && pl->count_regions_at (0)) {
750 region_boundary_cache.push_back (0);
756 //allow regions to snap to the video start (if any) as if it were a "region"
757 if (ARDOUR_UI::instance()->video_timeline) {
758 region_boundary_cache.push_back (ARDOUR_UI::instance()->video_timeline->get_video_start_offset());
761 std::pair<samplepos_t, samplepos_t> ext = session_gui_extents (false);
762 samplepos_t session_end = ext.second;
764 while (pos < session_end && !at_end) {
767 samplepos_t lpos = session_end;
769 for (vector<RegionPoint>::iterator p = interesting_points.begin(); p != interesting_points.end(); ++p) {
771 if ((r = find_next_region (pos, *p, 1, tlist, &ontrack)) == 0) {
772 if (*p == interesting_points.back()) {
775 /* move to next point type */
781 rpos = r->first_sample();
785 rpos = r->last_sample();
789 rpos = r->sync_position ();
800 /* prevent duplicates, but we don't use set<> because we want to be able
804 vector<samplepos_t>::iterator ri;
806 for (ri = region_boundary_cache.begin(); ri != region_boundary_cache.end(); ++ri) {
812 if (ri == region_boundary_cache.end()) {
813 region_boundary_cache.push_back (rpos);
820 /* finally sort to be sure that the order is correct */
822 sort (region_boundary_cache.begin(), region_boundary_cache.end());
824 _region_boundary_cache_dirty = false;
827 boost::shared_ptr<Region>
828 Editor::find_next_region (samplepos_t sample, RegionPoint point, int32_t dir, TrackViewList& tracks, TimeAxisView **ontrack)
830 TrackViewList::iterator i;
831 samplepos_t closest = max_samplepos;
832 boost::shared_ptr<Region> ret;
833 samplepos_t rpos = 0;
835 samplepos_t track_sample;
837 for (i = tracks.begin(); i != tracks.end(); ++i) {
839 samplecnt_t distance;
840 boost::shared_ptr<Region> r;
842 track_sample = sample;
844 if ((r = (*i)->find_next_region (track_sample, point, dir)) == 0) {
850 rpos = r->first_sample ();
854 rpos = r->last_sample ();
858 rpos = r->sync_position ();
863 distance = rpos - sample;
865 distance = sample - rpos;
868 if (distance < closest) {
880 Editor::find_next_region_boundary (samplepos_t pos, int32_t dir, const TrackViewList& tracks)
882 samplecnt_t distance = max_samplepos;
883 samplepos_t current_nearest = -1;
885 for (TrackViewList::const_iterator i = tracks.begin(); i != tracks.end(); ++i) {
886 samplepos_t contender;
889 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (*i);
895 if ((contender = rtv->find_next_region_boundary (pos, dir)) < 0) {
899 d = ::llabs (pos - contender);
902 current_nearest = contender;
907 return current_nearest;
911 Editor::get_region_boundary (samplepos_t pos, int32_t dir, bool with_selection, bool only_onscreen)
916 if (with_selection && Config->get_region_boundaries_from_selected_tracks()) {
918 if (!selection->tracks.empty()) {
920 target = find_next_region_boundary (pos, dir, selection->tracks);
924 if (only_onscreen || Config->get_region_boundaries_from_onscreen_tracks()) {
925 get_onscreen_tracks (tvl);
926 target = find_next_region_boundary (pos, dir, tvl);
928 target = find_next_region_boundary (pos, dir, track_views);
934 if (only_onscreen || Config->get_region_boundaries_from_onscreen_tracks()) {
935 get_onscreen_tracks (tvl);
936 target = find_next_region_boundary (pos, dir, tvl);
938 target = find_next_region_boundary (pos, dir, track_views);
946 Editor::cursor_to_region_boundary (bool with_selection, int32_t dir)
948 samplepos_t pos = playhead_cursor->current_sample ();
955 // so we don't find the current region again..
956 if (dir > 0 || pos > 0) {
960 if ((target = get_region_boundary (pos, dir, with_selection, false)) < 0) {
964 _session->request_locate (target);
968 Editor::cursor_to_next_region_boundary (bool with_selection)
970 cursor_to_region_boundary (with_selection, 1);
974 Editor::cursor_to_previous_region_boundary (bool with_selection)
976 cursor_to_region_boundary (with_selection, -1);
980 Editor::cursor_to_region_point (EditorCursor* cursor, RegionPoint point, int32_t dir)
982 boost::shared_ptr<Region> r;
983 samplepos_t pos = cursor->current_sample ();
989 TimeAxisView *ontrack = 0;
991 // so we don't find the current region again..
995 if (!selection->tracks.empty()) {
997 r = find_next_region (pos, point, dir, selection->tracks, &ontrack);
999 } else if (clicked_axisview) {
1002 t.push_back (clicked_axisview);
1004 r = find_next_region (pos, point, dir, t, &ontrack);
1008 r = find_next_region (pos, point, dir, track_views, &ontrack);
1017 pos = r->first_sample ();
1021 pos = r->last_sample ();
1025 pos = r->sync_position ();
1029 if (cursor == playhead_cursor) {
1030 _session->request_locate (pos);
1032 cursor->set_position (pos);
1037 Editor::cursor_to_next_region_point (EditorCursor* cursor, RegionPoint point)
1039 cursor_to_region_point (cursor, point, 1);
1043 Editor::cursor_to_previous_region_point (EditorCursor* cursor, RegionPoint point)
1045 cursor_to_region_point (cursor, point, -1);
1049 Editor::cursor_to_selection_start (EditorCursor *cursor)
1051 samplepos_t pos = 0;
1053 switch (mouse_mode) {
1055 if (!selection->regions.empty()) {
1056 pos = selection->regions.start();
1061 if (!selection->time.empty()) {
1062 pos = selection->time.start ();
1070 if (cursor == playhead_cursor) {
1071 _session->request_locate (pos);
1073 cursor->set_position (pos);
1078 Editor::cursor_to_selection_end (EditorCursor *cursor)
1080 samplepos_t pos = 0;
1082 switch (mouse_mode) {
1084 if (!selection->regions.empty()) {
1085 pos = selection->regions.end_sample();
1090 if (!selection->time.empty()) {
1091 pos = selection->time.end_sample ();
1099 if (cursor == playhead_cursor) {
1100 _session->request_locate (pos);
1102 cursor->set_position (pos);
1107 Editor::selected_marker_to_region_boundary (bool with_selection, int32_t dir)
1117 if (selection->markers.empty()) {
1121 if (!mouse_sample (mouse, ignored)) {
1125 add_location_mark (mouse);
1128 if ((loc = find_location_from_marker (selection->markers.front(), ignored)) == 0) {
1132 samplepos_t pos = loc->start();
1134 // so we don't find the current region again..
1135 if (dir > 0 || pos > 0) {
1139 if ((target = get_region_boundary (pos, dir, with_selection, false)) < 0) {
1143 loc->move_to (target, 0);
1147 Editor::selected_marker_to_next_region_boundary (bool with_selection)
1149 selected_marker_to_region_boundary (with_selection, 1);
1153 Editor::selected_marker_to_previous_region_boundary (bool with_selection)
1155 selected_marker_to_region_boundary (with_selection, -1);
1159 Editor::selected_marker_to_region_point (RegionPoint point, int32_t dir)
1161 boost::shared_ptr<Region> r;
1166 if (!_session || selection->markers.empty()) {
1170 if ((loc = find_location_from_marker (selection->markers.front(), ignored)) == 0) {
1174 TimeAxisView *ontrack = 0;
1178 // so we don't find the current region again..
1182 if (!selection->tracks.empty()) {
1184 r = find_next_region (pos, point, dir, selection->tracks, &ontrack);
1188 r = find_next_region (pos, point, dir, track_views, &ontrack);
1197 pos = r->first_sample ();
1201 pos = r->last_sample ();
1205 pos = r->adjust_to_sync (r->first_sample());
1209 loc->move_to (pos, 0);
1213 Editor::selected_marker_to_next_region_point (RegionPoint point)
1215 selected_marker_to_region_point (point, 1);
1219 Editor::selected_marker_to_previous_region_point (RegionPoint point)
1221 selected_marker_to_region_point (point, -1);
1225 Editor::selected_marker_to_selection_start ()
1227 samplepos_t pos = 0;
1231 if (!_session || selection->markers.empty()) {
1235 if ((loc = find_location_from_marker (selection->markers.front(), ignored)) == 0) {
1239 switch (mouse_mode) {
1241 if (!selection->regions.empty()) {
1242 pos = selection->regions.start();
1247 if (!selection->time.empty()) {
1248 pos = selection->time.start ();
1256 loc->move_to (pos, 0);
1260 Editor::selected_marker_to_selection_end ()
1262 samplepos_t pos = 0;
1266 if (!_session || selection->markers.empty()) {
1270 if ((loc = find_location_from_marker (selection->markers.front(), ignored)) == 0) {
1274 switch (mouse_mode) {
1276 if (!selection->regions.empty()) {
1277 pos = selection->regions.end_sample();
1282 if (!selection->time.empty()) {
1283 pos = selection->time.end_sample ();
1291 loc->move_to (pos, 0);
1295 Editor::scroll_playhead (bool forward)
1297 samplepos_t pos = playhead_cursor->current_sample ();
1298 samplecnt_t delta = (samplecnt_t) floor (current_page_samples() / 0.8);
1301 if (pos == max_samplepos) {
1305 if (pos < max_samplepos - delta) {
1308 pos = max_samplepos;
1324 _session->request_locate (pos);
1328 Editor::cursor_align (bool playhead_to_edit)
1334 if (playhead_to_edit) {
1336 if (selection->markers.empty()) {
1340 _session->request_locate (selection->markers.front()->position(), _session->transport_rolling());
1343 const int32_t divisions = get_grid_music_divisions (0);
1344 /* move selected markers to playhead */
1346 for (MarkerSelection::iterator i = selection->markers.begin(); i != selection->markers.end(); ++i) {
1349 Location* loc = find_location_from_marker (*i, ignored);
1351 if (loc->is_mark()) {
1352 loc->set_start (playhead_cursor->current_sample (), false, true, divisions);
1354 loc->set (playhead_cursor->current_sample (),
1355 playhead_cursor->current_sample () + loc->length(), true, divisions);
1362 Editor::scroll_backward (float pages)
1364 samplepos_t const one_page = (samplepos_t) rint (_visible_canvas_width * samples_per_pixel);
1365 samplepos_t const cnt = (samplepos_t) floor (pages * one_page);
1368 if (_leftmost_sample < cnt) {
1371 sample = _leftmost_sample - cnt;
1374 reset_x_origin (sample);
1378 Editor::scroll_forward (float pages)
1380 samplepos_t const one_page = (samplepos_t) rint (_visible_canvas_width * samples_per_pixel);
1381 samplepos_t const cnt = (samplepos_t) floor (pages * one_page);
1384 if (max_samplepos - cnt < _leftmost_sample) {
1385 sample = max_samplepos - cnt;
1387 sample = _leftmost_sample + cnt;
1390 reset_x_origin (sample);
1394 Editor::scroll_tracks_down ()
1396 double vert_value = vertical_adjustment.get_value() + vertical_adjustment.get_page_size();
1397 if (vert_value > vertical_adjustment.get_upper() - _visible_canvas_height) {
1398 vert_value = vertical_adjustment.get_upper() - _visible_canvas_height;
1401 vertical_adjustment.set_value (vert_value);
1405 Editor::scroll_tracks_up ()
1407 vertical_adjustment.set_value (vertical_adjustment.get_value() - vertical_adjustment.get_page_size());
1411 Editor::scroll_tracks_down_line ()
1413 double vert_value = vertical_adjustment.get_value() + 60;
1415 if (vert_value > vertical_adjustment.get_upper() - _visible_canvas_height) {
1416 vert_value = vertical_adjustment.get_upper() - _visible_canvas_height;
1419 vertical_adjustment.set_value (vert_value);
1423 Editor::scroll_tracks_up_line ()
1425 reset_y_origin (vertical_adjustment.get_value() - 60);
1429 Editor::select_topmost_track ()
1431 const double top_of_trackviews = vertical_adjustment.get_value();
1432 for (TrackViewList::iterator t = track_views.begin(); t != track_views.end(); ++t) {
1433 if ((*t)->hidden()) {
1436 std::pair<TimeAxisView*,double> res = (*t)->covers_y_position (top_of_trackviews);
1438 selection->set (*t);
1445 Editor::scroll_down_one_track (bool skip_child_views)
1447 TrackViewList::reverse_iterator next = track_views.rend();
1448 const double top_of_trackviews = vertical_adjustment.get_value();
1450 for (TrackViewList::reverse_iterator t = track_views.rbegin(); t != track_views.rend(); ++t) {
1451 if ((*t)->hidden()) {
1455 /* If this is the upper-most visible trackview, we want to display
1456 * the one above it (next)
1458 * Note that covers_y_position() is recursive and includes child views
1460 std::pair<TimeAxisView*,double> res = (*t)->covers_y_position (top_of_trackviews);
1463 if (skip_child_views) {
1466 /* automation lane (one level, non-recursive)
1468 * - if no automation lane exists -> move to next tack
1469 * - if the first (here: bottom-most) matches -> move to next tack
1470 * - if no y-axis match is found -> the current track is at the top
1471 * -> move to last (here: top-most) automation lane
1473 TimeAxisView::Children kids = (*t)->get_child_list();
1474 TimeAxisView::Children::reverse_iterator nkid = kids.rend();
1476 for (TimeAxisView::Children::reverse_iterator ci = kids.rbegin(); ci != kids.rend(); ++ci) {
1477 if ((*ci)->hidden()) {
1481 std::pair<TimeAxisView*,double> dev;
1482 dev = (*ci)->covers_y_position (top_of_trackviews);
1484 /* some automation lane is currently at the top */
1485 if (ci == kids.rbegin()) {
1486 /* first (bottom-most) autmation lane is at the top.
1487 * -> move to next track
1496 if (nkid != kids.rend()) {
1497 ensure_time_axis_view_is_visible (**nkid, true);
1505 /* move to the track below the first one that covers the */
1507 if (next != track_views.rend()) {
1508 ensure_time_axis_view_is_visible (**next, true);
1516 Editor::scroll_up_one_track (bool skip_child_views)
1518 TrackViewList::iterator prev = track_views.end();
1519 double top_of_trackviews = vertical_adjustment.get_value ();
1521 for (TrackViewList::iterator t = track_views.begin(); t != track_views.end(); ++t) {
1523 if ((*t)->hidden()) {
1527 /* find the trackview at the top of the trackview group
1529 * Note that covers_y_position() is recursive and includes child views
1531 std::pair<TimeAxisView*,double> res = (*t)->covers_y_position (top_of_trackviews);
1534 if (skip_child_views) {
1537 /* automation lane (one level, non-recursive)
1539 * - if no automation lane exists -> move to prev tack
1540 * - if no y-axis match is found -> the current track is at the top -> move to prev track
1541 * (actually last automation lane of previous track, see below)
1542 * - if first (top-most) lane is at the top -> move to this track
1543 * - else move up one lane
1545 TimeAxisView::Children kids = (*t)->get_child_list();
1546 TimeAxisView::Children::iterator pkid = kids.end();
1548 for (TimeAxisView::Children::iterator ci = kids.begin(); ci != kids.end(); ++ci) {
1549 if ((*ci)->hidden()) {
1553 std::pair<TimeAxisView*,double> dev;
1554 dev = (*ci)->covers_y_position (top_of_trackviews);
1556 /* some automation lane is currently at the top */
1557 if (ci == kids.begin()) {
1558 /* first (top-most) autmation lane is at the top.
1559 * jump directly to this track's top
1561 ensure_time_axis_view_is_visible (**t, true);
1564 else if (pkid != kids.end()) {
1565 /* some other automation lane is at the top.
1566 * move up to prev automation lane.
1568 ensure_time_axis_view_is_visible (**pkid, true);
1571 assert(0); // not reached
1582 if (prev != track_views.end()) {
1583 // move to bottom-most automation-lane of the previous track
1584 TimeAxisView::Children kids = (*prev)->get_child_list();
1585 TimeAxisView::Children::reverse_iterator pkid = kids.rend();
1586 if (!skip_child_views) {
1587 // find the last visible lane
1588 for (TimeAxisView::Children::reverse_iterator ci = kids.rbegin(); ci != kids.rend(); ++ci) {
1589 if (!(*ci)->hidden()) {
1595 if (pkid != kids.rend()) {
1596 ensure_time_axis_view_is_visible (**pkid, true);
1598 ensure_time_axis_view_is_visible (**prev, true);
1607 Editor::scroll_left_step ()
1609 samplepos_t xdelta = (current_page_samples() / 8);
1611 if (_leftmost_sample > xdelta) {
1612 reset_x_origin (_leftmost_sample - xdelta);
1620 Editor::scroll_right_step ()
1622 samplepos_t xdelta = (current_page_samples() / 8);
1624 if (max_samplepos - xdelta > _leftmost_sample) {
1625 reset_x_origin (_leftmost_sample + xdelta);
1627 reset_x_origin (max_samplepos - current_page_samples());
1632 Editor::scroll_left_half_page ()
1634 samplepos_t xdelta = (current_page_samples() / 2);
1635 if (_leftmost_sample > xdelta) {
1636 reset_x_origin (_leftmost_sample - xdelta);
1643 Editor::scroll_right_half_page ()
1645 samplepos_t xdelta = (current_page_samples() / 2);
1646 if (max_samplepos - xdelta > _leftmost_sample) {
1647 reset_x_origin (_leftmost_sample + xdelta);
1649 reset_x_origin (max_samplepos - current_page_samples());
1656 Editor::tav_zoom_step (bool coarser)
1658 DisplaySuspender ds;
1662 if (selection->tracks.empty()) {
1665 ts = &selection->tracks;
1668 for (TrackViewList::iterator i = ts->begin(); i != ts->end(); ++i) {
1669 TimeAxisView *tv = (static_cast<TimeAxisView*>(*i));
1670 tv->step_height (coarser);
1675 Editor::tav_zoom_smooth (bool coarser, bool force_all)
1677 DisplaySuspender ds;
1681 if (selection->tracks.empty() || force_all) {
1684 ts = &selection->tracks;
1687 for (TrackViewList::iterator i = ts->begin(); i != ts->end(); ++i) {
1688 TimeAxisView *tv = (static_cast<TimeAxisView*>(*i));
1689 uint32_t h = tv->current_height ();
1694 if (h >= TimeAxisView::preset_height (HeightSmall)) {
1699 tv->set_height (h + 5);
1705 Editor::temporal_zoom_step_mouse_focus_scale (bool zoom_out, double scale)
1707 Editing::ZoomFocus temp_focus = zoom_focus;
1708 zoom_focus = Editing::ZoomFocusMouse;
1709 temporal_zoom_step_scale (zoom_out, scale);
1710 zoom_focus = temp_focus;
1714 Editor::temporal_zoom_step_mouse_focus (bool zoom_out)
1716 temporal_zoom_step_mouse_focus_scale (zoom_out, 2.0);
1720 Editor::temporal_zoom_step (bool zoom_out)
1722 temporal_zoom_step_scale (zoom_out, 2.0);
1726 Editor::temporal_zoom_step_scale (bool zoom_out, double scale)
1728 ENSURE_GUI_THREAD (*this, &Editor::temporal_zoom_step, zoom_out, scale)
1730 samplecnt_t nspp = samples_per_pixel;
1734 if (nspp == samples_per_pixel) {
1739 if (nspp == samples_per_pixel) {
1744 //zoom-behavior-tweaks
1745 //limit our maximum zoom to the session gui extents value
1746 std::pair<samplepos_t, samplepos_t> ext = session_gui_extents();
1747 samplecnt_t session_extents_pp = (ext.second - ext.first) / _visible_canvas_width;
1748 if (nspp > session_extents_pp)
1749 nspp = session_extents_pp;
1751 temporal_zoom (nspp);
1755 Editor::temporal_zoom (samplecnt_t fpp)
1761 samplepos_t current_page = current_page_samples();
1762 samplepos_t current_leftmost = _leftmost_sample;
1763 samplepos_t current_rightmost;
1764 samplepos_t current_center;
1765 samplepos_t new_page_size;
1766 samplepos_t half_page_size;
1767 samplepos_t leftmost_after_zoom = 0;
1769 bool in_track_canvas;
1770 bool use_mouse_sample = true;
1774 if (fpp == samples_per_pixel) {
1778 // Imposing an arbitrary limit to zoom out as too much zoom out produces
1779 // segfaults for lack of memory. If somebody decides this is not high enough I
1780 // believe it can be raisen to higher values but some limit must be in place.
1782 // This constant represents 1 day @ 48kHz on a 1600 pixel wide display
1783 // all of which is used for the editor track displays. The whole day
1784 // would be 4147200000 samples, so 2592000 samples per pixel.
1786 nfpp = min (fpp, (samplecnt_t) 2592000);
1787 nfpp = max ((samplecnt_t) 1, nfpp);
1789 new_page_size = (samplepos_t) floor (_visible_canvas_width * nfpp);
1790 half_page_size = new_page_size / 2;
1792 switch (zoom_focus) {
1794 leftmost_after_zoom = current_leftmost;
1797 case ZoomFocusRight:
1798 current_rightmost = _leftmost_sample + current_page;
1799 if (current_rightmost < new_page_size) {
1800 leftmost_after_zoom = 0;
1802 leftmost_after_zoom = current_rightmost - new_page_size;
1806 case ZoomFocusCenter:
1807 current_center = current_leftmost + (current_page/2);
1808 if (current_center < half_page_size) {
1809 leftmost_after_zoom = 0;
1811 leftmost_after_zoom = current_center - half_page_size;
1815 case ZoomFocusPlayhead:
1816 /* centre playhead */
1817 l = playhead_cursor->current_sample () - (new_page_size * 0.5);
1820 leftmost_after_zoom = 0;
1821 } else if (l > max_samplepos) {
1822 leftmost_after_zoom = max_samplepos - new_page_size;
1824 leftmost_after_zoom = (samplepos_t) l;
1828 case ZoomFocusMouse:
1829 /* try to keep the mouse over the same point in the display */
1831 if (_drags->active()) {
1832 where = _drags->current_pointer_sample ();
1833 } else if (!mouse_sample (where, in_track_canvas)) {
1834 use_mouse_sample = false;
1837 if (use_mouse_sample) {
1838 l = - ((new_page_size * ((where - current_leftmost)/(double)current_page)) - where);
1841 leftmost_after_zoom = 0;
1842 } else if (l > max_samplepos) {
1843 leftmost_after_zoom = max_samplepos - new_page_size;
1845 leftmost_after_zoom = (samplepos_t) l;
1848 /* use playhead instead */
1849 where = playhead_cursor->current_sample ();
1851 if (where < half_page_size) {
1852 leftmost_after_zoom = 0;
1854 leftmost_after_zoom = where - half_page_size;
1860 /* try to keep the edit point in the same place */
1861 where = get_preferred_edit_position ();
1865 double l = - ((new_page_size * ((where - current_leftmost)/(double)current_page)) - where);
1868 leftmost_after_zoom = 0;
1869 } else if (l > max_samplepos) {
1870 leftmost_after_zoom = max_samplepos - new_page_size;
1872 leftmost_after_zoom = (samplepos_t) l;
1876 /* edit point not defined */
1883 // leftmost_after_zoom = min (leftmost_after_zoom, _session->current_end_sample());
1885 reposition_and_zoom (leftmost_after_zoom, nfpp);
1889 Editor::calc_extra_zoom_edges(samplepos_t &start, samplepos_t &end)
1891 /* this func helps make sure we leave a little space
1892 at each end of the editor so that the zoom doesn't fit the region
1893 precisely to the screen.
1896 GdkScreen* screen = gdk_screen_get_default ();
1897 const gint pixwidth = gdk_screen_get_width (screen);
1898 const gint mmwidth = gdk_screen_get_width_mm (screen);
1899 const double pix_per_mm = (double) pixwidth/ (double) mmwidth;
1900 const double one_centimeter_in_pixels = pix_per_mm * 10.0;
1902 const samplepos_t range = end - start;
1903 const samplecnt_t new_fpp = (samplecnt_t) ceil ((double) range / (double) _visible_canvas_width);
1904 const samplepos_t extra_samples = (samplepos_t) floor (one_centimeter_in_pixels * new_fpp);
1906 if (start > extra_samples) {
1907 start -= extra_samples;
1912 if (max_samplepos - extra_samples > end) {
1913 end += extra_samples;
1915 end = max_samplepos;
1920 Editor::get_selection_extents (samplepos_t &start, samplepos_t &end) const
1922 start = max_samplepos;
1926 //ToDo: if notes are selected, set extents to that selection
1928 //ToDo: if control points are selected, set extents to that selection
1930 if (!selection->regions.empty()) {
1931 RegionSelection rs = get_regions_from_selection_and_entered ();
1933 for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
1935 if ((*i)->region()->position() < start) {
1936 start = (*i)->region()->position();
1939 if ((*i)->region()->last_sample() + 1 > end) {
1940 end = (*i)->region()->last_sample() + 1;
1944 } else if (!selection->time.empty()) {
1945 start = selection->time.start();
1946 end = selection->time.end_sample();
1948 ret = false; //no selection found
1951 if ((start == 0 && end == 0) || end < start) {
1960 Editor::temporal_zoom_selection (Editing::ZoomAxis axes)
1962 if (!selection) return;
1964 if (selection->regions.empty() && selection->time.empty()) {
1965 if (axes == Horizontal || axes == Both) {
1966 temporal_zoom_step(true);
1968 if (axes == Vertical || axes == Both) {
1969 if (!track_views.empty()) {
1973 //implicit hack: by extending the top & bottom check outside the current view limits, we include the trackviews immediately above & below what is visible
1974 const double top = vertical_adjustment.get_value() - 10;
1975 const double btm = top + _visible_canvas_height + 10;
1977 for (TrackViewList::iterator iter = track_views.begin(); iter != track_views.end(); ++iter) {
1978 if ((*iter)->covered_by_y_range (top, btm)) {
1979 tvl.push_back(*iter);
1989 //ToDo: if notes are selected, zoom to that
1991 //ToDo: if control points are selected, zoom to that
1993 if (axes == Horizontal || axes == Both) {
1995 samplepos_t start, end;
1996 if (get_selection_extents (start, end)) {
1997 calc_extra_zoom_edges (start, end);
1998 temporal_zoom_by_sample (start, end);
2002 if (axes == Vertical || axes == Both) {
2006 //normally, we don't do anything "automatic" to the user's selection.
2007 //but in this case, we will clear the selection after a zoom-to-selection.
2012 Editor::temporal_zoom_session ()
2014 ENSURE_GUI_THREAD (*this, &Editor::temporal_zoom_session)
2017 samplecnt_t start = _session->current_start_sample();
2018 samplecnt_t end = _session->current_end_sample();
2020 if (_session->actively_recording ()) {
2021 samplepos_t cur = playhead_cursor->current_sample ();
2023 /* recording beyond the end marker; zoom out
2024 * by 5 seconds more so that if 'follow
2025 * playhead' is active we don't immediately
2028 end = cur + _session->sample_rate() * 5;
2032 if ((start == 0 && end == 0) || end < start) {
2036 calc_extra_zoom_edges(start, end);
2038 temporal_zoom_by_sample (start, end);
2043 Editor::temporal_zoom_extents ()
2045 ENSURE_GUI_THREAD (*this, &Editor::temporal_zoom_extents)
2048 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
2050 samplecnt_t start = ext.first;
2051 samplecnt_t end = ext.second;
2053 if (_session->actively_recording ()) {
2054 samplepos_t cur = playhead_cursor->current_sample ();
2056 /* recording beyond the end marker; zoom out
2057 * by 5 seconds more so that if 'follow
2058 * playhead' is active we don't immediately
2061 end = cur + _session->sample_rate() * 5;
2065 if ((start == 0 && end == 0) || end < start) {
2069 calc_extra_zoom_edges(start, end);
2071 temporal_zoom_by_sample (start, end);
2076 Editor::temporal_zoom_by_sample (samplepos_t start, samplepos_t end)
2078 if (!_session) return;
2080 if ((start == 0 && end == 0) || end < start) {
2084 samplepos_t range = end - start;
2086 const samplecnt_t new_fpp = (samplecnt_t) ceil ((double) range / (double) _visible_canvas_width);
2088 samplepos_t new_page = range;
2089 samplepos_t middle = (samplepos_t) floor ((double) start + ((double) range / 2.0f));
2090 samplepos_t new_leftmost = (samplepos_t) floor ((double) middle - ((double) new_page / 2.0f));
2092 if (new_leftmost > middle) {
2096 if (new_leftmost < 0) {
2100 reposition_and_zoom (new_leftmost, new_fpp);
2104 Editor::temporal_zoom_to_sample (bool coarser, samplepos_t sample)
2110 samplecnt_t range_before = sample - _leftmost_sample;
2111 samplecnt_t new_spp;
2114 if (samples_per_pixel <= 1) {
2117 new_spp = samples_per_pixel + (samples_per_pixel/2);
2119 range_before += range_before/2;
2121 if (samples_per_pixel >= 1) {
2122 new_spp = samples_per_pixel - (samples_per_pixel/2);
2124 /* could bail out here since we cannot zoom any finer,
2125 but leave that to the equality test below
2127 new_spp = samples_per_pixel;
2130 range_before -= range_before/2;
2133 if (new_spp == samples_per_pixel) {
2137 /* zoom focus is automatically taken as @param sample when this
2141 samplepos_t new_leftmost = sample - (samplepos_t)range_before;
2143 if (new_leftmost > sample) {
2147 if (new_leftmost < 0) {
2151 reposition_and_zoom (new_leftmost, new_spp);
2156 Editor::choose_new_marker_name(string &name) {
2158 if (!UIConfiguration::instance().get_name_new_markers()) {
2159 /* don't prompt user for a new name */
2163 Prompter dialog (true);
2165 dialog.set_prompt (_("New Name:"));
2167 dialog.set_title (_("New Location Marker"));
2169 dialog.set_name ("MarkNameWindow");
2170 dialog.set_size_request (250, -1);
2171 dialog.set_position (Gtk::WIN_POS_MOUSE);
2173 dialog.add_button (Stock::OK, RESPONSE_ACCEPT);
2174 dialog.set_initial_text (name);
2178 switch (dialog.run ()) {
2179 case RESPONSE_ACCEPT:
2185 dialog.get_result(name);
2192 Editor::add_location_from_selection ()
2196 if (selection->time.empty()) {
2200 if (_session == 0 || clicked_axisview == 0) {
2204 samplepos_t start = selection->time[clicked_selection].start;
2205 samplepos_t end = selection->time[clicked_selection].end;
2207 _session->locations()->next_available_name(rangename,"selection");
2208 Location *location = new Location (*_session, start, end, rangename, Location::IsRangeMarker, get_grid_music_divisions(0));
2210 begin_reversible_command (_("add marker"));
2212 XMLNode &before = _session->locations()->get_state();
2213 _session->locations()->add (location, true);
2214 XMLNode &after = _session->locations()->get_state();
2215 _session->add_command(new MementoCommand<Locations>(*(_session->locations()), &before, &after));
2217 commit_reversible_command ();
2221 Editor::add_location_mark (samplepos_t where)
2225 select_new_marker = true;
2227 _session->locations()->next_available_name(markername,"mark");
2228 if (!choose_new_marker_name(markername)) {
2231 Location *location = new Location (*_session, where, where, markername, Location::IsMark, get_grid_music_divisions (0));
2232 begin_reversible_command (_("add marker"));
2234 XMLNode &before = _session->locations()->get_state();
2235 _session->locations()->add (location, true);
2236 XMLNode &after = _session->locations()->get_state();
2237 _session->add_command(new MementoCommand<Locations>(*(_session->locations()), &before, &after));
2239 commit_reversible_command ();
2243 Editor::set_session_start_from_playhead ()
2249 if ((loc = _session->locations()->session_range_location()) == 0) { //should never happen
2250 _session->set_session_extents (_session->audible_sample(), _session->audible_sample());
2252 XMLNode &before = loc->get_state();
2254 _session->set_session_extents (_session->audible_sample(), loc->end());
2256 XMLNode &after = loc->get_state();
2258 begin_reversible_command (_("Set session start"));
2260 _session->add_command (new MementoCommand<Location>(*loc, &before, &after));
2262 commit_reversible_command ();
2267 Editor::set_session_end_from_playhead ()
2273 if ((loc = _session->locations()->session_range_location()) == 0) { //should never happen
2274 _session->set_session_extents (_session->audible_sample(), _session->audible_sample());
2276 XMLNode &before = loc->get_state();
2278 _session->set_session_extents (loc->start(), _session->audible_sample());
2280 XMLNode &after = loc->get_state();
2282 begin_reversible_command (_("Set session start"));
2284 _session->add_command (new MementoCommand<Location>(*loc, &before, &after));
2286 commit_reversible_command ();
2289 _session->set_end_is_free (false);
2294 Editor::toggle_location_at_playhead_cursor ()
2296 if (!do_remove_location_at_playhead_cursor())
2298 add_location_from_playhead_cursor();
2303 Editor::add_location_from_playhead_cursor ()
2305 add_location_mark (_session->audible_sample());
2309 Editor::do_remove_location_at_playhead_cursor ()
2311 bool removed = false;
2314 XMLNode &before = _session->locations()->get_state();
2316 //find location(s) at this time
2317 Locations::LocationList locs;
2318 _session->locations()->find_all_between (_session->audible_sample(), _session->audible_sample()+1, locs, Location::Flags(0));
2319 for (Locations::LocationList::iterator i = locs.begin(); i != locs.end(); ++i) {
2320 if ((*i)->is_mark()) {
2321 _session->locations()->remove (*i);
2328 begin_reversible_command (_("remove marker"));
2329 XMLNode &after = _session->locations()->get_state();
2330 _session->add_command(new MementoCommand<Locations>(*(_session->locations()), &before, &after));
2331 commit_reversible_command ();
2338 Editor::remove_location_at_playhead_cursor ()
2340 do_remove_location_at_playhead_cursor ();
2343 /** Add a range marker around each selected region */
2345 Editor::add_locations_from_region ()
2347 RegionSelection rs = get_regions_from_selection_and_entered ();
2352 bool commit = false;
2354 XMLNode &before = _session->locations()->get_state();
2356 for (RegionSelection::iterator i = rs.begin (); i != rs.end (); ++i) {
2358 boost::shared_ptr<Region> region = (*i)->region ();
2360 Location *location = new Location (*_session, region->position(), region->last_sample(), region->name(), Location::IsRangeMarker, 0);
2362 _session->locations()->add (location, true);
2367 begin_reversible_command (selection->regions.size () > 1 ? _("add markers") : _("add marker"));
2368 XMLNode &after = _session->locations()->get_state();
2369 _session->add_command (new MementoCommand<Locations>(*(_session->locations()), &before, &after));
2370 commit_reversible_command ();
2374 /** Add a single range marker around all selected regions */
2376 Editor::add_location_from_region ()
2378 RegionSelection rs = get_regions_from_selection_and_entered ();
2384 XMLNode &before = _session->locations()->get_state();
2388 if (rs.size() > 1) {
2389 _session->locations()->next_available_name(markername, "regions");
2391 RegionView* rv = *(rs.begin());
2392 boost::shared_ptr<Region> region = rv->region();
2393 markername = region->name();
2396 if (!choose_new_marker_name(markername)) {
2400 // single range spanning all selected
2401 Location *location = new Location (*_session, selection->regions.start(), selection->regions.end_sample(), markername, Location::IsRangeMarker, 0);
2402 _session->locations()->add (location, true);
2404 begin_reversible_command (_("add marker"));
2405 XMLNode &after = _session->locations()->get_state();
2406 _session->add_command (new MementoCommand<Locations>(*(_session->locations()), &before, &after));
2407 commit_reversible_command ();
2413 Editor::jump_forward_to_mark ()
2419 samplepos_t pos = _session->locations()->first_mark_after (playhead_cursor->current_sample());
2425 _session->request_locate (pos, _session->transport_rolling());
2429 Editor::jump_backward_to_mark ()
2435 samplepos_t pos = _session->locations()->first_mark_before (playhead_cursor->current_sample());
2437 //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...
2438 if (_session->transport_rolling()) {
2439 if ((playhead_cursor->current_sample() - pos) < _session->sample_rate()/2) {
2440 samplepos_t prior = _session->locations()->first_mark_before (pos);
2449 _session->request_locate (pos, _session->transport_rolling());
2455 samplepos_t const pos = _session->audible_sample ();
2458 _session->locations()->next_available_name (markername, "mark");
2460 if (!choose_new_marker_name (markername)) {
2464 _session->locations()->add (new Location (*_session, pos, 0, markername, Location::IsMark, 0), true);
2468 Editor::clear_markers ()
2471 begin_reversible_command (_("clear markers"));
2473 XMLNode &before = _session->locations()->get_state();
2474 _session->locations()->clear_markers ();
2475 XMLNode &after = _session->locations()->get_state();
2476 _session->add_command(new MementoCommand<Locations>(*(_session->locations()), &before, &after));
2478 commit_reversible_command ();
2483 Editor::clear_ranges ()
2486 begin_reversible_command (_("clear ranges"));
2488 XMLNode &before = _session->locations()->get_state();
2490 _session->locations()->clear_ranges ();
2492 XMLNode &after = _session->locations()->get_state();
2493 _session->add_command(new MementoCommand<Locations>(*(_session->locations()), &before, &after));
2495 commit_reversible_command ();
2500 Editor::clear_locations ()
2502 begin_reversible_command (_("clear locations"));
2504 XMLNode &before = _session->locations()->get_state();
2505 _session->locations()->clear ();
2506 XMLNode &after = _session->locations()->get_state();
2507 _session->add_command(new MementoCommand<Locations>(*(_session->locations()), &before, &after));
2509 commit_reversible_command ();
2513 Editor::unhide_markers ()
2515 for (LocationMarkerMap::iterator i = location_markers.begin(); i != location_markers.end(); ++i) {
2516 Location *l = (*i).first;
2517 if (l->is_hidden() && l->is_mark()) {
2518 l->set_hidden(false, this);
2524 Editor::unhide_ranges ()
2526 for (LocationMarkerMap::iterator i = location_markers.begin(); i != location_markers.end(); ++i) {
2527 Location *l = (*i).first;
2528 if (l->is_hidden() && l->is_range_marker()) {
2529 l->set_hidden(false, this);
2534 /* INSERT/REPLACE */
2537 Editor::insert_region_list_selection (float times)
2539 RouteTimeAxisView *tv = 0;
2540 boost::shared_ptr<Playlist> playlist;
2542 if (clicked_routeview != 0) {
2543 tv = clicked_routeview;
2544 } else if (!selection->tracks.empty()) {
2545 if ((tv = dynamic_cast<RouteTimeAxisView*>(selection->tracks.front())) == 0) {
2548 } else if (entered_track != 0) {
2549 if ((tv = dynamic_cast<RouteTimeAxisView*>(entered_track)) == 0) {
2556 if ((playlist = tv->playlist()) == 0) {
2560 boost::shared_ptr<Region> region = _regions->get_single_selection ();
2565 begin_reversible_command (_("insert region"));
2566 playlist->clear_changes ();
2567 playlist->add_region ((RegionFactory::create (region, true)), get_preferred_edit_position(), times);
2568 if (Config->get_edit_mode() == Ripple)
2569 playlist->ripple (get_preferred_edit_position(), region->length() * times, boost::shared_ptr<Region>());
2571 _session->add_command(new StatefulDiffCommand (playlist));
2572 commit_reversible_command ();
2575 /* BUILT-IN EFFECTS */
2578 Editor::reverse_selection ()
2583 /* GAIN ENVELOPE EDITING */
2586 Editor::edit_envelope ()
2593 Editor::transition_to_rolling (bool fwd)
2599 if (_session->config.get_external_sync()) {
2600 switch (TransportMasterManager::instance().current()->type()) {
2604 /* transport controlled by the master */
2609 if (_session->is_auditioning()) {
2610 _session->cancel_audition ();
2614 _session->request_transport_speed (fwd ? 1.0f : -1.0f);
2618 Editor::play_from_start ()
2620 _session->request_locate (_session->current_start_sample(), true);
2624 Editor::play_from_edit_point ()
2626 _session->request_locate (get_preferred_edit_position(), true);
2630 Editor::play_from_edit_point_and_return ()
2632 samplepos_t start_sample;
2633 samplepos_t return_sample;
2635 start_sample = get_preferred_edit_position (EDIT_IGNORE_PHEAD);
2637 if (_session->transport_rolling()) {
2638 _session->request_locate (start_sample, false);
2642 /* don't reset the return sample if its already set */
2644 if ((return_sample = _session->requested_return_sample()) < 0) {
2645 return_sample = _session->audible_sample();
2648 if (start_sample >= 0) {
2649 _session->request_roll_at_and_return (start_sample, return_sample);
2654 Editor::play_selection ()
2656 samplepos_t start, end;
2657 if (!get_selection_extents (start, end))
2660 AudioRange ar (start, end, 0);
2661 list<AudioRange> lar;
2664 _session->request_play_range (&lar, true);
2669 Editor::maybe_locate_with_edit_preroll (samplepos_t location)
2671 if (_session->transport_rolling() || !UIConfiguration::instance().get_follow_edits() || _session->config.get_external_sync())
2674 location -= _session->preroll_samples (location);
2676 //don't try to locate before the beginning of time
2681 //if follow_playhead is on, keep the playhead on the screen
2682 if (_follow_playhead)
2683 if (location < _leftmost_sample)
2684 location = _leftmost_sample;
2686 _session->request_locate (location);
2690 Editor::play_with_preroll ()
2692 samplepos_t start, end;
2693 if (UIConfiguration::instance().get_follow_edits() && get_selection_extents (start, end)) {
2694 const samplepos_t preroll = _session->preroll_samples (start);
2696 samplepos_t ret = start;
2698 if (start > preroll) {
2699 start = start - preroll;
2702 end = end + preroll; //"post-roll"
2704 AudioRange ar (start, end, 0);
2705 list<AudioRange> lar;
2708 _session->request_play_range (&lar, true);
2709 _session->set_requested_return_sample (ret); //force auto-return to return to range start, without the preroll
2711 samplepos_t ph = playhead_cursor->current_sample ();
2712 const samplepos_t preroll = _session->preroll_samples (ph);
2715 start = ph - preroll;
2719 _session->request_locate (start, true);
2720 _session->set_requested_return_sample (ph); //force auto-return to return to playhead location, without the preroll
2725 Editor::rec_with_preroll ()
2727 samplepos_t ph = playhead_cursor->current_sample ();
2728 samplepos_t preroll = _session->preroll_samples (ph);
2729 _session->request_preroll_record_trim (ph, preroll);
2733 Editor::rec_with_count_in ()
2735 _session->request_count_in_record ();
2739 Editor::play_location (Location& location)
2741 if (location.start() <= location.end()) {
2745 _session->request_bounded_roll (location.start(), location.end());
2749 Editor::loop_location (Location& location)
2751 if (location.start() <= location.end()) {
2757 if ((tll = transport_loop_location()) != 0) {
2758 tll->set (location.start(), location.end());
2760 // enable looping, reposition and start rolling
2761 _session->request_locate (tll->start(), true);
2762 _session->request_play_loop (true);
2767 Editor::do_layer_operation (LayerOperation op)
2769 if (selection->regions.empty ()) {
2773 bool const multiple = selection->regions.size() > 1;
2777 begin_reversible_command (_("raise regions"));
2779 begin_reversible_command (_("raise region"));
2785 begin_reversible_command (_("raise regions to top"));
2787 begin_reversible_command (_("raise region to top"));
2793 begin_reversible_command (_("lower regions"));
2795 begin_reversible_command (_("lower region"));
2801 begin_reversible_command (_("lower regions to bottom"));
2803 begin_reversible_command (_("lower region"));
2808 set<boost::shared_ptr<Playlist> > playlists = selection->regions.playlists ();
2809 for (set<boost::shared_ptr<Playlist> >::iterator i = playlists.begin(); i != playlists.end(); ++i) {
2810 (*i)->clear_owned_changes ();
2813 for (RegionSelection::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
2814 boost::shared_ptr<Region> r = (*i)->region ();
2826 r->lower_to_bottom ();
2830 for (set<boost::shared_ptr<Playlist> >::iterator i = playlists.begin(); i != playlists.end(); ++i) {
2831 vector<Command*> cmds;
2833 _session->add_commands (cmds);
2836 commit_reversible_command ();
2840 Editor::raise_region ()
2842 do_layer_operation (Raise);
2846 Editor::raise_region_to_top ()
2848 do_layer_operation (RaiseToTop);
2852 Editor::lower_region ()
2854 do_layer_operation (Lower);
2858 Editor::lower_region_to_bottom ()
2860 do_layer_operation (LowerToBottom);
2863 /** Show the region editor for the selected regions */
2865 Editor::show_region_properties ()
2867 selection->foreach_regionview (&RegionView::show_region_editor);
2870 /** Show the midi list editor for the selected MIDI regions */
2872 Editor::show_midi_list_editor ()
2874 selection->foreach_midi_regionview (&MidiRegionView::show_list_editor);
2878 Editor::rename_region ()
2880 RegionSelection rs = get_regions_from_selection_and_entered ();
2886 ArdourDialog d (_("Rename Region"), true, false);
2888 Label label (_("New name:"));
2891 hbox.set_spacing (6);
2892 hbox.pack_start (label, false, false);
2893 hbox.pack_start (entry, true, true);
2895 d.get_vbox()->set_border_width (12);
2896 d.get_vbox()->pack_start (hbox, false, false);
2898 d.add_button(Gtk::Stock::CANCEL, Gtk::RESPONSE_CANCEL);
2899 d.add_button(Gtk::Stock::OK, Gtk::RESPONSE_OK);
2901 d.set_size_request (300, -1);
2903 entry.set_text (rs.front()->region()->name());
2904 entry.select_region (0, -1);
2906 entry.signal_activate().connect (sigc::bind (sigc::mem_fun (d, &Dialog::response), RESPONSE_OK));
2912 int const ret = d.run();
2916 if (ret != RESPONSE_OK) {
2920 std::string str = entry.get_text();
2921 strip_whitespace_edges (str);
2923 rs.front()->region()->set_name (str);
2924 _regions->redisplay ();
2928 /** Start an audition of the first selected region */
2930 Editor::play_edit_range ()
2932 samplepos_t start, end;
2934 if (get_edit_op_range (start, end)) {
2935 _session->request_bounded_roll (start, end);
2940 Editor::play_selected_region ()
2942 samplepos_t start = max_samplepos;
2943 samplepos_t end = 0;
2945 RegionSelection rs = get_regions_from_selection_and_entered ();
2951 for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
2952 if ((*i)->region()->position() < start) {
2953 start = (*i)->region()->position();
2955 if ((*i)->region()->last_sample() + 1 > end) {
2956 end = (*i)->region()->last_sample() + 1;
2960 _session->request_bounded_roll (start, end);
2964 Editor::audition_playlist_region_standalone (boost::shared_ptr<Region> region)
2966 _session->audition_region (region);
2970 Editor::region_from_selection ()
2972 if (clicked_axisview == 0) {
2976 if (selection->time.empty()) {
2980 samplepos_t start = selection->time[clicked_selection].start;
2981 samplepos_t end = selection->time[clicked_selection].end;
2983 TrackViewList tracks = get_tracks_for_range_action ();
2985 samplepos_t selection_cnt = end - start + 1;
2987 for (TrackSelection::iterator i = tracks.begin(); i != tracks.end(); ++i) {
2988 boost::shared_ptr<Region> current;
2989 boost::shared_ptr<Playlist> pl;
2990 samplepos_t internal_start;
2993 if ((pl = (*i)->playlist()) == 0) {
2997 if ((current = pl->top_region_at (start)) == 0) {
3001 internal_start = start - current->position();
3002 RegionFactory::region_name (new_name, current->name(), true);
3006 plist.add (ARDOUR::Properties::start, current->start() + internal_start);
3007 plist.add (ARDOUR::Properties::length, selection_cnt);
3008 plist.add (ARDOUR::Properties::name, new_name);
3009 plist.add (ARDOUR::Properties::layer, 0);
3011 boost::shared_ptr<Region> region (RegionFactory::create (current, plist));
3016 Editor::create_region_from_selection (vector<boost::shared_ptr<Region> >& new_regions)
3018 if (selection->time.empty() || selection->tracks.empty()) {
3022 samplepos_t start, end;
3023 if (clicked_selection) {
3024 start = selection->time[clicked_selection].start;
3025 end = selection->time[clicked_selection].end;
3027 start = selection->time.start();
3028 end = selection->time.end_sample();
3031 TrackViewList ts = selection->tracks.filter_to_unique_playlists ();
3032 sort_track_selection (ts);
3034 for (TrackSelection::iterator i = ts.begin(); i != ts.end(); ++i) {
3035 boost::shared_ptr<Region> current;
3036 boost::shared_ptr<Playlist> playlist;
3037 samplepos_t internal_start;
3040 if ((playlist = (*i)->playlist()) == 0) {
3044 if ((current = playlist->top_region_at(start)) == 0) {
3048 internal_start = start - current->position();
3049 RegionFactory::region_name (new_name, current->name(), true);
3053 plist.add (ARDOUR::Properties::start, current->start() + internal_start);
3054 plist.add (ARDOUR::Properties::length, end - start + 1);
3055 plist.add (ARDOUR::Properties::name, new_name);
3057 new_regions.push_back (RegionFactory::create (current, plist));
3062 Editor::split_multichannel_region ()
3064 RegionSelection rs = get_regions_from_selection_and_entered ();
3070 vector< boost::shared_ptr<Region> > v;
3072 for (list<RegionView*>::iterator x = rs.begin(); x != rs.end(); ++x) {
3073 (*x)->region()->separate_by_channel (v);
3078 Editor::new_region_from_selection ()
3080 region_from_selection ();
3081 cancel_selection ();
3085 add_if_covered (RegionView* rv, const AudioRange* ar, RegionSelection* rs)
3087 switch (rv->region()->coverage (ar->start, ar->end - 1)) {
3088 // n.b. -1 because AudioRange::end is one past the end, but coverage expects inclusive ranges
3089 case Evoral::OverlapNone:
3097 * - selected tracks, or if there are none...
3098 * - tracks containing selected regions, or if there are none...
3103 Editor::get_tracks_for_range_action () const
3107 if (selection->tracks.empty()) {
3109 /* use tracks with selected regions */
3111 RegionSelection rs = selection->regions;
3113 for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
3114 TimeAxisView* tv = &(*i)->get_time_axis_view();
3116 if (!t.contains (tv)) {
3122 /* no regions and no tracks: use all tracks */
3128 t = selection->tracks;
3131 return t.filter_to_unique_playlists();
3135 Editor::separate_regions_between (const TimeSelection& ts)
3137 bool in_command = false;
3138 boost::shared_ptr<Playlist> playlist;
3139 RegionSelection new_selection;
3141 TrackViewList tmptracks = get_tracks_for_range_action ();
3142 sort_track_selection (tmptracks);
3144 for (TrackSelection::iterator i = tmptracks.begin(); i != tmptracks.end(); ++i) {
3146 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> ((*i));
3152 if (!rtv->is_track()) {
3156 /* no edits to destructive tracks */
3158 if (rtv->track()->destructive()) {
3162 if ((playlist = rtv->playlist()) != 0) {
3164 playlist->clear_changes ();
3166 /* XXX need to consider musical time selections here at some point */
3168 for (list<AudioRange>::const_iterator t = ts.begin(); t != ts.end(); ++t) {
3170 sigc::connection c = rtv->view()->RegionViewAdded.connect (
3171 sigc::mem_fun(*this, &Editor::collect_new_region_view));
3173 latest_regionviews.clear ();
3175 playlist->partition ((*t).start, (*t).end, false);
3179 if (!latest_regionviews.empty()) {
3181 rtv->view()->foreach_regionview (sigc::bind (
3182 sigc::ptr_fun (add_if_covered),
3183 &(*t), &new_selection));
3186 begin_reversible_command (_("separate"));
3190 /* pick up changes to existing regions */
3192 vector<Command*> cmds;
3193 playlist->rdiff (cmds);
3194 _session->add_commands (cmds);
3196 /* pick up changes to the playlist itself (adds/removes)
3199 _session->add_command(new StatefulDiffCommand (playlist));
3206 // selection->set (new_selection);
3208 commit_reversible_command ();
3212 struct PlaylistState {
3213 boost::shared_ptr<Playlist> playlist;
3217 /** Take tracks from get_tracks_for_range_action and cut any regions
3218 * on those tracks so that the tracks are empty over the time
3222 Editor::separate_region_from_selection ()
3224 /* preferentially use *all* ranges in the time selection if we're in range mode
3225 to allow discontiguous operation, since get_edit_op_range() currently
3226 returns a single range.
3229 if (!selection->time.empty()) {
3231 separate_regions_between (selection->time);
3238 if (get_edit_op_range (start, end)) {
3240 AudioRange ar (start, end, 1);
3244 separate_regions_between (ts);
3250 Editor::separate_region_from_punch ()
3252 Location* loc = _session->locations()->auto_punch_location();
3254 separate_regions_using_location (*loc);
3259 Editor::separate_region_from_loop ()
3261 Location* loc = _session->locations()->auto_loop_location();
3263 separate_regions_using_location (*loc);
3268 Editor::separate_regions_using_location (Location& loc)
3270 if (loc.is_mark()) {
3274 AudioRange ar (loc.start(), loc.end(), 1);
3279 separate_regions_between (ts);
3282 /** Separate regions under the selected region */
3284 Editor::separate_under_selected_regions ()
3286 vector<PlaylistState> playlists;
3290 rs = get_regions_from_selection_and_entered();
3292 if (!_session || rs.empty()) {
3296 begin_reversible_command (_("separate region under"));
3298 list<boost::shared_ptr<Region> > regions_to_remove;
3300 for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
3301 // we can't just remove the region(s) in this loop because
3302 // this removes them from the RegionSelection, and they thus
3303 // disappear from underneath the iterator, and the ++i above
3304 // SEGVs in a puzzling fashion.
3306 // so, first iterate over the regions to be removed from rs and
3307 // add them to the regions_to_remove list, and then
3308 // iterate over the list to actually remove them.
3310 regions_to_remove.push_back ((*i)->region());
3313 for (list<boost::shared_ptr<Region> >::iterator rl = regions_to_remove.begin(); rl != regions_to_remove.end(); ++rl) {
3315 boost::shared_ptr<Playlist> playlist = (*rl)->playlist();
3318 // is this check necessary?
3322 vector<PlaylistState>::iterator i;
3324 //only take state if this is a new playlist.
3325 for (i = playlists.begin(); i != playlists.end(); ++i) {
3326 if ((*i).playlist == playlist) {
3331 if (i == playlists.end()) {
3333 PlaylistState before;
3334 before.playlist = playlist;
3335 before.before = &playlist->get_state();
3336 playlist->clear_changes ();
3337 playlist->freeze ();
3338 playlists.push_back(before);
3341 //Partition on the region bounds
3342 playlist->partition ((*rl)->first_sample() - 1, (*rl)->last_sample() + 1, true);
3344 //Re-add region that was just removed due to the partition operation
3345 playlist->add_region ((*rl), (*rl)->first_sample());
3348 vector<PlaylistState>::iterator pl;
3350 for (pl = playlists.begin(); pl != playlists.end(); ++pl) {
3351 (*pl).playlist->thaw ();
3352 _session->add_command(new MementoCommand<Playlist>(*(*pl).playlist, (*pl).before, &(*pl).playlist->get_state()));
3355 commit_reversible_command ();
3359 Editor::crop_region_to_selection ()
3361 if (!selection->time.empty()) {
3363 begin_reversible_command (_("Crop Regions to Time Selection"));
3364 for (std::list<AudioRange>::iterator i = selection->time.begin(); i != selection->time.end(); ++i) {
3365 crop_region_to ((*i).start, (*i).end);
3367 commit_reversible_command();
3373 if (get_edit_op_range (start, end)) {
3374 begin_reversible_command (_("Crop Regions to Edit Range"));
3376 crop_region_to (start, end);
3378 commit_reversible_command();
3385 Editor::crop_region_to (samplepos_t start, samplepos_t end)
3387 vector<boost::shared_ptr<Playlist> > playlists;
3388 boost::shared_ptr<Playlist> playlist;
3391 if (selection->tracks.empty()) {
3392 ts = track_views.filter_to_unique_playlists();
3394 ts = selection->tracks.filter_to_unique_playlists ();
3397 sort_track_selection (ts);
3399 for (TrackSelection::iterator i = ts.begin(); i != ts.end(); ++i) {
3401 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> ((*i));
3407 boost::shared_ptr<Track> t = rtv->track();
3409 if (t != 0 && ! t->destructive()) {
3411 if ((playlist = rtv->playlist()) != 0) {
3412 playlists.push_back (playlist);
3417 if (playlists.empty()) {
3422 samplepos_t new_start;
3423 samplepos_t new_end;
3424 samplecnt_t new_length;
3426 for (vector<boost::shared_ptr<Playlist> >::iterator i = playlists.begin(); i != playlists.end(); ++i) {
3428 /* Only the top regions at start and end have to be cropped */
3429 boost::shared_ptr<Region> region_at_start = (*i)->top_region_at(start);
3430 boost::shared_ptr<Region> region_at_end = (*i)->top_region_at(end);
3432 vector<boost::shared_ptr<Region> > regions;
3434 if (region_at_start != 0) {
3435 regions.push_back (region_at_start);
3437 if (region_at_end != 0) {
3438 regions.push_back (region_at_end);
3441 /* now adjust lengths */
3442 for (vector<boost::shared_ptr<Region> >::iterator i = regions.begin(); i != regions.end(); ++i) {
3444 pos = (*i)->position();
3445 new_start = max (start, pos);
3446 if (max_samplepos - pos > (*i)->length()) {
3447 new_end = pos + (*i)->length() - 1;
3449 new_end = max_samplepos;
3451 new_end = min (end, new_end);
3452 new_length = new_end - new_start + 1;
3454 (*i)->clear_changes ();
3455 (*i)->trim_to (new_start, new_length);
3456 _session->add_command (new StatefulDiffCommand (*i));
3462 Editor::region_fill_track ()
3464 boost::shared_ptr<Playlist> playlist;
3465 RegionSelection regions = get_regions_from_selection_and_entered ();
3466 RegionSelection foo;
3468 samplepos_t const end = _session->current_end_sample ();
3470 if (regions.empty () || regions.end_sample () + 1 >= end) {
3474 samplepos_t const start_sample = regions.start ();
3475 samplepos_t const end_sample = regions.end_sample ();
3476 samplecnt_t const gap = end_sample - start_sample + 1;
3478 begin_reversible_command (Operations::region_fill);
3480 selection->clear_regions ();
3482 for (RegionSelection::iterator i = regions.begin(); i != regions.end(); ++i) {
3484 boost::shared_ptr<Region> r ((*i)->region());
3486 TimeAxisView& tv = (*i)->get_time_axis_view();
3487 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (&tv);
3488 latest_regionviews.clear ();
3489 sigc::connection c = rtv->view()->RegionViewAdded.connect (sigc::mem_fun(*this, &Editor::collect_new_region_view));
3491 samplepos_t const position = end_sample + (r->first_sample() - start_sample + 1);
3492 playlist = (*i)->region()->playlist();
3493 playlist->clear_changes ();
3494 playlist->duplicate_until (r, position, gap, end);
3495 _session->add_command(new StatefulDiffCommand (playlist));
3499 foo.insert (foo.end(), latest_regionviews.begin(), latest_regionviews.end());
3503 selection->set (foo);
3506 commit_reversible_command ();
3510 Editor::set_region_sync_position ()
3512 set_sync_point (get_preferred_edit_position (), get_regions_from_selection_and_edit_point ());
3516 Editor::set_sync_point (samplepos_t where, const RegionSelection& rs)
3518 bool in_command = false;
3520 for (RegionSelection::const_iterator r = rs.begin(); r != rs.end(); ++r) {
3522 if (!(*r)->region()->covers (where)) {
3526 boost::shared_ptr<Region> region ((*r)->region());
3529 begin_reversible_command (_("set sync point"));
3533 region->clear_changes ();
3534 region->set_sync_position (where);
3535 _session->add_command(new StatefulDiffCommand (region));
3539 commit_reversible_command ();
3543 /** Remove the sync positions of the selection */
3545 Editor::remove_region_sync ()
3547 RegionSelection rs = get_regions_from_selection_and_entered ();
3553 begin_reversible_command (_("remove region sync"));
3555 for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
3557 (*i)->region()->clear_changes ();
3558 (*i)->region()->clear_sync_position ();
3559 _session->add_command(new StatefulDiffCommand ((*i)->region()));
3562 commit_reversible_command ();
3566 Editor::naturalize_region ()
3568 RegionSelection rs = get_regions_from_selection_and_entered ();
3574 if (rs.size() > 1) {
3575 begin_reversible_command (_("move regions to original position"));
3577 begin_reversible_command (_("move region to original position"));
3580 for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
3581 (*i)->region()->clear_changes ();
3582 (*i)->region()->move_to_natural_position ();
3583 _session->add_command (new StatefulDiffCommand ((*i)->region()));
3586 commit_reversible_command ();
3590 Editor::align_regions (RegionPoint what)
3592 RegionSelection const rs = get_regions_from_selection_and_edit_point ();
3598 begin_reversible_command (_("align selection"));
3600 samplepos_t const position = get_preferred_edit_position ();
3602 for (RegionSelection::const_iterator i = rs.begin(); i != rs.end(); ++i) {
3603 align_region_internal ((*i)->region(), what, position);
3606 commit_reversible_command ();
3609 struct RegionSortByTime {
3610 bool operator() (const RegionView* a, const RegionView* b) {
3611 return a->region()->position() < b->region()->position();
3616 Editor::align_regions_relative (RegionPoint point)
3618 RegionSelection const rs = get_regions_from_selection_and_edit_point ();
3624 samplepos_t const position = get_preferred_edit_position ();
3626 samplepos_t distance = 0;
3627 samplepos_t pos = 0;
3630 list<RegionView*> sorted;
3631 rs.by_position (sorted);
3633 boost::shared_ptr<Region> r ((*sorted.begin())->region());
3638 if (position > r->position()) {
3639 distance = position - r->position();
3641 distance = r->position() - position;
3647 if (position > r->last_sample()) {
3648 distance = position - r->last_sample();
3649 pos = r->position() + distance;
3651 distance = r->last_sample() - position;
3652 pos = r->position() - distance;
3658 pos = r->adjust_to_sync (position);
3659 if (pos > r->position()) {
3660 distance = pos - r->position();
3662 distance = r->position() - pos;
3668 if (pos == r->position()) {
3672 begin_reversible_command (_("align selection (relative)"));
3674 /* move first one specially */
3676 r->clear_changes ();
3677 r->set_position (pos);
3678 _session->add_command(new StatefulDiffCommand (r));
3680 /* move rest by the same amount */
3684 for (list<RegionView*>::iterator i = sorted.begin(); i != sorted.end(); ++i) {
3686 boost::shared_ptr<Region> region ((*i)->region());
3688 region->clear_changes ();
3691 region->set_position (region->position() + distance);
3693 region->set_position (region->position() - distance);
3696 _session->add_command(new StatefulDiffCommand (region));
3700 commit_reversible_command ();
3704 Editor::align_region (boost::shared_ptr<Region> region, RegionPoint point, samplepos_t position)
3706 begin_reversible_command (_("align region"));
3707 align_region_internal (region, point, position);
3708 commit_reversible_command ();
3712 Editor::align_region_internal (boost::shared_ptr<Region> region, RegionPoint point, samplepos_t position)
3714 region->clear_changes ();
3718 region->set_position (region->adjust_to_sync (position));
3722 if (position > region->length()) {
3723 region->set_position (position - region->length());
3728 region->set_position (position);
3732 _session->add_command(new StatefulDiffCommand (region));
3736 Editor::trim_region_front ()
3742 Editor::trim_region_back ()
3744 trim_region (false);
3748 Editor::trim_region (bool front)
3750 samplepos_t where = get_preferred_edit_position();
3751 RegionSelection rs = get_regions_from_selection_and_edit_point ();
3757 begin_reversible_command (front ? _("trim front") : _("trim back"));
3759 for (list<RegionView*>::const_iterator i = rs.by_layer().begin(); i != rs.by_layer().end(); ++i) {
3760 if (!(*i)->region()->locked()) {
3762 (*i)->region()->clear_changes ();
3765 (*i)->region()->trim_front (where);
3767 (*i)->region()->trim_end (where);
3770 _session->add_command (new StatefulDiffCommand ((*i)->region()));
3774 commit_reversible_command ();
3777 /** Trim the end of the selected regions to the position of the edit cursor */
3779 Editor::trim_region_to_loop ()
3781 Location* loc = _session->locations()->auto_loop_location();
3785 trim_region_to_location (*loc, _("trim to loop"));
3789 Editor::trim_region_to_punch ()
3791 Location* loc = _session->locations()->auto_punch_location();
3795 trim_region_to_location (*loc, _("trim to punch"));
3799 Editor::trim_region_to_location (const Location& loc, const char* str)
3801 RegionSelection rs = get_regions_from_selection_and_entered ();
3802 bool in_command = false;
3804 for (RegionSelection::iterator x = rs.begin(); x != rs.end(); ++x) {
3805 RegionView* rv = (*x);
3807 /* require region to span proposed trim */
3808 switch (rv->region()->coverage (loc.start(), loc.end())) {
3809 case Evoral::OverlapInternal:
3815 RouteTimeAxisView* tav = dynamic_cast<RouteTimeAxisView*> (&rv->get_time_axis_view());
3823 start = loc.start();
3826 rv->region()->clear_changes ();
3827 rv->region()->trim_to (start, (end - start));
3830 begin_reversible_command (str);
3833 _session->add_command(new StatefulDiffCommand (rv->region()));
3837 commit_reversible_command ();
3842 Editor::trim_region_to_previous_region_end ()
3844 return trim_to_region(false);
3848 Editor::trim_region_to_next_region_start ()
3850 return trim_to_region(true);
3854 Editor::trim_to_region(bool forward)
3856 RegionSelection rs = get_regions_from_selection_and_entered ();
3857 bool in_command = false;
3859 boost::shared_ptr<Region> next_region;
3861 for (RegionSelection::iterator x = rs.begin(); x != rs.end(); ++x) {
3863 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (*x);
3869 AudioTimeAxisView* atav = dynamic_cast<AudioTimeAxisView*> (&arv->get_time_axis_view());
3875 boost::shared_ptr<Region> region = arv->region();
3876 boost::shared_ptr<Playlist> playlist (region->playlist());
3878 region->clear_changes ();
3882 next_region = playlist->find_next_region (region->first_sample(), Start, 1);
3888 region->trim_end (next_region->first_sample() - 1);
3889 arv->region_changed (PropertyChange (ARDOUR::Properties::length));
3893 next_region = playlist->find_next_region (region->first_sample(), Start, 0);
3899 region->trim_front (next_region->last_sample() + 1);
3900 arv->region_changed (ARDOUR::bounds_change);
3904 begin_reversible_command (_("trim to region"));
3907 _session->add_command(new StatefulDiffCommand (region));
3911 commit_reversible_command ();
3916 Editor::unfreeze_route ()
3918 if (clicked_routeview == 0 || !clicked_routeview->is_track()) {
3922 clicked_routeview->track()->unfreeze ();
3926 Editor::_freeze_thread (void* arg)
3928 return static_cast<Editor*>(arg)->freeze_thread ();
3932 Editor::freeze_thread ()
3934 /* create event pool because we may need to talk to the session */
3935 SessionEvent::create_per_thread_pool ("freeze events", 64);
3936 /* create per-thread buffers for process() tree to use */
3937 clicked_routeview->audio_track()->freeze_me (*current_interthread_info);
3938 current_interthread_info->done = true;
3943 Editor::freeze_route ()
3949 /* stop transport before we start. this is important */
3951 _session->request_transport_speed (0.0);
3953 /* wait for just a little while, because the above call is asynchronous */
3955 Glib::usleep (250000);
3957 if (clicked_routeview == 0 || !clicked_routeview->is_audio_track()) {
3961 if (!clicked_routeview->track()->bounceable (clicked_routeview->track()->main_outs(), true)) {
3963 _("This track/bus cannot be frozen because the signal adds or loses channels before reaching the outputs.\n"
3964 "This is typically caused by plugins that generate stereo output from mono input or vice versa.")
3966 d.set_title (_("Cannot freeze"));
3971 if (clicked_routeview->track()->has_external_redirects()) {
3972 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"
3973 "Freezing will only process the signal as far as the first send/insert/return."),
3974 clicked_routeview->track()->name()), true, MESSAGE_INFO, BUTTONS_NONE, true);
3976 d.add_button (_("Freeze anyway"), Gtk::RESPONSE_OK);
3977 d.add_button (_("Don't freeze"), Gtk::RESPONSE_CANCEL);
3978 d.set_title (_("Freeze Limits"));
3980 int response = d.run ();
3983 case Gtk::RESPONSE_CANCEL:
3990 InterThreadInfo itt;
3991 current_interthread_info = &itt;
3993 InterthreadProgressWindow ipw (current_interthread_info, _("Freeze"), _("Cancel Freeze"));
3995 pthread_create_and_store (X_("freezer"), &itt.thread, _freeze_thread, this);
3997 CursorContext::Handle cursor_ctx = CursorContext::create(*this, _cursors->wait);
3999 while (!itt.done && !itt.cancel) {
4000 gtk_main_iteration ();
4003 pthread_join (itt.thread, 0);
4004 current_interthread_info = 0;
4008 Editor::bounce_range_selection (bool replace, bool enable_processing)
4010 if (selection->time.empty()) {
4014 TrackSelection views = selection->tracks;
4016 for (TrackViewList::iterator i = views.begin(); i != views.end(); ++i) {
4018 if (enable_processing) {
4020 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (*i);
4022 if (rtv && rtv->track() && replace && enable_processing && !rtv->track()->bounceable (rtv->track()->main_outs(), false)) {
4024 _("You can't perform this operation because the processing of the signal "
4025 "will cause one or more of the tracks to end up with a region with more channels than this track has inputs.\n\n"
4026 "You can do this without processing, which is a different operation.")
4028 d.set_title (_("Cannot bounce"));
4035 samplepos_t start = selection->time[clicked_selection].start;
4036 samplepos_t end = selection->time[clicked_selection].end;
4037 samplepos_t cnt = end - start + 1;
4038 bool in_command = false;
4040 for (TrackViewList::iterator i = views.begin(); i != views.end(); ++i) {
4042 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (*i);
4048 boost::shared_ptr<Playlist> playlist;
4050 if ((playlist = rtv->playlist()) == 0) {
4054 InterThreadInfo itt;
4056 playlist->clear_changes ();
4057 playlist->clear_owned_changes ();
4059 boost::shared_ptr<Region> r;
4061 if (enable_processing) {
4062 r = rtv->track()->bounce_range (start, start+cnt, itt, rtv->track()->main_outs(), false);
4064 r = rtv->track()->bounce_range (start, start+cnt, itt, boost::shared_ptr<Processor>(), false);
4072 list<AudioRange> ranges;
4073 ranges.push_back (AudioRange (start, start+cnt, 0));
4074 playlist->cut (ranges); // discard result
4075 playlist->add_region (r, start);
4079 begin_reversible_command (_("bounce range"));
4082 vector<Command*> cmds;
4083 playlist->rdiff (cmds);
4084 _session->add_commands (cmds);
4086 _session->add_command (new StatefulDiffCommand (playlist));
4090 commit_reversible_command ();
4094 /** Delete selected regions, automation points or a time range */
4098 //special case: if the user is pointing in the editor/mixer strip, they may be trying to delete a plugin.
4099 //we need this because the editor-mixer strip is in the editor window, so it doesn't get the bindings from the mix window
4100 bool deleted = false;
4101 if (current_mixer_strip && current_mixer_strip == MixerStrip::entered_mixer_strip())
4102 deleted = current_mixer_strip->delete_processors ();
4108 /** Cut selected regions, automation points or a time range */
4115 /** Copy selected regions, automation points or a time range */
4123 /** @return true if a Cut, Copy or Clear is possible */
4125 Editor::can_cut_copy () const
4127 if (!selection->time.empty() || !selection->regions.empty() || !selection->points.empty())
4134 /** Cut, copy or clear selected regions, automation points or a time range.
4135 * @param op Operation (Delete, Cut, Copy or Clear)
4138 Editor::cut_copy (CutCopyOp op)
4140 /* only cancel selection if cut/copy is successful.*/
4146 opname = _("delete");
4155 opname = _("clear");
4159 /* if we're deleting something, and the mouse is still pressed,
4160 the thing we started a drag for will be gone when we release
4161 the mouse button(s). avoid this. see part 2 at the end of
4165 if (op == Delete || op == Cut || op == Clear) {
4166 if (_drags->active ()) {
4171 if (op != Delete) { //"Delete" doesn't change copy/paste buf
4172 cut_buffer->clear ();
4175 if (entered_marker) {
4177 /* cut/delete op while pointing at a marker */
4180 Location* loc = find_location_from_marker (entered_marker, ignored);
4182 if (_session && loc) {
4183 entered_marker = NULL;
4184 Glib::signal_idle().connect (sigc::bind (sigc::mem_fun(*this, &Editor::really_remove_marker), loc));
4191 switch (mouse_mode) {
4194 begin_reversible_command (opname + ' ' + X_("MIDI"));
4196 commit_reversible_command ();
4202 bool did_edit = false;
4204 if (!selection->regions.empty() || !selection->points.empty()) {
4205 begin_reversible_command (opname + ' ' + _("objects"));
4208 if (!selection->regions.empty()) {
4209 cut_copy_regions (op, selection->regions);
4211 if (op == Cut || op == Delete) {
4212 selection->clear_regions ();
4216 if (!selection->points.empty()) {
4217 cut_copy_points (op);
4219 if (op == Cut || op == Delete) {
4220 selection->clear_points ();
4223 } else if (selection->time.empty()) {
4224 samplepos_t start, end;
4225 /* no time selection, see if we can get an edit range
4228 if (get_edit_op_range (start, end)) {
4229 selection->set (start, end);
4231 } else if (!selection->time.empty()) {
4232 begin_reversible_command (opname + ' ' + _("range"));
4235 cut_copy_ranges (op);
4237 if (op == Cut || op == Delete) {
4238 selection->clear_time ();
4243 /* reset repeated paste state */
4245 last_paste_pos = -1;
4246 commit_reversible_command ();
4249 if (op == Delete || op == Cut || op == Clear) {
4255 struct AutomationRecord {
4256 AutomationRecord () : state (0) , line(NULL) {}
4257 AutomationRecord (XMLNode* s, const AutomationLine* l) : state (s) , line (l) {}
4259 XMLNode* state; ///< state before any operation
4260 const AutomationLine* line; ///< line this came from
4261 boost::shared_ptr<Evoral::ControlList> copy; ///< copied events for the cut buffer
4264 struct PointsSelectionPositionSorter {
4265 bool operator() (ControlPoint* a, ControlPoint* b) {
4266 return (*(a->model()))->when < (*(b->model()))->when;
4270 /** Cut, copy or clear selected automation points.
4271 * @param op Operation (Cut, Copy or Clear)
4274 Editor::cut_copy_points (Editing::CutCopyOp op, Temporal::Beats earliest, bool midi)
4276 if (selection->points.empty ()) {
4280 /* XXX: not ideal, as there may be more than one track involved in the point selection */
4281 _last_cut_copy_source_track = &selection->points.front()->line().trackview;
4283 /* Keep a record of the AutomationLists that we end up using in this operation */
4284 typedef std::map<boost::shared_ptr<AutomationList>, AutomationRecord> Lists;
4287 /* user could select points in any order */
4288 selection->points.sort(PointsSelectionPositionSorter ());
4290 /* Go through all selected points, making an AutomationRecord for each distinct AutomationList */
4291 for (PointSelection::iterator sel_point = selection->points.begin(); sel_point != selection->points.end(); ++sel_point) {
4292 const AutomationLine& line = (*sel_point)->line();
4293 const boost::shared_ptr<AutomationList> al = line.the_list();
4294 if (lists.find (al) == lists.end ()) {
4295 /* We haven't seen this list yet, so make a record for it. This includes
4296 taking a copy of its current state, in case this is needed for undo later.
4298 lists[al] = AutomationRecord (&al->get_state (), &line);
4302 if (op == Cut || op == Copy) {
4303 /* This operation will involve putting things in the cut buffer, so create an empty
4304 ControlList for each of our source lists to put the cut buffer data in.
4306 for (Lists::iterator i = lists.begin(); i != lists.end(); ++i) {
4307 i->second.copy = i->first->create (i->first->parameter (), i->first->descriptor());
4310 /* Add all selected points to the relevant copy ControlLists */
4311 MusicSample start (std::numeric_limits<samplepos_t>::max(), 0);
4312 for (PointSelection::iterator sel_point = selection->points.begin(); sel_point != selection->points.end(); ++sel_point) {
4313 boost::shared_ptr<AutomationList> al = (*sel_point)->line().the_list();
4314 AutomationList::const_iterator ctrl_evt = (*sel_point)->model ();
4316 lists[al].copy->fast_simple_add ((*ctrl_evt)->when, (*ctrl_evt)->value);
4318 /* Update earliest MIDI start time in beats */
4319 earliest = std::min(earliest, Temporal::Beats((*ctrl_evt)->when));
4321 /* Update earliest session start time in samples */
4322 start.sample = std::min(start.sample, (*sel_point)->line().session_position(ctrl_evt));
4326 /* Snap start time backwards, so copy/paste is snap aligned. */
4328 if (earliest == std::numeric_limits<Temporal::Beats>::max()) {
4329 earliest = Temporal::Beats(); // Weird... don't offset
4331 earliest.round_down_to_beat();
4333 if (start.sample == std::numeric_limits<double>::max()) {
4334 start.sample = 0; // Weird... don't offset
4336 snap_to(start, RoundDownMaybe);
4339 const double line_offset = midi ? earliest.to_double() : start.sample;
4340 for (Lists::iterator i = lists.begin(); i != lists.end(); ++i) {
4341 /* Correct this copy list so that it is relative to the earliest
4342 start time, so relative ordering between points is preserved
4343 when copying from several lists and the paste starts at the
4344 earliest copied piece of data. */
4345 boost::shared_ptr<Evoral::ControlList> &al_cpy = i->second.copy;
4346 for (AutomationList::iterator ctrl_evt = al_cpy->begin(); ctrl_evt != al_cpy->end(); ++ctrl_evt) {
4347 (*ctrl_evt)->when -= line_offset;
4350 /* And add it to the cut buffer */
4351 cut_buffer->add (al_cpy);
4355 if (op == Delete || op == Cut) {
4356 /* This operation needs to remove things from the main AutomationList, so do that now */
4358 for (Lists::iterator i = lists.begin(); i != lists.end(); ++i) {
4359 i->first->freeze ();
4362 /* Remove each selected point from its AutomationList */
4363 for (PointSelection::iterator sel_point = selection->points.begin(); sel_point != selection->points.end(); ++sel_point) {
4364 AutomationLine& line = (*sel_point)->line ();
4365 boost::shared_ptr<AutomationList> al = line.the_list();
4369 if (dynamic_cast<AudioRegionGainLine*> (&line)) {
4370 /* removing of first and last gain point in region gain lines is prohibited*/
4371 if (line.is_last_point (*(*sel_point)) || line.is_first_point (*(*sel_point))) {
4377 al->erase ((*sel_point)->model ());
4381 /* Thaw the lists and add undo records for them */
4382 for (Lists::iterator i = lists.begin(); i != lists.end(); ++i) {
4383 boost::shared_ptr<AutomationList> al = i->first;
4385 _session->add_command (new MementoCommand<AutomationList> (*al.get(), i->second.state, &(al->get_state ())));
4390 /** Cut, copy or clear selected automation points.
4391 * @param op Operation (Cut, Copy or Clear)
4394 Editor::cut_copy_midi (CutCopyOp op)
4396 Temporal::Beats earliest = std::numeric_limits<Temporal::Beats>::max();
4397 for (MidiRegionSelection::iterator i = selection->midi_regions.begin(); i != selection->midi_regions.end(); ++i) {
4398 MidiRegionView* mrv = dynamic_cast<MidiRegionView*>(*i);
4400 if (!mrv->selection().empty()) {
4401 earliest = std::min(earliest, (*mrv->selection().begin())->note()->time());
4403 mrv->cut_copy_clear (op);
4405 /* XXX: not ideal, as there may be more than one track involved in the selection */
4406 _last_cut_copy_source_track = &mrv->get_time_axis_view();
4410 if (!selection->points.empty()) {
4411 cut_copy_points (op, earliest, true);
4412 if (op == Cut || op == Delete) {
4413 selection->clear_points ();
4418 struct lt_playlist {
4419 bool operator () (const PlaylistState& a, const PlaylistState& b) {
4420 return a.playlist < b.playlist;
4424 struct PlaylistMapping {
4426 boost::shared_ptr<Playlist> pl;
4428 PlaylistMapping (TimeAxisView* tvp) : tv (tvp) {}
4431 /** Remove `clicked_regionview' */
4433 Editor::remove_clicked_region ()
4435 if (clicked_routeview == 0 || clicked_regionview == 0) {
4439 begin_reversible_command (_("remove region"));
4441 boost::shared_ptr<Playlist> playlist = clicked_routeview->playlist();
4443 playlist->clear_changes ();
4444 playlist->clear_owned_changes ();
4445 playlist->remove_region (clicked_regionview->region());
4446 if (Config->get_edit_mode() == Ripple)
4447 playlist->ripple (clicked_regionview->region()->position(), -clicked_regionview->region()->length(), boost::shared_ptr<Region>());
4449 /* We might have removed regions, which alters other regions' layering_index,
4450 so we need to do a recursive diff here.
4452 vector<Command*> cmds;
4453 playlist->rdiff (cmds);
4454 _session->add_commands (cmds);
4456 _session->add_command(new StatefulDiffCommand (playlist));
4457 commit_reversible_command ();
4461 /** Remove the selected regions */
4463 Editor::remove_selected_regions ()
4465 RegionSelection rs = get_regions_from_selection_and_entered ();
4467 if (!_session || rs.empty()) {
4471 list<boost::shared_ptr<Region> > regions_to_remove;
4473 for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
4474 // we can't just remove the region(s) in this loop because
4475 // this removes them from the RegionSelection, and they thus
4476 // disappear from underneath the iterator, and the ++i above
4477 // SEGVs in a puzzling fashion.
4479 // so, first iterate over the regions to be removed from rs and
4480 // add them to the regions_to_remove list, and then
4481 // iterate over the list to actually remove them.
4483 regions_to_remove.push_back ((*i)->region());
4486 vector<boost::shared_ptr<Playlist> > playlists;
4488 for (list<boost::shared_ptr<Region> >::iterator rl = regions_to_remove.begin(); rl != regions_to_remove.end(); ++rl) {
4490 boost::shared_ptr<Playlist> playlist = (*rl)->playlist();
4493 // is this check necessary?
4497 /* get_regions_from_selection_and_entered() guarantees that
4498 the playlists involved are unique, so there is no need
4502 playlists.push_back (playlist);
4504 playlist->clear_changes ();
4505 playlist->clear_owned_changes ();
4506 playlist->freeze ();
4507 playlist->remove_region (*rl);
4508 if (Config->get_edit_mode() == Ripple)
4509 playlist->ripple ((*rl)->position(), -(*rl)->length(), boost::shared_ptr<Region>());
4513 vector<boost::shared_ptr<Playlist> >::iterator pl;
4514 bool in_command = false;
4516 for (pl = playlists.begin(); pl != playlists.end(); ++pl) {
4519 /* We might have removed regions, which alters other regions' layering_index,
4520 so we need to do a recursive diff here.
4524 begin_reversible_command (_("remove region"));
4527 vector<Command*> cmds;
4528 (*pl)->rdiff (cmds);
4529 _session->add_commands (cmds);
4531 _session->add_command(new StatefulDiffCommand (*pl));
4535 commit_reversible_command ();
4539 /** Cut, copy or clear selected regions.
4540 * @param op Operation (Cut, Copy or Clear)
4543 Editor::cut_copy_regions (CutCopyOp op, RegionSelection& rs)
4545 /* we can't use a std::map here because the ordering is important, and we can't trivially sort
4546 a map when we want ordered access to both elements. i think.
4549 vector<PlaylistMapping> pmap;
4551 samplepos_t first_position = max_samplepos;
4553 typedef set<boost::shared_ptr<Playlist> > FreezeList;
4554 FreezeList freezelist;
4556 /* get ordering correct before we cut/copy */
4558 rs.sort_by_position_and_track ();
4560 for (RegionSelection::iterator x = rs.begin(); x != rs.end(); ++x) {
4562 first_position = min ((samplepos_t) (*x)->region()->position(), first_position);
4564 if (op == Cut || op == Clear || op == Delete) {
4565 boost::shared_ptr<Playlist> pl = (*x)->region()->playlist();
4568 FreezeList::iterator fl;
4570 // only take state if this is a new playlist.
4571 for (fl = freezelist.begin(); fl != freezelist.end(); ++fl) {
4577 if (fl == freezelist.end()) {
4578 pl->clear_changes();
4579 pl->clear_owned_changes ();
4581 freezelist.insert (pl);
4586 TimeAxisView* tv = &(*x)->get_time_axis_view();
4587 vector<PlaylistMapping>::iterator z;
4589 for (z = pmap.begin(); z != pmap.end(); ++z) {
4590 if ((*z).tv == tv) {
4595 if (z == pmap.end()) {
4596 pmap.push_back (PlaylistMapping (tv));
4600 for (RegionSelection::iterator x = rs.begin(); x != rs.end(); ) {
4602 boost::shared_ptr<Playlist> pl = (*x)->region()->playlist();
4605 /* region not yet associated with a playlist (e.g. unfinished
4612 TimeAxisView& tv = (*x)->get_time_axis_view();
4613 boost::shared_ptr<Playlist> npl;
4614 RegionSelection::iterator tmp;
4621 vector<PlaylistMapping>::iterator z;
4623 for (z = pmap.begin(); z != pmap.end(); ++z) {
4624 if ((*z).tv == &tv) {
4629 assert (z != pmap.end());
4632 npl = PlaylistFactory::create (pl->data_type(), *_session, "cutlist", true);
4640 boost::shared_ptr<Region> r = (*x)->region();
4641 boost::shared_ptr<Region> _xx;
4647 pl->remove_region (r);
4648 if (Config->get_edit_mode() == Ripple)
4649 pl->ripple (r->position(), -r->length(), boost::shared_ptr<Region>());
4653 _xx = RegionFactory::create (r);
4654 npl->add_region (_xx, r->position() - first_position);
4655 pl->remove_region (r);
4656 if (Config->get_edit_mode() == Ripple)
4657 pl->ripple (r->position(), -r->length(), boost::shared_ptr<Region>());
4661 /* copy region before adding, so we're not putting same object into two different playlists */
4662 npl->add_region (RegionFactory::create (r), r->position() - first_position);
4666 pl->remove_region (r);
4667 if (Config->get_edit_mode() == Ripple)
4668 pl->ripple (r->position(), -r->length(), boost::shared_ptr<Region>());
4677 list<boost::shared_ptr<Playlist> > foo;
4679 /* the pmap is in the same order as the tracks in which selected regions occurred */
4681 for (vector<PlaylistMapping>::iterator i = pmap.begin(); i != pmap.end(); ++i) {
4684 foo.push_back ((*i).pl);
4689 cut_buffer->set (foo);
4693 _last_cut_copy_source_track = 0;
4695 _last_cut_copy_source_track = pmap.front().tv;
4699 for (FreezeList::iterator pl = freezelist.begin(); pl != freezelist.end(); ++pl) {
4702 /* We might have removed regions, which alters other regions' layering_index,
4703 so we need to do a recursive diff here.
4705 vector<Command*> cmds;
4706 (*pl)->rdiff (cmds);
4707 _session->add_commands (cmds);
4709 _session->add_command (new StatefulDiffCommand (*pl));
4714 Editor::cut_copy_ranges (CutCopyOp op)
4716 TrackViewList ts = selection->tracks.filter_to_unique_playlists ();
4718 /* Sort the track selection now, so that it if is used, the playlists
4719 selected by the calls below to cut_copy_clear are in the order that
4720 their tracks appear in the editor. This makes things like paste
4721 of ranges work properly.
4724 sort_track_selection (ts);
4727 if (!entered_track) {
4730 ts.push_back (entered_track);
4733 for (TrackViewList::iterator i = ts.begin(); i != ts.end(); ++i) {
4734 (*i)->cut_copy_clear (*selection, op);
4739 Editor::paste (float times, bool from_context)
4741 DEBUG_TRACE (DEBUG::CutNPaste, "paste to preferred edit pos\n");
4742 MusicSample where (get_preferred_edit_position (EDIT_IGNORE_NONE, from_context), 0);
4743 paste_internal (where.sample, times, 0);
4747 Editor::mouse_paste ()
4749 MusicSample where (0, 0);
4751 if (!mouse_sample (where.sample, ignored)) {
4756 paste_internal (where.sample, 1, where.division);
4760 Editor::paste_internal (samplepos_t position, float times, const int32_t sub_num)
4762 DEBUG_TRACE (DEBUG::CutNPaste, string_compose ("apparent paste position is %1\n", position));
4764 if (cut_buffer->empty(internal_editing())) {
4768 if (position == max_samplepos) {
4769 position = get_preferred_edit_position();
4770 DEBUG_TRACE (DEBUG::CutNPaste, string_compose ("preferred edit position is %1\n", position));
4773 if (position != last_paste_pos) {
4774 /* paste in new location, reset repeated paste state */
4776 last_paste_pos = position;
4779 /* get everything in the correct order */
4782 if (!selection->tracks.empty()) {
4783 /* If there is a track selection, paste into exactly those tracks and
4784 * only those tracks. This allows the user to be explicit and override
4785 * the below "do the reasonable thing" logic. */
4786 ts = selection->tracks.filter_to_unique_playlists ();
4787 sort_track_selection (ts);
4789 /* Figure out which track to base the paste at. */
4790 TimeAxisView* base_track = NULL;
4791 if (_edit_point == Editing::EditAtMouse && entered_track) {
4792 /* With the mouse edit point, paste onto the track under the mouse. */
4793 base_track = entered_track;
4794 } else if (_edit_point == Editing::EditAtMouse && entered_regionview) {
4795 /* With the mouse edit point, paste onto the track of the region under the mouse. */
4796 base_track = &entered_regionview->get_time_axis_view();
4797 } else if (_last_cut_copy_source_track) {
4798 /* Paste to the track that the cut/copy came from (see mantis #333). */
4799 base_track = _last_cut_copy_source_track;
4801 /* This is "impossible" since we've copied... well, do nothing. */
4805 /* Walk up to parent if necessary, so base track is a route. */
4806 while (base_track->get_parent()) {
4807 base_track = base_track->get_parent();
4810 /* Add base track and all tracks below it. The paste logic will select
4811 the appropriate object types from the cut buffer in relative order. */
4812 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
4813 if ((*i)->order() >= base_track->order()) {
4818 /* Sort tracks so the nth track of type T will pick the nth object of type T. */
4819 sort_track_selection (ts);
4821 /* Add automation children of each track in order, for pasting several lines. */
4822 for (TrackViewList::iterator i = ts.begin(); i != ts.end();) {
4823 /* Add any automation children for pasting several lines */
4824 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*>(*i++);
4829 typedef RouteTimeAxisView::AutomationTracks ATracks;
4830 const ATracks& atracks = rtv->automation_tracks();
4831 for (ATracks::const_iterator a = atracks.begin(); a != atracks.end(); ++a) {
4832 i = ts.insert(i, a->second.get());
4837 /* We now have a list of trackviews starting at base_track, including
4838 automation children, in the order shown in the editor, e.g. R1,
4839 R1.A1, R1.A2, R2, R2.A1, ... */
4842 begin_reversible_command (Operations::paste);
4844 if (ts.size() == 1 && cut_buffer->lines.size() == 1 &&
4845 dynamic_cast<AutomationTimeAxisView*>(ts.front())) {
4846 /* Only one line copied, and one automation track selected. Do a
4847 "greedy" paste from one automation type to another. */
4849 PasteContext ctx(paste_count, times, ItemCounts(), true);
4850 ts.front()->paste (position, *cut_buffer, ctx, sub_num);
4854 /* Paste into tracks */
4856 PasteContext ctx(paste_count, times, ItemCounts(), false);
4857 for (TrackViewList::iterator i = ts.begin(); i != ts.end(); ++i) {
4858 (*i)->paste (position, *cut_buffer, ctx, sub_num);
4864 commit_reversible_command ();
4868 Editor::duplicate_regions (float times)
4870 RegionSelection rs (get_regions_from_selection_and_entered());
4871 duplicate_some_regions (rs, times);
4875 Editor::duplicate_some_regions (RegionSelection& regions, float times)
4877 if (regions.empty ()) {
4881 boost::shared_ptr<Playlist> playlist;
4882 std::set<boost::shared_ptr<Playlist> > playlists; // list of unique playlists affected by duplication
4883 RegionSelection sel = regions; // clear (below) may clear the argument list if its the current region selection
4884 RegionSelection foo;
4886 samplepos_t const start_sample = regions.start ();
4887 samplepos_t const end_sample = regions.end_sample ();
4888 samplecnt_t const span = end_sample - start_sample + 1;
4890 begin_reversible_command (Operations::duplicate_region);
4892 selection->clear_regions ();
4894 /* ripple first so that we don't move the duplicates that will be added */
4896 if (Config->get_edit_mode() == Ripple) {
4898 /* convert RegionSelection into RegionList so that we can pass it to ripple and exclude the regions we will duplicate */
4902 for (RegionSelection::iterator i = sel.begin(); i != sel.end(); ++i) {
4903 exclude.push_back ((*i)->region());
4904 playlist = (*i)->region()->playlist();
4905 if (playlists.insert (playlist).second) {
4906 /* successfully inserted into set, so it's the first time we've seen this playlist */
4907 playlist->clear_changes ();
4911 for (set<boost::shared_ptr<Playlist> >::iterator p = playlists.begin(); p != playlists.end(); ++p) {
4912 (*p)->ripple (start_sample, span * times, &exclude);
4916 for (RegionSelection::iterator i = sel.begin(); i != sel.end(); ++i) {
4918 boost::shared_ptr<Region> r ((*i)->region());
4920 TimeAxisView& tv = (*i)->get_time_axis_view();
4921 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (&tv);
4922 latest_regionviews.clear ();
4923 sigc::connection c = rtv->view()->RegionViewAdded.connect (sigc::mem_fun(*this, &Editor::collect_new_region_view));
4925 samplepos_t const position = end_sample + (r->first_sample() - start_sample + 1);
4926 playlist = (*i)->region()->playlist();
4928 if (Config->get_edit_mode() != Ripple) {
4929 if (playlists.insert (playlist).second) {
4930 playlist->clear_changes ();
4934 playlist->duplicate (r, position, span, times);
4938 foo.insert (foo.end(), latest_regionviews.begin(), latest_regionviews.end());
4941 for (set<boost::shared_ptr<Playlist> >::iterator p = playlists.begin(); p != playlists.end(); ++p) {
4942 _session->add_command (new StatefulDiffCommand (*p));
4943 vector<Command*> cmds;
4945 _session->add_commands (cmds);
4949 selection->set (foo);
4952 commit_reversible_command ();
4956 Editor::duplicate_selection (float times)
4958 if (selection->time.empty() || selection->tracks.empty()) {
4962 boost::shared_ptr<Playlist> playlist;
4964 TrackViewList ts = selection->tracks.filter_to_unique_playlists ();
4966 bool in_command = false;
4968 for (TrackViewList::iterator i = ts.begin(); i != ts.end(); ++i) {
4969 if ((playlist = (*i)->playlist()) == 0) {
4972 playlist->clear_changes ();
4974 if (clicked_selection) {
4975 playlist->duplicate_range (selection->time[clicked_selection], times);
4977 playlist->duplicate_ranges (selection->time, times);
4981 begin_reversible_command (_("duplicate range selection"));
4984 _session->add_command (new StatefulDiffCommand (playlist));
4989 if (times == 1.0f) {
4990 // now "move" range selection to after the current range selection
4991 samplecnt_t distance = 0;
4993 if (clicked_selection) {
4995 selection->time[clicked_selection].end - selection->time[clicked_selection].start;
4997 distance = selection->time.end_sample () - selection->time.start ();
5000 selection->move_time (distance);
5002 commit_reversible_command ();
5006 /** Reset all selected points to the relevant default value */
5008 Editor::reset_point_selection ()
5010 for (PointSelection::iterator i = selection->points.begin(); i != selection->points.end(); ++i) {
5011 ARDOUR::AutomationList::iterator j = (*i)->model ();
5012 (*j)->value = (*i)->line().the_list()->descriptor ().normal;
5017 Editor::center_playhead ()
5019 float const page = _visible_canvas_width * samples_per_pixel;
5020 center_screen_internal (playhead_cursor->current_sample (), page);
5024 Editor::center_edit_point ()
5026 float const page = _visible_canvas_width * samples_per_pixel;
5027 center_screen_internal (get_preferred_edit_position(), page);
5030 /** Caller must begin and commit a reversible command */
5032 Editor::clear_playlist (boost::shared_ptr<Playlist> playlist)
5034 playlist->clear_changes ();
5036 _session->add_command (new StatefulDiffCommand (playlist));
5040 Editor::nudge_track (bool use_edit, bool forwards)
5042 boost::shared_ptr<Playlist> playlist;
5043 samplepos_t distance;
5044 samplepos_t next_distance;
5048 start = get_preferred_edit_position();
5053 if ((distance = get_nudge_distance (start, next_distance)) == 0) {
5057 if (selection->tracks.empty()) {
5061 TrackViewList ts = selection->tracks.filter_to_unique_playlists ();
5062 bool in_command = false;
5064 for (TrackViewList::iterator i = ts.begin(); i != ts.end(); ++i) {
5066 if ((playlist = (*i)->playlist()) == 0) {
5070 playlist->clear_changes ();
5071 playlist->clear_owned_changes ();
5073 playlist->nudge_after (start, distance, forwards);
5076 begin_reversible_command (_("nudge track"));
5079 vector<Command*> cmds;
5081 playlist->rdiff (cmds);
5082 _session->add_commands (cmds);
5084 _session->add_command (new StatefulDiffCommand (playlist));
5088 commit_reversible_command ();
5093 Editor::remove_last_capture ()
5095 vector<string> choices;
5102 if (Config->get_verify_remove_last_capture()) {
5103 prompt = _("Do you really want to destroy the last capture?"
5104 "\n(This is destructive and cannot be undone)");
5106 choices.push_back (_("No, do nothing."));
5107 choices.push_back (_("Yes, destroy it."));
5109 Choice prompter (_("Destroy last capture"), prompt, choices);
5111 if (prompter.run () == 1) {
5112 _session->remove_last_capture ();
5113 _regions->redisplay ();
5117 _session->remove_last_capture();
5118 _regions->redisplay ();
5123 Editor::normalize_region ()
5129 RegionSelection rs = get_regions_from_selection_and_entered ();
5135 NormalizeDialog dialog (rs.size() > 1);
5137 if (dialog.run () != RESPONSE_ACCEPT) {
5141 CursorContext::Handle cursor_ctx = CursorContext::create(*this, _cursors->wait);
5144 /* XXX: should really only count audio regions here */
5145 int const regions = rs.size ();
5147 /* Make a list of the selected audio regions' maximum amplitudes, and also
5148 obtain the maximum amplitude of them all.
5150 list<double> max_amps;
5151 list<double> rms_vals;
5154 bool use_rms = dialog.constrain_rms ();
5156 for (RegionSelection::const_iterator i = rs.begin(); i != rs.end(); ++i) {
5157 AudioRegionView const * arv = dynamic_cast<AudioRegionView const *> (*i);
5161 dialog.descend (1.0 / regions);
5162 double const a = arv->audio_region()->maximum_amplitude (&dialog);
5164 double r = arv->audio_region()->rms (&dialog);
5165 max_rms = max (max_rms, r);
5166 rms_vals.push_back (r);
5170 /* the user cancelled the operation */
5174 max_amps.push_back (a);
5175 max_amp = max (max_amp, a);
5179 list<double>::const_iterator a = max_amps.begin ();
5180 list<double>::const_iterator l = rms_vals.begin ();
5181 bool in_command = false;
5183 for (RegionSelection::iterator r = rs.begin(); r != rs.end(); ++r) {
5184 AudioRegionView* const arv = dynamic_cast<AudioRegionView*> (*r);
5189 arv->region()->clear_changes ();
5191 double amp = dialog.normalize_individually() ? *a : max_amp;
5192 double target = dialog.target_peak (); // dB
5195 double const amp_rms = dialog.normalize_individually() ? *l : max_rms;
5196 const double t_rms = dialog.target_rms ();
5197 const gain_t c_peak = dB_to_coefficient (target);
5198 const gain_t c_rms = dB_to_coefficient (t_rms);
5199 if ((amp_rms / c_rms) > (amp / c_peak)) {
5205 arv->audio_region()->normalize (amp, target);
5208 begin_reversible_command (_("normalize"));
5211 _session->add_command (new StatefulDiffCommand (arv->region()));
5218 commit_reversible_command ();
5224 Editor::reset_region_scale_amplitude ()
5230 RegionSelection rs = get_regions_from_selection_and_entered ();
5236 bool in_command = false;
5238 for (RegionSelection::iterator r = rs.begin(); r != rs.end(); ++r) {
5239 AudioRegionView* const arv = dynamic_cast<AudioRegionView*>(*r);
5242 arv->region()->clear_changes ();
5243 arv->audio_region()->set_scale_amplitude (1.0f);
5246 begin_reversible_command ("reset gain");
5249 _session->add_command (new StatefulDiffCommand (arv->region()));
5253 commit_reversible_command ();
5258 Editor::adjust_region_gain (bool up)
5260 RegionSelection rs = get_regions_from_selection_and_entered ();
5262 if (!_session || rs.empty()) {
5266 bool in_command = false;
5268 for (RegionSelection::iterator r = rs.begin(); r != rs.end(); ++r) {
5269 AudioRegionView* const arv = dynamic_cast<AudioRegionView*>(*r);
5274 arv->region()->clear_changes ();
5276 double dB = accurate_coefficient_to_dB (arv->audio_region()->scale_amplitude ());
5284 arv->audio_region()->set_scale_amplitude (dB_to_coefficient (dB));
5287 begin_reversible_command ("adjust region gain");
5290 _session->add_command (new StatefulDiffCommand (arv->region()));
5294 commit_reversible_command ();
5299 Editor::reset_region_gain ()
5301 RegionSelection rs = get_regions_from_selection_and_entered ();
5303 if (!_session || rs.empty()) {
5307 bool in_command = false;
5309 for (RegionSelection::iterator r = rs.begin(); r != rs.end(); ++r) {
5310 AudioRegionView* const arv = dynamic_cast<AudioRegionView*>(*r);
5315 arv->region()->clear_changes ();
5317 arv->audio_region()->set_scale_amplitude (1.0f);
5320 begin_reversible_command ("reset region gain");
5323 _session->add_command (new StatefulDiffCommand (arv->region()));
5327 commit_reversible_command ();
5332 Editor::reverse_region ()
5338 Reverse rev (*_session);
5339 apply_filter (rev, _("reverse regions"));
5343 Editor::strip_region_silence ()
5349 RegionSelection rs = get_regions_from_selection_and_entered ();
5355 std::list<RegionView*> audio_only;
5357 for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
5358 AudioRegionView* const arv = dynamic_cast<AudioRegionView*> (*i);
5360 audio_only.push_back (arv);
5364 assert (!audio_only.empty());
5366 StripSilenceDialog d (_session, audio_only);
5367 int const r = d.run ();
5371 if (r == Gtk::RESPONSE_OK) {
5372 ARDOUR::AudioIntervalMap silences;
5373 d.silences (silences);
5374 StripSilence s (*_session, silences, d.fade_length());
5376 apply_filter (s, _("strip silence"), &d);
5381 Editor::apply_midi_note_edit_op_to_region (MidiOperator& op, MidiRegionView& mrv)
5383 Evoral::Sequence<Temporal::Beats>::Notes selected;
5384 mrv.selection_as_notelist (selected, true);
5386 vector<Evoral::Sequence<Temporal::Beats>::Notes> v;
5387 v.push_back (selected);
5389 Temporal::Beats pos_beats = Temporal::Beats (mrv.midi_region()->beat()) - mrv.midi_region()->start_beats();
5391 return op (mrv.midi_region()->model(), pos_beats, v);
5395 Editor::apply_midi_note_edit_op (MidiOperator& op, const RegionSelection& rs)
5401 bool in_command = false;
5403 for (RegionSelection::const_iterator r = rs.begin(); r != rs.end(); ) {
5404 RegionSelection::const_iterator tmp = r;
5407 MidiRegionView* const mrv = dynamic_cast<MidiRegionView*> (*r);
5410 Command* cmd = apply_midi_note_edit_op_to_region (op, *mrv);
5413 begin_reversible_command (op.name ());
5417 _session->add_command (cmd);
5425 commit_reversible_command ();
5426 _session->set_dirty ();
5431 Editor::fork_region ()
5433 RegionSelection rs = get_regions_from_selection_and_entered ();
5439 CursorContext::Handle cursor_ctx = CursorContext::create(*this, _cursors->wait);
5440 bool in_command = false;
5444 for (RegionSelection::iterator r = rs.begin(); r != rs.end(); ) {
5445 RegionSelection::iterator tmp = r;
5448 MidiRegionView* const mrv = dynamic_cast<MidiRegionView*>(*r);
5452 boost::shared_ptr<Playlist> playlist = mrv->region()->playlist();
5453 boost::shared_ptr<MidiSource> new_source = _session->create_midi_source_by_stealing_name (mrv->midi_view()->track());
5454 boost::shared_ptr<MidiRegion> newregion = mrv->midi_region()->clone (new_source);
5457 begin_reversible_command (_("Fork Region(s)"));
5460 playlist->clear_changes ();
5461 playlist->replace_region (mrv->region(), newregion, mrv->region()->position());
5462 _session->add_command(new StatefulDiffCommand (playlist));
5464 error << string_compose (_("Could not unlink %1"), mrv->region()->name()) << endmsg;
5472 commit_reversible_command ();
5477 Editor::quantize_region ()
5480 quantize_regions(get_regions_from_selection_and_entered ());
5485 Editor::quantize_regions (const RegionSelection& rs)
5487 if (rs.n_midi_regions() == 0) {
5491 if (!quantize_dialog) {
5492 quantize_dialog = new QuantizeDialog (*this);
5495 if (quantize_dialog->is_mapped()) {
5496 /* in progress already */
5500 quantize_dialog->present ();
5501 const int r = quantize_dialog->run ();
5502 quantize_dialog->hide ();
5504 if (r == Gtk::RESPONSE_OK) {
5505 Quantize quant (quantize_dialog->snap_start(),
5506 quantize_dialog->snap_end(),
5507 quantize_dialog->start_grid_size(),
5508 quantize_dialog->end_grid_size(),
5509 quantize_dialog->strength(),
5510 quantize_dialog->swing(),
5511 quantize_dialog->threshold());
5513 apply_midi_note_edit_op (quant, rs);
5518 Editor::legatize_region (bool shrink_only)
5521 legatize_regions(get_regions_from_selection_and_entered (), shrink_only);
5526 Editor::legatize_regions (const RegionSelection& rs, bool shrink_only)
5528 if (rs.n_midi_regions() == 0) {
5532 Legatize legatize(shrink_only);
5533 apply_midi_note_edit_op (legatize, rs);
5537 Editor::transform_region ()
5540 transform_regions(get_regions_from_selection_and_entered ());
5545 Editor::transform_regions (const RegionSelection& rs)
5547 if (rs.n_midi_regions() == 0) {
5554 const int r = td.run();
5557 if (r == Gtk::RESPONSE_OK) {
5558 Transform transform(td.get());
5559 apply_midi_note_edit_op(transform, rs);
5564 Editor::transpose_region ()
5567 transpose_regions(get_regions_from_selection_and_entered ());
5572 Editor::transpose_regions (const RegionSelection& rs)
5574 if (rs.n_midi_regions() == 0) {
5579 int const r = d.run ();
5581 if (r == RESPONSE_ACCEPT) {
5582 Transpose transpose(d.semitones ());
5583 apply_midi_note_edit_op (transpose, rs);
5588 Editor::insert_patch_change (bool from_context)
5590 RegionSelection rs = get_regions_from_selection_and_entered ();
5596 const samplepos_t p = get_preferred_edit_position (EDIT_IGNORE_NONE, from_context);
5598 /* XXX: bit of a hack; use the MIDNAM from the first selected region;
5599 there may be more than one, but the PatchChangeDialog can only offer
5600 one set of patch menus.
5602 MidiRegionView* first = dynamic_cast<MidiRegionView*> (rs.front ());
5604 Evoral::PatchChange<Temporal::Beats> empty (Temporal::Beats(), 0, 0, 0);
5605 PatchChangeDialog d (0, _session, empty, first->instrument_info(), Gtk::Stock::ADD);
5607 if (d.run() == RESPONSE_CANCEL) {
5611 for (RegionSelection::iterator i = rs.begin (); i != rs.end(); ++i) {
5612 MidiRegionView* const mrv = dynamic_cast<MidiRegionView*> (*i);
5614 if (p >= mrv->region()->first_sample() && p <= mrv->region()->last_sample()) {
5615 mrv->add_patch_change (p - mrv->region()->position(), d.patch ());
5622 Editor::apply_filter (Filter& filter, string command, ProgressReporter* progress)
5624 RegionSelection rs = get_regions_from_selection_and_entered ();
5630 CursorContext::Handle cursor_ctx = CursorContext::create(*this, _cursors->wait);
5631 bool in_command = false;
5636 int const N = rs.size ();
5638 for (RegionSelection::iterator r = rs.begin(); r != rs.end(); ) {
5639 RegionSelection::iterator tmp = r;
5642 AudioRegionView* const arv = dynamic_cast<AudioRegionView*>(*r);
5644 boost::shared_ptr<Playlist> playlist = arv->region()->playlist();
5647 progress->descend (1.0 / N);
5650 if (arv->audio_region()->apply (filter, progress) == 0) {
5652 playlist->clear_changes ();
5653 playlist->clear_owned_changes ();
5656 begin_reversible_command (command);
5660 if (filter.results.empty ()) {
5662 /* no regions returned; remove the old one */
5663 playlist->remove_region (arv->region ());
5667 std::vector<boost::shared_ptr<Region> >::iterator res = filter.results.begin ();
5669 /* first region replaces the old one */
5670 playlist->replace_region (arv->region(), *res, (*res)->position());
5674 while (res != filter.results.end()) {
5675 playlist->add_region (*res, (*res)->position());
5681 /* We might have removed regions, which alters other regions' layering_index,
5682 so we need to do a recursive diff here.
5684 vector<Command*> cmds;
5685 playlist->rdiff (cmds);
5686 _session->add_commands (cmds);
5688 _session->add_command(new StatefulDiffCommand (playlist));
5692 progress->ascend ();
5701 commit_reversible_command ();
5706 Editor::external_edit_region ()
5712 Editor::reset_region_gain_envelopes ()
5714 RegionSelection rs = get_regions_from_selection_and_entered ();
5716 if (!_session || rs.empty()) {
5720 bool in_command = false;
5722 for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
5723 AudioRegionView* const arv = dynamic_cast<AudioRegionView*>(*i);
5725 boost::shared_ptr<AutomationList> alist (arv->audio_region()->envelope());
5726 XMLNode& before (alist->get_state());
5728 arv->audio_region()->set_default_envelope ();
5731 begin_reversible_command (_("reset region gain"));
5734 _session->add_command (new MementoCommand<AutomationList>(*arv->audio_region()->envelope().get(), &before, &alist->get_state()));
5739 commit_reversible_command ();
5744 Editor::set_region_gain_visibility (RegionView* rv)
5746 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (rv);
5748 arv->update_envelope_visibility();
5753 Editor::set_gain_envelope_visibility ()
5759 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
5760 AudioTimeAxisView* v = dynamic_cast<AudioTimeAxisView*>(*i);
5762 v->audio_view()->foreach_regionview (sigc::mem_fun (this, &Editor::set_region_gain_visibility));
5768 Editor::toggle_gain_envelope_active ()
5770 if (_ignore_region_action) {
5774 RegionSelection rs = get_regions_from_selection_and_entered ();
5776 if (!_session || rs.empty()) {
5780 bool in_command = false;
5782 for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
5783 AudioRegionView* const arv = dynamic_cast<AudioRegionView*>(*i);
5785 arv->region()->clear_changes ();
5786 arv->audio_region()->set_envelope_active (!arv->audio_region()->envelope_active());
5789 begin_reversible_command (_("region gain envelope active"));
5792 _session->add_command (new StatefulDiffCommand (arv->region()));
5797 commit_reversible_command ();
5802 Editor::toggle_region_lock ()
5804 if (_ignore_region_action) {
5808 RegionSelection rs = get_regions_from_selection_and_entered ();
5810 if (!_session || rs.empty()) {
5814 begin_reversible_command (_("toggle region lock"));
5816 for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
5817 (*i)->region()->clear_changes ();
5818 (*i)->region()->set_locked (!(*i)->region()->locked());
5819 _session->add_command (new StatefulDiffCommand ((*i)->region()));
5822 commit_reversible_command ();
5826 Editor::toggle_region_video_lock ()
5828 if (_ignore_region_action) {
5832 RegionSelection rs = get_regions_from_selection_and_entered ();
5834 if (!_session || rs.empty()) {
5838 begin_reversible_command (_("Toggle Video Lock"));
5840 for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
5841 (*i)->region()->clear_changes ();
5842 (*i)->region()->set_video_locked (!(*i)->region()->video_locked());
5843 _session->add_command (new StatefulDiffCommand ((*i)->region()));
5846 commit_reversible_command ();
5850 Editor::toggle_region_lock_style ()
5852 if (_ignore_region_action) {
5856 RegionSelection rs = get_regions_from_selection_and_entered ();
5858 if (!_session || rs.empty()) {
5862 Glib::RefPtr<ToggleAction> a = Glib::RefPtr<ToggleAction>::cast_dynamic (_region_actions->get_action("toggle-region-lock-style"));
5863 vector<Widget*> proxies = a->get_proxies();
5864 Gtk::CheckMenuItem* cmi = dynamic_cast<Gtk::CheckMenuItem*> (proxies.front());
5868 begin_reversible_command (_("toggle region lock style"));
5870 for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
5871 (*i)->region()->clear_changes ();
5872 PositionLockStyle const ns = ((*i)->region()->position_lock_style() == AudioTime && !cmi->get_inconsistent()) ? MusicTime : AudioTime;
5873 (*i)->region()->set_position_lock_style (ns);
5874 _session->add_command (new StatefulDiffCommand ((*i)->region()));
5877 commit_reversible_command ();
5881 Editor::toggle_opaque_region ()
5883 if (_ignore_region_action) {
5887 RegionSelection rs = get_regions_from_selection_and_entered ();
5889 if (!_session || rs.empty()) {
5893 begin_reversible_command (_("change region opacity"));
5895 for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
5896 (*i)->region()->clear_changes ();
5897 (*i)->region()->set_opaque (!(*i)->region()->opaque());
5898 _session->add_command (new StatefulDiffCommand ((*i)->region()));
5901 commit_reversible_command ();
5905 Editor::toggle_record_enable ()
5907 bool new_state = false;
5909 for (TrackSelection::iterator i = selection->tracks.begin(); i != selection->tracks.end(); ++i) {
5910 RouteTimeAxisView *rtav = dynamic_cast<RouteTimeAxisView *>(*i);
5913 if (!rtav->is_track())
5917 new_state = !rtav->track()->rec_enable_control()->get_value();
5921 rtav->track()->rec_enable_control()->set_value (new_state, Controllable::UseGroup);
5926 tracklist_to_stripables (TrackViewList list)
5930 for (TrackSelection::iterator i = list.begin(); i != list.end(); ++i) {
5931 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> ((*i));
5933 if (rtv && rtv->is_track()) {
5934 ret.push_back (rtv->track());
5942 Editor::play_solo_selection (bool restart)
5944 //note: session::solo_selection takes care of invalidating the region playlist
5946 if ((!selection->tracks.empty()) && selection->time.length() > 0) { //a range is selected; solo the tracks and roll
5948 StripableList sl = tracklist_to_stripables (selection->tracks);
5949 _session->solo_selection (sl, true);
5952 samplepos_t start = selection->time.start();
5953 samplepos_t end = selection->time.end_sample();
5954 _session->request_bounded_roll (start, end);
5956 } else if (! selection->tracks.empty()) { //no range is selected, but tracks are selected; solo the tracks and roll
5957 StripableList sl = tracklist_to_stripables (selection->tracks);
5958 _session->solo_selection (sl, true);
5959 _session->request_cancel_play_range();
5960 transition_to_rolling (true);
5962 } else if (! selection->regions.empty()) { //solo any tracks with selected regions, and roll
5963 StripableList sl = tracklist_to_stripables (get_tracks_for_range_action());
5964 _session->solo_selection (sl, true);
5965 _session->request_cancel_play_range();
5966 transition_to_rolling (true);
5968 _session->request_cancel_play_range();
5969 transition_to_rolling (true); //no selection. just roll.
5974 Editor::toggle_solo ()
5976 bool new_state = false;
5978 boost::shared_ptr<ControlList> cl (new ControlList);
5980 for (TrackSelection::iterator i = selection->tracks.begin(); i != selection->tracks.end(); ++i) {
5981 StripableTimeAxisView *stav = dynamic_cast<StripableTimeAxisView *>(*i);
5983 if (!stav || !stav->stripable()->solo_control()) {
5988 new_state = !stav->stripable()->solo_control()->soloed ();
5992 cl->push_back (stav->stripable()->solo_control());
5995 _session->set_controls (cl, new_state ? 1.0 : 0.0, Controllable::UseGroup);
5999 Editor::toggle_mute ()
6001 bool new_state = false;
6003 boost::shared_ptr<ControlList> cl (new ControlList);
6005 for (TrackSelection::iterator i = selection->tracks.begin(); i != selection->tracks.end(); ++i) {
6006 StripableTimeAxisView *stav = dynamic_cast<StripableTimeAxisView *>(*i);
6008 if (!stav || !stav->stripable()->mute_control()) {
6013 new_state = !stav->stripable()->mute_control()->muted();
6017 cl->push_back (stav->stripable()->mute_control());
6020 _session->set_controls (cl, new_state, Controllable::UseGroup);
6024 Editor::toggle_solo_isolate ()
6030 Editor::fade_range ()
6032 TrackViewList ts = selection->tracks.filter_to_unique_playlists ();
6034 begin_reversible_command (_("fade range"));
6036 for (TrackViewList::iterator i = ts.begin(); i != ts.end(); ++i) {
6037 (*i)->fade_range (selection->time);
6040 commit_reversible_command ();
6045 Editor::set_fade_length (bool in)
6047 RegionSelection rs = get_regions_from_selection_and_entered ();
6053 /* we need a region to measure the offset from the start */
6055 RegionView* rv = rs.front ();
6057 samplepos_t pos = get_preferred_edit_position();
6061 if (pos > rv->region()->last_sample() || pos < rv->region()->first_sample()) {
6062 /* edit point is outside the relevant region */
6067 if (pos <= rv->region()->position()) {
6071 len = pos - rv->region()->position();
6072 cmd = _("set fade in length");
6074 if (pos >= rv->region()->last_sample()) {
6078 len = rv->region()->last_sample() - pos;
6079 cmd = _("set fade out length");
6082 bool in_command = false;
6084 for (RegionSelection::iterator x = rs.begin(); x != rs.end(); ++x) {
6085 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (*x);
6091 boost::shared_ptr<AutomationList> alist;
6093 alist = tmp->audio_region()->fade_in();
6095 alist = tmp->audio_region()->fade_out();
6098 XMLNode &before = alist->get_state();
6101 tmp->audio_region()->set_fade_in_length (len);
6102 tmp->audio_region()->set_fade_in_active (true);
6104 tmp->audio_region()->set_fade_out_length (len);
6105 tmp->audio_region()->set_fade_out_active (true);
6109 begin_reversible_command (cmd);
6112 XMLNode &after = alist->get_state();
6113 _session->add_command(new MementoCommand<AutomationList>(*alist, &before, &after));
6117 commit_reversible_command ();
6122 Editor::set_fade_in_shape (FadeShape shape)
6124 RegionSelection rs = get_regions_from_selection_and_entered ();
6129 bool in_command = false;
6131 for (RegionSelection::iterator x = rs.begin(); x != rs.end(); ++x) {
6132 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (*x);
6138 boost::shared_ptr<AutomationList> alist = tmp->audio_region()->fade_in();
6139 XMLNode &before = alist->get_state();
6141 tmp->audio_region()->set_fade_in_shape (shape);
6144 begin_reversible_command (_("set fade in shape"));
6147 XMLNode &after = alist->get_state();
6148 _session->add_command(new MementoCommand<AutomationList>(*alist.get(), &before, &after));
6152 commit_reversible_command ();
6157 Editor::set_fade_out_shape (FadeShape shape)
6159 RegionSelection rs = get_regions_from_selection_and_entered ();
6164 bool in_command = false;
6166 for (RegionSelection::iterator x = rs.begin(); x != rs.end(); ++x) {
6167 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (*x);
6173 boost::shared_ptr<AutomationList> alist = tmp->audio_region()->fade_out();
6174 XMLNode &before = alist->get_state();
6176 tmp->audio_region()->set_fade_out_shape (shape);
6179 begin_reversible_command (_("set fade out shape"));
6182 XMLNode &after = alist->get_state();
6183 _session->add_command(new MementoCommand<AutomationList>(*alist.get(), &before, &after));
6187 commit_reversible_command ();
6192 Editor::set_fade_in_active (bool yn)
6194 RegionSelection rs = get_regions_from_selection_and_entered ();
6199 bool in_command = false;
6201 for (RegionSelection::iterator x = rs.begin(); x != rs.end(); ++x) {
6202 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (*x);
6209 boost::shared_ptr<AudioRegion> ar (tmp->audio_region());
6211 ar->clear_changes ();
6212 ar->set_fade_in_active (yn);
6215 begin_reversible_command (_("set fade in active"));
6218 _session->add_command (new StatefulDiffCommand (ar));
6222 commit_reversible_command ();
6227 Editor::set_fade_out_active (bool yn)
6229 RegionSelection rs = get_regions_from_selection_and_entered ();
6234 bool in_command = false;
6236 for (RegionSelection::iterator x = rs.begin(); x != rs.end(); ++x) {
6237 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (*x);
6243 boost::shared_ptr<AudioRegion> ar (tmp->audio_region());
6245 ar->clear_changes ();
6246 ar->set_fade_out_active (yn);
6249 begin_reversible_command (_("set fade out active"));
6252 _session->add_command(new StatefulDiffCommand (ar));
6256 commit_reversible_command ();
6261 Editor::toggle_region_fades (int dir)
6263 if (_ignore_region_action) {
6267 boost::shared_ptr<AudioRegion> ar;
6270 RegionSelection rs = get_regions_from_selection_and_entered ();
6276 RegionSelection::iterator i;
6277 for (i = rs.begin(); i != rs.end(); ++i) {
6278 if ((ar = boost::dynamic_pointer_cast<AudioRegion>((*i)->region())) != 0) {
6280 yn = ar->fade_out_active ();
6282 yn = ar->fade_in_active ();
6288 if (i == rs.end()) {
6292 /* XXX should this undo-able? */
6293 bool in_command = false;
6295 for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
6296 if ((ar = boost::dynamic_pointer_cast<AudioRegion>((*i)->region())) == 0) {
6299 ar->clear_changes ();
6301 if (dir == 1 || dir == 0) {
6302 ar->set_fade_in_active (!yn);
6305 if (dir == -1 || dir == 0) {
6306 ar->set_fade_out_active (!yn);
6309 begin_reversible_command (_("toggle fade active"));
6312 _session->add_command(new StatefulDiffCommand (ar));
6316 commit_reversible_command ();
6321 /** Update region fade visibility after its configuration has been changed */
6323 Editor::update_region_fade_visibility ()
6325 bool _fade_visibility = _session->config.get_show_region_fades ();
6327 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
6328 AudioTimeAxisView* v = dynamic_cast<AudioTimeAxisView*>(*i);
6330 if (_fade_visibility) {
6331 v->audio_view()->show_all_fades ();
6333 v->audio_view()->hide_all_fades ();
6340 Editor::set_edit_point ()
6343 MusicSample where (0, 0);
6345 if (!mouse_sample (where.sample, ignored)) {
6351 if (selection->markers.empty()) {
6353 mouse_add_new_marker (where.sample);
6358 Location* loc = find_location_from_marker (selection->markers.front(), ignored);
6361 loc->move_to (where.sample, where.division);
6367 Editor::set_playhead_cursor ()
6369 if (entered_marker) {
6370 _session->request_locate (entered_marker->position(), _session->transport_rolling());
6372 MusicSample where (0, 0);
6375 if (!mouse_sample (where.sample, ignored)) {
6382 _session->request_locate (where.sample, _session->transport_rolling());
6386 //not sure what this was for; remove it for now.
6387 // if (UIConfiguration::instance().get_follow_edits() && (!_session || !_session->config.get_external_sync())) {
6388 // cancel_time_selection();
6394 Editor::split_region ()
6396 if (_dragging_playhead) {
6398 } else if (_drags->active ()) {
6399 /*any other kind of drag, bail out so we avoid Undo snafu*/
6403 //if a range is selected, separate it
6404 if (!selection->time.empty()) {
6405 separate_regions_between (selection->time);
6409 //if no range was selected, try to find some regions to split
6410 if (current_mouse_mode() == MouseObject || current_mouse_mode() == MouseRange ) { //don't try this for Internal Edit, Stretch, Draw, etc.
6412 RegionSelection rs = get_regions_from_selection_and_edit_point ();
6413 const samplepos_t pos = get_preferred_edit_position();
6414 const int32_t division = get_grid_music_divisions (0);
6415 MusicSample where (pos, division);
6421 split_regions_at (where, rs);
6427 Editor::select_next_stripable (bool routes_only)
6429 _session->selection().select_next_stripable (false, routes_only);
6433 Editor::select_prev_stripable (bool routes_only)
6435 _session->selection().select_prev_stripable (false, routes_only);
6439 Editor::set_loop_from_selection (bool play)
6441 if (_session == 0) {
6445 samplepos_t start, end;
6446 if (!get_selection_extents (start, end))
6449 set_loop_range (start, end, _("set loop range from selection"));
6452 _session->request_play_loop (true, true);
6457 Editor::set_loop_from_region (bool play)
6459 samplepos_t start, end;
6460 if (!get_selection_extents (start, end))
6463 set_loop_range (start, end, _("set loop range from region"));
6466 _session->request_locate (start, true);
6467 _session->request_play_loop (true);
6472 Editor::set_punch_from_selection ()
6474 if (_session == 0) {
6478 samplepos_t start, end;
6479 if (!get_selection_extents (start, end))
6482 set_punch_range (start, end, _("set punch range from selection"));
6486 Editor::set_auto_punch_range ()
6488 // auto punch in/out button from a single button
6489 // If Punch In is unset, set punch range from playhead to end, enable punch in
6490 // If Punch In is set, the next punch sets Punch Out, unless the playhead has been
6491 // rewound beyond the Punch In marker, in which case that marker will be moved back
6492 // to the current playhead position.
6493 // If punch out is set, it clears the punch range and Punch In/Out buttons
6495 if (_session == 0) {
6499 Location* tpl = transport_punch_location();
6500 samplepos_t now = playhead_cursor->current_sample();
6501 samplepos_t begin = now;
6502 samplepos_t end = _session->current_end_sample();
6504 if (!_session->config.get_punch_in()) {
6505 // First Press - set punch in and create range from here to eternity
6506 set_punch_range (begin, end, _("Auto Punch In"));
6507 _session->config.set_punch_in(true);
6508 } else if (tpl && !_session->config.get_punch_out()) {
6509 // Second press - update end range marker and set punch_out
6510 if (now < tpl->start()) {
6511 // playhead has been rewound - move start back and pretend nothing happened
6513 set_punch_range (begin, end, _("Auto Punch In/Out"));
6515 // normal case for 2nd press - set the punch out
6516 end = playhead_cursor->current_sample ();
6517 set_punch_range (tpl->start(), now, _("Auto Punch In/Out"));
6518 _session->config.set_punch_out(true);
6521 if (_session->config.get_punch_out()) {
6522 _session->config.set_punch_out(false);
6525 if (_session->config.get_punch_in()) {
6526 _session->config.set_punch_in(false);
6531 // third press - unset punch in/out and remove range
6532 _session->locations()->remove(tpl);
6539 Editor::set_session_extents_from_selection ()
6541 if (_session == 0) {
6545 samplepos_t start, end;
6546 if (!get_selection_extents (start, end))
6550 if ((loc = _session->locations()->session_range_location()) == 0) {
6551 _session->set_session_extents (start, end); // this will create a new session range; no need for UNDO
6553 XMLNode &before = loc->get_state();
6555 _session->set_session_extents (start, end);
6557 XMLNode &after = loc->get_state();
6559 begin_reversible_command (_("set session start/end from selection"));
6561 _session->add_command (new MementoCommand<Location>(*loc, &before, &after));
6563 commit_reversible_command ();
6566 _session->set_end_is_free (false);
6570 Editor::set_punch_start_from_edit_point ()
6574 MusicSample start (0, 0);
6575 samplepos_t end = max_samplepos;
6577 //use the existing punch end, if any
6578 Location* tpl = transport_punch_location();
6583 if ((_edit_point == EditAtPlayhead) && _session->transport_rolling()) {
6584 start.sample = _session->audible_sample();
6586 start.sample = get_preferred_edit_position();
6589 //if there's not already a sensible selection endpoint, go "forever"
6590 if (start.sample > end) {
6591 end = max_samplepos;
6594 set_punch_range (start.sample, end, _("set punch start from EP"));
6600 Editor::set_punch_end_from_edit_point ()
6604 samplepos_t start = 0;
6605 MusicSample end (max_samplepos, 0);
6607 //use the existing punch start, if any
6608 Location* tpl = transport_punch_location();
6610 start = tpl->start();
6613 if ((_edit_point == EditAtPlayhead) && _session->transport_rolling()) {
6614 end.sample = _session->audible_sample();
6616 end.sample = get_preferred_edit_position();
6619 set_punch_range (start, end.sample, _("set punch end from EP"));
6625 Editor::set_loop_start_from_edit_point ()
6629 MusicSample start (0, 0);
6630 samplepos_t end = max_samplepos;
6632 //use the existing loop end, if any
6633 Location* tpl = transport_loop_location();
6638 if ((_edit_point == EditAtPlayhead) && _session->transport_rolling()) {
6639 start.sample = _session->audible_sample();
6641 start.sample = get_preferred_edit_position();
6644 //if there's not already a sensible selection endpoint, go "forever"
6645 if (start.sample > end) {
6646 end = max_samplepos;
6649 set_loop_range (start.sample, end, _("set loop start from EP"));
6655 Editor::set_loop_end_from_edit_point ()
6659 samplepos_t start = 0;
6660 MusicSample end (max_samplepos, 0);
6662 //use the existing loop start, if any
6663 Location* tpl = transport_loop_location();
6665 start = tpl->start();
6668 if ((_edit_point == EditAtPlayhead) && _session->transport_rolling()) {
6669 end.sample = _session->audible_sample();
6671 end.sample = get_preferred_edit_position();
6674 set_loop_range (start, end.sample, _("set loop end from EP"));
6679 Editor::set_punch_from_region ()
6681 samplepos_t start, end;
6682 if (!get_selection_extents (start, end))
6685 set_punch_range (start, end, _("set punch range from region"));
6689 Editor::pitch_shift_region ()
6691 RegionSelection rs = get_regions_from_selection_and_entered ();
6693 RegionSelection audio_rs;
6694 for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
6695 if (dynamic_cast<AudioRegionView*> (*i)) {
6696 audio_rs.push_back (*i);
6700 if (audio_rs.empty()) {
6704 pitch_shift (audio_rs, 1.2);
6708 Editor::set_tempo_from_region ()
6710 RegionSelection rs = get_regions_from_selection_and_entered ();
6712 if (!_session || rs.empty()) {
6716 RegionView* rv = rs.front();
6718 define_one_bar (rv->region()->position(), rv->region()->last_sample() + 1);
6722 Editor::use_range_as_bar ()
6724 samplepos_t start, end;
6725 if (get_edit_op_range (start, end)) {
6726 define_one_bar (start, end);
6731 Editor::define_one_bar (samplepos_t start, samplepos_t end)
6733 samplepos_t length = end - start;
6735 const Meter& m (_session->tempo_map().meter_at_sample (start));
6737 /* length = 1 bar */
6739 /* We're going to deliver a constant tempo here,
6740 so we can use samples per beat to determine length.
6741 now we want samples per beat.
6742 we have samples per bar, and beats per bar, so ...
6745 /* XXXX METER MATH */
6747 double samples_per_beat = length / m.divisions_per_bar();
6749 /* beats per minute = */
6751 double beats_per_minute = (_session->sample_rate() * 60.0) / samples_per_beat;
6753 /* now decide whether to:
6755 (a) set global tempo
6756 (b) add a new tempo marker
6760 const TempoSection& t (_session->tempo_map().tempo_section_at_sample (start));
6762 bool do_global = false;
6764 if ((_session->tempo_map().n_tempos() == 1) && (_session->tempo_map().n_meters() == 1)) {
6766 /* only 1 tempo & 1 meter: ask if the user wants to set the tempo
6767 at the start, or create a new marker
6770 vector<string> options;
6771 options.push_back (_("Cancel"));
6772 options.push_back (_("Add new marker"));
6773 options.push_back (_("Set global tempo"));
6776 _("Define one bar"),
6777 _("Do you want to set the global tempo or add a new tempo marker?"),
6781 c.set_default_response (2);
6797 /* more than 1 tempo and/or meter section already, go ahead do the "usual":
6798 if the marker is at the region starter, change it, otherwise add
6803 begin_reversible_command (_("set tempo from region"));
6804 XMLNode& before (_session->tempo_map().get_state());
6807 _session->tempo_map().change_initial_tempo (beats_per_minute, t.note_type(), t.end_note_types_per_minute());
6808 } else if (t.sample() == start) {
6809 _session->tempo_map().change_existing_tempo_at (start, beats_per_minute, t.note_type(), t.end_note_types_per_minute());
6811 /* constant tempo */
6812 const Tempo tempo (beats_per_minute, t.note_type());
6813 _session->tempo_map().add_tempo (tempo, 0.0, start, AudioTime);
6816 XMLNode& after (_session->tempo_map().get_state());
6818 _session->add_command (new MementoCommand<TempoMap>(_session->tempo_map(), &before, &after));
6819 commit_reversible_command ();
6823 Editor::split_region_at_transients ()
6825 AnalysisFeatureList positions;
6827 RegionSelection rs = get_regions_from_selection_and_entered ();
6829 if (!_session || rs.empty()) {
6833 begin_reversible_command (_("split regions"));
6835 for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ) {
6837 RegionSelection::iterator tmp;
6842 boost::shared_ptr<AudioRegion> ar = boost::dynamic_pointer_cast<AudioRegion> ((*i)->region());
6845 ar->transients (positions);
6846 split_region_at_points ((*i)->region(), positions, true);
6853 commit_reversible_command ();
6858 Editor::split_region_at_points (boost::shared_ptr<Region> r, AnalysisFeatureList& positions, bool can_ferret, bool select_new)
6860 bool use_rhythmic_rodent = false;
6862 boost::shared_ptr<Playlist> pl = r->playlist();
6864 list<boost::shared_ptr<Region> > new_regions;
6870 if (positions.empty()) {
6874 if (positions.size() > 20 && can_ferret) {
6875 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);
6876 MessageDialog msg (msgstr,
6879 Gtk::BUTTONS_OK_CANCEL);
6882 msg.add_button (_("Call for the Ferret!"), RESPONSE_APPLY);
6883 msg.set_secondary_text (_("Press OK to continue with this split operation\nor ask the Ferret dialog to tune the analysis"));
6885 msg.set_secondary_text (_("Press OK to continue with this split operation"));
6888 msg.set_title (_("Excessive split?"));
6891 int response = msg.run();
6897 case RESPONSE_APPLY:
6898 use_rhythmic_rodent = true;
6905 if (use_rhythmic_rodent) {
6906 show_rhythm_ferret ();
6910 AnalysisFeatureList::const_iterator x;
6912 pl->clear_changes ();
6913 pl->clear_owned_changes ();
6915 x = positions.begin();
6917 if (x == positions.end()) {
6922 pl->remove_region (r);
6924 samplepos_t pos = 0;
6926 samplepos_t rstart = r->first_sample ();
6927 samplepos_t rend = r->last_sample ();
6929 while (x != positions.end()) {
6931 /* deal with positons that are out of scope of present region bounds */
6932 if (*x <= rstart || *x > rend) {
6937 /* file start = original start + how far we from the initial position ? */
6939 samplepos_t file_start = r->start() + pos;
6941 /* length = next position - current position */
6943 samplepos_t len = (*x) - pos - rstart;
6945 /* XXX we do we really want to allow even single-sample regions?
6946 * shouldn't we have some kind of lower limit on region size?
6955 if (RegionFactory::region_name (new_name, r->name())) {
6959 /* do NOT announce new regions 1 by one, just wait till they are all done */
6963 plist.add (ARDOUR::Properties::start, file_start);
6964 plist.add (ARDOUR::Properties::length, len);
6965 plist.add (ARDOUR::Properties::name, new_name);
6966 plist.add (ARDOUR::Properties::layer, 0);
6967 // TODO set transients_offset
6969 boost::shared_ptr<Region> nr = RegionFactory::create (r->sources(), plist, false);
6970 /* because we set annouce to false, manually add the new region to the
6973 RegionFactory::map_add (nr);
6975 pl->add_region (nr, rstart + pos);
6978 new_regions.push_front(nr);
6987 RegionFactory::region_name (new_name, r->name());
6989 /* Add the final region */
6992 plist.add (ARDOUR::Properties::start, r->start() + pos);
6993 plist.add (ARDOUR::Properties::length, r->last_sample() - (r->position() + pos) + 1);
6994 plist.add (ARDOUR::Properties::name, new_name);
6995 plist.add (ARDOUR::Properties::layer, 0);
6997 boost::shared_ptr<Region> nr = RegionFactory::create (r->sources(), plist, false);
6998 /* because we set annouce to false, manually add the new region to the
7001 RegionFactory::map_add (nr);
7002 pl->add_region (nr, r->position() + pos);
7005 new_regions.push_front(nr);
7010 /* We might have removed regions, which alters other regions' layering_index,
7011 so we need to do a recursive diff here.
7013 vector<Command*> cmds;
7015 _session->add_commands (cmds);
7017 _session->add_command (new StatefulDiffCommand (pl));
7021 for (list<boost::shared_ptr<Region> >::iterator i = new_regions.begin(); i != new_regions.end(); ++i){
7022 set_selected_regionview_from_region_list ((*i), Selection::Add);
7028 Editor::place_transient()
7034 RegionSelection rs = get_regions_from_selection_and_edit_point ();
7040 samplepos_t where = get_preferred_edit_position();
7042 begin_reversible_command (_("place transient"));
7044 for (RegionSelection::iterator r = rs.begin(); r != rs.end(); ++r) {
7045 (*r)->region()->add_transient(where);
7048 commit_reversible_command ();
7052 Editor::remove_transient(ArdourCanvas::Item* item)
7058 ArdourCanvas::Line* _line = reinterpret_cast<ArdourCanvas::Line*> (item);
7061 AudioRegionView* _arv = reinterpret_cast<AudioRegionView*> (item->get_data ("regionview"));
7062 _arv->remove_transient (*(float*) _line->get_data ("position"));
7066 Editor::snap_regions_to_grid ()
7068 list <boost::shared_ptr<Playlist > > used_playlists;
7070 RegionSelection rs = get_regions_from_selection_and_entered ();
7072 if (!_session || rs.empty()) {
7076 begin_reversible_command (_("snap regions to grid"));
7078 for (RegionSelection::iterator r = rs.begin(); r != rs.end(); ++r) {
7080 boost::shared_ptr<Playlist> pl = (*r)->region()->playlist();
7082 if (!pl->frozen()) {
7083 /* we haven't seen this playlist before */
7085 /* remember used playlists so we can thaw them later */
7086 used_playlists.push_back(pl);
7089 (*r)->region()->clear_changes ();
7091 MusicSample start ((*r)->region()->first_sample (), 0);
7092 snap_to (start, RoundNearest, SnapToGrid_Unscaled, true);
7093 (*r)->region()->set_position (start.sample, start.division);
7094 _session->add_command(new StatefulDiffCommand ((*r)->region()));
7097 while (used_playlists.size() > 0) {
7098 list <boost::shared_ptr<Playlist > >::iterator i = used_playlists.begin();
7100 used_playlists.pop_front();
7103 commit_reversible_command ();
7107 Editor::close_region_gaps ()
7109 list <boost::shared_ptr<Playlist > > used_playlists;
7111 RegionSelection rs = get_regions_from_selection_and_entered ();
7113 if (!_session || rs.empty()) {
7117 Dialog dialog (_("Close Region Gaps"));
7120 table.set_spacings (12);
7121 table.set_border_width (12);
7122 Label* l = manage (left_aligned_label (_("Crossfade length")));
7123 table.attach (*l, 0, 1, 0, 1);
7125 SpinButton spin_crossfade (1, 0);
7126 spin_crossfade.set_range (0, 15);
7127 spin_crossfade.set_increments (1, 1);
7128 spin_crossfade.set_value (5);
7129 table.attach (spin_crossfade, 1, 2, 0, 1);
7131 table.attach (*manage (new Label (_("ms"))), 2, 3, 0, 1);
7133 l = manage (left_aligned_label (_("Pull-back length")));
7134 table.attach (*l, 0, 1, 1, 2);
7136 SpinButton spin_pullback (1, 0);
7137 spin_pullback.set_range (0, 100);
7138 spin_pullback.set_increments (1, 1);
7139 spin_pullback.set_value(30);
7140 table.attach (spin_pullback, 1, 2, 1, 2);
7142 table.attach (*manage (new Label (_("ms"))), 2, 3, 1, 2);
7144 dialog.get_vbox()->pack_start (table);
7145 dialog.add_button (Stock::CANCEL, RESPONSE_CANCEL);
7146 dialog.add_button (_("Ok"), RESPONSE_ACCEPT);
7149 if (dialog.run () == RESPONSE_CANCEL) {
7153 samplepos_t crossfade_len = spin_crossfade.get_value();
7154 samplepos_t pull_back_samples = spin_pullback.get_value();
7156 crossfade_len = lrintf (crossfade_len * _session->sample_rate()/1000);
7157 pull_back_samples = lrintf (pull_back_samples * _session->sample_rate()/1000);
7159 /* Iterate over the region list and make adjacent regions overlap by crossfade_len_ms */
7161 begin_reversible_command (_("close region gaps"));
7164 boost::shared_ptr<Region> last_region;
7166 rs.sort_by_position_and_track();
7168 for (RegionSelection::iterator r = rs.begin(); r != rs.end(); ++r) {
7170 boost::shared_ptr<Playlist> pl = (*r)->region()->playlist();
7172 if (!pl->frozen()) {
7173 /* we haven't seen this playlist before */
7175 /* remember used playlists so we can thaw them later */
7176 used_playlists.push_back(pl);
7180 samplepos_t position = (*r)->region()->position();
7182 if (idx == 0 || position < last_region->position()){
7183 last_region = (*r)->region();
7188 (*r)->region()->clear_changes ();
7189 (*r)->region()->trim_front((position - pull_back_samples));
7191 last_region->clear_changes ();
7192 last_region->trim_end ((position - pull_back_samples + crossfade_len));
7194 _session->add_command (new StatefulDiffCommand ((*r)->region()));
7195 _session->add_command (new StatefulDiffCommand (last_region));
7197 last_region = (*r)->region();
7201 while (used_playlists.size() > 0) {
7202 list <boost::shared_ptr<Playlist > >::iterator i = used_playlists.begin();
7204 used_playlists.pop_front();
7207 commit_reversible_command ();
7211 Editor::tab_to_transient (bool forward)
7213 AnalysisFeatureList positions;
7215 RegionSelection rs = get_regions_from_selection_and_entered ();
7221 samplepos_t pos = _session->audible_sample ();
7223 if (!selection->tracks.empty()) {
7225 /* don't waste time searching for transients in duplicate playlists.
7228 TrackViewList ts = selection->tracks.filter_to_unique_playlists ();
7230 for (TrackViewList::iterator t = ts.begin(); t != ts.end(); ++t) {
7232 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (*t);
7235 boost::shared_ptr<Track> tr = rtv->track();
7237 boost::shared_ptr<Playlist> pl = tr->playlist ();
7239 samplepos_t result = pl->find_next_transient (pos, forward ? 1 : -1);
7242 positions.push_back (result);
7255 for (RegionSelection::iterator r = rs.begin(); r != rs.end(); ++r) {
7256 (*r)->region()->get_transients (positions);
7260 TransientDetector::cleanup_transients (positions, _session->sample_rate(), 3.0);
7263 AnalysisFeatureList::iterator x;
7265 for (x = positions.begin(); x != positions.end(); ++x) {
7271 if (x != positions.end ()) {
7272 _session->request_locate (*x);
7276 AnalysisFeatureList::reverse_iterator x;
7278 for (x = positions.rbegin(); x != positions.rend(); ++x) {
7284 if (x != positions.rend ()) {
7285 _session->request_locate (*x);
7291 Editor::playhead_forward_to_grid ()
7297 MusicSample pos (playhead_cursor->current_sample (), 0);
7299 if ( _grid_type == GridTypeNone) {
7300 if (pos.sample < max_samplepos - current_page_samples()*0.1) {
7301 pos.sample += current_page_samples()*0.1;
7302 _session->request_locate (pos.sample);
7304 _session->request_locate (0);
7308 if (pos.sample < max_samplepos - 1) {
7310 pos = snap_to_grid (pos, RoundUpAlways, SnapToGrid_Scaled);
7311 _session->request_locate (pos.sample);
7316 /* keep PH visible in window */
7317 if (pos.sample > (_leftmost_sample + current_page_samples() *0.9)) {
7318 reset_x_origin (pos.sample - (current_page_samples()*0.9));
7324 Editor::playhead_backward_to_grid ()
7330 MusicSample pos (playhead_cursor->current_sample (), 0);
7332 if ( _grid_type == GridTypeNone) {
7333 if ( pos.sample > current_page_samples()*0.1 ) {
7334 pos.sample -= current_page_samples()*0.1;
7335 _session->request_locate (pos.sample);
7337 _session->request_locate (0);
7341 if (pos.sample > 2) {
7343 pos = snap_to_grid (pos, RoundDownAlways, SnapToGrid_Scaled);
7346 //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...
7347 //also see: jump_backward_to_mark
7348 if (_session->transport_rolling()) {
7349 if ((playhead_cursor->current_sample() - pos.sample) < _session->sample_rate()/2) {
7350 pos = snap_to_grid (pos, RoundDownAlways, SnapToGrid_Scaled);
7354 _session->request_locate (pos.sample, _session->transport_rolling());
7357 /* keep PH visible in window */
7358 if (pos.sample < (_leftmost_sample + current_page_samples() *0.1)) {
7359 reset_x_origin (pos.sample - (current_page_samples()*0.1));
7364 Editor::set_track_height (Height h)
7366 TrackSelection& ts (selection->tracks);
7368 for (TrackSelection::iterator x = ts.begin(); x != ts.end(); ++x) {
7369 (*x)->set_height_enum (h);
7374 Editor::toggle_tracks_active ()
7376 TrackSelection& ts (selection->tracks);
7378 bool target = false;
7384 for (TrackSelection::iterator x = ts.begin(); x != ts.end(); ++x) {
7385 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*>(*x);
7389 target = !rtv->_route->active();
7392 rtv->_route->set_active (target, this);
7398 Editor::remove_tracks ()
7400 /* this will delete GUI objects that may be the subject of an event
7401 handler in which this method is called. Defer actual deletion to the
7402 next idle callback, when all event handling is finished.
7404 Glib::signal_idle().connect (sigc::mem_fun (*this, &Editor::idle_remove_tracks));
7408 Editor::idle_remove_tracks ()
7410 Session::StateProtector sp (_session);
7412 return false; /* do not call again */
7416 Editor::_remove_tracks ()
7418 TrackSelection& ts (selection->tracks);
7424 if (!ARDOUR_UI_UTILS::engine_is_running ()) {
7428 vector<string> choices;
7433 const char* trackstr;
7436 vector<boost::shared_ptr<Route> > routes;
7437 vector<boost::shared_ptr<VCA> > vcas;
7438 bool special_bus = false;
7440 for (TrackSelection::iterator x = ts.begin(); x != ts.end(); ++x) {
7441 VCATimeAxisView* vtv = dynamic_cast<VCATimeAxisView*> (*x);
7443 vcas.push_back (vtv->vca());
7447 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (*x);
7451 if (rtv->is_track()) {
7456 routes.push_back (rtv->_route);
7458 if (rtv->route()->is_master() || rtv->route()->is_monitor()) {
7463 if (special_bus && !Config->get_allow_special_bus_removal()) {
7464 MessageDialog msg (_("That would be bad news ...."),
7468 msg.set_secondary_text (string_compose (_(
7469 "Removing the master or monitor bus is such a bad idea\n\
7470 that %1 is not going to allow it.\n\
7472 If you really want to do this sort of thing\n\
7473 edit your ardour.rc file to set the\n\
7474 \"allow-special-bus-removal\" option to be \"yes\""), PROGRAM_NAME));
7481 if (ntracks + nbusses + nvcas == 0) {
7487 trackstr = P_("track", "tracks", ntracks);
7488 busstr = P_("bus", "busses", nbusses);
7489 vcastr = P_("VCA", "VCAs", nvcas);
7491 if (ntracks > 0 && nbusses > 0 && nvcas > 0) {
7492 title = _("Remove various strips");
7493 prompt = string_compose (_("Do you really want to remove %1 %2, %3 %4 and %5 %6?"),
7494 ntracks, trackstr, nbusses, busstr, nvcas, vcastr);
7496 else if (ntracks > 0 && nbusses > 0) {
7497 title = string_compose (_("Remove %1 and %2"), trackstr, busstr);
7498 prompt = string_compose (_("Do you really want to remove %1 %2 and %3 %4?"),
7499 ntracks, trackstr, nbusses, busstr);
7501 else if (ntracks > 0 && nvcas > 0) {
7502 title = string_compose (_("Remove %1 and %2"), trackstr, vcastr);
7503 prompt = string_compose (_("Do you really want to remove %1 %2 and %3 %4?"),
7504 ntracks, trackstr, nvcas, vcastr);
7506 else if (nbusses > 0 && nvcas > 0) {
7507 title = string_compose (_("Remove %1 and %2"), busstr, vcastr);
7508 prompt = string_compose (_("Do you really want to remove %1 %2 and %3 %4?"),
7509 nbusses, busstr, nvcas, vcastr);
7511 else if (ntracks > 0) {
7512 title = string_compose (_("Remove %1"), trackstr);
7513 prompt = string_compose (_("Do you really want to remove %1 %2?"),
7516 else if (nbusses > 0) {
7517 title = string_compose (_("Remove %1"), busstr);
7518 prompt = string_compose (_("Do you really want to remove %1 %2?"),
7521 else if (nvcas > 0) {
7522 title = string_compose (_("Remove %1"), vcastr);
7523 prompt = string_compose (_("Do you really want to remove %1 %2?"),
7531 prompt += "\n" + string_compose ("(You may also lose the playlists associated with the %1)", trackstr) + "\n";
7534 prompt += "\n" + string(_("This action cannot be undone, and the session file will be overwritten!"));
7536 choices.push_back (_("No, do nothing."));
7537 if (ntracks + nbusses + nvcas > 1) {
7538 choices.push_back (_("Yes, remove them."));
7540 choices.push_back (_("Yes, remove it."));
7543 Choice prompter (title, prompt, choices);
7545 if (prompter.run () != 1) {
7549 if (current_mixer_strip && routes.size () > 1 && std::find (routes.begin(), routes.end(), current_mixer_strip->route()) != routes.end ()) {
7550 /* Route deletion calls Editor::timeaxisview_deleted() iteratively (for each deleted
7551 * route). If the deleted route is currently displayed in the Editor-Mixer (highly
7552 * likely because deletion requires selection) this will call
7553 * Editor::set_selected_mixer_strip () which is expensive (MixerStrip::set_route()).
7554 * It's likewise likely that the route that has just been displayed in the
7555 * Editor-Mixer will be next in line for deletion.
7557 * So simply switch to the master-bus (if present)
7559 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
7560 if ((*i)->stripable ()->is_master ()) {
7561 set_selected_mixer_strip (*(*i));
7568 PresentationInfo::ChangeSuspender cs;
7569 DisplaySuspender ds;
7571 boost::shared_ptr<RouteList> rl (new RouteList);
7572 for (vector<boost::shared_ptr<Route> >::iterator x = routes.begin(); x != routes.end(); ++x) {
7575 _session->remove_routes (rl);
7577 for (vector<boost::shared_ptr<VCA> >::iterator x = vcas.begin(); x != vcas.end(); ++x) {
7578 _session->vca_manager().remove_vca (*x);
7582 /* TrackSelection and RouteList leave scope,
7583 * destructors are called,
7584 * diskstream drops references, save_state is called (again for every track)
7589 Editor::do_insert_time ()
7591 if (selection->tracks.empty()) {
7592 MessageDialog msg (_("You must first select some tracks to Insert Time."),
7593 true, MESSAGE_INFO, BUTTONS_OK, true);
7594 msg.set_position (WIN_POS_MOUSE);
7599 if (Config->get_edit_mode() == Lock) {
7600 MessageDialog msg (_("You cannot insert time in Lock Edit mode."),
7601 true, MESSAGE_INFO, BUTTONS_OK, true);
7602 msg.set_position (WIN_POS_MOUSE);
7607 InsertRemoveTimeDialog d (*this);
7608 int response = d.run ();
7610 if (response != RESPONSE_OK) {
7614 if (d.distance() == 0) {
7621 d.intersected_region_action (),
7625 d.move_glued_markers(),
7626 d.move_locked_markers(),
7632 Editor::insert_time (
7633 samplepos_t pos, samplecnt_t samples, InsertTimeOption opt,
7634 bool all_playlists, bool ignore_music_glue, bool markers_too, bool glued_markers_too, bool locked_markers_too, bool tempo_too
7638 if (Config->get_edit_mode() == Lock) {
7641 bool in_command = false;
7643 TrackViewList ts = selection->tracks.filter_to_unique_playlists ();
7645 for (TrackViewList::iterator x = ts.begin(); x != ts.end(); ++x) {
7649 /* don't operate on any playlist more than once, which could
7650 * happen if "all playlists" is enabled, but there is more
7651 * than 1 track using playlists "from" a given track.
7654 set<boost::shared_ptr<Playlist> > pl;
7656 if (all_playlists) {
7657 RouteTimeAxisView* rtav = dynamic_cast<RouteTimeAxisView*> (*x);
7658 if (rtav && rtav->track ()) {
7659 vector<boost::shared_ptr<Playlist> > all = _session->playlists->playlists_for_track (rtav->track ());
7660 for (vector<boost::shared_ptr<Playlist> >::iterator p = all.begin(); p != all.end(); ++p) {
7665 if ((*x)->playlist ()) {
7666 pl.insert ((*x)->playlist ());
7670 for (set<boost::shared_ptr<Playlist> >::iterator i = pl.begin(); i != pl.end(); ++i) {
7672 (*i)->clear_changes ();
7673 (*i)->clear_owned_changes ();
7676 begin_reversible_command (_("insert time"));
7680 if (opt == SplitIntersected) {
7681 /* non musical split */
7682 (*i)->split (MusicSample (pos, 0));
7685 (*i)->shift (pos, samples, (opt == MoveIntersected), ignore_music_glue);
7687 vector<Command*> cmds;
7689 _session->add_commands (cmds);
7691 _session->add_command (new StatefulDiffCommand (*i));
7695 RouteTimeAxisView* rtav = dynamic_cast<RouteTimeAxisView*> (*x);
7698 begin_reversible_command (_("insert time"));
7701 rtav->route ()->shift (pos, samples);
7708 const int32_t divisions = get_grid_music_divisions (0);
7709 XMLNode& before (_session->locations()->get_state());
7710 Locations::LocationList copy (_session->locations()->list());
7712 for (Locations::LocationList::iterator i = copy.begin(); i != copy.end(); ++i) {
7714 Locations::LocationList::const_iterator tmp;
7716 if ((*i)->position_lock_style() == AudioTime || glued_markers_too) {
7717 bool const was_locked = (*i)->locked ();
7718 if (locked_markers_too) {
7722 if ((*i)->start() >= pos) {
7723 // move end first, in case we're moving by more than the length of the range
7724 if (!(*i)->is_mark()) {
7725 (*i)->set_end ((*i)->end() + samples, false, true, divisions);
7727 (*i)->set_start ((*i)->start() + samples, false, true, divisions);
7739 begin_reversible_command (_("insert time"));
7742 XMLNode& after (_session->locations()->get_state());
7743 _session->add_command (new MementoCommand<Locations>(*_session->locations(), &before, &after));
7749 begin_reversible_command (_("insert time"));
7752 XMLNode& before (_session->tempo_map().get_state());
7753 _session->tempo_map().insert_time (pos, samples);
7754 XMLNode& after (_session->tempo_map().get_state());
7755 _session->add_command (new MementoCommand<TempoMap>(_session->tempo_map(), &before, &after));
7759 commit_reversible_command ();
7764 Editor::do_remove_time ()
7766 if (selection->tracks.empty()) {
7767 MessageDialog msg (_("You must first select some tracks to Remove Time."),
7768 true, MESSAGE_INFO, BUTTONS_OK, true);
7769 msg.set_position (WIN_POS_MOUSE);
7774 if (Config->get_edit_mode() == Lock) {
7775 MessageDialog msg (_("You cannot remove time in Lock Edit mode."),
7776 true, MESSAGE_INFO, BUTTONS_OK, true);
7777 msg.set_position (WIN_POS_MOUSE);
7782 InsertRemoveTimeDialog d (*this, true);
7784 int response = d.run ();
7786 if (response != RESPONSE_OK) {
7790 samplecnt_t distance = d.distance();
7792 if (distance == 0) {
7802 d.move_glued_markers(),
7803 d.move_locked_markers(),
7809 Editor::remove_time (samplepos_t pos, samplecnt_t samples, InsertTimeOption opt,
7810 bool ignore_music_glue, bool markers_too, bool glued_markers_too, bool locked_markers_too, bool tempo_too)
7812 if (Config->get_edit_mode() == Lock) {
7813 error << (_("Cannot insert or delete time when in Lock edit.")) << endmsg;
7816 bool in_command = false;
7818 for (TrackSelection::iterator x = selection->tracks.begin(); x != selection->tracks.end(); ++x) {
7820 boost::shared_ptr<Playlist> pl = (*x)->playlist();
7824 XMLNode &before = pl->get_state();
7827 begin_reversible_command (_("remove time"));
7831 std::list<AudioRange> rl;
7832 AudioRange ar(pos, pos+samples, 0);
7835 pl->shift (pos, -samples, true, ignore_music_glue);
7837 XMLNode &after = pl->get_state();
7839 _session->add_command (new MementoCommand<Playlist> (*pl, &before, &after));
7843 RouteTimeAxisView* rtav = dynamic_cast<RouteTimeAxisView*> (*x);
7846 begin_reversible_command (_("remove time"));
7849 rtav->route ()->shift (pos, -samples);
7853 const int32_t divisions = get_grid_music_divisions (0);
7854 std::list<Location*> loc_kill_list;
7859 XMLNode& before (_session->locations()->get_state());
7860 Locations::LocationList copy (_session->locations()->list());
7862 for (Locations::LocationList::iterator i = copy.begin(); i != copy.end(); ++i) {
7863 if ((*i)->position_lock_style() == AudioTime || glued_markers_too) {
7865 bool const was_locked = (*i)->locked ();
7866 if (locked_markers_too) {
7870 if (!(*i)->is_mark()) { // it's a range; have to handle both start and end
7871 if ((*i)->end() >= pos
7872 && (*i)->end() < pos+samples
7873 && (*i)->start() >= pos
7874 && (*i)->end() < pos+samples) { // range is completely enclosed; kill it
7876 loc_kill_list.push_back(*i);
7877 } else { // only start or end is included, try to do the right thing
7878 // move start before moving end, to avoid trying to move the end to before the start
7879 // if we're removing more time than the length of the range
7880 if ((*i)->start() >= pos && (*i)->start() < pos+samples) {
7881 // start is within cut
7882 (*i)->set_start (pos, false, true,divisions); // bring the start marker to the beginning of the cut
7884 } else if ((*i)->start() >= pos+samples) {
7885 // start (and thus entire range) lies beyond end of cut
7886 (*i)->set_start ((*i)->start() - samples, false, true, divisions); // slip the start marker back
7889 if ((*i)->end() >= pos && (*i)->end() < pos+samples) {
7890 // end is inside cut
7891 (*i)->set_end (pos, false, true, divisions); // bring the end to the cut
7893 } else if ((*i)->end() >= pos+samples) {
7894 // end is beyond end of cut
7895 (*i)->set_end ((*i)->end() - samples, false, true, divisions); // slip the end marker back
7900 } else if ((*i)->start() >= pos && (*i)->start() < pos+samples) {
7901 loc_kill_list.push_back(*i);
7903 } else if ((*i)->start() >= pos) {
7904 (*i)->set_start ((*i)->start() -samples, false, true, divisions);
7914 for (list<Location*>::iterator i = loc_kill_list.begin(); i != loc_kill_list.end(); ++i) {
7915 _session->locations()->remove (*i);
7920 begin_reversible_command (_("remove time"));
7923 XMLNode& after (_session->locations()->get_state());
7924 _session->add_command (new MementoCommand<Locations>(*_session->locations(), &before, &after));
7929 XMLNode& before (_session->tempo_map().get_state());
7931 if (_session->tempo_map().remove_time (pos, samples)) {
7933 begin_reversible_command (_("remove time"));
7936 XMLNode& after (_session->tempo_map().get_state());
7937 _session->add_command (new MementoCommand<TempoMap>(_session->tempo_map(), &before, &after));
7942 commit_reversible_command ();
7947 Editor::fit_selection ()
7949 if (!selection->tracks.empty()) {
7950 fit_tracks (selection->tracks);
7954 /* no selected tracks - use tracks with selected regions */
7956 if (!selection->regions.empty()) {
7957 for (RegionSelection::iterator r = selection->regions.begin(); r != selection->regions.end(); ++r) {
7958 tvl.push_back (&(*r)->get_time_axis_view ());
7964 } else if (internal_editing()) {
7965 /* no selected tracks, or regions, but in internal edit mode, so follow the mouse and use
7968 if (entered_track) {
7969 tvl.push_back (entered_track);
7977 Editor::fit_tracks (TrackViewList & tracks)
7979 if (tracks.empty()) {
7983 uint32_t child_heights = 0;
7984 int visible_tracks = 0;
7986 for (TrackSelection::iterator t = tracks.begin(); t != tracks.end(); ++t) {
7988 if (!(*t)->marked_for_display()) {
7992 child_heights += (*t)->effective_height() - (*t)->current_height();
7996 /* compute the per-track height from:
7998 * total canvas visible height
7999 * - height that will be taken by visible children of selected tracks
8000 * - height of the ruler/hscroll area
8002 uint32_t h = (uint32_t) floor ((trackviews_height() - child_heights) / visible_tracks);
8003 double first_y_pos = DBL_MAX;
8005 if (h < TimeAxisView::preset_height (HeightSmall)) {
8006 MessageDialog msg (_("There are too many tracks to fit in the current window"));
8007 /* too small to be displayed */
8011 undo_visual_stack.push_back (current_visual_state (true));
8012 PBD::Unwinder<bool> nsv (no_save_visual, true);
8014 /* build a list of all tracks, including children */
8017 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
8019 TimeAxisView::Children c = (*i)->get_child_list ();
8020 for (TimeAxisView::Children::iterator j = c.begin(); j != c.end(); ++j) {
8021 all.push_back (j->get());
8026 // find selection range.
8027 // if someone knows how to user TrackViewList::iterator for this
8029 int selected_top = -1;
8030 int selected_bottom = -1;
8032 for (TrackViewList::iterator t = all.begin(); t != all.end(); ++t, ++i) {
8033 if ((*t)->marked_for_display ()) {
8034 if (tracks.contains(*t)) {
8035 if (selected_top == -1) {
8038 selected_bottom = i;
8044 for (TrackViewList::iterator t = all.begin(); t != all.end(); ++t, ++i) {
8045 if ((*t)->marked_for_display ()) {
8046 if (tracks.contains(*t)) {
8047 (*t)->set_height (h);
8048 first_y_pos = std::min ((*t)->y_position (), first_y_pos);
8050 if (i > selected_top && i < selected_bottom) {
8051 hide_track_in_display (*t);
8058 set the controls_layout height now, because waiting for its size
8059 request signal handler will cause the vertical adjustment setting to fail
8062 controls_layout.property_height () = _full_canvas_height;
8063 vertical_adjustment.set_value (first_y_pos);
8065 redo_visual_stack.push_back (current_visual_state (true));
8067 visible_tracks_selector.set_text (_("Sel"));
8071 Editor::save_visual_state (uint32_t n)
8073 while (visual_states.size() <= n) {
8074 visual_states.push_back (0);
8077 if (visual_states[n] != 0) {
8078 delete visual_states[n];
8081 visual_states[n] = current_visual_state (true);
8086 Editor::goto_visual_state (uint32_t n)
8088 if (visual_states.size() <= n) {
8092 if (visual_states[n] == 0) {
8096 use_visual_state (*visual_states[n]);
8100 Editor::start_visual_state_op (uint32_t n)
8102 save_visual_state (n);
8104 PopUp* pup = new PopUp (WIN_POS_MOUSE, 1000, true);
8106 snprintf (buf, sizeof (buf), _("Saved view %u"), n+1);
8107 pup->set_text (buf);
8112 Editor::cancel_visual_state_op (uint32_t n)
8114 goto_visual_state (n);
8118 Editor::toggle_region_mute ()
8120 if (_ignore_region_action) {
8124 RegionSelection rs = get_regions_from_selection_and_entered ();
8130 if (rs.size() > 1) {
8131 begin_reversible_command (_("mute regions"));
8133 begin_reversible_command (_("mute region"));
8136 for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
8138 (*i)->region()->playlist()->clear_changes ();
8139 (*i)->region()->set_muted (!(*i)->region()->muted ());
8140 _session->add_command (new StatefulDiffCommand ((*i)->region()));
8144 commit_reversible_command ();
8148 Editor::combine_regions ()
8150 /* foreach track with selected regions, take all selected regions
8151 and join them into a new region containing the subregions (as a
8155 typedef set<RouteTimeAxisView*> RTVS;
8158 if (selection->regions.empty()) {
8162 for (RegionSelection::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
8163 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*>(&(*i)->get_time_axis_view());
8166 tracks.insert (rtv);
8170 begin_reversible_command (_("combine regions"));
8172 vector<RegionView*> new_selection;
8174 for (RTVS::iterator i = tracks.begin(); i != tracks.end(); ++i) {
8177 if ((rv = (*i)->combine_regions ()) != 0) {
8178 new_selection.push_back (rv);
8182 selection->clear_regions ();
8183 for (vector<RegionView*>::iterator i = new_selection.begin(); i != new_selection.end(); ++i) {
8184 selection->add (*i);
8187 commit_reversible_command ();
8191 Editor::uncombine_regions ()
8193 typedef set<RouteTimeAxisView*> RTVS;
8196 if (selection->regions.empty()) {
8200 for (RegionSelection::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
8201 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*>(&(*i)->get_time_axis_view());
8204 tracks.insert (rtv);
8208 begin_reversible_command (_("uncombine regions"));
8210 for (RTVS::iterator i = tracks.begin(); i != tracks.end(); ++i) {
8211 (*i)->uncombine_regions ();
8214 commit_reversible_command ();
8218 Editor::toggle_midi_input_active (bool flip_others)
8221 boost::shared_ptr<RouteList> rl (new RouteList);
8223 for (TrackSelection::iterator i = selection->tracks.begin(); i != selection->tracks.end(); ++i) {
8224 RouteTimeAxisView *rtav = dynamic_cast<RouteTimeAxisView *>(*i);
8230 boost::shared_ptr<MidiTrack> mt = rtav->midi_track();
8233 rl->push_back (rtav->route());
8234 onoff = !mt->input_active();
8238 _session->set_exclusive_input_active (rl, onoff, flip_others);
8241 static bool ok_fine (GdkEventAny*) { return true; }
8247 lock_dialog = new Gtk::Dialog (string_compose (_("%1: Locked"), PROGRAM_NAME), true);
8249 Gtk::Image* padlock = manage (new Gtk::Image (ARDOUR_UI_UTILS::get_icon ("padlock_closed")));
8250 lock_dialog->get_vbox()->pack_start (*padlock);
8251 lock_dialog->signal_delete_event ().connect (sigc::ptr_fun (ok_fine));
8253 ArdourButton* b = manage (new ArdourButton);
8254 b->set_name ("lock button");
8255 b->set_text (_("Click to unlock"));
8256 b->signal_clicked.connect (sigc::mem_fun (*this, &Editor::unlock));
8257 lock_dialog->get_vbox()->pack_start (*b);
8259 lock_dialog->get_vbox()->show_all ();
8260 lock_dialog->set_size_request (200, 200);
8263 delete _main_menu_disabler;
8264 _main_menu_disabler = new MainMenuDisabler;
8266 lock_dialog->present ();
8268 lock_dialog->get_window()->set_decorations (Gdk::WMDecoration (0));
8274 lock_dialog->hide ();
8276 delete _main_menu_disabler;
8277 _main_menu_disabler = 0;
8279 if (UIConfiguration::instance().get_lock_gui_after_seconds()) {
8280 start_lock_event_timing ();
8285 Editor::bring_in_callback (Gtk::Label* label, uint32_t n, uint32_t total, string name)
8287 Gtkmm2ext::UI::instance()->call_slot (invalidator (*this), boost::bind (&Editor::update_bring_in_message, this, label, n, total, name));
8291 Editor::update_bring_in_message (Gtk::Label* label, uint32_t n, uint32_t total, string name)
8293 Timers::TimerSuspender t;
8294 label->set_text (string_compose ("Copying %1, %2 of %3", name, n, total));
8295 Gtkmm2ext::UI::instance()->flush_pending (1);
8299 Editor::bring_all_sources_into_session ()
8306 ArdourDialog w (_("Moving embedded files into session folder"));
8307 w.get_vbox()->pack_start (msg);
8310 /* flush all pending GUI events because we're about to start copying
8314 Timers::TimerSuspender t;
8315 Gtkmm2ext::UI::instance()->flush_pending (3);
8319 _session->bring_all_sources_into_session (boost::bind (&Editor::bring_in_callback, this, &msg, _1, _2, _3));