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 "pbd/error.h"
32 #include "pbd/basename.h"
33 #include "pbd/pthread_utils.h"
34 #include "pbd/memento_command.h"
35 #include "pbd/unwind.h"
36 #include "pbd/whitespace.h"
37 #include "pbd/stateful_diff_command.h"
39 #include "gtkmm2ext/utils.h"
41 #include "widgets/choice.h"
42 #include "widgets/popup.h"
44 #include "ardour/audio_track.h"
45 #include "ardour/audioregion.h"
46 #include "ardour/boost_debug.h"
47 #include "ardour/dB.h"
48 #include "ardour/location.h"
49 #include "ardour/midi_region.h"
50 #include "ardour/midi_track.h"
51 #include "ardour/operations.h"
52 #include "ardour/playlist_factory.h"
53 #include "ardour/profile.h"
54 #include "ardour/quantize.h"
55 #include "ardour/legatize.h"
56 #include "ardour/region_factory.h"
57 #include "ardour/reverse.h"
58 #include "ardour/session.h"
59 #include "ardour/session_playlists.h"
60 #include "ardour/strip_silence.h"
61 #include "ardour/transient_detector.h"
62 #include "ardour/transpose.h"
64 #include "canvas/canvas.h"
67 #include "audio_region_view.h"
68 #include "audio_streamview.h"
69 #include "audio_time_axis.h"
70 #include "automation_region_view.h"
71 #include "automation_time_axis.h"
72 #include "control_point.h"
76 #include "editor_cursors.h"
77 #include "editor_drag.h"
78 #include "editor_regions.h"
79 #include "editor_routes.h"
80 #include "gui_thread.h"
81 #include "insert_remove_time_dialog.h"
82 #include "interthread_progress_window.h"
83 #include "item_counts.h"
85 #include "midi_region_view.h"
87 #include "mixer_strip.h"
88 #include "mouse_cursors.h"
89 #include "normalize_dialog.h"
91 #include "paste_context.h"
92 #include "patch_change_dialog.h"
93 #include "quantize_dialog.h"
94 #include "region_gain_line.h"
95 #include "rgb_macros.h"
96 #include "route_time_axis.h"
97 #include "selection.h"
98 #include "selection_templates.h"
99 #include "streamview.h"
100 #include "strip_silence_dialog.h"
101 #include "time_axis_view.h"
103 #include "transpose_dialog.h"
104 #include "transform_dialog.h"
105 #include "ui_config.h"
107 #include "pbd/i18n.h"
110 using namespace ARDOUR;
113 using namespace Gtkmm2ext;
114 using namespace ArdourWidgets;
115 using namespace Editing;
116 using Gtkmm2ext::Keyboard;
118 /***********************************************************************
120 ***********************************************************************/
123 Editor::undo (uint32_t n)
125 if (_session && _session->actively_recording()) {
126 /* no undo allowed while recording. Session will check also,
127 but we don't even want to get to that.
132 if (_drags->active ()) {
138 if (_session->undo_depth() == 0) {
139 undo_action->set_sensitive(false);
141 redo_action->set_sensitive(true);
142 begin_selection_op_history ();
147 Editor::redo (uint32_t n)
149 if (_session && _session->actively_recording()) {
150 /* no redo allowed while recording. Session will check also,
151 but we don't even want to get to that.
156 if (_drags->active ()) {
162 if (_session->redo_depth() == 0) {
163 redo_action->set_sensitive(false);
165 undo_action->set_sensitive(true);
166 begin_selection_op_history ();
171 Editor::split_regions_at (MusicFrame where, RegionSelection& regions, bool snap_frame)
175 RegionSelection pre_selected_regions = selection->regions;
176 bool working_on_selection = !pre_selected_regions.empty();
178 list<boost::shared_ptr<Playlist> > used_playlists;
179 list<RouteTimeAxisView*> used_trackviews;
181 if (regions.empty()) {
185 begin_reversible_command (_("split"));
187 // if splitting a single region, and snap-to is using
188 // region boundaries, don't pay attention to them
190 if (regions.size() == 1) {
191 switch (_snap_type) {
192 case SnapToRegionStart:
193 case SnapToRegionSync:
194 case SnapToRegionEnd:
207 EditorFreeze(); /* Emit Signal */
210 for (RegionSelection::iterator a = regions.begin(); a != regions.end(); ) {
212 RegionSelection::iterator tmp;
214 /* XXX this test needs to be more complicated, to make sure we really
215 have something to split.
218 if (!(*a)->region()->covers (where.frame)) {
226 boost::shared_ptr<Playlist> pl = (*a)->region()->playlist();
234 /* we haven't seen this playlist before */
236 /* remember used playlists so we can thaw them later */
237 used_playlists.push_back(pl);
239 TimeAxisView& tv = (*a)->get_time_axis_view();
240 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (&tv);
242 used_trackviews.push_back (rtv);
249 pl->clear_changes ();
250 pl->split_region ((*a)->region(), where);
251 _session->add_command (new StatefulDiffCommand (pl));
257 latest_regionviews.clear ();
259 vector<sigc::connection> region_added_connections;
261 for (list<RouteTimeAxisView*>::iterator i = used_trackviews.begin(); i != used_trackviews.end(); ++i) {
262 region_added_connections.push_back ((*i)->view()->RegionViewAdded.connect (sigc::mem_fun(*this, &Editor::collect_new_region_view)));
265 while (used_playlists.size() > 0) {
266 list <boost::shared_ptr<Playlist > >::iterator i = used_playlists.begin();
268 used_playlists.pop_front();
271 for (vector<sigc::connection>::iterator c = region_added_connections.begin(); c != region_added_connections.end(); ++c) {
276 EditorThaw(); /* Emit Signal */
279 if (working_on_selection) {
280 // IFF we were working on selected regions, try to reinstate the other region selections that existed before the freeze/thaw.
282 RegionSelectionAfterSplit rsas = Config->get_region_selection_after_split();
283 /* There are three classes of regions that we might want selected after
284 splitting selected regions:
285 - regions selected before the split operation, and unaffected by it
286 - newly-created regions before the split
287 - newly-created regions after the split
290 if (rsas & Existing) {
291 // region selections that existed before the split.
292 selection->add ( pre_selected_regions );
295 for (RegionSelection::iterator ri = latest_regionviews.begin(); ri != latest_regionviews.end(); ri++) {
296 if ((*ri)->region()->position() < where.frame) {
297 // new regions created before the split
298 if (rsas & NewlyCreatedLeft) {
299 selection->add (*ri);
302 // new regions created after the split
303 if (rsas & NewlyCreatedRight) {
304 selection->add (*ri);
309 if( working_on_selection ) {
310 selection->add (latest_regionviews); //these are the new regions created after the split
314 commit_reversible_command ();
317 /** Move one extreme of the current range selection. If more than one range is selected,
318 * the start of the earliest range or the end of the latest range is moved.
320 * @param move_end true to move the end of the current range selection, false to move
322 * @param next true to move the extreme to the next region boundary, false to move to
326 Editor::move_range_selection_start_or_end_to_region_boundary (bool move_end, bool next)
328 if (selection->time.start() == selection->time.end_frame()) {
332 framepos_t start = selection->time.start ();
333 framepos_t end = selection->time.end_frame ();
335 /* the position of the thing we may move */
336 framepos_t pos = move_end ? end : start;
337 int dir = next ? 1 : -1;
339 /* so we don't find the current region again */
340 if (dir > 0 || pos > 0) {
344 framepos_t const target = get_region_boundary (pos, dir, true, false);
359 begin_reversible_selection_op (_("alter selection"));
360 selection->set_preserving_all_ranges (start, end);
361 commit_reversible_selection_op ();
365 Editor::nudge_forward_release (GdkEventButton* ev)
367 if (ev->state & Keyboard::PrimaryModifier) {
368 nudge_forward (false, true);
370 nudge_forward (false, false);
376 Editor::nudge_backward_release (GdkEventButton* ev)
378 if (ev->state & Keyboard::PrimaryModifier) {
379 nudge_backward (false, true);
381 nudge_backward (false, false);
388 Editor::nudge_forward (bool next, bool force_playhead)
391 framepos_t next_distance;
397 RegionSelection rs = get_regions_from_selection_and_entered ();
399 if (!force_playhead && !rs.empty()) {
401 begin_reversible_command (_("nudge regions forward"));
403 for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
404 boost::shared_ptr<Region> r ((*i)->region());
406 distance = get_nudge_distance (r->position(), next_distance);
409 distance = next_distance;
413 r->set_position (r->position() + distance);
414 _session->add_command (new StatefulDiffCommand (r));
417 commit_reversible_command ();
420 } else if (!force_playhead && !selection->markers.empty()) {
423 bool in_command = false;
424 const int32_t divisions = get_grid_music_divisions (0);
426 for (MarkerSelection::iterator i = selection->markers.begin(); i != selection->markers.end(); ++i) {
428 Location* loc = find_location_from_marker ((*i), is_start);
432 XMLNode& before (loc->get_state());
435 distance = get_nudge_distance (loc->start(), next_distance);
437 distance = next_distance;
439 if (max_framepos - distance > loc->start() + loc->length()) {
440 loc->set_start (loc->start() + distance, false, true, divisions);
442 loc->set_start (max_framepos - loc->length(), false, true, divisions);
445 distance = get_nudge_distance (loc->end(), next_distance);
447 distance = next_distance;
449 if (max_framepos - distance > loc->end()) {
450 loc->set_end (loc->end() + distance, false, true, divisions);
452 loc->set_end (max_framepos, false, true, divisions);
454 if (loc->is_session_range()) {
455 _session->set_end_is_free (false);
459 begin_reversible_command (_("nudge location forward"));
462 XMLNode& after (loc->get_state());
463 _session->add_command (new MementoCommand<Location>(*loc, &before, &after));
468 commit_reversible_command ();
471 distance = get_nudge_distance (playhead_cursor->current_frame (), next_distance);
472 _session->request_locate (playhead_cursor->current_frame () + distance);
477 Editor::nudge_backward (bool next, bool force_playhead)
480 framepos_t next_distance;
486 RegionSelection rs = get_regions_from_selection_and_entered ();
488 if (!force_playhead && !rs.empty()) {
490 begin_reversible_command (_("nudge regions backward"));
492 for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
493 boost::shared_ptr<Region> r ((*i)->region());
495 distance = get_nudge_distance (r->position(), next_distance);
498 distance = next_distance;
503 if (r->position() > distance) {
504 r->set_position (r->position() - distance);
508 _session->add_command (new StatefulDiffCommand (r));
511 commit_reversible_command ();
513 } else if (!force_playhead && !selection->markers.empty()) {
516 bool in_command = false;
518 for (MarkerSelection::iterator i = selection->markers.begin(); i != selection->markers.end(); ++i) {
520 Location* loc = find_location_from_marker ((*i), is_start);
524 XMLNode& before (loc->get_state());
527 distance = get_nudge_distance (loc->start(), next_distance);
529 distance = next_distance;
531 if (distance < loc->start()) {
532 loc->set_start (loc->start() - distance, false, true, get_grid_music_divisions(0));
534 loc->set_start (0, false, true, get_grid_music_divisions(0));
537 distance = get_nudge_distance (loc->end(), next_distance);
540 distance = next_distance;
543 if (distance < loc->end() - loc->length()) {
544 loc->set_end (loc->end() - distance, false, true, get_grid_music_divisions(0));
546 loc->set_end (loc->length(), false, true, get_grid_music_divisions(0));
548 if (loc->is_session_range()) {
549 _session->set_end_is_free (false);
553 begin_reversible_command (_("nudge location forward"));
556 XMLNode& after (loc->get_state());
557 _session->add_command (new MementoCommand<Location>(*loc, &before, &after));
561 commit_reversible_command ();
566 distance = get_nudge_distance (playhead_cursor->current_frame (), next_distance);
568 if (playhead_cursor->current_frame () > distance) {
569 _session->request_locate (playhead_cursor->current_frame () - distance);
571 _session->goto_start();
577 Editor::nudge_forward_capture_offset ()
579 RegionSelection rs = get_regions_from_selection_and_entered ();
581 if (!_session || rs.empty()) {
585 begin_reversible_command (_("nudge forward"));
587 framepos_t const distance = _session->worst_output_latency();
589 for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
590 boost::shared_ptr<Region> r ((*i)->region());
593 r->set_position (r->position() + distance);
594 _session->add_command(new StatefulDiffCommand (r));
597 commit_reversible_command ();
601 Editor::nudge_backward_capture_offset ()
603 RegionSelection rs = get_regions_from_selection_and_entered ();
605 if (!_session || rs.empty()) {
609 begin_reversible_command (_("nudge backward"));
611 framepos_t const distance = _session->worst_output_latency();
613 for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
614 boost::shared_ptr<Region> r ((*i)->region());
618 if (r->position() > distance) {
619 r->set_position (r->position() - distance);
623 _session->add_command(new StatefulDiffCommand (r));
626 commit_reversible_command ();
629 struct RegionSelectionPositionSorter {
630 bool operator() (RegionView* a, RegionView* b) {
631 return a->region()->position() < b->region()->position();
636 Editor::sequence_regions ()
639 framepos_t r_end_prev;
647 RegionSelection rs = get_regions_from_selection_and_entered ();
648 rs.sort(RegionSelectionPositionSorter());
652 bool in_command = false;
654 for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
655 boost::shared_ptr<Region> r ((*i)->region());
663 if(r->position_locked())
670 r->set_position(r_end_prev);
674 begin_reversible_command (_("sequence regions"));
677 _session->add_command (new StatefulDiffCommand (r));
679 r_end=r->position() + r->length();
685 commit_reversible_command ();
694 Editor::move_to_start ()
696 _session->goto_start ();
700 Editor::move_to_end ()
703 _session->request_locate (_session->current_end_frame());
707 Editor::build_region_boundary_cache ()
710 vector<RegionPoint> interesting_points;
711 boost::shared_ptr<Region> r;
712 TrackViewList tracks;
715 region_boundary_cache.clear ();
721 switch (_snap_type) {
722 case SnapToRegionStart:
723 interesting_points.push_back (Start);
725 case SnapToRegionEnd:
726 interesting_points.push_back (End);
728 case SnapToRegionSync:
729 interesting_points.push_back (SyncPoint);
731 case SnapToRegionBoundary:
732 interesting_points.push_back (Start);
733 interesting_points.push_back (End);
736 fatal << string_compose (_("build_region_boundary_cache called with snap_type = %1"), _snap_type) << endmsg;
737 abort(); /*NOTREACHED*/
741 TimeAxisView *ontrack = 0;
744 if (!selection->tracks.empty()) {
745 tlist = selection->tracks.filter_to_unique_playlists ();
747 tlist = track_views.filter_to_unique_playlists ();
750 while (pos < _session->current_end_frame() && !at_end) {
753 framepos_t lpos = max_framepos;
755 for (vector<RegionPoint>::iterator p = interesting_points.begin(); p != interesting_points.end(); ++p) {
757 if ((r = find_next_region (pos, *p, 1, tlist, &ontrack)) == 0) {
758 if (*p == interesting_points.back()) {
761 /* move to next point type */
767 rpos = r->first_frame();
771 rpos = r->last_frame();
775 rpos = r->sync_position ();
783 RouteTimeAxisView *rtav;
785 if (ontrack != 0 && (rtav = dynamic_cast<RouteTimeAxisView*>(ontrack)) != 0 ) {
786 if (rtav->track() != 0) {
787 speed = rtav->track()->speed();
791 rpos = track_frame_to_session_frame (rpos, speed);
797 /* prevent duplicates, but we don't use set<> because we want to be able
801 vector<framepos_t>::iterator ri;
803 for (ri = region_boundary_cache.begin(); ri != region_boundary_cache.end(); ++ri) {
809 if (ri == region_boundary_cache.end()) {
810 region_boundary_cache.push_back (rpos);
817 /* finally sort to be sure that the order is correct */
819 sort (region_boundary_cache.begin(), region_boundary_cache.end());
822 boost::shared_ptr<Region>
823 Editor::find_next_region (framepos_t frame, RegionPoint point, int32_t dir, TrackViewList& tracks, TimeAxisView **ontrack)
825 TrackViewList::iterator i;
826 framepos_t closest = max_framepos;
827 boost::shared_ptr<Region> ret;
831 framepos_t track_frame;
832 RouteTimeAxisView *rtav;
834 for (i = tracks.begin(); i != tracks.end(); ++i) {
837 boost::shared_ptr<Region> r;
840 if ( (rtav = dynamic_cast<RouteTimeAxisView*>(*i)) != 0 ) {
841 if (rtav->track()!=0)
842 track_speed = rtav->track()->speed();
845 track_frame = session_frame_to_track_frame(frame, track_speed);
847 if ((r = (*i)->find_next_region (track_frame, point, dir)) == 0) {
853 rpos = r->first_frame ();
857 rpos = r->last_frame ();
861 rpos = r->sync_position ();
865 // rpos is a "track frame", converting it to "_session frame"
866 rpos = track_frame_to_session_frame(rpos, track_speed);
869 distance = rpos - frame;
871 distance = frame - rpos;
874 if (distance < closest) {
886 Editor::find_next_region_boundary (framepos_t pos, int32_t dir, const TrackViewList& tracks)
888 framecnt_t distance = max_framepos;
889 framepos_t current_nearest = -1;
891 for (TrackViewList::const_iterator i = tracks.begin(); i != tracks.end(); ++i) {
892 framepos_t contender;
895 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (*i);
901 if ((contender = rtv->find_next_region_boundary (pos, dir)) < 0) {
905 d = ::llabs (pos - contender);
908 current_nearest = contender;
913 return current_nearest;
917 Editor::get_region_boundary (framepos_t pos, int32_t dir, bool with_selection, bool only_onscreen)
922 if (with_selection && Config->get_region_boundaries_from_selected_tracks()) {
924 if (!selection->tracks.empty()) {
926 target = find_next_region_boundary (pos, dir, selection->tracks);
930 if (only_onscreen || Config->get_region_boundaries_from_onscreen_tracks()) {
931 get_onscreen_tracks (tvl);
932 target = find_next_region_boundary (pos, dir, tvl);
934 target = find_next_region_boundary (pos, dir, track_views);
940 if (only_onscreen || Config->get_region_boundaries_from_onscreen_tracks()) {
941 get_onscreen_tracks (tvl);
942 target = find_next_region_boundary (pos, dir, tvl);
944 target = find_next_region_boundary (pos, dir, track_views);
952 Editor::cursor_to_region_boundary (bool with_selection, int32_t dir)
954 framepos_t pos = playhead_cursor->current_frame ();
961 // so we don't find the current region again..
962 if (dir > 0 || pos > 0) {
966 if ((target = get_region_boundary (pos, dir, with_selection, false)) < 0) {
970 _session->request_locate (target);
974 Editor::cursor_to_next_region_boundary (bool with_selection)
976 cursor_to_region_boundary (with_selection, 1);
980 Editor::cursor_to_previous_region_boundary (bool with_selection)
982 cursor_to_region_boundary (with_selection, -1);
986 Editor::cursor_to_region_point (EditorCursor* cursor, RegionPoint point, int32_t dir)
988 boost::shared_ptr<Region> r;
989 framepos_t pos = cursor->current_frame ();
995 TimeAxisView *ontrack = 0;
997 // so we don't find the current region again..
1001 if (!selection->tracks.empty()) {
1003 r = find_next_region (pos, point, dir, selection->tracks, &ontrack);
1005 } else if (clicked_axisview) {
1008 t.push_back (clicked_axisview);
1010 r = find_next_region (pos, point, dir, t, &ontrack);
1014 r = find_next_region (pos, point, dir, track_views, &ontrack);
1023 pos = r->first_frame ();
1027 pos = r->last_frame ();
1031 pos = r->sync_position ();
1036 RouteTimeAxisView *rtav;
1038 if ( ontrack != 0 && (rtav = dynamic_cast<RouteTimeAxisView*>(ontrack)) != 0 ) {
1039 if (rtav->track() != 0) {
1040 speed = rtav->track()->speed();
1044 pos = track_frame_to_session_frame(pos, speed);
1046 if (cursor == playhead_cursor) {
1047 _session->request_locate (pos);
1049 cursor->set_position (pos);
1054 Editor::cursor_to_next_region_point (EditorCursor* cursor, RegionPoint point)
1056 cursor_to_region_point (cursor, point, 1);
1060 Editor::cursor_to_previous_region_point (EditorCursor* cursor, RegionPoint point)
1062 cursor_to_region_point (cursor, point, -1);
1066 Editor::cursor_to_selection_start (EditorCursor *cursor)
1070 switch (mouse_mode) {
1072 if (!selection->regions.empty()) {
1073 pos = selection->regions.start();
1078 if (!selection->time.empty()) {
1079 pos = selection->time.start ();
1087 if (cursor == playhead_cursor) {
1088 _session->request_locate (pos);
1090 cursor->set_position (pos);
1095 Editor::cursor_to_selection_end (EditorCursor *cursor)
1099 switch (mouse_mode) {
1101 if (!selection->regions.empty()) {
1102 pos = selection->regions.end_frame();
1107 if (!selection->time.empty()) {
1108 pos = selection->time.end_frame ();
1116 if (cursor == playhead_cursor) {
1117 _session->request_locate (pos);
1119 cursor->set_position (pos);
1124 Editor::selected_marker_to_region_boundary (bool with_selection, int32_t dir)
1134 if (selection->markers.empty()) {
1138 if (!mouse_frame (mouse, ignored)) {
1142 add_location_mark (mouse);
1145 if ((loc = find_location_from_marker (selection->markers.front(), ignored)) == 0) {
1149 framepos_t pos = loc->start();
1151 // so we don't find the current region again..
1152 if (dir > 0 || pos > 0) {
1156 if ((target = get_region_boundary (pos, dir, with_selection, false)) < 0) {
1160 loc->move_to (target, 0);
1164 Editor::selected_marker_to_next_region_boundary (bool with_selection)
1166 selected_marker_to_region_boundary (with_selection, 1);
1170 Editor::selected_marker_to_previous_region_boundary (bool with_selection)
1172 selected_marker_to_region_boundary (with_selection, -1);
1176 Editor::selected_marker_to_region_point (RegionPoint point, int32_t dir)
1178 boost::shared_ptr<Region> r;
1183 if (!_session || selection->markers.empty()) {
1187 if ((loc = find_location_from_marker (selection->markers.front(), ignored)) == 0) {
1191 TimeAxisView *ontrack = 0;
1195 // so we don't find the current region again..
1199 if (!selection->tracks.empty()) {
1201 r = find_next_region (pos, point, dir, selection->tracks, &ontrack);
1205 r = find_next_region (pos, point, dir, track_views, &ontrack);
1214 pos = r->first_frame ();
1218 pos = r->last_frame ();
1222 pos = r->adjust_to_sync (r->first_frame());
1227 RouteTimeAxisView *rtav;
1229 if (ontrack != 0 && (rtav = dynamic_cast<RouteTimeAxisView*>(ontrack)) != 0) {
1230 if (rtav->track() != 0) {
1231 speed = rtav->track()->speed();
1235 pos = track_frame_to_session_frame(pos, speed);
1237 loc->move_to (pos, 0);
1241 Editor::selected_marker_to_next_region_point (RegionPoint point)
1243 selected_marker_to_region_point (point, 1);
1247 Editor::selected_marker_to_previous_region_point (RegionPoint point)
1249 selected_marker_to_region_point (point, -1);
1253 Editor::selected_marker_to_selection_start ()
1259 if (!_session || selection->markers.empty()) {
1263 if ((loc = find_location_from_marker (selection->markers.front(), ignored)) == 0) {
1267 switch (mouse_mode) {
1269 if (!selection->regions.empty()) {
1270 pos = selection->regions.start();
1275 if (!selection->time.empty()) {
1276 pos = selection->time.start ();
1284 loc->move_to (pos, 0);
1288 Editor::selected_marker_to_selection_end ()
1294 if (!_session || selection->markers.empty()) {
1298 if ((loc = find_location_from_marker (selection->markers.front(), ignored)) == 0) {
1302 switch (mouse_mode) {
1304 if (!selection->regions.empty()) {
1305 pos = selection->regions.end_frame();
1310 if (!selection->time.empty()) {
1311 pos = selection->time.end_frame ();
1319 loc->move_to (pos, 0);
1323 Editor::scroll_playhead (bool forward)
1325 framepos_t pos = playhead_cursor->current_frame ();
1326 framecnt_t delta = (framecnt_t) floor (current_page_samples() / 0.8);
1329 if (pos == max_framepos) {
1333 if (pos < max_framepos - delta) {
1352 _session->request_locate (pos);
1356 Editor::cursor_align (bool playhead_to_edit)
1362 if (playhead_to_edit) {
1364 if (selection->markers.empty()) {
1368 _session->request_locate (selection->markers.front()->position(), _session->transport_rolling());
1371 const int32_t divisions = get_grid_music_divisions (0);
1372 /* move selected markers to playhead */
1374 for (MarkerSelection::iterator i = selection->markers.begin(); i != selection->markers.end(); ++i) {
1377 Location* loc = find_location_from_marker (*i, ignored);
1379 if (loc->is_mark()) {
1380 loc->set_start (playhead_cursor->current_frame (), false, true, divisions);
1382 loc->set (playhead_cursor->current_frame (),
1383 playhead_cursor->current_frame () + loc->length(), true, divisions);
1390 Editor::scroll_backward (float pages)
1392 framepos_t const one_page = (framepos_t) rint (_visible_canvas_width * samples_per_pixel);
1393 framepos_t const cnt = (framepos_t) floor (pages * one_page);
1396 if (leftmost_frame < cnt) {
1399 frame = leftmost_frame - cnt;
1402 reset_x_origin (frame);
1406 Editor::scroll_forward (float pages)
1408 framepos_t const one_page = (framepos_t) rint (_visible_canvas_width * samples_per_pixel);
1409 framepos_t const cnt = (framepos_t) floor (pages * one_page);
1412 if (max_framepos - cnt < leftmost_frame) {
1413 frame = max_framepos - cnt;
1415 frame = leftmost_frame + cnt;
1418 reset_x_origin (frame);
1422 Editor::scroll_tracks_down ()
1424 double vert_value = vertical_adjustment.get_value() + vertical_adjustment.get_page_size();
1425 if (vert_value > vertical_adjustment.get_upper() - _visible_canvas_height) {
1426 vert_value = vertical_adjustment.get_upper() - _visible_canvas_height;
1429 vertical_adjustment.set_value (vert_value);
1433 Editor::scroll_tracks_up ()
1435 vertical_adjustment.set_value (vertical_adjustment.get_value() - vertical_adjustment.get_page_size());
1439 Editor::scroll_tracks_down_line ()
1441 double vert_value = vertical_adjustment.get_value() + 60;
1443 if (vert_value > vertical_adjustment.get_upper() - _visible_canvas_height) {
1444 vert_value = vertical_adjustment.get_upper() - _visible_canvas_height;
1447 vertical_adjustment.set_value (vert_value);
1451 Editor::scroll_tracks_up_line ()
1453 reset_y_origin (vertical_adjustment.get_value() - 60);
1457 Editor::select_topmost_track ()
1459 const double top_of_trackviews = vertical_adjustment.get_value();
1460 for (TrackViewList::iterator t = track_views.begin(); t != track_views.end(); ++t) {
1461 if ((*t)->hidden()) {
1464 std::pair<TimeAxisView*,double> res = (*t)->covers_y_position (top_of_trackviews);
1466 selection->set (*t);
1473 Editor::scroll_down_one_track (bool skip_child_views)
1475 TrackViewList::reverse_iterator next = track_views.rend();
1476 const double top_of_trackviews = vertical_adjustment.get_value();
1478 for (TrackViewList::reverse_iterator t = track_views.rbegin(); t != track_views.rend(); ++t) {
1479 if ((*t)->hidden()) {
1483 /* If this is the upper-most visible trackview, we want to display
1484 * the one above it (next)
1486 * Note that covers_y_position() is recursive and includes child views
1488 std::pair<TimeAxisView*,double> res = (*t)->covers_y_position (top_of_trackviews);
1491 if (skip_child_views) {
1494 /* automation lane (one level, non-recursive)
1496 * - if no automation lane exists -> move to next tack
1497 * - if the first (here: bottom-most) matches -> move to next tack
1498 * - if no y-axis match is found -> the current track is at the top
1499 * -> move to last (here: top-most) automation lane
1501 TimeAxisView::Children kids = (*t)->get_child_list();
1502 TimeAxisView::Children::reverse_iterator nkid = kids.rend();
1504 for (TimeAxisView::Children::reverse_iterator ci = kids.rbegin(); ci != kids.rend(); ++ci) {
1505 if ((*ci)->hidden()) {
1509 std::pair<TimeAxisView*,double> dev;
1510 dev = (*ci)->covers_y_position (top_of_trackviews);
1512 /* some automation lane is currently at the top */
1513 if (ci == kids.rbegin()) {
1514 /* first (bottom-most) autmation lane is at the top.
1515 * -> move to next track
1524 if (nkid != kids.rend()) {
1525 ensure_time_axis_view_is_visible (**nkid, true);
1533 /* move to the track below the first one that covers the */
1535 if (next != track_views.rend()) {
1536 ensure_time_axis_view_is_visible (**next, true);
1544 Editor::scroll_up_one_track (bool skip_child_views)
1546 TrackViewList::iterator prev = track_views.end();
1547 double top_of_trackviews = vertical_adjustment.get_value ();
1549 for (TrackViewList::iterator t = track_views.begin(); t != track_views.end(); ++t) {
1551 if ((*t)->hidden()) {
1555 /* find the trackview at the top of the trackview group
1557 * Note that covers_y_position() is recursive and includes child views
1559 std::pair<TimeAxisView*,double> res = (*t)->covers_y_position (top_of_trackviews);
1562 if (skip_child_views) {
1565 /* automation lane (one level, non-recursive)
1567 * - if no automation lane exists -> move to prev tack
1568 * - if no y-axis match is found -> the current track is at the top -> move to prev track
1569 * (actually last automation lane of previous track, see below)
1570 * - if first (top-most) lane is at the top -> move to this track
1571 * - else move up one lane
1573 TimeAxisView::Children kids = (*t)->get_child_list();
1574 TimeAxisView::Children::iterator pkid = kids.end();
1576 for (TimeAxisView::Children::iterator ci = kids.begin(); ci != kids.end(); ++ci) {
1577 if ((*ci)->hidden()) {
1581 std::pair<TimeAxisView*,double> dev;
1582 dev = (*ci)->covers_y_position (top_of_trackviews);
1584 /* some automation lane is currently at the top */
1585 if (ci == kids.begin()) {
1586 /* first (top-most) autmation lane is at the top.
1587 * jump directly to this track's top
1589 ensure_time_axis_view_is_visible (**t, true);
1592 else if (pkid != kids.end()) {
1593 /* some other automation lane is at the top.
1594 * move up to prev automation lane.
1596 ensure_time_axis_view_is_visible (**pkid, true);
1599 assert(0); // not reached
1610 if (prev != track_views.end()) {
1611 // move to bottom-most automation-lane of the previous track
1612 TimeAxisView::Children kids = (*prev)->get_child_list();
1613 TimeAxisView::Children::reverse_iterator pkid = kids.rend();
1614 if (!skip_child_views) {
1615 // find the last visible lane
1616 for (TimeAxisView::Children::reverse_iterator ci = kids.rbegin(); ci != kids.rend(); ++ci) {
1617 if (!(*ci)->hidden()) {
1623 if (pkid != kids.rend()) {
1624 ensure_time_axis_view_is_visible (**pkid, true);
1626 ensure_time_axis_view_is_visible (**prev, true);
1635 Editor::scroll_left_step ()
1637 framepos_t xdelta = (current_page_samples() / 8);
1639 if (leftmost_frame > xdelta) {
1640 reset_x_origin (leftmost_frame - xdelta);
1648 Editor::scroll_right_step ()
1650 framepos_t xdelta = (current_page_samples() / 8);
1652 if (max_framepos - xdelta > leftmost_frame) {
1653 reset_x_origin (leftmost_frame + xdelta);
1655 reset_x_origin (max_framepos - current_page_samples());
1660 Editor::scroll_left_half_page ()
1662 framepos_t xdelta = (current_page_samples() / 2);
1663 if (leftmost_frame > xdelta) {
1664 reset_x_origin (leftmost_frame - xdelta);
1671 Editor::scroll_right_half_page ()
1673 framepos_t xdelta = (current_page_samples() / 2);
1674 if (max_framepos - xdelta > leftmost_frame) {
1675 reset_x_origin (leftmost_frame + xdelta);
1677 reset_x_origin (max_framepos - current_page_samples());
1684 Editor::tav_zoom_step (bool coarser)
1686 DisplaySuspender ds;
1690 if (selection->tracks.empty()) {
1693 ts = &selection->tracks;
1696 for (TrackViewList::iterator i = ts->begin(); i != ts->end(); ++i) {
1697 TimeAxisView *tv = (static_cast<TimeAxisView*>(*i));
1698 tv->step_height (coarser);
1703 Editor::tav_zoom_smooth (bool coarser, bool force_all)
1705 DisplaySuspender ds;
1709 if (selection->tracks.empty() || force_all) {
1712 ts = &selection->tracks;
1715 for (TrackViewList::iterator i = ts->begin(); i != ts->end(); ++i) {
1716 TimeAxisView *tv = (static_cast<TimeAxisView*>(*i));
1717 uint32_t h = tv->current_height ();
1722 if (h >= TimeAxisView::preset_height (HeightSmall)) {
1727 tv->set_height (h + 5);
1733 Editor::temporal_zoom_step_mouse_focus_scale (bool zoom_out, double scale)
1735 Editing::ZoomFocus temp_focus = zoom_focus;
1736 zoom_focus = Editing::ZoomFocusMouse;
1737 temporal_zoom_step_scale (zoom_out, scale);
1738 zoom_focus = temp_focus;
1742 Editor::temporal_zoom_step_mouse_focus (bool zoom_out)
1744 temporal_zoom_step_mouse_focus_scale (zoom_out, 2.0);
1748 Editor::temporal_zoom_step (bool zoom_out)
1750 temporal_zoom_step_scale (zoom_out, 2.0);
1754 Editor::temporal_zoom_step_scale (bool zoom_out, double scale)
1756 ENSURE_GUI_THREAD (*this, &Editor::temporal_zoom_step, zoom_out, scale)
1758 framecnt_t nspp = samples_per_pixel;
1762 if (nspp == samples_per_pixel) {
1767 if (nspp == samples_per_pixel) {
1772 temporal_zoom (nspp);
1776 Editor::temporal_zoom (framecnt_t fpp)
1782 framepos_t current_page = current_page_samples();
1783 framepos_t current_leftmost = leftmost_frame;
1784 framepos_t current_rightmost;
1785 framepos_t current_center;
1786 framepos_t new_page_size;
1787 framepos_t half_page_size;
1788 framepos_t leftmost_after_zoom = 0;
1790 bool in_track_canvas;
1791 bool use_mouse_frame = true;
1795 if (fpp == samples_per_pixel) {
1799 // Imposing an arbitrary limit to zoom out as too much zoom out produces
1800 // segfaults for lack of memory. If somebody decides this is not high enough I
1801 // believe it can be raisen to higher values but some limit must be in place.
1803 // This constant represents 1 day @ 48kHz on a 1600 pixel wide display
1804 // all of which is used for the editor track displays. The whole day
1805 // would be 4147200000 samples, so 2592000 samples per pixel.
1807 nfpp = min (fpp, (framecnt_t) 2592000);
1808 nfpp = max ((framecnt_t) 1, nfpp);
1810 new_page_size = (framepos_t) floor (_visible_canvas_width * nfpp);
1811 half_page_size = new_page_size / 2;
1813 switch (zoom_focus) {
1815 leftmost_after_zoom = current_leftmost;
1818 case ZoomFocusRight:
1819 current_rightmost = leftmost_frame + current_page;
1820 if (current_rightmost < new_page_size) {
1821 leftmost_after_zoom = 0;
1823 leftmost_after_zoom = current_rightmost - new_page_size;
1827 case ZoomFocusCenter:
1828 current_center = current_leftmost + (current_page/2);
1829 if (current_center < half_page_size) {
1830 leftmost_after_zoom = 0;
1832 leftmost_after_zoom = current_center - half_page_size;
1836 case ZoomFocusPlayhead:
1837 /* centre playhead */
1838 l = playhead_cursor->current_frame () - (new_page_size * 0.5);
1841 leftmost_after_zoom = 0;
1842 } else if (l > max_framepos) {
1843 leftmost_after_zoom = max_framepos - new_page_size;
1845 leftmost_after_zoom = (framepos_t) l;
1849 case ZoomFocusMouse:
1850 /* try to keep the mouse over the same point in the display */
1852 if (_drags->active()) {
1853 where = _drags->current_pointer_frame ();
1854 } else if (!mouse_frame (where, in_track_canvas)) {
1855 use_mouse_frame = false;
1858 if (use_mouse_frame) {
1859 l = - ((new_page_size * ((where - current_leftmost)/(double)current_page)) - where);
1862 leftmost_after_zoom = 0;
1863 } else if (l > max_framepos) {
1864 leftmost_after_zoom = max_framepos - new_page_size;
1866 leftmost_after_zoom = (framepos_t) l;
1869 /* use playhead instead */
1870 where = playhead_cursor->current_frame ();
1872 if (where < half_page_size) {
1873 leftmost_after_zoom = 0;
1875 leftmost_after_zoom = where - half_page_size;
1881 /* try to keep the edit point in the same place */
1882 where = get_preferred_edit_position ();
1886 double l = - ((new_page_size * ((where - current_leftmost)/(double)current_page)) - where);
1889 leftmost_after_zoom = 0;
1890 } else if (l > max_framepos) {
1891 leftmost_after_zoom = max_framepos - new_page_size;
1893 leftmost_after_zoom = (framepos_t) l;
1897 /* edit point not defined */
1904 // leftmost_after_zoom = min (leftmost_after_zoom, _session->current_end_frame());
1906 reposition_and_zoom (leftmost_after_zoom, nfpp);
1910 Editor::calc_extra_zoom_edges(framepos_t &start, framepos_t &end)
1912 /* this func helps make sure we leave a little space
1913 at each end of the editor so that the zoom doesn't fit the region
1914 precisely to the screen.
1917 GdkScreen* screen = gdk_screen_get_default ();
1918 const gint pixwidth = gdk_screen_get_width (screen);
1919 const gint mmwidth = gdk_screen_get_width_mm (screen);
1920 const double pix_per_mm = (double) pixwidth/ (double) mmwidth;
1921 const double one_centimeter_in_pixels = pix_per_mm * 10.0;
1923 const framepos_t range = end - start;
1924 const framecnt_t new_fpp = (framecnt_t) ceil ((double) range / (double) _visible_canvas_width);
1925 const framepos_t extra_samples = (framepos_t) floor (one_centimeter_in_pixels * new_fpp);
1927 if (start > extra_samples) {
1928 start -= extra_samples;
1933 if (max_framepos - extra_samples > end) {
1934 end += extra_samples;
1941 Editor::get_selection_extents (framepos_t &start, framepos_t &end) const
1943 start = max_framepos;
1947 //ToDo: if notes are selected, set extents to that selection
1949 //ToDo: if control points are selected, set extents to that selection
1951 if ( !selection->regions.empty() ) {
1952 RegionSelection rs = get_regions_from_selection_and_entered ();
1954 for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
1956 if ((*i)->region()->position() < start) {
1957 start = (*i)->region()->position();
1960 if ((*i)->region()->last_frame() + 1 > end) {
1961 end = (*i)->region()->last_frame() + 1;
1965 } else if (!selection->time.empty()) {
1966 start = selection->time.start();
1967 end = selection->time.end_frame();
1969 ret = false; //no selection found
1972 if ((start == 0 && end == 0) || end < start) {
1981 Editor::temporal_zoom_selection (Editing::ZoomAxis axes)
1983 if (!selection) return;
1985 //ToDo: if notes are selected, zoom to that
1987 //ToDo: if control points are selected, zoom to that
1989 if (axes == Horizontal || axes == Both) {
1991 framepos_t start, end;
1992 if (get_selection_extents (start, end)) {
1993 calc_extra_zoom_edges (start, end);
1994 temporal_zoom_by_frame (start, end);
1998 if (axes == Vertical || axes == Both) {
2004 Editor::temporal_zoom_session ()
2006 ENSURE_GUI_THREAD (*this, &Editor::temporal_zoom_session)
2009 framecnt_t start = _session->current_start_frame();
2010 framecnt_t end = _session->current_end_frame();
2012 if (_session->actively_recording () ) {
2013 framepos_t cur = playhead_cursor->current_frame ();
2015 /* recording beyond the end marker; zoom out
2016 * by 5 seconds more so that if 'follow
2017 * playhead' is active we don't immediately
2020 end = cur + _session->frame_rate() * 5;
2024 if ((start == 0 && end == 0) || end < start) {
2028 calc_extra_zoom_edges(start, end);
2030 temporal_zoom_by_frame (start, end);
2035 Editor::temporal_zoom_by_frame (framepos_t start, framepos_t end)
2037 if (!_session) return;
2039 if ((start == 0 && end == 0) || end < start) {
2043 framepos_t range = end - start;
2045 const framecnt_t new_fpp = (framecnt_t) ceil ((double) range / (double) _visible_canvas_width);
2047 framepos_t new_page = range;
2048 framepos_t middle = (framepos_t) floor ((double) start + ((double) range / 2.0f));
2049 framepos_t new_leftmost = (framepos_t) floor ((double) middle - ((double) new_page / 2.0f));
2051 if (new_leftmost > middle) {
2055 if (new_leftmost < 0) {
2059 reposition_and_zoom (new_leftmost, new_fpp);
2063 Editor::temporal_zoom_to_frame (bool coarser, framepos_t frame)
2069 framecnt_t range_before = frame - leftmost_frame;
2073 if (samples_per_pixel <= 1) {
2076 new_spp = samples_per_pixel + (samples_per_pixel/2);
2078 range_before += range_before/2;
2080 if (samples_per_pixel >= 1) {
2081 new_spp = samples_per_pixel - (samples_per_pixel/2);
2083 /* could bail out here since we cannot zoom any finer,
2084 but leave that to the equality test below
2086 new_spp = samples_per_pixel;
2089 range_before -= range_before/2;
2092 if (new_spp == samples_per_pixel) {
2096 /* zoom focus is automatically taken as @param frame when this
2100 framepos_t new_leftmost = frame - (framepos_t)range_before;
2102 if (new_leftmost > frame) {
2106 if (new_leftmost < 0) {
2110 reposition_and_zoom (new_leftmost, new_spp);
2115 Editor::choose_new_marker_name(string &name) {
2117 if (!UIConfiguration::instance().get_name_new_markers()) {
2118 /* don't prompt user for a new name */
2122 ArdourPrompter dialog (true);
2124 dialog.set_prompt (_("New Name:"));
2126 dialog.set_title (_("New Location Marker"));
2128 dialog.set_name ("MarkNameWindow");
2129 dialog.set_size_request (250, -1);
2130 dialog.set_position (Gtk::WIN_POS_MOUSE);
2132 dialog.add_button (Stock::OK, RESPONSE_ACCEPT);
2133 dialog.set_initial_text (name);
2137 switch (dialog.run ()) {
2138 case RESPONSE_ACCEPT:
2144 dialog.get_result(name);
2151 Editor::add_location_from_selection ()
2155 if (selection->time.empty()) {
2159 if (_session == 0 || clicked_axisview == 0) {
2163 framepos_t start = selection->time[clicked_selection].start;
2164 framepos_t end = selection->time[clicked_selection].end;
2166 _session->locations()->next_available_name(rangename,"selection");
2167 Location *location = new Location (*_session, start, end, rangename, Location::IsRangeMarker, get_grid_music_divisions(0));
2169 begin_reversible_command (_("add marker"));
2171 XMLNode &before = _session->locations()->get_state();
2172 _session->locations()->add (location, true);
2173 XMLNode &after = _session->locations()->get_state();
2174 _session->add_command(new MementoCommand<Locations>(*(_session->locations()), &before, &after));
2176 commit_reversible_command ();
2180 Editor::add_location_mark (framepos_t where)
2184 select_new_marker = true;
2186 _session->locations()->next_available_name(markername,"mark");
2187 if (!choose_new_marker_name(markername)) {
2190 Location *location = new Location (*_session, where, where, markername, Location::IsMark, get_grid_music_divisions (0));
2191 begin_reversible_command (_("add marker"));
2193 XMLNode &before = _session->locations()->get_state();
2194 _session->locations()->add (location, true);
2195 XMLNode &after = _session->locations()->get_state();
2196 _session->add_command(new MementoCommand<Locations>(*(_session->locations()), &before, &after));
2198 commit_reversible_command ();
2202 Editor::set_session_start_from_playhead ()
2208 if ((loc = _session->locations()->session_range_location()) == 0) { //should never happen
2209 _session->set_session_extents ( _session->audible_frame(), _session->audible_frame() );
2211 XMLNode &before = loc->get_state();
2213 _session->set_session_extents ( _session->audible_frame(), loc->end() );
2215 XMLNode &after = loc->get_state();
2217 begin_reversible_command (_("Set session start"));
2219 _session->add_command (new MementoCommand<Location>(*loc, &before, &after));
2221 commit_reversible_command ();
2226 Editor::set_session_end_from_playhead ()
2232 if ((loc = _session->locations()->session_range_location()) == 0) { //should never happen
2233 _session->set_session_extents ( _session->audible_frame(), _session->audible_frame() );
2235 XMLNode &before = loc->get_state();
2237 _session->set_session_extents ( loc->start(), _session->audible_frame() );
2239 XMLNode &after = loc->get_state();
2241 begin_reversible_command (_("Set session start"));
2243 _session->add_command (new MementoCommand<Location>(*loc, &before, &after));
2245 commit_reversible_command ();
2248 _session->set_end_is_free (false);
2253 Editor::toggle_location_at_playhead_cursor ()
2255 if (!do_remove_location_at_playhead_cursor())
2257 add_location_from_playhead_cursor();
2262 Editor::add_location_from_playhead_cursor ()
2264 add_location_mark (_session->audible_frame());
2268 Editor::do_remove_location_at_playhead_cursor ()
2270 bool removed = false;
2273 XMLNode &before = _session->locations()->get_state();
2275 //find location(s) at this time
2276 Locations::LocationList locs;
2277 _session->locations()->find_all_between (_session->audible_frame(), _session->audible_frame()+1, locs, Location::Flags(0));
2278 for (Locations::LocationList::iterator i = locs.begin(); i != locs.end(); ++i) {
2279 if ((*i)->is_mark()) {
2280 _session->locations()->remove (*i);
2287 begin_reversible_command (_("remove marker"));
2288 XMLNode &after = _session->locations()->get_state();
2289 _session->add_command(new MementoCommand<Locations>(*(_session->locations()), &before, &after));
2290 commit_reversible_command ();
2297 Editor::remove_location_at_playhead_cursor ()
2299 do_remove_location_at_playhead_cursor ();
2302 /** Add a range marker around each selected region */
2304 Editor::add_locations_from_region ()
2306 RegionSelection rs = get_regions_from_selection_and_entered ();
2311 bool commit = false;
2313 XMLNode &before = _session->locations()->get_state();
2315 for (RegionSelection::iterator i = rs.begin (); i != rs.end (); ++i) {
2317 boost::shared_ptr<Region> region = (*i)->region ();
2319 Location *location = new Location (*_session, region->position(), region->last_frame(), region->name(), Location::IsRangeMarker, 0);
2321 _session->locations()->add (location, true);
2326 begin_reversible_command (selection->regions.size () > 1 ? _("add markers") : _("add marker"));
2327 XMLNode &after = _session->locations()->get_state();
2328 _session->add_command (new MementoCommand<Locations>(*(_session->locations()), &before, &after));
2329 commit_reversible_command ();
2333 /** Add a single range marker around all selected regions */
2335 Editor::add_location_from_region ()
2337 RegionSelection rs = get_regions_from_selection_and_entered ();
2343 XMLNode &before = _session->locations()->get_state();
2347 if (rs.size() > 1) {
2348 _session->locations()->next_available_name(markername, "regions");
2350 RegionView* rv = *(rs.begin());
2351 boost::shared_ptr<Region> region = rv->region();
2352 markername = region->name();
2355 if (!choose_new_marker_name(markername)) {
2359 // single range spanning all selected
2360 Location *location = new Location (*_session, selection->regions.start(), selection->regions.end_frame(), markername, Location::IsRangeMarker, 0);
2361 _session->locations()->add (location, true);
2363 begin_reversible_command (_("add marker"));
2364 XMLNode &after = _session->locations()->get_state();
2365 _session->add_command (new MementoCommand<Locations>(*(_session->locations()), &before, &after));
2366 commit_reversible_command ();
2372 Editor::jump_forward_to_mark ()
2378 framepos_t pos = _session->locations()->first_mark_after (playhead_cursor->current_frame());
2384 _session->request_locate (pos, _session->transport_rolling());
2388 Editor::jump_backward_to_mark ()
2394 framepos_t pos = _session->locations()->first_mark_before (playhead_cursor->current_frame());
2396 //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...
2397 if ( _session->transport_rolling() ) {
2398 if ( (playhead_cursor->current_frame() - pos) < _session->frame_rate()/2 ) {
2399 framepos_t prior = _session->locations()->first_mark_before ( pos );
2408 _session->request_locate (pos, _session->transport_rolling());
2414 framepos_t const pos = _session->audible_frame ();
2417 _session->locations()->next_available_name (markername, "mark");
2419 if (!choose_new_marker_name (markername)) {
2423 _session->locations()->add (new Location (*_session, pos, 0, markername, Location::IsMark, 0), true);
2427 Editor::clear_markers ()
2430 begin_reversible_command (_("clear markers"));
2432 XMLNode &before = _session->locations()->get_state();
2433 _session->locations()->clear_markers ();
2434 XMLNode &after = _session->locations()->get_state();
2435 _session->add_command(new MementoCommand<Locations>(*(_session->locations()), &before, &after));
2437 commit_reversible_command ();
2442 Editor::clear_ranges ()
2445 begin_reversible_command (_("clear ranges"));
2447 XMLNode &before = _session->locations()->get_state();
2449 _session->locations()->clear_ranges ();
2451 XMLNode &after = _session->locations()->get_state();
2452 _session->add_command(new MementoCommand<Locations>(*(_session->locations()), &before, &after));
2454 commit_reversible_command ();
2459 Editor::clear_locations ()
2461 begin_reversible_command (_("clear locations"));
2463 XMLNode &before = _session->locations()->get_state();
2464 _session->locations()->clear ();
2465 XMLNode &after = _session->locations()->get_state();
2466 _session->add_command(new MementoCommand<Locations>(*(_session->locations()), &before, &after));
2468 commit_reversible_command ();
2472 Editor::unhide_markers ()
2474 for (LocationMarkerMap::iterator i = location_markers.begin(); i != location_markers.end(); ++i) {
2475 Location *l = (*i).first;
2476 if (l->is_hidden() && l->is_mark()) {
2477 l->set_hidden(false, this);
2483 Editor::unhide_ranges ()
2485 for (LocationMarkerMap::iterator i = location_markers.begin(); i != location_markers.end(); ++i) {
2486 Location *l = (*i).first;
2487 if (l->is_hidden() && l->is_range_marker()) {
2488 l->set_hidden(false, this);
2493 /* INSERT/REPLACE */
2496 Editor::insert_region_list_selection (float times)
2498 RouteTimeAxisView *tv = 0;
2499 boost::shared_ptr<Playlist> playlist;
2501 if (clicked_routeview != 0) {
2502 tv = clicked_routeview;
2503 } else if (!selection->tracks.empty()) {
2504 if ((tv = dynamic_cast<RouteTimeAxisView*>(selection->tracks.front())) == 0) {
2507 } else if (entered_track != 0) {
2508 if ((tv = dynamic_cast<RouteTimeAxisView*>(entered_track)) == 0) {
2515 if ((playlist = tv->playlist()) == 0) {
2519 boost::shared_ptr<Region> region = _regions->get_single_selection ();
2524 begin_reversible_command (_("insert region"));
2525 playlist->clear_changes ();
2526 playlist->add_region ((RegionFactory::create (region, true)), get_preferred_edit_position(), times);
2527 if (Config->get_edit_mode() == Ripple)
2528 playlist->ripple (get_preferred_edit_position(), region->length() * times, boost::shared_ptr<Region>());
2530 _session->add_command(new StatefulDiffCommand (playlist));
2531 commit_reversible_command ();
2534 /* BUILT-IN EFFECTS */
2537 Editor::reverse_selection ()
2542 /* GAIN ENVELOPE EDITING */
2545 Editor::edit_envelope ()
2552 Editor::transition_to_rolling (bool fwd)
2558 if (_session->config.get_external_sync()) {
2559 switch (Config->get_sync_source()) {
2563 /* transport controlled by the master */
2568 if (_session->is_auditioning()) {
2569 _session->cancel_audition ();
2573 _session->request_transport_speed (fwd ? 1.0f : -1.0f);
2577 Editor::play_from_start ()
2579 _session->request_locate (_session->current_start_frame(), true);
2583 Editor::play_from_edit_point ()
2585 _session->request_locate (get_preferred_edit_position(), true);
2589 Editor::play_from_edit_point_and_return ()
2591 framepos_t start_frame;
2592 framepos_t return_frame;
2594 start_frame = get_preferred_edit_position ( EDIT_IGNORE_PHEAD );
2596 if (_session->transport_rolling()) {
2597 _session->request_locate (start_frame, false);
2601 /* don't reset the return frame if its already set */
2603 if ((return_frame = _session->requested_return_frame()) < 0) {
2604 return_frame = _session->audible_frame();
2607 if (start_frame >= 0) {
2608 _session->request_roll_at_and_return (start_frame, return_frame);
2613 Editor::play_selection ()
2615 framepos_t start, end;
2616 if (!get_selection_extents ( start, end))
2619 AudioRange ar (start, end, 0);
2620 list<AudioRange> lar;
2623 _session->request_play_range (&lar, true);
2628 Editor::maybe_locate_with_edit_preroll (framepos_t location)
2630 if ( _session->transport_rolling() || !UIConfiguration::instance().get_follow_edits() || _session->config.get_external_sync() )
2633 location -= _session->preroll_samples (location);
2635 //don't try to locate before the beginning of time
2640 //if follow_playhead is on, keep the playhead on the screen
2641 if ( _follow_playhead )
2642 if ( location < leftmost_frame )
2643 location = leftmost_frame;
2645 _session->request_locate( location );
2649 Editor::play_with_preroll ()
2651 framepos_t start, end;
2652 if ( UIConfiguration::instance().get_follow_edits() && get_selection_extents ( start, end) ) {
2653 const framepos_t preroll = _session->preroll_samples (start);
2655 framepos_t ret = start;
2657 if (start > preroll) {
2658 start = start - preroll;
2661 end = end + preroll; //"post-roll"
2663 AudioRange ar (start, end, 0);
2664 list<AudioRange> lar;
2667 _session->request_play_range (&lar, true);
2668 _session->set_requested_return_frame (ret); //force auto-return to return to range start, without the preroll
2670 framepos_t ph = playhead_cursor->current_frame ();
2671 const framepos_t preroll = _session->preroll_samples (ph);
2674 start = ph - preroll;
2678 _session->request_locate (start, true);
2679 _session->set_requested_return_frame (ph); //force auto-return to return to playhead location, without the preroll
2684 Editor::rec_with_preroll ()
2686 framepos_t ph = playhead_cursor->current_frame ();
2687 framepos_t preroll = _session->preroll_samples (ph);
2688 _session->request_preroll_record_trim (ph, preroll);
2692 Editor::rec_with_count_in ()
2694 _session->request_count_in_record ();
2698 Editor::play_location (Location& location)
2700 if (location.start() <= location.end()) {
2704 _session->request_bounded_roll (location.start(), location.end());
2708 Editor::loop_location (Location& location)
2710 if (location.start() <= location.end()) {
2716 if ((tll = transport_loop_location()) != 0) {
2717 tll->set (location.start(), location.end());
2719 // enable looping, reposition and start rolling
2720 _session->request_locate (tll->start(), true);
2721 _session->request_play_loop (true);
2726 Editor::do_layer_operation (LayerOperation op)
2728 if (selection->regions.empty ()) {
2732 bool const multiple = selection->regions.size() > 1;
2736 begin_reversible_command (_("raise regions"));
2738 begin_reversible_command (_("raise region"));
2744 begin_reversible_command (_("raise regions to top"));
2746 begin_reversible_command (_("raise region to top"));
2752 begin_reversible_command (_("lower regions"));
2754 begin_reversible_command (_("lower region"));
2760 begin_reversible_command (_("lower regions to bottom"));
2762 begin_reversible_command (_("lower region"));
2767 set<boost::shared_ptr<Playlist> > playlists = selection->regions.playlists ();
2768 for (set<boost::shared_ptr<Playlist> >::iterator i = playlists.begin(); i != playlists.end(); ++i) {
2769 (*i)->clear_owned_changes ();
2772 for (RegionSelection::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
2773 boost::shared_ptr<Region> r = (*i)->region ();
2785 r->lower_to_bottom ();
2789 for (set<boost::shared_ptr<Playlist> >::iterator i = playlists.begin(); i != playlists.end(); ++i) {
2790 vector<Command*> cmds;
2792 _session->add_commands (cmds);
2795 commit_reversible_command ();
2799 Editor::raise_region ()
2801 do_layer_operation (Raise);
2805 Editor::raise_region_to_top ()
2807 do_layer_operation (RaiseToTop);
2811 Editor::lower_region ()
2813 do_layer_operation (Lower);
2817 Editor::lower_region_to_bottom ()
2819 do_layer_operation (LowerToBottom);
2822 /** Show the region editor for the selected regions */
2824 Editor::show_region_properties ()
2826 selection->foreach_regionview (&RegionView::show_region_editor);
2829 /** Show the midi list editor for the selected MIDI regions */
2831 Editor::show_midi_list_editor ()
2833 selection->foreach_midi_regionview (&MidiRegionView::show_list_editor);
2837 Editor::rename_region ()
2839 RegionSelection rs = get_regions_from_selection_and_entered ();
2845 ArdourDialog d (_("Rename Region"), true, false);
2847 Label label (_("New name:"));
2850 hbox.set_spacing (6);
2851 hbox.pack_start (label, false, false);
2852 hbox.pack_start (entry, true, true);
2854 d.get_vbox()->set_border_width (12);
2855 d.get_vbox()->pack_start (hbox, false, false);
2857 d.add_button(Gtk::Stock::CANCEL, Gtk::RESPONSE_CANCEL);
2858 d.add_button(Gtk::Stock::OK, Gtk::RESPONSE_OK);
2860 d.set_size_request (300, -1);
2862 entry.set_text (rs.front()->region()->name());
2863 entry.select_region (0, -1);
2865 entry.signal_activate().connect (sigc::bind (sigc::mem_fun (d, &Dialog::response), RESPONSE_OK));
2871 int const ret = d.run();
2875 if (ret != RESPONSE_OK) {
2879 std::string str = entry.get_text();
2880 strip_whitespace_edges (str);
2882 rs.front()->region()->set_name (str);
2883 _regions->redisplay ();
2887 /** Start an audition of the first selected region */
2889 Editor::play_edit_range ()
2891 framepos_t start, end;
2893 if (get_edit_op_range (start, end)) {
2894 _session->request_bounded_roll (start, end);
2899 Editor::play_selected_region ()
2901 framepos_t start = max_framepos;
2904 RegionSelection rs = get_regions_from_selection_and_entered ();
2910 for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
2911 if ((*i)->region()->position() < start) {
2912 start = (*i)->region()->position();
2914 if ((*i)->region()->last_frame() + 1 > end) {
2915 end = (*i)->region()->last_frame() + 1;
2919 _session->request_bounded_roll (start, end);
2923 Editor::audition_playlist_region_standalone (boost::shared_ptr<Region> region)
2925 _session->audition_region (region);
2929 Editor::region_from_selection ()
2931 if (clicked_axisview == 0) {
2935 if (selection->time.empty()) {
2939 framepos_t start = selection->time[clicked_selection].start;
2940 framepos_t end = selection->time[clicked_selection].end;
2942 TrackViewList tracks = get_tracks_for_range_action ();
2944 framepos_t selection_cnt = end - start + 1;
2946 for (TrackSelection::iterator i = tracks.begin(); i != tracks.end(); ++i) {
2947 boost::shared_ptr<Region> current;
2948 boost::shared_ptr<Playlist> pl;
2949 framepos_t internal_start;
2952 if ((pl = (*i)->playlist()) == 0) {
2956 if ((current = pl->top_region_at (start)) == 0) {
2960 internal_start = start - current->position();
2961 RegionFactory::region_name (new_name, current->name(), true);
2965 plist.add (ARDOUR::Properties::start, current->start() + internal_start);
2966 plist.add (ARDOUR::Properties::length, selection_cnt);
2967 plist.add (ARDOUR::Properties::name, new_name);
2968 plist.add (ARDOUR::Properties::layer, 0);
2970 boost::shared_ptr<Region> region (RegionFactory::create (current, plist));
2975 Editor::create_region_from_selection (vector<boost::shared_ptr<Region> >& new_regions)
2977 if (selection->time.empty() || selection->tracks.empty()) {
2981 framepos_t start, end;
2982 if (clicked_selection) {
2983 start = selection->time[clicked_selection].start;
2984 end = selection->time[clicked_selection].end;
2986 start = selection->time.start();
2987 end = selection->time.end_frame();
2990 TrackViewList ts = selection->tracks.filter_to_unique_playlists ();
2991 sort_track_selection (ts);
2993 for (TrackSelection::iterator i = ts.begin(); i != ts.end(); ++i) {
2994 boost::shared_ptr<Region> current;
2995 boost::shared_ptr<Playlist> playlist;
2996 framepos_t internal_start;
2999 if ((playlist = (*i)->playlist()) == 0) {
3003 if ((current = playlist->top_region_at(start)) == 0) {
3007 internal_start = start - current->position();
3008 RegionFactory::region_name (new_name, current->name(), true);
3012 plist.add (ARDOUR::Properties::start, current->start() + internal_start);
3013 plist.add (ARDOUR::Properties::length, end - start + 1);
3014 plist.add (ARDOUR::Properties::name, new_name);
3016 new_regions.push_back (RegionFactory::create (current, plist));
3021 Editor::split_multichannel_region ()
3023 RegionSelection rs = get_regions_from_selection_and_entered ();
3029 vector< boost::shared_ptr<Region> > v;
3031 for (list<RegionView*>::iterator x = rs.begin(); x != rs.end(); ++x) {
3032 (*x)->region()->separate_by_channel (*_session, v);
3037 Editor::new_region_from_selection ()
3039 region_from_selection ();
3040 cancel_selection ();
3044 add_if_covered (RegionView* rv, const AudioRange* ar, RegionSelection* rs)
3046 switch (rv->region()->coverage (ar->start, ar->end - 1)) {
3047 // n.b. -1 because AudioRange::end is one past the end, but coverage expects inclusive ranges
3048 case Evoral::OverlapNone:
3056 * - selected tracks, or if there are none...
3057 * - tracks containing selected regions, or if there are none...
3062 Editor::get_tracks_for_range_action () const
3066 if (selection->tracks.empty()) {
3068 /* use tracks with selected regions */
3070 RegionSelection rs = selection->regions;
3072 for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
3073 TimeAxisView* tv = &(*i)->get_time_axis_view();
3075 if (!t.contains (tv)) {
3081 /* no regions and no tracks: use all tracks */
3087 t = selection->tracks;
3090 return t.filter_to_unique_playlists();
3094 Editor::separate_regions_between (const TimeSelection& ts)
3096 bool in_command = false;
3097 boost::shared_ptr<Playlist> playlist;
3098 RegionSelection new_selection;
3100 TrackViewList tmptracks = get_tracks_for_range_action ();
3101 sort_track_selection (tmptracks);
3103 for (TrackSelection::iterator i = tmptracks.begin(); i != tmptracks.end(); ++i) {
3105 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> ((*i));
3111 if (!rtv->is_track()) {
3115 /* no edits to destructive tracks */
3117 if (rtv->track()->destructive()) {
3121 if ((playlist = rtv->playlist()) != 0) {
3123 playlist->clear_changes ();
3125 /* XXX need to consider musical time selections here at some point */
3127 double speed = rtv->track()->speed();
3129 for (list<AudioRange>::const_iterator t = ts.begin(); t != ts.end(); ++t) {
3131 sigc::connection c = rtv->view()->RegionViewAdded.connect (
3132 sigc::mem_fun(*this, &Editor::collect_new_region_view));
3134 latest_regionviews.clear ();
3136 playlist->partition ((framepos_t)((*t).start * speed),
3137 (framepos_t)((*t).end * speed), false);
3141 if (!latest_regionviews.empty()) {
3143 rtv->view()->foreach_regionview (sigc::bind (
3144 sigc::ptr_fun (add_if_covered),
3145 &(*t), &new_selection));
3148 begin_reversible_command (_("separate"));
3152 /* pick up changes to existing regions */
3154 vector<Command*> cmds;
3155 playlist->rdiff (cmds);
3156 _session->add_commands (cmds);
3158 /* pick up changes to the playlist itself (adds/removes)
3161 _session->add_command(new StatefulDiffCommand (playlist));
3168 // selection->set (new_selection);
3170 commit_reversible_command ();
3174 struct PlaylistState {
3175 boost::shared_ptr<Playlist> playlist;
3179 /** Take tracks from get_tracks_for_range_action and cut any regions
3180 * on those tracks so that the tracks are empty over the time
3184 Editor::separate_region_from_selection ()
3186 /* preferentially use *all* ranges in the time selection if we're in range mode
3187 to allow discontiguous operation, since get_edit_op_range() currently
3188 returns a single range.
3191 if (!selection->time.empty()) {
3193 separate_regions_between (selection->time);
3200 if (get_edit_op_range (start, end)) {
3202 AudioRange ar (start, end, 1);
3206 separate_regions_between (ts);
3212 Editor::separate_region_from_punch ()
3214 Location* loc = _session->locations()->auto_punch_location();
3216 separate_regions_using_location (*loc);
3221 Editor::separate_region_from_loop ()
3223 Location* loc = _session->locations()->auto_loop_location();
3225 separate_regions_using_location (*loc);
3230 Editor::separate_regions_using_location (Location& loc)
3232 if (loc.is_mark()) {
3236 AudioRange ar (loc.start(), loc.end(), 1);
3241 separate_regions_between (ts);
3244 /** Separate regions under the selected region */
3246 Editor::separate_under_selected_regions ()
3248 vector<PlaylistState> playlists;
3252 rs = get_regions_from_selection_and_entered();
3254 if (!_session || rs.empty()) {
3258 begin_reversible_command (_("separate region under"));
3260 list<boost::shared_ptr<Region> > regions_to_remove;
3262 for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
3263 // we can't just remove the region(s) in this loop because
3264 // this removes them from the RegionSelection, and they thus
3265 // disappear from underneath the iterator, and the ++i above
3266 // SEGVs in a puzzling fashion.
3268 // so, first iterate over the regions to be removed from rs and
3269 // add them to the regions_to_remove list, and then
3270 // iterate over the list to actually remove them.
3272 regions_to_remove.push_back ((*i)->region());
3275 for (list<boost::shared_ptr<Region> >::iterator rl = regions_to_remove.begin(); rl != regions_to_remove.end(); ++rl) {
3277 boost::shared_ptr<Playlist> playlist = (*rl)->playlist();
3280 // is this check necessary?
3284 vector<PlaylistState>::iterator i;
3286 //only take state if this is a new playlist.
3287 for (i = playlists.begin(); i != playlists.end(); ++i) {
3288 if ((*i).playlist == playlist) {
3293 if (i == playlists.end()) {
3295 PlaylistState before;
3296 before.playlist = playlist;
3297 before.before = &playlist->get_state();
3299 playlist->freeze ();
3300 playlists.push_back(before);
3303 //Partition on the region bounds
3304 playlist->partition ((*rl)->first_frame() - 1, (*rl)->last_frame() + 1, true);
3306 //Re-add region that was just removed due to the partition operation
3307 playlist->add_region( (*rl), (*rl)->first_frame() );
3310 vector<PlaylistState>::iterator pl;
3312 for (pl = playlists.begin(); pl != playlists.end(); ++pl) {
3313 (*pl).playlist->thaw ();
3314 _session->add_command(new MementoCommand<Playlist>(*(*pl).playlist, (*pl).before, &(*pl).playlist->get_state()));
3317 commit_reversible_command ();
3321 Editor::crop_region_to_selection ()
3323 if (!selection->time.empty()) {
3325 crop_region_to (selection->time.start(), selection->time.end_frame());
3332 if (get_edit_op_range (start, end)) {
3333 crop_region_to (start, end);
3340 Editor::crop_region_to (framepos_t start, framepos_t end)
3342 vector<boost::shared_ptr<Playlist> > playlists;
3343 boost::shared_ptr<Playlist> playlist;
3346 if (selection->tracks.empty()) {
3347 ts = track_views.filter_to_unique_playlists();
3349 ts = selection->tracks.filter_to_unique_playlists ();
3352 sort_track_selection (ts);
3354 for (TrackSelection::iterator i = ts.begin(); i != ts.end(); ++i) {
3356 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> ((*i));
3362 boost::shared_ptr<Track> t = rtv->track();
3364 if (t != 0 && ! t->destructive()) {
3366 if ((playlist = rtv->playlist()) != 0) {
3367 playlists.push_back (playlist);
3372 if (playlists.empty()) {
3377 framepos_t new_start;
3379 framecnt_t new_length;
3380 bool in_command = false;
3382 for (vector<boost::shared_ptr<Playlist> >::iterator i = playlists.begin(); i != playlists.end(); ++i) {
3384 /* Only the top regions at start and end have to be cropped */
3385 boost::shared_ptr<Region> region_at_start = (*i)->top_region_at(start);
3386 boost::shared_ptr<Region> region_at_end = (*i)->top_region_at(end);
3388 vector<boost::shared_ptr<Region> > regions;
3390 if (region_at_start != 0) {
3391 regions.push_back (region_at_start);
3393 if (region_at_end != 0) {
3394 regions.push_back (region_at_end);
3397 /* now adjust lengths */
3398 for (vector<boost::shared_ptr<Region> >::iterator i = regions.begin(); i != regions.end(); ++i) {
3400 pos = (*i)->position();
3401 new_start = max (start, pos);
3402 if (max_framepos - pos > (*i)->length()) {
3403 new_end = pos + (*i)->length() - 1;
3405 new_end = max_framepos;
3407 new_end = min (end, new_end);
3408 new_length = new_end - new_start + 1;
3411 begin_reversible_command (_("trim to selection"));
3414 (*i)->clear_changes ();
3415 (*i)->trim_to (new_start, new_length);
3416 _session->add_command (new StatefulDiffCommand (*i));
3421 commit_reversible_command ();
3426 Editor::region_fill_track ()
3428 boost::shared_ptr<Playlist> playlist;
3429 RegionSelection regions = get_regions_from_selection_and_entered ();
3430 RegionSelection foo;
3432 framepos_t const end = _session->current_end_frame ();
3434 if (regions.empty () || regions.end_frame () + 1 >= end) {
3438 framepos_t const start_frame = regions.start ();
3439 framepos_t const end_frame = regions.end_frame ();
3440 framecnt_t const gap = end_frame - start_frame + 1;
3442 begin_reversible_command (Operations::region_fill);
3444 selection->clear_regions ();
3446 for (RegionSelection::iterator i = regions.begin(); i != regions.end(); ++i) {
3448 boost::shared_ptr<Region> r ((*i)->region());
3450 TimeAxisView& tv = (*i)->get_time_axis_view();
3451 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (&tv);
3452 latest_regionviews.clear ();
3453 sigc::connection c = rtv->view()->RegionViewAdded.connect (sigc::mem_fun(*this, &Editor::collect_new_region_view));
3455 framepos_t const position = end_frame + (r->first_frame() - start_frame + 1);
3456 playlist = (*i)->region()->playlist();
3457 playlist->clear_changes ();
3458 playlist->duplicate_until (r, position, gap, end);
3459 _session->add_command(new StatefulDiffCommand (playlist));
3463 foo.insert (foo.end(), latest_regionviews.begin(), latest_regionviews.end());
3467 selection->set (foo);
3470 commit_reversible_command ();
3474 Editor::set_region_sync_position ()
3476 set_sync_point (get_preferred_edit_position (), get_regions_from_selection_and_edit_point ());
3480 Editor::set_sync_point (framepos_t where, const RegionSelection& rs)
3482 bool in_command = false;
3484 for (RegionSelection::const_iterator r = rs.begin(); r != rs.end(); ++r) {
3486 if (!(*r)->region()->covers (where)) {
3490 boost::shared_ptr<Region> region ((*r)->region());
3493 begin_reversible_command (_("set sync point"));
3497 region->clear_changes ();
3498 region->set_sync_position (where);
3499 _session->add_command(new StatefulDiffCommand (region));
3503 commit_reversible_command ();
3507 /** Remove the sync positions of the selection */
3509 Editor::remove_region_sync ()
3511 RegionSelection rs = get_regions_from_selection_and_entered ();
3517 begin_reversible_command (_("remove region sync"));
3519 for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
3521 (*i)->region()->clear_changes ();
3522 (*i)->region()->clear_sync_position ();
3523 _session->add_command(new StatefulDiffCommand ((*i)->region()));
3526 commit_reversible_command ();
3530 Editor::naturalize_region ()
3532 RegionSelection rs = get_regions_from_selection_and_entered ();
3538 if (rs.size() > 1) {
3539 begin_reversible_command (_("move regions to original position"));
3541 begin_reversible_command (_("move region to original position"));
3544 for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
3545 (*i)->region()->clear_changes ();
3546 (*i)->region()->move_to_natural_position ();
3547 _session->add_command (new StatefulDiffCommand ((*i)->region()));
3550 commit_reversible_command ();
3554 Editor::align_regions (RegionPoint what)
3556 RegionSelection const rs = get_regions_from_selection_and_edit_point ();
3562 begin_reversible_command (_("align selection"));
3564 framepos_t const position = get_preferred_edit_position ();
3566 for (RegionSelection::const_iterator i = rs.begin(); i != rs.end(); ++i) {
3567 align_region_internal ((*i)->region(), what, position);
3570 commit_reversible_command ();
3573 struct RegionSortByTime {
3574 bool operator() (const RegionView* a, const RegionView* b) {
3575 return a->region()->position() < b->region()->position();
3580 Editor::align_regions_relative (RegionPoint point)
3582 RegionSelection const rs = get_regions_from_selection_and_edit_point ();
3588 framepos_t const position = get_preferred_edit_position ();
3590 framepos_t distance = 0;
3594 list<RegionView*> sorted;
3595 rs.by_position (sorted);
3597 boost::shared_ptr<Region> r ((*sorted.begin())->region());
3602 if (position > r->position()) {
3603 distance = position - r->position();
3605 distance = r->position() - position;
3611 if (position > r->last_frame()) {
3612 distance = position - r->last_frame();
3613 pos = r->position() + distance;
3615 distance = r->last_frame() - position;
3616 pos = r->position() - distance;
3622 pos = r->adjust_to_sync (position);
3623 if (pos > r->position()) {
3624 distance = pos - r->position();
3626 distance = r->position() - pos;
3632 if (pos == r->position()) {
3636 begin_reversible_command (_("align selection (relative)"));
3638 /* move first one specially */
3640 r->clear_changes ();
3641 r->set_position (pos);
3642 _session->add_command(new StatefulDiffCommand (r));
3644 /* move rest by the same amount */
3648 for (list<RegionView*>::iterator i = sorted.begin(); i != sorted.end(); ++i) {
3650 boost::shared_ptr<Region> region ((*i)->region());
3652 region->clear_changes ();
3655 region->set_position (region->position() + distance);
3657 region->set_position (region->position() - distance);
3660 _session->add_command(new StatefulDiffCommand (region));
3664 commit_reversible_command ();
3668 Editor::align_region (boost::shared_ptr<Region> region, RegionPoint point, framepos_t position)
3670 begin_reversible_command (_("align region"));
3671 align_region_internal (region, point, position);
3672 commit_reversible_command ();
3676 Editor::align_region_internal (boost::shared_ptr<Region> region, RegionPoint point, framepos_t position)
3678 region->clear_changes ();
3682 region->set_position (region->adjust_to_sync (position));
3686 if (position > region->length()) {
3687 region->set_position (position - region->length());
3692 region->set_position (position);
3696 _session->add_command(new StatefulDiffCommand (region));
3700 Editor::trim_region_front ()
3706 Editor::trim_region_back ()
3708 trim_region (false);
3712 Editor::trim_region (bool front)
3714 framepos_t where = get_preferred_edit_position();
3715 RegionSelection rs = get_regions_from_selection_and_edit_point ();
3721 begin_reversible_command (front ? _("trim front") : _("trim back"));
3723 for (list<RegionView*>::const_iterator i = rs.by_layer().begin(); i != rs.by_layer().end(); ++i) {
3724 if (!(*i)->region()->locked()) {
3726 (*i)->region()->clear_changes ();
3729 (*i)->region()->trim_front (where);
3731 (*i)->region()->trim_end (where);
3734 _session->add_command (new StatefulDiffCommand ((*i)->region()));
3738 commit_reversible_command ();
3741 /** Trim the end of the selected regions to the position of the edit cursor */
3743 Editor::trim_region_to_loop ()
3745 Location* loc = _session->locations()->auto_loop_location();
3749 trim_region_to_location (*loc, _("trim to loop"));
3753 Editor::trim_region_to_punch ()
3755 Location* loc = _session->locations()->auto_punch_location();
3759 trim_region_to_location (*loc, _("trim to punch"));
3763 Editor::trim_region_to_location (const Location& loc, const char* str)
3765 RegionSelection rs = get_regions_from_selection_and_entered ();
3766 bool in_command = false;
3768 for (RegionSelection::iterator x = rs.begin(); x != rs.end(); ++x) {
3769 RegionView* rv = (*x);
3771 /* require region to span proposed trim */
3772 switch (rv->region()->coverage (loc.start(), loc.end())) {
3773 case Evoral::OverlapInternal:
3779 RouteTimeAxisView* tav = dynamic_cast<RouteTimeAxisView*> (&rv->get_time_axis_view());
3788 if (tav->track() != 0) {
3789 speed = tav->track()->speed();
3792 start = session_frame_to_track_frame (loc.start(), speed);
3793 end = session_frame_to_track_frame (loc.end(), speed);
3795 rv->region()->clear_changes ();
3796 rv->region()->trim_to (start, (end - start));
3799 begin_reversible_command (str);
3802 _session->add_command(new StatefulDiffCommand (rv->region()));
3806 commit_reversible_command ();
3811 Editor::trim_region_to_previous_region_end ()
3813 return trim_to_region(false);
3817 Editor::trim_region_to_next_region_start ()
3819 return trim_to_region(true);
3823 Editor::trim_to_region(bool forward)
3825 RegionSelection rs = get_regions_from_selection_and_entered ();
3826 bool in_command = false;
3828 boost::shared_ptr<Region> next_region;
3830 for (RegionSelection::iterator x = rs.begin(); x != rs.end(); ++x) {
3832 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (*x);
3838 AudioTimeAxisView* atav = dynamic_cast<AudioTimeAxisView*> (&arv->get_time_axis_view());
3846 if (atav->track() != 0) {
3847 speed = atav->track()->speed();
3851 boost::shared_ptr<Region> region = arv->region();
3852 boost::shared_ptr<Playlist> playlist (region->playlist());
3854 region->clear_changes ();
3858 next_region = playlist->find_next_region (region->first_frame(), Start, 1);
3864 region->trim_end((framepos_t) ( (next_region->first_frame() - 1) * speed));
3865 arv->region_changed (PropertyChange (ARDOUR::Properties::length));
3869 next_region = playlist->find_next_region (region->first_frame(), Start, 0);
3875 region->trim_front((framepos_t) ((next_region->last_frame() + 1) * speed));
3877 arv->region_changed (ARDOUR::bounds_change);
3881 begin_reversible_command (_("trim to region"));
3884 _session->add_command(new StatefulDiffCommand (region));
3888 commit_reversible_command ();
3893 Editor::unfreeze_route ()
3895 if (clicked_routeview == 0 || !clicked_routeview->is_track()) {
3899 clicked_routeview->track()->unfreeze ();
3903 Editor::_freeze_thread (void* arg)
3905 return static_cast<Editor*>(arg)->freeze_thread ();
3909 Editor::freeze_thread ()
3911 /* create event pool because we may need to talk to the session */
3912 SessionEvent::create_per_thread_pool ("freeze events", 64);
3913 /* create per-thread buffers for process() tree to use */
3914 clicked_routeview->audio_track()->freeze_me (*current_interthread_info);
3915 current_interthread_info->done = true;
3920 Editor::freeze_route ()
3926 /* stop transport before we start. this is important */
3928 _session->request_transport_speed (0.0);
3930 /* wait for just a little while, because the above call is asynchronous */
3932 Glib::usleep (250000);
3934 if (clicked_routeview == 0 || !clicked_routeview->is_audio_track()) {
3938 if (!clicked_routeview->track()->bounceable (clicked_routeview->track()->main_outs(), true)) {
3940 _("This track/bus cannot be frozen because the signal adds or loses channels before reaching the outputs.\n"
3941 "This is typically caused by plugins that generate stereo output from mono input or vice versa.")
3943 d.set_title (_("Cannot freeze"));
3948 if (clicked_routeview->track()->has_external_redirects()) {
3949 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"
3950 "Freezing will only process the signal as far as the first send/insert/return."),
3951 clicked_routeview->track()->name()), true, MESSAGE_INFO, BUTTONS_NONE, true);
3953 d.add_button (_("Freeze anyway"), Gtk::RESPONSE_OK);
3954 d.add_button (_("Don't freeze"), Gtk::RESPONSE_CANCEL);
3955 d.set_title (_("Freeze Limits"));
3957 int response = d.run ();
3960 case Gtk::RESPONSE_CANCEL:
3967 InterThreadInfo itt;
3968 current_interthread_info = &itt;
3970 InterthreadProgressWindow ipw (current_interthread_info, _("Freeze"), _("Cancel Freeze"));
3972 pthread_create_and_store (X_("freezer"), &itt.thread, _freeze_thread, this);
3974 CursorContext::Handle cursor_ctx = CursorContext::create(*this, _cursors->wait);
3976 while (!itt.done && !itt.cancel) {
3977 gtk_main_iteration ();
3980 pthread_join (itt.thread, 0);
3981 current_interthread_info = 0;
3985 Editor::bounce_range_selection (bool replace, bool enable_processing)
3987 if (selection->time.empty()) {
3991 TrackSelection views = selection->tracks;
3993 for (TrackViewList::iterator i = views.begin(); i != views.end(); ++i) {
3995 if (enable_processing) {
3997 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (*i);
3999 if (rtv && rtv->track() && replace && enable_processing && !rtv->track()->bounceable (rtv->track()->main_outs(), false)) {
4001 _("You can't perform this operation because the processing of the signal "
4002 "will cause one or more of the tracks to end up with a region with more channels than this track has inputs.\n\n"
4003 "You can do this without processing, which is a different operation.")
4005 d.set_title (_("Cannot bounce"));
4012 framepos_t start = selection->time[clicked_selection].start;
4013 framepos_t end = selection->time[clicked_selection].end;
4014 framepos_t cnt = end - start + 1;
4015 bool in_command = false;
4017 for (TrackViewList::iterator i = views.begin(); i != views.end(); ++i) {
4019 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (*i);
4025 boost::shared_ptr<Playlist> playlist;
4027 if ((playlist = rtv->playlist()) == 0) {
4031 InterThreadInfo itt;
4033 playlist->clear_changes ();
4034 playlist->clear_owned_changes ();
4036 boost::shared_ptr<Region> r;
4038 if (enable_processing) {
4039 r = rtv->track()->bounce_range (start, start+cnt, itt, rtv->track()->main_outs(), false);
4041 r = rtv->track()->bounce_range (start, start+cnt, itt, boost::shared_ptr<Processor>(), false);
4049 list<AudioRange> ranges;
4050 ranges.push_back (AudioRange (start, start+cnt, 0));
4051 playlist->cut (ranges); // discard result
4052 playlist->add_region (r, start);
4056 begin_reversible_command (_("bounce range"));
4059 vector<Command*> cmds;
4060 playlist->rdiff (cmds);
4061 _session->add_commands (cmds);
4063 _session->add_command (new StatefulDiffCommand (playlist));
4067 commit_reversible_command ();
4071 /** Delete selected regions, automation points or a time range */
4075 //special case: if the user is pointing in the editor/mixer strip, they may be trying to delete a plugin.
4076 //we need this because the editor-mixer strip is in the editor window, so it doesn't get the bindings from the mix window
4077 bool deleted = false;
4078 if ( current_mixer_strip && current_mixer_strip == MixerStrip::entered_mixer_strip() )
4079 deleted = current_mixer_strip->delete_processors ();
4085 /** Cut selected regions, automation points or a time range */
4092 /** Copy selected regions, automation points or a time range */
4100 /** @return true if a Cut, Copy or Clear is possible */
4102 Editor::can_cut_copy () const
4104 if (!selection->time.empty() || !selection->regions.empty() || !selection->points.empty())
4111 /** Cut, copy or clear selected regions, automation points or a time range.
4112 * @param op Operation (Delete, Cut, Copy or Clear)
4115 Editor::cut_copy (CutCopyOp op)
4117 /* only cancel selection if cut/copy is successful.*/
4123 opname = _("delete");
4132 opname = _("clear");
4136 /* if we're deleting something, and the mouse is still pressed,
4137 the thing we started a drag for will be gone when we release
4138 the mouse button(s). avoid this. see part 2 at the end of
4142 if (op == Delete || op == Cut || op == Clear) {
4143 if (_drags->active ()) {
4148 if ( op != Delete ) //"Delete" doesn't change copy/paste buf
4149 cut_buffer->clear ();
4151 if (entered_marker) {
4153 /* cut/delete op while pointing at a marker */
4156 Location* loc = find_location_from_marker (entered_marker, ignored);
4158 if (_session && loc) {
4159 entered_marker = NULL;
4160 Glib::signal_idle().connect (sigc::bind (sigc::mem_fun(*this, &Editor::really_remove_marker), loc));
4167 switch (mouse_mode) {
4170 begin_reversible_command (opname + ' ' + X_("MIDI"));
4172 commit_reversible_command ();
4178 bool did_edit = false;
4180 if (!selection->regions.empty() || !selection->points.empty()) {
4181 begin_reversible_command (opname + ' ' + _("objects"));
4184 if (!selection->regions.empty()) {
4185 cut_copy_regions (op, selection->regions);
4187 if (op == Cut || op == Delete) {
4188 selection->clear_regions ();
4192 if (!selection->points.empty()) {
4193 cut_copy_points (op);
4195 if (op == Cut || op == Delete) {
4196 selection->clear_points ();
4199 } else if (selection->time.empty()) {
4200 framepos_t start, end;
4201 /* no time selection, see if we can get an edit range
4204 if (get_edit_op_range (start, end)) {
4205 selection->set (start, end);
4207 } else if (!selection->time.empty()) {
4208 begin_reversible_command (opname + ' ' + _("range"));
4211 cut_copy_ranges (op);
4213 if (op == Cut || op == Delete) {
4214 selection->clear_time ();
4219 /* reset repeated paste state */
4222 commit_reversible_command ();
4225 if (op == Delete || op == Cut || op == Clear) {
4231 struct AutomationRecord {
4232 AutomationRecord () : state (0) , line(NULL) {}
4233 AutomationRecord (XMLNode* s, const AutomationLine* l) : state (s) , line (l) {}
4235 XMLNode* state; ///< state before any operation
4236 const AutomationLine* line; ///< line this came from
4237 boost::shared_ptr<Evoral::ControlList> copy; ///< copied events for the cut buffer
4240 struct PointsSelectionPositionSorter {
4241 bool operator() (ControlPoint* a, ControlPoint* b) {
4242 return (*(a->model()))->when < (*(b->model()))->when;
4246 /** Cut, copy or clear selected automation points.
4247 * @param op Operation (Cut, Copy or Clear)
4250 Editor::cut_copy_points (Editing::CutCopyOp op, Evoral::Beats earliest, bool midi)
4252 if (selection->points.empty ()) {
4256 /* XXX: not ideal, as there may be more than one track involved in the point selection */
4257 _last_cut_copy_source_track = &selection->points.front()->line().trackview;
4259 /* Keep a record of the AutomationLists that we end up using in this operation */
4260 typedef std::map<boost::shared_ptr<AutomationList>, AutomationRecord> Lists;
4263 /* user could select points in any order */
4264 selection->points.sort(PointsSelectionPositionSorter ());
4266 /* Go through all selected points, making an AutomationRecord for each distinct AutomationList */
4267 for (PointSelection::iterator sel_point = selection->points.begin(); sel_point != selection->points.end(); ++sel_point) {
4268 const AutomationLine& line = (*sel_point)->line();
4269 const boost::shared_ptr<AutomationList> al = line.the_list();
4270 if (lists.find (al) == lists.end ()) {
4271 /* We haven't seen this list yet, so make a record for it. This includes
4272 taking a copy of its current state, in case this is needed for undo later.
4274 lists[al] = AutomationRecord (&al->get_state (), &line);
4278 if (op == Cut || op == Copy) {
4279 /* This operation will involve putting things in the cut buffer, so create an empty
4280 ControlList for each of our source lists to put the cut buffer data in.
4282 for (Lists::iterator i = lists.begin(); i != lists.end(); ++i) {
4283 i->second.copy = i->first->create (i->first->parameter (), i->first->descriptor());
4286 /* Add all selected points to the relevant copy ControlLists */
4287 MusicFrame start (std::numeric_limits<framepos_t>::max(), 0);
4288 for (PointSelection::iterator sel_point = selection->points.begin(); sel_point != selection->points.end(); ++sel_point) {
4289 boost::shared_ptr<AutomationList> al = (*sel_point)->line().the_list();
4290 AutomationList::const_iterator ctrl_evt = (*sel_point)->model ();
4292 lists[al].copy->fast_simple_add ((*ctrl_evt)->when, (*ctrl_evt)->value);
4294 /* Update earliest MIDI start time in beats */
4295 earliest = std::min(earliest, Evoral::Beats((*ctrl_evt)->when));
4297 /* Update earliest session start time in frames */
4298 start.frame = std::min(start.frame, (*sel_point)->line().session_position(ctrl_evt));
4302 /* Snap start time backwards, so copy/paste is snap aligned. */
4304 if (earliest == Evoral::Beats::max()) {
4305 earliest = Evoral::Beats(); // Weird... don't offset
4307 earliest.round_down_to_beat();
4309 if (start.frame == std::numeric_limits<double>::max()) {
4310 start.frame = 0; // Weird... don't offset
4312 snap_to(start, RoundDownMaybe);
4315 const double line_offset = midi ? earliest.to_double() : start.frame;
4316 for (Lists::iterator i = lists.begin(); i != lists.end(); ++i) {
4317 /* Correct this copy list so that it is relative to the earliest
4318 start time, so relative ordering between points is preserved
4319 when copying from several lists and the paste starts at the
4320 earliest copied piece of data. */
4321 boost::shared_ptr<Evoral::ControlList> &al_cpy = i->second.copy;
4322 for (AutomationList::iterator ctrl_evt = al_cpy->begin(); ctrl_evt != al_cpy->end(); ++ctrl_evt) {
4323 (*ctrl_evt)->when -= line_offset;
4326 /* And add it to the cut buffer */
4327 cut_buffer->add (al_cpy);
4331 if (op == Delete || op == Cut) {
4332 /* This operation needs to remove things from the main AutomationList, so do that now */
4334 for (Lists::iterator i = lists.begin(); i != lists.end(); ++i) {
4335 i->first->freeze ();
4338 /* Remove each selected point from its AutomationList */
4339 for (PointSelection::iterator sel_point = selection->points.begin(); sel_point != selection->points.end(); ++sel_point) {
4340 AutomationLine& line = (*sel_point)->line ();
4341 boost::shared_ptr<AutomationList> al = line.the_list();
4345 if (dynamic_cast<AudioRegionGainLine*> (&line)) {
4346 /* removing of first and last gain point in region gain lines is prohibited*/
4347 if (line.is_last_point (*(*sel_point)) || line.is_first_point (*(*sel_point))) {
4353 al->erase ((*sel_point)->model ());
4357 /* Thaw the lists and add undo records for them */
4358 for (Lists::iterator i = lists.begin(); i != lists.end(); ++i) {
4359 boost::shared_ptr<AutomationList> al = i->first;
4361 _session->add_command (new MementoCommand<AutomationList> (*al.get(), i->second.state, &(al->get_state ())));
4366 /** Cut, copy or clear selected automation points.
4367 * @param op Operation (Cut, Copy or Clear)
4370 Editor::cut_copy_midi (CutCopyOp op)
4372 Evoral::Beats earliest = Evoral::Beats::max();
4373 for (MidiRegionSelection::iterator i = selection->midi_regions.begin(); i != selection->midi_regions.end(); ++i) {
4374 MidiRegionView* mrv = dynamic_cast<MidiRegionView*>(*i);
4376 if (!mrv->selection().empty()) {
4377 earliest = std::min(earliest, (*mrv->selection().begin())->note()->time());
4379 mrv->cut_copy_clear (op);
4381 /* XXX: not ideal, as there may be more than one track involved in the selection */
4382 _last_cut_copy_source_track = &mrv->get_time_axis_view();
4386 if (!selection->points.empty()) {
4387 cut_copy_points (op, earliest, true);
4388 if (op == Cut || op == Delete) {
4389 selection->clear_points ();
4394 struct lt_playlist {
4395 bool operator () (const PlaylistState& a, const PlaylistState& b) {
4396 return a.playlist < b.playlist;
4400 struct PlaylistMapping {
4402 boost::shared_ptr<Playlist> pl;
4404 PlaylistMapping (TimeAxisView* tvp) : tv (tvp) {}
4407 /** Remove `clicked_regionview' */
4409 Editor::remove_clicked_region ()
4411 if (clicked_routeview == 0 || clicked_regionview == 0) {
4415 begin_reversible_command (_("remove region"));
4417 boost::shared_ptr<Playlist> playlist = clicked_routeview->playlist();
4419 playlist->clear_changes ();
4420 playlist->clear_owned_changes ();
4421 playlist->remove_region (clicked_regionview->region());
4422 if (Config->get_edit_mode() == Ripple)
4423 playlist->ripple (clicked_regionview->region()->position(), -clicked_regionview->region()->length(), boost::shared_ptr<Region>());
4425 /* We might have removed regions, which alters other regions' layering_index,
4426 so we need to do a recursive diff here.
4428 vector<Command*> cmds;
4429 playlist->rdiff (cmds);
4430 _session->add_commands (cmds);
4432 _session->add_command(new StatefulDiffCommand (playlist));
4433 commit_reversible_command ();
4437 /** Remove the selected regions */
4439 Editor::remove_selected_regions ()
4441 RegionSelection rs = get_regions_from_selection_and_entered ();
4443 if (!_session || rs.empty()) {
4447 list<boost::shared_ptr<Region> > regions_to_remove;
4449 for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
4450 // we can't just remove the region(s) in this loop because
4451 // this removes them from the RegionSelection, and they thus
4452 // disappear from underneath the iterator, and the ++i above
4453 // SEGVs in a puzzling fashion.
4455 // so, first iterate over the regions to be removed from rs and
4456 // add them to the regions_to_remove list, and then
4457 // iterate over the list to actually remove them.
4459 regions_to_remove.push_back ((*i)->region());
4462 vector<boost::shared_ptr<Playlist> > playlists;
4464 for (list<boost::shared_ptr<Region> >::iterator rl = regions_to_remove.begin(); rl != regions_to_remove.end(); ++rl) {
4466 boost::shared_ptr<Playlist> playlist = (*rl)->playlist();
4469 // is this check necessary?
4473 /* get_regions_from_selection_and_entered() guarantees that
4474 the playlists involved are unique, so there is no need
4478 playlists.push_back (playlist);
4480 playlist->clear_changes ();
4481 playlist->clear_owned_changes ();
4482 playlist->freeze ();
4483 playlist->remove_region (*rl);
4484 if (Config->get_edit_mode() == Ripple)
4485 playlist->ripple ((*rl)->position(), -(*rl)->length(), boost::shared_ptr<Region>());
4489 vector<boost::shared_ptr<Playlist> >::iterator pl;
4490 bool in_command = false;
4492 for (pl = playlists.begin(); pl != playlists.end(); ++pl) {
4495 /* We might have removed regions, which alters other regions' layering_index,
4496 so we need to do a recursive diff here.
4500 begin_reversible_command (_("remove region"));
4503 vector<Command*> cmds;
4504 (*pl)->rdiff (cmds);
4505 _session->add_commands (cmds);
4507 _session->add_command(new StatefulDiffCommand (*pl));
4511 commit_reversible_command ();
4515 /** Cut, copy or clear selected regions.
4516 * @param op Operation (Cut, Copy or Clear)
4519 Editor::cut_copy_regions (CutCopyOp op, RegionSelection& rs)
4521 /* we can't use a std::map here because the ordering is important, and we can't trivially sort
4522 a map when we want ordered access to both elements. i think.
4525 vector<PlaylistMapping> pmap;
4527 framepos_t first_position = max_framepos;
4529 typedef set<boost::shared_ptr<Playlist> > FreezeList;
4530 FreezeList freezelist;
4532 /* get ordering correct before we cut/copy */
4534 rs.sort_by_position_and_track ();
4536 for (RegionSelection::iterator x = rs.begin(); x != rs.end(); ++x) {
4538 first_position = min ((framepos_t) (*x)->region()->position(), first_position);
4540 if (op == Cut || op == Clear || op == Delete) {
4541 boost::shared_ptr<Playlist> pl = (*x)->region()->playlist();
4544 FreezeList::iterator fl;
4546 // only take state if this is a new playlist.
4547 for (fl = freezelist.begin(); fl != freezelist.end(); ++fl) {
4553 if (fl == freezelist.end()) {
4554 pl->clear_changes();
4555 pl->clear_owned_changes ();
4557 freezelist.insert (pl);
4562 TimeAxisView* tv = &(*x)->get_time_axis_view();
4563 vector<PlaylistMapping>::iterator z;
4565 for (z = pmap.begin(); z != pmap.end(); ++z) {
4566 if ((*z).tv == tv) {
4571 if (z == pmap.end()) {
4572 pmap.push_back (PlaylistMapping (tv));
4576 for (RegionSelection::iterator x = rs.begin(); x != rs.end(); ) {
4578 boost::shared_ptr<Playlist> pl = (*x)->region()->playlist();
4581 /* region not yet associated with a playlist (e.g. unfinished
4588 TimeAxisView& tv = (*x)->get_time_axis_view();
4589 boost::shared_ptr<Playlist> npl;
4590 RegionSelection::iterator tmp;
4597 vector<PlaylistMapping>::iterator z;
4599 for (z = pmap.begin(); z != pmap.end(); ++z) {
4600 if ((*z).tv == &tv) {
4605 assert (z != pmap.end());
4608 npl = PlaylistFactory::create (pl->data_type(), *_session, "cutlist", true);
4616 boost::shared_ptr<Region> r = (*x)->region();
4617 boost::shared_ptr<Region> _xx;
4623 pl->remove_region (r);
4624 if (Config->get_edit_mode() == Ripple)
4625 pl->ripple (r->position(), -r->length(), boost::shared_ptr<Region>());
4629 _xx = RegionFactory::create (r);
4630 npl->add_region (_xx, r->position() - first_position);
4631 pl->remove_region (r);
4632 if (Config->get_edit_mode() == Ripple)
4633 pl->ripple (r->position(), -r->length(), boost::shared_ptr<Region>());
4637 /* copy region before adding, so we're not putting same object into two different playlists */
4638 npl->add_region (RegionFactory::create (r), r->position() - first_position);
4642 pl->remove_region (r);
4643 if (Config->get_edit_mode() == Ripple)
4644 pl->ripple (r->position(), -r->length(), boost::shared_ptr<Region>());
4653 list<boost::shared_ptr<Playlist> > foo;
4655 /* the pmap is in the same order as the tracks in which selected regions occurred */
4657 for (vector<PlaylistMapping>::iterator i = pmap.begin(); i != pmap.end(); ++i) {
4660 foo.push_back ((*i).pl);
4665 cut_buffer->set (foo);
4669 _last_cut_copy_source_track = 0;
4671 _last_cut_copy_source_track = pmap.front().tv;
4675 for (FreezeList::iterator pl = freezelist.begin(); pl != freezelist.end(); ++pl) {
4678 /* We might have removed regions, which alters other regions' layering_index,
4679 so we need to do a recursive diff here.
4681 vector<Command*> cmds;
4682 (*pl)->rdiff (cmds);
4683 _session->add_commands (cmds);
4685 _session->add_command (new StatefulDiffCommand (*pl));
4690 Editor::cut_copy_ranges (CutCopyOp op)
4692 TrackViewList ts = selection->tracks.filter_to_unique_playlists ();
4694 /* Sort the track selection now, so that it if is used, the playlists
4695 selected by the calls below to cut_copy_clear are in the order that
4696 their tracks appear in the editor. This makes things like paste
4697 of ranges work properly.
4700 sort_track_selection (ts);
4703 if (!entered_track) {
4706 ts.push_back (entered_track);
4709 for (TrackViewList::iterator i = ts.begin(); i != ts.end(); ++i) {
4710 (*i)->cut_copy_clear (*selection, op);
4715 Editor::paste (float times, bool from_context)
4717 DEBUG_TRACE (DEBUG::CutNPaste, "paste to preferred edit pos\n");
4718 MusicFrame where (get_preferred_edit_position (EDIT_IGNORE_NONE, from_context), 0);
4719 paste_internal (where.frame, times, 0);
4723 Editor::mouse_paste ()
4725 MusicFrame where (0, 0);
4727 if (!mouse_frame (where.frame, ignored)) {
4732 paste_internal (where.frame, 1, where.division);
4736 Editor::paste_internal (framepos_t position, float times, const int32_t sub_num)
4738 DEBUG_TRACE (DEBUG::CutNPaste, string_compose ("apparent paste position is %1\n", position));
4740 if (cut_buffer->empty(internal_editing())) {
4744 if (position == max_framepos) {
4745 position = get_preferred_edit_position();
4746 DEBUG_TRACE (DEBUG::CutNPaste, string_compose ("preferred edit position is %1\n", position));
4749 if (position == last_paste_pos) {
4750 /* repeated paste in the same position */
4753 /* paste in new location, reset repeated paste state */
4755 last_paste_pos = position;
4758 /* get everything in the correct order */
4761 if (!selection->tracks.empty()) {
4762 /* If there is a track selection, paste into exactly those tracks and
4763 * only those tracks. This allows the user to be explicit and override
4764 * the below "do the reasonable thing" logic. */
4765 ts = selection->tracks.filter_to_unique_playlists ();
4766 sort_track_selection (ts);
4768 /* Figure out which track to base the paste at. */
4769 TimeAxisView* base_track = NULL;
4770 if (_edit_point == Editing::EditAtMouse && entered_track) {
4771 /* With the mouse edit point, paste onto the track under the mouse. */
4772 base_track = entered_track;
4773 } else if (_edit_point == Editing::EditAtMouse && entered_regionview) {
4774 /* With the mouse edit point, paste onto the track of the region under the mouse. */
4775 base_track = &entered_regionview->get_time_axis_view();
4776 } else if (_last_cut_copy_source_track) {
4777 /* Paste to the track that the cut/copy came from (see mantis #333). */
4778 base_track = _last_cut_copy_source_track;
4780 /* This is "impossible" since we've copied... well, do nothing. */
4784 /* Walk up to parent if necessary, so base track is a route. */
4785 while (base_track->get_parent()) {
4786 base_track = base_track->get_parent();
4789 /* Add base track and all tracks below it. The paste logic will select
4790 the appropriate object types from the cut buffer in relative order. */
4791 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
4792 if ((*i)->order() >= base_track->order()) {
4797 /* Sort tracks so the nth track of type T will pick the nth object of type T. */
4798 sort_track_selection (ts);
4800 /* Add automation children of each track in order, for pasting several lines. */
4801 for (TrackViewList::iterator i = ts.begin(); i != ts.end();) {
4802 /* Add any automation children for pasting several lines */
4803 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*>(*i++);
4808 typedef RouteTimeAxisView::AutomationTracks ATracks;
4809 const ATracks& atracks = rtv->automation_tracks();
4810 for (ATracks::const_iterator a = atracks.begin(); a != atracks.end(); ++a) {
4811 i = ts.insert(i, a->second.get());
4816 /* We now have a list of trackviews starting at base_track, including
4817 automation children, in the order shown in the editor, e.g. R1,
4818 R1.A1, R1.A2, R2, R2.A1, ... */
4821 begin_reversible_command (Operations::paste);
4823 if (ts.size() == 1 && cut_buffer->lines.size() == 1 &&
4824 dynamic_cast<AutomationTimeAxisView*>(ts.front())) {
4825 /* Only one line copied, and one automation track selected. Do a
4826 "greedy" paste from one automation type to another. */
4828 PasteContext ctx(paste_count, times, ItemCounts(), true);
4829 ts.front()->paste (position, *cut_buffer, ctx, sub_num);
4833 /* Paste into tracks */
4835 PasteContext ctx(paste_count, times, ItemCounts(), false);
4836 for (TrackViewList::iterator i = ts.begin(); i != ts.end(); ++i) {
4837 (*i)->paste (position, *cut_buffer, ctx, sub_num);
4841 commit_reversible_command ();
4845 Editor::duplicate_regions (float times)
4847 RegionSelection rs (get_regions_from_selection_and_entered());
4848 duplicate_some_regions (rs, times);
4852 Editor::duplicate_some_regions (RegionSelection& regions, float times)
4854 if (regions.empty ()) {
4858 boost::shared_ptr<Playlist> playlist;
4859 RegionSelection sel = regions; // clear (below) may clear the argument list if its the current region selection
4860 RegionSelection foo;
4862 framepos_t const start_frame = regions.start ();
4863 framepos_t const end_frame = regions.end_frame ();
4864 framecnt_t const gap = end_frame - start_frame + 1;
4866 begin_reversible_command (Operations::duplicate_region);
4868 selection->clear_regions ();
4870 for (RegionSelection::iterator i = sel.begin(); i != sel.end(); ++i) {
4872 boost::shared_ptr<Region> r ((*i)->region());
4874 TimeAxisView& tv = (*i)->get_time_axis_view();
4875 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (&tv);
4876 latest_regionviews.clear ();
4877 sigc::connection c = rtv->view()->RegionViewAdded.connect (sigc::mem_fun(*this, &Editor::collect_new_region_view));
4879 framepos_t const position = end_frame + (r->first_frame() - start_frame + 1);
4880 playlist = (*i)->region()->playlist();
4881 playlist->clear_changes ();
4882 playlist->duplicate (r, position, gap, times);
4883 _session->add_command(new StatefulDiffCommand (playlist));
4887 foo.insert (foo.end(), latest_regionviews.begin(), latest_regionviews.end());
4891 selection->set (foo);
4894 commit_reversible_command ();
4898 Editor::duplicate_selection (float times)
4900 if (selection->time.empty() || selection->tracks.empty()) {
4904 boost::shared_ptr<Playlist> playlist;
4906 TrackViewList ts = selection->tracks.filter_to_unique_playlists ();
4908 bool in_command = false;
4910 for (TrackViewList::iterator i = ts.begin(); i != ts.end(); ++i) {
4911 if ((playlist = (*i)->playlist()) == 0) {
4914 playlist->clear_changes ();
4916 if (clicked_selection) {
4917 playlist->duplicate_range (selection->time[clicked_selection], times);
4919 playlist->duplicate_ranges (selection->time, times);
4923 begin_reversible_command (_("duplicate range selection"));
4926 _session->add_command (new StatefulDiffCommand (playlist));
4931 if (times == 1.0f) {
4932 // now "move" range selection to after the current range selection
4933 framecnt_t distance = 0;
4935 if (clicked_selection) {
4937 selection->time[clicked_selection].end - selection->time[clicked_selection].start;
4939 distance = selection->time.end_frame () - selection->time.start ();
4942 selection->move_time (distance);
4944 commit_reversible_command ();
4948 /** Reset all selected points to the relevant default value */
4950 Editor::reset_point_selection ()
4952 for (PointSelection::iterator i = selection->points.begin(); i != selection->points.end(); ++i) {
4953 ARDOUR::AutomationList::iterator j = (*i)->model ();
4954 (*j)->value = (*i)->line().the_list()->descriptor ().normal;
4959 Editor::center_playhead ()
4961 float const page = _visible_canvas_width * samples_per_pixel;
4962 center_screen_internal (playhead_cursor->current_frame (), page);
4966 Editor::center_edit_point ()
4968 float const page = _visible_canvas_width * samples_per_pixel;
4969 center_screen_internal (get_preferred_edit_position(), page);
4972 /** Caller must begin and commit a reversible command */
4974 Editor::clear_playlist (boost::shared_ptr<Playlist> playlist)
4976 playlist->clear_changes ();
4978 _session->add_command (new StatefulDiffCommand (playlist));
4982 Editor::nudge_track (bool use_edit, bool forwards)
4984 boost::shared_ptr<Playlist> playlist;
4985 framepos_t distance;
4986 framepos_t next_distance;
4990 start = get_preferred_edit_position();
4995 if ((distance = get_nudge_distance (start, next_distance)) == 0) {
4999 if (selection->tracks.empty()) {
5003 TrackViewList ts = selection->tracks.filter_to_unique_playlists ();
5004 bool in_command = false;
5006 for (TrackViewList::iterator i = ts.begin(); i != ts.end(); ++i) {
5008 if ((playlist = (*i)->playlist()) == 0) {
5012 playlist->clear_changes ();
5013 playlist->clear_owned_changes ();
5015 playlist->nudge_after (start, distance, forwards);
5018 begin_reversible_command (_("nudge track"));
5021 vector<Command*> cmds;
5023 playlist->rdiff (cmds);
5024 _session->add_commands (cmds);
5026 _session->add_command (new StatefulDiffCommand (playlist));
5030 commit_reversible_command ();
5035 Editor::remove_last_capture ()
5037 vector<string> choices;
5044 if (Config->get_verify_remove_last_capture()) {
5045 prompt = _("Do you really want to destroy the last capture?"
5046 "\n(This is destructive and cannot be undone)");
5048 choices.push_back (_("No, do nothing."));
5049 choices.push_back (_("Yes, destroy it."));
5051 Choice prompter (_("Destroy last capture"), prompt, choices);
5053 if (prompter.run () == 1) {
5054 _session->remove_last_capture ();
5055 _regions->redisplay ();
5059 _session->remove_last_capture();
5060 _regions->redisplay ();
5065 Editor::normalize_region ()
5071 RegionSelection rs = get_regions_from_selection_and_entered ();
5077 NormalizeDialog dialog (rs.size() > 1);
5079 if (dialog.run () != RESPONSE_ACCEPT) {
5083 CursorContext::Handle cursor_ctx = CursorContext::create(*this, _cursors->wait);
5086 /* XXX: should really only count audio regions here */
5087 int const regions = rs.size ();
5089 /* Make a list of the selected audio regions' maximum amplitudes, and also
5090 obtain the maximum amplitude of them all.
5092 list<double> max_amps;
5093 list<double> rms_vals;
5096 bool use_rms = dialog.constrain_rms ();
5098 for (RegionSelection::const_iterator i = rs.begin(); i != rs.end(); ++i) {
5099 AudioRegionView const * arv = dynamic_cast<AudioRegionView const *> (*i);
5103 dialog.descend (1.0 / regions);
5104 double const a = arv->audio_region()->maximum_amplitude (&dialog);
5106 double r = arv->audio_region()->rms (&dialog);
5107 max_rms = max (max_rms, r);
5108 rms_vals.push_back (r);
5112 /* the user cancelled the operation */
5116 max_amps.push_back (a);
5117 max_amp = max (max_amp, a);
5121 list<double>::const_iterator a = max_amps.begin ();
5122 list<double>::const_iterator l = rms_vals.begin ();
5123 bool in_command = false;
5125 for (RegionSelection::iterator r = rs.begin(); r != rs.end(); ++r) {
5126 AudioRegionView* const arv = dynamic_cast<AudioRegionView*> (*r);
5131 arv->region()->clear_changes ();
5133 double amp = dialog.normalize_individually() ? *a : max_amp;
5134 double target = dialog.target_peak (); // dB
5137 double const amp_rms = dialog.normalize_individually() ? *l : max_rms;
5138 const double t_rms = dialog.target_rms ();
5139 const gain_t c_peak = dB_to_coefficient (target);
5140 const gain_t c_rms = dB_to_coefficient (t_rms);
5141 if ((amp_rms / c_rms) > (amp / c_peak)) {
5147 arv->audio_region()->normalize (amp, target);
5150 begin_reversible_command (_("normalize"));
5153 _session->add_command (new StatefulDiffCommand (arv->region()));
5160 commit_reversible_command ();
5166 Editor::reset_region_scale_amplitude ()
5172 RegionSelection rs = get_regions_from_selection_and_entered ();
5178 bool in_command = false;
5180 for (RegionSelection::iterator r = rs.begin(); r != rs.end(); ++r) {
5181 AudioRegionView* const arv = dynamic_cast<AudioRegionView*>(*r);
5184 arv->region()->clear_changes ();
5185 arv->audio_region()->set_scale_amplitude (1.0f);
5188 begin_reversible_command ("reset gain");
5191 _session->add_command (new StatefulDiffCommand (arv->region()));
5195 commit_reversible_command ();
5200 Editor::adjust_region_gain (bool up)
5202 RegionSelection rs = get_regions_from_selection_and_entered ();
5204 if (!_session || rs.empty()) {
5208 bool in_command = false;
5210 for (RegionSelection::iterator r = rs.begin(); r != rs.end(); ++r) {
5211 AudioRegionView* const arv = dynamic_cast<AudioRegionView*>(*r);
5216 arv->region()->clear_changes ();
5218 double dB = accurate_coefficient_to_dB (arv->audio_region()->scale_amplitude ());
5226 arv->audio_region()->set_scale_amplitude (dB_to_coefficient (dB));
5229 begin_reversible_command ("adjust region gain");
5232 _session->add_command (new StatefulDiffCommand (arv->region()));
5236 commit_reversible_command ();
5241 Editor::reset_region_gain ()
5243 RegionSelection rs = get_regions_from_selection_and_entered ();
5245 if (!_session || rs.empty()) {
5249 bool in_command = false;
5251 for (RegionSelection::iterator r = rs.begin(); r != rs.end(); ++r) {
5252 AudioRegionView* const arv = dynamic_cast<AudioRegionView*>(*r);
5257 arv->region()->clear_changes ();
5259 arv->audio_region()->set_scale_amplitude (1.0f);
5262 begin_reversible_command ("reset region gain");
5265 _session->add_command (new StatefulDiffCommand (arv->region()));
5269 commit_reversible_command ();
5274 Editor::reverse_region ()
5280 Reverse rev (*_session);
5281 apply_filter (rev, _("reverse regions"));
5285 Editor::strip_region_silence ()
5291 RegionSelection rs = get_regions_from_selection_and_entered ();
5297 std::list<RegionView*> audio_only;
5299 for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
5300 AudioRegionView* const arv = dynamic_cast<AudioRegionView*> (*i);
5302 audio_only.push_back (arv);
5306 assert (!audio_only.empty());
5308 StripSilenceDialog d (_session, audio_only);
5309 int const r = d.run ();
5313 if (r == Gtk::RESPONSE_OK) {
5314 ARDOUR::AudioIntervalMap silences;
5315 d.silences (silences);
5316 StripSilence s (*_session, silences, d.fade_length());
5318 apply_filter (s, _("strip silence"), &d);
5323 Editor::apply_midi_note_edit_op_to_region (MidiOperator& op, MidiRegionView& mrv)
5325 Evoral::Sequence<Evoral::Beats>::Notes selected;
5326 mrv.selection_as_notelist (selected, true);
5328 vector<Evoral::Sequence<Evoral::Beats>::Notes> v;
5329 v.push_back (selected);
5331 Evoral::Beats pos_beats = Evoral::Beats (mrv.midi_region()->beat()) - mrv.midi_region()->start_beats();
5333 return op (mrv.midi_region()->model(), pos_beats, v);
5337 Editor::apply_midi_note_edit_op (MidiOperator& op, const RegionSelection& rs)
5343 bool in_command = false;
5345 for (RegionSelection::const_iterator r = rs.begin(); r != rs.end(); ) {
5346 RegionSelection::const_iterator tmp = r;
5349 MidiRegionView* const mrv = dynamic_cast<MidiRegionView*> (*r);
5352 Command* cmd = apply_midi_note_edit_op_to_region (op, *mrv);
5355 begin_reversible_command (op.name ());
5359 _session->add_command (cmd);
5367 commit_reversible_command ();
5372 Editor::fork_region ()
5374 RegionSelection rs = get_regions_from_selection_and_entered ();
5380 CursorContext::Handle cursor_ctx = CursorContext::create(*this, _cursors->wait);
5381 bool in_command = false;
5385 for (RegionSelection::iterator r = rs.begin(); r != rs.end(); ) {
5386 RegionSelection::iterator tmp = r;
5389 MidiRegionView* const mrv = dynamic_cast<MidiRegionView*>(*r);
5393 boost::shared_ptr<Playlist> playlist = mrv->region()->playlist();
5394 boost::shared_ptr<MidiSource> new_source = _session->create_midi_source_by_stealing_name (mrv->midi_view()->track());
5395 boost::shared_ptr<MidiRegion> newregion = mrv->midi_region()->clone (new_source);
5398 begin_reversible_command (_("Fork Region(s)"));
5401 playlist->clear_changes ();
5402 playlist->replace_region (mrv->region(), newregion, mrv->region()->position());
5403 _session->add_command(new StatefulDiffCommand (playlist));
5405 error << string_compose (_("Could not unlink %1"), mrv->region()->name()) << endmsg;
5413 commit_reversible_command ();
5418 Editor::quantize_region ()
5421 quantize_regions(get_regions_from_selection_and_entered ());
5426 Editor::quantize_regions (const RegionSelection& rs)
5428 if (rs.n_midi_regions() == 0) {
5432 if (!quantize_dialog) {
5433 quantize_dialog = new QuantizeDialog (*this);
5436 if (quantize_dialog->is_mapped()) {
5437 /* in progress already */
5441 quantize_dialog->present ();
5442 const int r = quantize_dialog->run ();
5443 quantize_dialog->hide ();
5445 if (r == Gtk::RESPONSE_OK) {
5446 Quantize quant (quantize_dialog->snap_start(),
5447 quantize_dialog->snap_end(),
5448 quantize_dialog->start_grid_size(),
5449 quantize_dialog->end_grid_size(),
5450 quantize_dialog->strength(),
5451 quantize_dialog->swing(),
5452 quantize_dialog->threshold());
5454 apply_midi_note_edit_op (quant, rs);
5459 Editor::legatize_region (bool shrink_only)
5462 legatize_regions(get_regions_from_selection_and_entered (), shrink_only);
5467 Editor::legatize_regions (const RegionSelection& rs, bool shrink_only)
5469 if (rs.n_midi_regions() == 0) {
5473 Legatize legatize(shrink_only);
5474 apply_midi_note_edit_op (legatize, rs);
5478 Editor::transform_region ()
5481 transform_regions(get_regions_from_selection_and_entered ());
5486 Editor::transform_regions (const RegionSelection& rs)
5488 if (rs.n_midi_regions() == 0) {
5495 const int r = td.run();
5498 if (r == Gtk::RESPONSE_OK) {
5499 Transform transform(td.get());
5500 apply_midi_note_edit_op(transform, rs);
5505 Editor::transpose_region ()
5508 transpose_regions(get_regions_from_selection_and_entered ());
5513 Editor::transpose_regions (const RegionSelection& rs)
5515 if (rs.n_midi_regions() == 0) {
5520 int const r = d.run ();
5522 if (r == RESPONSE_ACCEPT) {
5523 Transpose transpose(d.semitones ());
5524 apply_midi_note_edit_op (transpose, rs);
5529 Editor::insert_patch_change (bool from_context)
5531 RegionSelection rs = get_regions_from_selection_and_entered ();
5537 const framepos_t p = get_preferred_edit_position (EDIT_IGNORE_NONE, from_context);
5539 /* XXX: bit of a hack; use the MIDNAM from the first selected region;
5540 there may be more than one, but the PatchChangeDialog can only offer
5541 one set of patch menus.
5543 MidiRegionView* first = dynamic_cast<MidiRegionView*> (rs.front ());
5545 Evoral::PatchChange<Evoral::Beats> empty (Evoral::Beats(), 0, 0, 0);
5546 PatchChangeDialog d (0, _session, empty, first->instrument_info(), Gtk::Stock::ADD);
5548 if (d.run() == RESPONSE_CANCEL) {
5552 for (RegionSelection::iterator i = rs.begin (); i != rs.end(); ++i) {
5553 MidiRegionView* const mrv = dynamic_cast<MidiRegionView*> (*i);
5555 if (p >= mrv->region()->first_frame() && p <= mrv->region()->last_frame()) {
5556 mrv->add_patch_change (p - mrv->region()->position(), d.patch ());
5563 Editor::apply_filter (Filter& filter, string command, ProgressReporter* progress)
5565 RegionSelection rs = get_regions_from_selection_and_entered ();
5571 CursorContext::Handle cursor_ctx = CursorContext::create(*this, _cursors->wait);
5572 bool in_command = false;
5577 int const N = rs.size ();
5579 for (RegionSelection::iterator r = rs.begin(); r != rs.end(); ) {
5580 RegionSelection::iterator tmp = r;
5583 AudioRegionView* const arv = dynamic_cast<AudioRegionView*>(*r);
5585 boost::shared_ptr<Playlist> playlist = arv->region()->playlist();
5588 progress->descend (1.0 / N);
5591 if (arv->audio_region()->apply (filter, progress) == 0) {
5593 playlist->clear_changes ();
5594 playlist->clear_owned_changes ();
5597 begin_reversible_command (command);
5601 if (filter.results.empty ()) {
5603 /* no regions returned; remove the old one */
5604 playlist->remove_region (arv->region ());
5608 std::vector<boost::shared_ptr<Region> >::iterator res = filter.results.begin ();
5610 /* first region replaces the old one */
5611 playlist->replace_region (arv->region(), *res, (*res)->position());
5615 while (res != filter.results.end()) {
5616 playlist->add_region (*res, (*res)->position());
5622 /* We might have removed regions, which alters other regions' layering_index,
5623 so we need to do a recursive diff here.
5625 vector<Command*> cmds;
5626 playlist->rdiff (cmds);
5627 _session->add_commands (cmds);
5629 _session->add_command(new StatefulDiffCommand (playlist));
5633 progress->ascend ();
5642 commit_reversible_command ();
5647 Editor::external_edit_region ()
5653 Editor::reset_region_gain_envelopes ()
5655 RegionSelection rs = get_regions_from_selection_and_entered ();
5657 if (!_session || rs.empty()) {
5661 bool in_command = false;
5663 for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
5664 AudioRegionView* const arv = dynamic_cast<AudioRegionView*>(*i);
5666 boost::shared_ptr<AutomationList> alist (arv->audio_region()->envelope());
5667 XMLNode& before (alist->get_state());
5669 arv->audio_region()->set_default_envelope ();
5672 begin_reversible_command (_("reset region gain"));
5675 _session->add_command (new MementoCommand<AutomationList>(*arv->audio_region()->envelope().get(), &before, &alist->get_state()));
5680 commit_reversible_command ();
5685 Editor::set_region_gain_visibility (RegionView* rv)
5687 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (rv);
5689 arv->update_envelope_visibility();
5694 Editor::set_gain_envelope_visibility ()
5700 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
5701 AudioTimeAxisView* v = dynamic_cast<AudioTimeAxisView*>(*i);
5703 v->audio_view()->foreach_regionview (sigc::mem_fun (this, &Editor::set_region_gain_visibility));
5709 Editor::toggle_gain_envelope_active ()
5711 if (_ignore_region_action) {
5715 RegionSelection rs = get_regions_from_selection_and_entered ();
5717 if (!_session || rs.empty()) {
5721 bool in_command = false;
5723 for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
5724 AudioRegionView* const arv = dynamic_cast<AudioRegionView*>(*i);
5726 arv->region()->clear_changes ();
5727 arv->audio_region()->set_envelope_active (!arv->audio_region()->envelope_active());
5730 begin_reversible_command (_("region gain envelope active"));
5733 _session->add_command (new StatefulDiffCommand (arv->region()));
5738 commit_reversible_command ();
5743 Editor::toggle_region_lock ()
5745 if (_ignore_region_action) {
5749 RegionSelection rs = get_regions_from_selection_and_entered ();
5751 if (!_session || rs.empty()) {
5755 begin_reversible_command (_("toggle region lock"));
5757 for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
5758 (*i)->region()->clear_changes ();
5759 (*i)->region()->set_locked (!(*i)->region()->locked());
5760 _session->add_command (new StatefulDiffCommand ((*i)->region()));
5763 commit_reversible_command ();
5767 Editor::toggle_region_video_lock ()
5769 if (_ignore_region_action) {
5773 RegionSelection rs = get_regions_from_selection_and_entered ();
5775 if (!_session || rs.empty()) {
5779 begin_reversible_command (_("Toggle Video Lock"));
5781 for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
5782 (*i)->region()->clear_changes ();
5783 (*i)->region()->set_video_locked (!(*i)->region()->video_locked());
5784 _session->add_command (new StatefulDiffCommand ((*i)->region()));
5787 commit_reversible_command ();
5791 Editor::toggle_region_lock_style ()
5793 if (_ignore_region_action) {
5797 RegionSelection rs = get_regions_from_selection_and_entered ();
5799 if (!_session || rs.empty()) {
5803 Glib::RefPtr<ToggleAction> a = Glib::RefPtr<ToggleAction>::cast_dynamic (_region_actions->get_action("toggle-region-lock-style"));
5804 vector<Widget*> proxies = a->get_proxies();
5805 Gtk::CheckMenuItem* cmi = dynamic_cast<Gtk::CheckMenuItem*> (proxies.front());
5809 begin_reversible_command (_("toggle region lock style"));
5811 for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
5812 (*i)->region()->clear_changes ();
5813 PositionLockStyle const ns = ((*i)->region()->position_lock_style() == AudioTime && !cmi->get_inconsistent()) ? MusicTime : AudioTime;
5814 (*i)->region()->set_position_lock_style (ns);
5815 _session->add_command (new StatefulDiffCommand ((*i)->region()));
5818 commit_reversible_command ();
5822 Editor::toggle_opaque_region ()
5824 if (_ignore_region_action) {
5828 RegionSelection rs = get_regions_from_selection_and_entered ();
5830 if (!_session || rs.empty()) {
5834 begin_reversible_command (_("change region opacity"));
5836 for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
5837 (*i)->region()->clear_changes ();
5838 (*i)->region()->set_opaque (!(*i)->region()->opaque());
5839 _session->add_command (new StatefulDiffCommand ((*i)->region()));
5842 commit_reversible_command ();
5846 Editor::toggle_record_enable ()
5848 bool new_state = false;
5850 for (TrackSelection::iterator i = selection->tracks.begin(); i != selection->tracks.end(); ++i) {
5851 RouteTimeAxisView *rtav = dynamic_cast<RouteTimeAxisView *>(*i);
5854 if (!rtav->is_track())
5858 new_state = !rtav->track()->rec_enable_control()->get_value();
5862 rtav->track()->rec_enable_control()->set_value (new_state, Controllable::UseGroup);
5867 Editor::toggle_solo ()
5869 bool new_state = false;
5871 boost::shared_ptr<ControlList> cl (new ControlList);
5873 for (TrackSelection::iterator i = selection->tracks.begin(); i != selection->tracks.end(); ++i) {
5874 RouteTimeAxisView *rtav = dynamic_cast<RouteTimeAxisView *>(*i);
5881 new_state = !rtav->route()->soloed ();
5885 cl->push_back (rtav->route()->solo_control());
5888 _session->set_controls (cl, new_state ? 1.0 : 0.0, Controllable::UseGroup);
5892 Editor::toggle_mute ()
5894 bool new_state = false;
5896 boost::shared_ptr<RouteList> rl (new RouteList);
5898 for (TrackSelection::iterator i = selection->tracks.begin(); i != selection->tracks.end(); ++i) {
5899 RouteTimeAxisView *rtav = dynamic_cast<RouteTimeAxisView *>(*i);
5906 new_state = !rtav->route()->muted();
5910 rl->push_back (rtav->route());
5913 _session->set_controls (route_list_to_control_list (rl, &Stripable::mute_control), new_state, Controllable::UseGroup);
5917 Editor::toggle_solo_isolate ()
5923 Editor::fade_range ()
5925 TrackViewList ts = selection->tracks.filter_to_unique_playlists ();
5927 begin_reversible_command (_("fade range"));
5929 for (TrackViewList::iterator i = ts.begin(); i != ts.end(); ++i) {
5930 (*i)->fade_range (selection->time);
5933 commit_reversible_command ();
5938 Editor::set_fade_length (bool in)
5940 RegionSelection rs = get_regions_from_selection_and_entered ();
5946 /* we need a region to measure the offset from the start */
5948 RegionView* rv = rs.front ();
5950 framepos_t pos = get_preferred_edit_position();
5954 if (pos > rv->region()->last_frame() || pos < rv->region()->first_frame()) {
5955 /* edit point is outside the relevant region */
5960 if (pos <= rv->region()->position()) {
5964 len = pos - rv->region()->position();
5965 cmd = _("set fade in length");
5967 if (pos >= rv->region()->last_frame()) {
5971 len = rv->region()->last_frame() - pos;
5972 cmd = _("set fade out length");
5975 bool in_command = false;
5977 for (RegionSelection::iterator x = rs.begin(); x != rs.end(); ++x) {
5978 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (*x);
5984 boost::shared_ptr<AutomationList> alist;
5986 alist = tmp->audio_region()->fade_in();
5988 alist = tmp->audio_region()->fade_out();
5991 XMLNode &before = alist->get_state();
5994 tmp->audio_region()->set_fade_in_length (len);
5995 tmp->audio_region()->set_fade_in_active (true);
5997 tmp->audio_region()->set_fade_out_length (len);
5998 tmp->audio_region()->set_fade_out_active (true);
6002 begin_reversible_command (cmd);
6005 XMLNode &after = alist->get_state();
6006 _session->add_command(new MementoCommand<AutomationList>(*alist, &before, &after));
6010 commit_reversible_command ();
6015 Editor::set_fade_in_shape (FadeShape shape)
6017 RegionSelection rs = get_regions_from_selection_and_entered ();
6022 bool in_command = false;
6024 for (RegionSelection::iterator x = rs.begin(); x != rs.end(); ++x) {
6025 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (*x);
6031 boost::shared_ptr<AutomationList> alist = tmp->audio_region()->fade_in();
6032 XMLNode &before = alist->get_state();
6034 tmp->audio_region()->set_fade_in_shape (shape);
6037 begin_reversible_command (_("set fade in shape"));
6040 XMLNode &after = alist->get_state();
6041 _session->add_command(new MementoCommand<AutomationList>(*alist.get(), &before, &after));
6045 commit_reversible_command ();
6050 Editor::set_fade_out_shape (FadeShape shape)
6052 RegionSelection rs = get_regions_from_selection_and_entered ();
6057 bool in_command = false;
6059 for (RegionSelection::iterator x = rs.begin(); x != rs.end(); ++x) {
6060 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (*x);
6066 boost::shared_ptr<AutomationList> alist = tmp->audio_region()->fade_out();
6067 XMLNode &before = alist->get_state();
6069 tmp->audio_region()->set_fade_out_shape (shape);
6072 begin_reversible_command (_("set fade out shape"));
6075 XMLNode &after = alist->get_state();
6076 _session->add_command(new MementoCommand<AutomationList>(*alist.get(), &before, &after));
6080 commit_reversible_command ();
6085 Editor::set_fade_in_active (bool yn)
6087 RegionSelection rs = get_regions_from_selection_and_entered ();
6092 bool in_command = false;
6094 for (RegionSelection::iterator x = rs.begin(); x != rs.end(); ++x) {
6095 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (*x);
6102 boost::shared_ptr<AudioRegion> ar (tmp->audio_region());
6104 ar->clear_changes ();
6105 ar->set_fade_in_active (yn);
6108 begin_reversible_command (_("set fade in active"));
6111 _session->add_command (new StatefulDiffCommand (ar));
6115 commit_reversible_command ();
6120 Editor::set_fade_out_active (bool yn)
6122 RegionSelection rs = get_regions_from_selection_and_entered ();
6127 bool in_command = false;
6129 for (RegionSelection::iterator x = rs.begin(); x != rs.end(); ++x) {
6130 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (*x);
6136 boost::shared_ptr<AudioRegion> ar (tmp->audio_region());
6138 ar->clear_changes ();
6139 ar->set_fade_out_active (yn);
6142 begin_reversible_command (_("set fade out active"));
6145 _session->add_command(new StatefulDiffCommand (ar));
6149 commit_reversible_command ();
6154 Editor::toggle_region_fades (int dir)
6156 if (_ignore_region_action) {
6160 boost::shared_ptr<AudioRegion> ar;
6163 RegionSelection rs = get_regions_from_selection_and_entered ();
6169 RegionSelection::iterator i;
6170 for (i = rs.begin(); i != rs.end(); ++i) {
6171 if ((ar = boost::dynamic_pointer_cast<AudioRegion>((*i)->region())) != 0) {
6173 yn = ar->fade_out_active ();
6175 yn = ar->fade_in_active ();
6181 if (i == rs.end()) {
6185 /* XXX should this undo-able? */
6186 bool in_command = false;
6188 for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
6189 if ((ar = boost::dynamic_pointer_cast<AudioRegion>((*i)->region())) == 0) {
6192 ar->clear_changes ();
6194 if (dir == 1 || dir == 0) {
6195 ar->set_fade_in_active (!yn);
6198 if (dir == -1 || dir == 0) {
6199 ar->set_fade_out_active (!yn);
6202 begin_reversible_command (_("toggle fade active"));
6205 _session->add_command(new StatefulDiffCommand (ar));
6209 commit_reversible_command ();
6214 /** Update region fade visibility after its configuration has been changed */
6216 Editor::update_region_fade_visibility ()
6218 bool _fade_visibility = _session->config.get_show_region_fades ();
6220 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
6221 AudioTimeAxisView* v = dynamic_cast<AudioTimeAxisView*>(*i);
6223 if (_fade_visibility) {
6224 v->audio_view()->show_all_fades ();
6226 v->audio_view()->hide_all_fades ();
6233 Editor::set_edit_point ()
6236 MusicFrame where (0, 0);
6238 if (!mouse_frame (where.frame, ignored)) {
6244 if (selection->markers.empty()) {
6246 mouse_add_new_marker (where.frame);
6251 Location* loc = find_location_from_marker (selection->markers.front(), ignored);
6254 loc->move_to (where.frame, where.division);
6260 Editor::set_playhead_cursor ()
6262 if (entered_marker) {
6263 _session->request_locate (entered_marker->position(), _session->transport_rolling());
6265 MusicFrame where (0, 0);
6268 if (!mouse_frame (where.frame, ignored)) {
6275 _session->request_locate (where.frame, _session->transport_rolling());
6279 //not sure what this was for; remove it for now.
6280 // if (UIConfiguration::instance().get_follow_edits() && (!_session || !_session->config.get_external_sync())) {
6281 // cancel_time_selection();
6287 Editor::split_region ()
6289 if (_drags->active ()) {
6293 //if a range is selected, separate it
6294 if ( !selection->time.empty()) {
6295 separate_regions_between (selection->time);
6299 //if no range was selected, try to find some regions to split
6300 if (current_mouse_mode() == MouseObject) { //don't try this for Internal Edit, Stretch, Draw, etc.
6302 RegionSelection rs = get_regions_from_selection_and_edit_point ();
6303 const framepos_t pos = get_preferred_edit_position();
6304 const int32_t division = get_grid_music_divisions (0);
6305 MusicFrame where (pos, division);
6311 split_regions_at (where, rs);
6317 Editor::select_next_route()
6319 if (selection->tracks.empty()) {
6320 selection->set (track_views.front());
6324 TimeAxisView* current = selection->tracks.front();
6328 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
6330 if (*i == current) {
6332 if (i != track_views.end()) {
6335 current = (*(track_views.begin()));
6336 //selection->set (*(track_views.begin()));
6342 rui = dynamic_cast<RouteUI *>(current);
6344 } while (current->hidden() || (rui == NULL) || !rui->route()->active());
6346 selection->set (current);
6348 ensure_time_axis_view_is_visible (*current, false);
6352 Editor::select_prev_route()
6354 if (selection->tracks.empty()) {
6355 selection->set (track_views.front());
6359 TimeAxisView* current = selection->tracks.front();
6363 for (TrackViewList::reverse_iterator i = track_views.rbegin(); i != track_views.rend(); ++i) {
6365 if (*i == current) {
6367 if (i != track_views.rend()) {
6370 current = *(track_views.rbegin());
6375 rui = dynamic_cast<RouteUI *>(current);
6377 } while (current->hidden() || (rui == NULL) || !rui->route()->active());
6379 selection->set (current);
6381 ensure_time_axis_view_is_visible (*current, false);
6385 Editor::set_loop_from_selection (bool play)
6387 if (_session == 0) {
6391 framepos_t start, end;
6392 if (!get_selection_extents ( start, end))
6395 set_loop_range (start, end, _("set loop range from selection"));
6398 _session->request_play_loop (true, true);
6403 Editor::set_loop_from_region (bool play)
6405 framepos_t start, end;
6406 if (!get_selection_extents ( start, end))
6409 set_loop_range (start, end, _("set loop range from region"));
6412 _session->request_locate (start, true);
6413 _session->request_play_loop (true);
6418 Editor::set_punch_from_selection ()
6420 if (_session == 0) {
6424 framepos_t start, end;
6425 if (!get_selection_extents ( start, end))
6428 set_punch_range (start, end, _("set punch range from selection"));
6432 Editor::set_auto_punch_range ()
6434 // auto punch in/out button from a single button
6435 // If Punch In is unset, set punch range from playhead to end, enable punch in
6436 // If Punch In is set, the next punch sets Punch Out, unless the playhead has been
6437 // rewound beyond the Punch In marker, in which case that marker will be moved back
6438 // to the current playhead position.
6439 // If punch out is set, it clears the punch range and Punch In/Out buttons
6441 if (_session == 0) {
6445 Location* tpl = transport_punch_location();
6446 framepos_t now = playhead_cursor->current_frame();
6447 framepos_t begin = now;
6448 framepos_t end = _session->current_end_frame();
6450 if (!_session->config.get_punch_in()) {
6451 // First Press - set punch in and create range from here to eternity
6452 set_punch_range (begin, end, _("Auto Punch In"));
6453 _session->config.set_punch_in(true);
6454 } else if (tpl && !_session->config.get_punch_out()) {
6455 // Second press - update end range marker and set punch_out
6456 if (now < tpl->start()) {
6457 // playhead has been rewound - move start back and pretend nothing happened
6459 set_punch_range (begin, end, _("Auto Punch In/Out"));
6461 // normal case for 2nd press - set the punch out
6462 end = playhead_cursor->current_frame ();
6463 set_punch_range (tpl->start(), now, _("Auto Punch In/Out"));
6464 _session->config.set_punch_out(true);
6467 if (_session->config.get_punch_out()) {
6468 _session->config.set_punch_out(false);
6471 if (_session->config.get_punch_in()) {
6472 _session->config.set_punch_in(false);
6477 // third press - unset punch in/out and remove range
6478 _session->locations()->remove(tpl);
6485 Editor::set_session_extents_from_selection ()
6487 if (_session == 0) {
6491 framepos_t start, end;
6492 if (!get_selection_extents ( start, end))
6496 if ((loc = _session->locations()->session_range_location()) == 0) {
6497 _session->set_session_extents (start, end); // this will create a new session range; no need for UNDO
6499 XMLNode &before = loc->get_state();
6501 _session->set_session_extents (start, end);
6503 XMLNode &after = loc->get_state();
6505 begin_reversible_command (_("set session start/end from selection"));
6507 _session->add_command (new MementoCommand<Location>(*loc, &before, &after));
6509 commit_reversible_command ();
6512 _session->set_end_is_free (false);
6516 Editor::set_punch_start_from_edit_point ()
6520 MusicFrame start (0, 0);
6521 framepos_t end = max_framepos;
6523 //use the existing punch end, if any
6524 Location* tpl = transport_punch_location();
6529 if ((_edit_point == EditAtPlayhead) && _session->transport_rolling()) {
6530 start.frame = _session->audible_frame();
6532 start.frame = get_preferred_edit_position();
6535 //snap the selection start/end
6538 //if there's not already a sensible selection endpoint, go "forever"
6539 if (start.frame > end ) {
6543 set_punch_range (start.frame, end, _("set punch start from EP"));
6549 Editor::set_punch_end_from_edit_point ()
6553 framepos_t start = 0;
6554 MusicFrame end (max_framepos, 0);
6556 //use the existing punch start, if any
6557 Location* tpl = transport_punch_location();
6559 start = tpl->start();
6562 if ((_edit_point == EditAtPlayhead) && _session->transport_rolling()) {
6563 end.frame = _session->audible_frame();
6565 end.frame = get_preferred_edit_position();
6568 //snap the selection start/end
6571 set_punch_range (start, end.frame, _("set punch end from EP"));
6577 Editor::set_loop_start_from_edit_point ()
6581 MusicFrame start (0, 0);
6582 framepos_t end = max_framepos;
6584 //use the existing loop end, if any
6585 Location* tpl = transport_loop_location();
6590 if ((_edit_point == EditAtPlayhead) && _session->transport_rolling()) {
6591 start.frame = _session->audible_frame();
6593 start.frame = get_preferred_edit_position();
6596 //snap the selection start/end
6599 //if there's not already a sensible selection endpoint, go "forever"
6600 if (start.frame > end ) {
6604 set_loop_range (start.frame, end, _("set loop start from EP"));
6610 Editor::set_loop_end_from_edit_point ()
6614 framepos_t start = 0;
6615 MusicFrame end (max_framepos, 0);
6617 //use the existing loop start, if any
6618 Location* tpl = transport_loop_location();
6620 start = tpl->start();
6623 if ((_edit_point == EditAtPlayhead) && _session->transport_rolling()) {
6624 end.frame = _session->audible_frame();
6626 end.frame = get_preferred_edit_position();
6629 //snap the selection start/end
6632 set_loop_range (start, end.frame, _("set loop end from EP"));
6637 Editor::set_punch_from_region ()
6639 framepos_t start, end;
6640 if (!get_selection_extents ( start, end))
6643 set_punch_range (start, end, _("set punch range from region"));
6647 Editor::pitch_shift_region ()
6649 RegionSelection rs = get_regions_from_selection_and_entered ();
6651 RegionSelection audio_rs;
6652 for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
6653 if (dynamic_cast<AudioRegionView*> (*i)) {
6654 audio_rs.push_back (*i);
6658 if (audio_rs.empty()) {
6662 pitch_shift (audio_rs, 1.2);
6666 Editor::set_tempo_from_region ()
6668 RegionSelection rs = get_regions_from_selection_and_entered ();
6670 if (!_session || rs.empty()) {
6674 RegionView* rv = rs.front();
6676 define_one_bar (rv->region()->position(), rv->region()->last_frame() + 1);
6680 Editor::use_range_as_bar ()
6682 framepos_t start, end;
6683 if (get_edit_op_range (start, end)) {
6684 define_one_bar (start, end);
6689 Editor::define_one_bar (framepos_t start, framepos_t end)
6691 framepos_t length = end - start;
6693 const Meter& m (_session->tempo_map().meter_at_frame (start));
6695 /* length = 1 bar */
6697 /* We're going to deliver a constant tempo here,
6698 so we can use frames per beat to determine length.
6699 now we want frames per beat.
6700 we have frames per bar, and beats per bar, so ...
6703 /* XXXX METER MATH */
6705 double frames_per_beat = length / m.divisions_per_bar();
6707 /* beats per minute = */
6709 double beats_per_minute = (_session->frame_rate() * 60.0) / frames_per_beat;
6711 /* now decide whether to:
6713 (a) set global tempo
6714 (b) add a new tempo marker
6718 const TempoSection& t (_session->tempo_map().tempo_section_at_frame (start));
6720 bool do_global = false;
6722 if ((_session->tempo_map().n_tempos() == 1) && (_session->tempo_map().n_meters() == 1)) {
6724 /* only 1 tempo & 1 meter: ask if the user wants to set the tempo
6725 at the start, or create a new marker
6728 vector<string> options;
6729 options.push_back (_("Cancel"));
6730 options.push_back (_("Add new marker"));
6731 options.push_back (_("Set global tempo"));
6734 _("Define one bar"),
6735 _("Do you want to set the global tempo or add a new tempo marker?"),
6739 c.set_default_response (2);
6755 /* more than 1 tempo and/or meter section already, go ahead do the "usual":
6756 if the marker is at the region starter, change it, otherwise add
6761 begin_reversible_command (_("set tempo from region"));
6762 XMLNode& before (_session->tempo_map().get_state());
6765 _session->tempo_map().change_initial_tempo (beats_per_minute, t.note_type(), t.end_note_types_per_minute());
6766 } else if (t.frame() == start) {
6767 _session->tempo_map().change_existing_tempo_at (start, beats_per_minute, t.note_type(), t.end_note_types_per_minute());
6769 /* constant tempo */
6770 const Tempo tempo (beats_per_minute, t.note_type());
6771 _session->tempo_map().add_tempo (tempo, 0.0, start, AudioTime);
6774 XMLNode& after (_session->tempo_map().get_state());
6776 _session->add_command (new MementoCommand<TempoMap>(_session->tempo_map(), &before, &after));
6777 commit_reversible_command ();
6781 Editor::split_region_at_transients ()
6783 AnalysisFeatureList positions;
6785 RegionSelection rs = get_regions_from_selection_and_entered ();
6787 if (!_session || rs.empty()) {
6791 begin_reversible_command (_("split regions"));
6793 for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ) {
6795 RegionSelection::iterator tmp;
6800 boost::shared_ptr<AudioRegion> ar = boost::dynamic_pointer_cast<AudioRegion> ((*i)->region());
6803 ar->transients (positions);
6804 split_region_at_points ((*i)->region(), positions, true);
6811 commit_reversible_command ();
6816 Editor::split_region_at_points (boost::shared_ptr<Region> r, AnalysisFeatureList& positions, bool can_ferret, bool select_new)
6818 bool use_rhythmic_rodent = false;
6820 boost::shared_ptr<Playlist> pl = r->playlist();
6822 list<boost::shared_ptr<Region> > new_regions;
6828 if (positions.empty()) {
6832 if (positions.size() > 20 && can_ferret) {
6833 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);
6834 MessageDialog msg (msgstr,
6837 Gtk::BUTTONS_OK_CANCEL);
6840 msg.add_button (_("Call for the Ferret!"), RESPONSE_APPLY);
6841 msg.set_secondary_text (_("Press OK to continue with this split operation\nor ask the Ferret dialog to tune the analysis"));
6843 msg.set_secondary_text (_("Press OK to continue with this split operation"));
6846 msg.set_title (_("Excessive split?"));
6849 int response = msg.run();
6855 case RESPONSE_APPLY:
6856 use_rhythmic_rodent = true;
6863 if (use_rhythmic_rodent) {
6864 show_rhythm_ferret ();
6868 AnalysisFeatureList::const_iterator x;
6870 pl->clear_changes ();
6871 pl->clear_owned_changes ();
6873 x = positions.begin();
6875 if (x == positions.end()) {
6880 pl->remove_region (r);
6884 framepos_t rstart = r->first_frame ();
6885 framepos_t rend = r->last_frame ();
6887 while (x != positions.end()) {
6889 /* deal with positons that are out of scope of present region bounds */
6890 if (*x <= rstart || *x > rend) {
6895 /* file start = original start + how far we from the initial position ? */
6897 framepos_t file_start = r->start() + pos;
6899 /* length = next position - current position */
6901 framepos_t len = (*x) - pos - rstart;
6903 /* XXX we do we really want to allow even single-sample regions?
6904 * shouldn't we have some kind of lower limit on region size?
6913 if (RegionFactory::region_name (new_name, r->name())) {
6917 /* do NOT announce new regions 1 by one, just wait till they are all done */
6921 plist.add (ARDOUR::Properties::start, file_start);
6922 plist.add (ARDOUR::Properties::length, len);
6923 plist.add (ARDOUR::Properties::name, new_name);
6924 plist.add (ARDOUR::Properties::layer, 0);
6925 // TODO set transients_offset
6927 boost::shared_ptr<Region> nr = RegionFactory::create (r->sources(), plist, false);
6928 /* because we set annouce to false, manually add the new region to the
6931 RegionFactory::map_add (nr);
6933 pl->add_region (nr, rstart + pos);
6936 new_regions.push_front(nr);
6945 RegionFactory::region_name (new_name, r->name());
6947 /* Add the final region */
6950 plist.add (ARDOUR::Properties::start, r->start() + pos);
6951 plist.add (ARDOUR::Properties::length, r->last_frame() - (r->position() + pos) + 1);
6952 plist.add (ARDOUR::Properties::name, new_name);
6953 plist.add (ARDOUR::Properties::layer, 0);
6955 boost::shared_ptr<Region> nr = RegionFactory::create (r->sources(), plist, false);
6956 /* because we set annouce to false, manually add the new region to the
6959 RegionFactory::map_add (nr);
6960 pl->add_region (nr, r->position() + pos);
6963 new_regions.push_front(nr);
6968 /* We might have removed regions, which alters other regions' layering_index,
6969 so we need to do a recursive diff here.
6971 vector<Command*> cmds;
6973 _session->add_commands (cmds);
6975 _session->add_command (new StatefulDiffCommand (pl));
6979 for (list<boost::shared_ptr<Region> >::iterator i = new_regions.begin(); i != new_regions.end(); ++i){
6980 set_selected_regionview_from_region_list ((*i), Selection::Add);
6986 Editor::place_transient()
6992 RegionSelection rs = get_regions_from_selection_and_edit_point ();
6998 framepos_t where = get_preferred_edit_position();
7000 begin_reversible_command (_("place transient"));
7002 for (RegionSelection::iterator r = rs.begin(); r != rs.end(); ++r) {
7003 (*r)->region()->add_transient(where);
7006 commit_reversible_command ();
7010 Editor::remove_transient(ArdourCanvas::Item* item)
7016 ArdourCanvas::Line* _line = reinterpret_cast<ArdourCanvas::Line*> (item);
7019 AudioRegionView* _arv = reinterpret_cast<AudioRegionView*> (item->get_data ("regionview"));
7020 _arv->remove_transient (*(float*) _line->get_data ("position"));
7024 Editor::snap_regions_to_grid ()
7026 list <boost::shared_ptr<Playlist > > used_playlists;
7028 RegionSelection rs = get_regions_from_selection_and_entered ();
7030 if (!_session || rs.empty()) {
7034 begin_reversible_command (_("snap regions to grid"));
7036 for (RegionSelection::iterator r = rs.begin(); r != rs.end(); ++r) {
7038 boost::shared_ptr<Playlist> pl = (*r)->region()->playlist();
7040 if (!pl->frozen()) {
7041 /* we haven't seen this playlist before */
7043 /* remember used playlists so we can thaw them later */
7044 used_playlists.push_back(pl);
7047 (*r)->region()->clear_changes ();
7049 MusicFrame start ((*r)->region()->first_frame (), 0);
7051 (*r)->region()->set_position (start.frame, start.division);
7052 _session->add_command(new StatefulDiffCommand ((*r)->region()));
7055 while (used_playlists.size() > 0) {
7056 list <boost::shared_ptr<Playlist > >::iterator i = used_playlists.begin();
7058 used_playlists.pop_front();
7061 commit_reversible_command ();
7065 Editor::close_region_gaps ()
7067 list <boost::shared_ptr<Playlist > > used_playlists;
7069 RegionSelection rs = get_regions_from_selection_and_entered ();
7071 if (!_session || rs.empty()) {
7075 Dialog dialog (_("Close Region Gaps"));
7078 table.set_spacings (12);
7079 table.set_border_width (12);
7080 Label* l = manage (left_aligned_label (_("Crossfade length")));
7081 table.attach (*l, 0, 1, 0, 1);
7083 SpinButton spin_crossfade (1, 0);
7084 spin_crossfade.set_range (0, 15);
7085 spin_crossfade.set_increments (1, 1);
7086 spin_crossfade.set_value (5);
7087 table.attach (spin_crossfade, 1, 2, 0, 1);
7089 table.attach (*manage (new Label (_("ms"))), 2, 3, 0, 1);
7091 l = manage (left_aligned_label (_("Pull-back length")));
7092 table.attach (*l, 0, 1, 1, 2);
7094 SpinButton spin_pullback (1, 0);
7095 spin_pullback.set_range (0, 100);
7096 spin_pullback.set_increments (1, 1);
7097 spin_pullback.set_value(30);
7098 table.attach (spin_pullback, 1, 2, 1, 2);
7100 table.attach (*manage (new Label (_("ms"))), 2, 3, 1, 2);
7102 dialog.get_vbox()->pack_start (table);
7103 dialog.add_button (Stock::CANCEL, RESPONSE_CANCEL);
7104 dialog.add_button (_("Ok"), RESPONSE_ACCEPT);
7107 if (dialog.run () == RESPONSE_CANCEL) {
7111 framepos_t crossfade_len = spin_crossfade.get_value();
7112 framepos_t pull_back_frames = spin_pullback.get_value();
7114 crossfade_len = lrintf (crossfade_len * _session->frame_rate()/1000);
7115 pull_back_frames = lrintf (pull_back_frames * _session->frame_rate()/1000);
7117 /* Iterate over the region list and make adjacent regions overlap by crossfade_len_ms */
7119 begin_reversible_command (_("close region gaps"));
7122 boost::shared_ptr<Region> last_region;
7124 rs.sort_by_position_and_track();
7126 for (RegionSelection::iterator r = rs.begin(); r != rs.end(); ++r) {
7128 boost::shared_ptr<Playlist> pl = (*r)->region()->playlist();
7130 if (!pl->frozen()) {
7131 /* we haven't seen this playlist before */
7133 /* remember used playlists so we can thaw them later */
7134 used_playlists.push_back(pl);
7138 framepos_t position = (*r)->region()->position();
7140 if (idx == 0 || position < last_region->position()){
7141 last_region = (*r)->region();
7146 (*r)->region()->clear_changes ();
7147 (*r)->region()->trim_front( (position - pull_back_frames));
7149 last_region->clear_changes ();
7150 last_region->trim_end( (position - pull_back_frames + crossfade_len));
7152 _session->add_command (new StatefulDiffCommand ((*r)->region()));
7153 _session->add_command (new StatefulDiffCommand (last_region));
7155 last_region = (*r)->region();
7159 while (used_playlists.size() > 0) {
7160 list <boost::shared_ptr<Playlist > >::iterator i = used_playlists.begin();
7162 used_playlists.pop_front();
7165 commit_reversible_command ();
7169 Editor::tab_to_transient (bool forward)
7171 AnalysisFeatureList positions;
7173 RegionSelection rs = get_regions_from_selection_and_entered ();
7179 framepos_t pos = _session->audible_frame ();
7181 if (!selection->tracks.empty()) {
7183 /* don't waste time searching for transients in duplicate playlists.
7186 TrackViewList ts = selection->tracks.filter_to_unique_playlists ();
7188 for (TrackViewList::iterator t = ts.begin(); t != ts.end(); ++t) {
7190 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (*t);
7193 boost::shared_ptr<Track> tr = rtv->track();
7195 boost::shared_ptr<Playlist> pl = tr->playlist ();
7197 framepos_t result = pl->find_next_transient (pos, forward ? 1 : -1);
7200 positions.push_back (result);
7213 for (RegionSelection::iterator r = rs.begin(); r != rs.end(); ++r) {
7214 (*r)->region()->get_transients (positions);
7218 TransientDetector::cleanup_transients (positions, _session->frame_rate(), 3.0);
7221 AnalysisFeatureList::iterator x;
7223 for (x = positions.begin(); x != positions.end(); ++x) {
7229 if (x != positions.end ()) {
7230 _session->request_locate (*x);
7234 AnalysisFeatureList::reverse_iterator x;
7236 for (x = positions.rbegin(); x != positions.rend(); ++x) {
7242 if (x != positions.rend ()) {
7243 _session->request_locate (*x);
7249 Editor::playhead_forward_to_grid ()
7255 MusicFrame pos (playhead_cursor->current_frame (), 0);
7257 if (pos.frame < max_framepos - 1) {
7259 snap_to_internal (pos, RoundUpAlways, false);
7260 _session->request_locate (pos.frame);
7266 Editor::playhead_backward_to_grid ()
7272 MusicFrame pos (playhead_cursor->current_frame (), 0);
7274 if (pos.frame > 2) {
7276 snap_to_internal (pos, RoundDownAlways, false);
7277 _session->request_locate (pos.frame);
7282 Editor::set_track_height (Height h)
7284 TrackSelection& ts (selection->tracks);
7286 for (TrackSelection::iterator x = ts.begin(); x != ts.end(); ++x) {
7287 (*x)->set_height_enum (h);
7292 Editor::toggle_tracks_active ()
7294 TrackSelection& ts (selection->tracks);
7296 bool target = false;
7302 for (TrackSelection::iterator x = ts.begin(); x != ts.end(); ++x) {
7303 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*>(*x);
7307 target = !rtv->_route->active();
7310 rtv->_route->set_active (target, this);
7316 Editor::remove_tracks ()
7318 /* this will delete GUI objects that may be the subject of an event
7319 handler in which this method is called. Defer actual deletion to the
7320 next idle callback, when all event handling is finished.
7322 Glib::signal_idle().connect (sigc::mem_fun (*this, &Editor::idle_remove_tracks));
7326 Editor::idle_remove_tracks ()
7328 Session::StateProtector sp (_session);
7330 return false; /* do not call again */
7334 Editor::_remove_tracks ()
7336 TrackSelection& ts (selection->tracks);
7342 vector<string> choices;
7346 const char* trackstr;
7348 vector<boost::shared_ptr<Route> > routes;
7349 bool special_bus = false;
7351 for (TrackSelection::iterator x = ts.begin(); x != ts.end(); ++x) {
7352 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (*x);
7356 if (rtv->is_track()) {
7361 routes.push_back (rtv->_route);
7363 if (rtv->route()->is_master() || rtv->route()->is_monitor()) {
7368 if (special_bus && !Config->get_allow_special_bus_removal()) {
7369 MessageDialog msg (_("That would be bad news ...."),
7373 msg.set_secondary_text (string_compose (_(
7374 "Removing the master or monitor bus is such a bad idea\n\
7375 that %1 is not going to allow it.\n\
7377 If you really want to do this sort of thing\n\
7378 edit your ardour.rc file to set the\n\
7379 \"allow-special-bus-removal\" option to be \"yes\""), PROGRAM_NAME));
7386 if (ntracks + nbusses == 0) {
7390 trackstr = P_("track", "tracks", ntracks);
7391 busstr = P_("bus", "busses", nbusses);
7395 prompt = string_compose (_("Do you really want to remove %1 %2 and %3 %4?\n"
7396 "(You may also lose the playlists associated with the %2)\n\n"
7397 "This action cannot be undone, and the session file will be overwritten!"),
7398 ntracks, trackstr, nbusses, busstr);
7400 prompt = string_compose (_("Do you really want to remove %1 %2?\n"
7401 "(You may also lose the playlists associated with the %2)\n\n"
7402 "This action cannot be undone, and the session file will be overwritten!"),
7405 } else if (nbusses) {
7406 prompt = string_compose (_("Do you really want to remove %1 %2?\n\n"
7407 "This action cannot be undone, and the session file will be overwritten"),
7411 choices.push_back (_("No, do nothing."));
7412 if (ntracks + nbusses > 1) {
7413 choices.push_back (_("Yes, remove them."));
7415 choices.push_back (_("Yes, remove it."));
7420 title = string_compose (_("Remove %1"), trackstr);
7422 title = string_compose (_("Remove %1"), busstr);
7425 Choice prompter (title, prompt, choices);
7427 if (prompter.run () != 1) {
7431 if (current_mixer_strip && routes.size () > 1 && std::find (routes.begin(), routes.end(), current_mixer_strip->route()) != routes.end ()) {
7432 /* Route deletion calls Editor::timeaxisview_deleted() iteratively (for each deleted
7433 * route). If the deleted route is currently displayed in the Editor-Mixer (highly
7434 * likely because deletion requires selection) this will call
7435 * Editor::set_selected_mixer_strip () which is expensive ( MixerStrip::set_route() ).
7436 * It's likewise likely that the route that has just been displayed in the
7437 * Editor-Mixer will be next in line for deletion.
7439 * So simply switch to the master-bus (if present)
7441 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
7442 if ((*i)->stripable ()->is_master ()) {
7443 set_selected_mixer_strip (*(*i));
7450 PresentationInfo::ChangeSuspender cs;
7451 DisplaySuspender ds;
7453 boost::shared_ptr<RouteList> rl (new RouteList);
7454 for (vector<boost::shared_ptr<Route> >::iterator x = routes.begin(); x != routes.end(); ++x) {
7457 _session->remove_routes (rl);
7459 /* TrackSelection and RouteList leave scope,
7460 * destructors are called,
7461 * diskstream drops references, save_state is called (again for every track)
7466 Editor::do_insert_time ()
7468 if (selection->tracks.empty()) {
7472 InsertRemoveTimeDialog d (*this);
7473 int response = d.run ();
7475 if (response != RESPONSE_OK) {
7479 if (d.distance() == 0) {
7486 d.intersected_region_action (),
7490 d.move_glued_markers(),
7491 d.move_locked_markers(),
7497 Editor::insert_time (
7498 framepos_t pos, framecnt_t frames, InsertTimeOption opt,
7499 bool all_playlists, bool ignore_music_glue, bool markers_too, bool glued_markers_too, bool locked_markers_too, bool tempo_too
7503 if (Config->get_edit_mode() == Lock) {
7506 bool in_command = false;
7508 TrackViewList ts = selection->tracks.filter_to_unique_playlists ();
7510 for (TrackViewList::iterator x = ts.begin(); x != ts.end(); ++x) {
7514 /* don't operate on any playlist more than once, which could
7515 * happen if "all playlists" is enabled, but there is more
7516 * than 1 track using playlists "from" a given track.
7519 set<boost::shared_ptr<Playlist> > pl;
7521 if (all_playlists) {
7522 RouteTimeAxisView* rtav = dynamic_cast<RouteTimeAxisView*> (*x);
7523 if (rtav && rtav->track ()) {
7524 vector<boost::shared_ptr<Playlist> > all = _session->playlists->playlists_for_track (rtav->track ());
7525 for (vector<boost::shared_ptr<Playlist> >::iterator p = all.begin(); p != all.end(); ++p) {
7530 if ((*x)->playlist ()) {
7531 pl.insert ((*x)->playlist ());
7535 for (set<boost::shared_ptr<Playlist> >::iterator i = pl.begin(); i != pl.end(); ++i) {
7537 (*i)->clear_changes ();
7538 (*i)->clear_owned_changes ();
7541 begin_reversible_command (_("insert time"));
7545 if (opt == SplitIntersected) {
7546 /* non musical split */
7547 (*i)->split (MusicFrame (pos, 0));
7550 (*i)->shift (pos, frames, (opt == MoveIntersected), ignore_music_glue);
7552 vector<Command*> cmds;
7554 _session->add_commands (cmds);
7556 _session->add_command (new StatefulDiffCommand (*i));
7560 RouteTimeAxisView* rtav = dynamic_cast<RouteTimeAxisView*> (*x);
7563 begin_reversible_command (_("insert time"));
7566 rtav->route ()->shift (pos, frames);
7573 const int32_t divisions = get_grid_music_divisions (0);
7574 XMLNode& before (_session->locations()->get_state());
7575 Locations::LocationList copy (_session->locations()->list());
7577 for (Locations::LocationList::iterator i = copy.begin(); i != copy.end(); ++i) {
7579 Locations::LocationList::const_iterator tmp;
7581 if ((*i)->position_lock_style() == AudioTime || glued_markers_too) {
7582 bool const was_locked = (*i)->locked ();
7583 if (locked_markers_too) {
7587 if ((*i)->start() >= pos) {
7588 // move end first, in case we're moving by more than the length of the range
7589 if (!(*i)->is_mark()) {
7590 (*i)->set_end ((*i)->end() + frames, false, true, divisions);
7592 (*i)->set_start ((*i)->start() + frames, false, true, divisions);
7604 begin_reversible_command (_("insert time"));
7607 XMLNode& after (_session->locations()->get_state());
7608 _session->add_command (new MementoCommand<Locations>(*_session->locations(), &before, &after));
7614 begin_reversible_command (_("insert time"));
7617 XMLNode& before (_session->tempo_map().get_state());
7618 _session->tempo_map().insert_time (pos, frames);
7619 XMLNode& after (_session->tempo_map().get_state());
7620 _session->add_command (new MementoCommand<TempoMap>(_session->tempo_map(), &before, &after));
7624 commit_reversible_command ();
7629 Editor::do_remove_time ()
7631 if (selection->tracks.empty()) {
7635 InsertRemoveTimeDialog d (*this, true);
7637 int response = d.run ();
7639 if (response != RESPONSE_OK) {
7643 framecnt_t distance = d.distance();
7645 if (distance == 0) {
7655 d.move_glued_markers(),
7656 d.move_locked_markers(),
7662 Editor::remove_time (framepos_t pos, framecnt_t frames, InsertTimeOption opt,
7663 bool ignore_music_glue, bool markers_too, bool glued_markers_too, bool locked_markers_too, bool tempo_too)
7665 if (Config->get_edit_mode() == Lock) {
7666 error << (_("Cannot insert or delete time when in Lock edit.")) << endmsg;
7669 bool in_command = false;
7671 for (TrackSelection::iterator x = selection->tracks.begin(); x != selection->tracks.end(); ++x) {
7673 boost::shared_ptr<Playlist> pl = (*x)->playlist();
7677 XMLNode &before = pl->get_state();
7680 begin_reversible_command (_("remove time"));
7684 std::list<AudioRange> rl;
7685 AudioRange ar(pos, pos+frames, 0);
7688 pl->shift (pos, -frames, true, ignore_music_glue);
7690 XMLNode &after = pl->get_state();
7692 _session->add_command (new MementoCommand<Playlist> (*pl, &before, &after));
7696 RouteTimeAxisView* rtav = dynamic_cast<RouteTimeAxisView*> (*x);
7699 begin_reversible_command (_("remove time"));
7702 rtav->route ()->shift (pos, -frames);
7706 const int32_t divisions = get_grid_music_divisions (0);
7707 std::list<Location*> loc_kill_list;
7712 XMLNode& before (_session->locations()->get_state());
7713 Locations::LocationList copy (_session->locations()->list());
7715 for (Locations::LocationList::iterator i = copy.begin(); i != copy.end(); ++i) {
7716 if ((*i)->position_lock_style() == AudioTime || glued_markers_too) {
7718 bool const was_locked = (*i)->locked ();
7719 if (locked_markers_too) {
7723 if (!(*i)->is_mark()) { // it's a range; have to handle both start and end
7724 if ((*i)->end() >= pos
7725 && (*i)->end() < pos+frames
7726 && (*i)->start() >= pos
7727 && (*i)->end() < pos+frames) { // range is completely enclosed; kill it
7729 loc_kill_list.push_back(*i);
7730 } else { // only start or end is included, try to do the right thing
7731 // move start before moving end, to avoid trying to move the end to before the start
7732 // if we're removing more time than the length of the range
7733 if ((*i)->start() >= pos && (*i)->start() < pos+frames) {
7734 // start is within cut
7735 (*i)->set_start (pos, false, true,divisions); // bring the start marker to the beginning of the cut
7737 } else if ((*i)->start() >= pos+frames) {
7738 // start (and thus entire range) lies beyond end of cut
7739 (*i)->set_start ((*i)->start() - frames, false, true, divisions); // slip the start marker back
7742 if ((*i)->end() >= pos && (*i)->end() < pos+frames) {
7743 // end is inside cut
7744 (*i)->set_end (pos, false, true, divisions); // bring the end to the cut
7746 } else if ((*i)->end() >= pos+frames) {
7747 // end is beyond end of cut
7748 (*i)->set_end ((*i)->end() - frames, false, true, divisions); // slip the end marker back
7753 } else if ((*i)->start() >= pos && (*i)->start() < pos+frames ) {
7754 loc_kill_list.push_back(*i);
7756 } else if ((*i)->start() >= pos) {
7757 (*i)->set_start ((*i)->start() -frames, false, true, divisions);
7767 for (list<Location*>::iterator i = loc_kill_list.begin(); i != loc_kill_list.end(); ++i) {
7768 _session->locations()->remove( *i );
7773 begin_reversible_command (_("remove time"));
7776 XMLNode& after (_session->locations()->get_state());
7777 _session->add_command (new MementoCommand<Locations>(*_session->locations(), &before, &after));
7782 XMLNode& before (_session->tempo_map().get_state());
7784 if (_session->tempo_map().remove_time (pos, frames) ) {
7786 begin_reversible_command (_("remove time"));
7789 XMLNode& after (_session->tempo_map().get_state());
7790 _session->add_command (new MementoCommand<TempoMap>(_session->tempo_map(), &before, &after));
7795 commit_reversible_command ();
7800 Editor::fit_selection ()
7802 if (!selection->tracks.empty()) {
7803 fit_tracks (selection->tracks);
7807 /* no selected tracks - use tracks with selected regions */
7809 if (!selection->regions.empty()) {
7810 for (RegionSelection::iterator r = selection->regions.begin(); r != selection->regions.end(); ++r) {
7811 tvl.push_back (&(*r)->get_time_axis_view ());
7817 } else if (internal_editing()) {
7818 /* no selected tracks, or regions, but in internal edit mode, so follow the mouse and use
7821 if (entered_track) {
7822 tvl.push_back (entered_track);
7830 Editor::fit_tracks (TrackViewList & tracks)
7832 if (tracks.empty()) {
7836 uint32_t child_heights = 0;
7837 int visible_tracks = 0;
7839 for (TrackSelection::iterator t = tracks.begin(); t != tracks.end(); ++t) {
7841 if (!(*t)->marked_for_display()) {
7845 child_heights += (*t)->effective_height() - (*t)->current_height();
7849 /* compute the per-track height from:
7851 * total canvas visible height
7852 * - height that will be taken by visible children of selected tracks
7853 * - height of the ruler/hscroll area
7855 uint32_t h = (uint32_t) floor ((trackviews_height() - child_heights) / visible_tracks);
7856 double first_y_pos = DBL_MAX;
7858 if (h < TimeAxisView::preset_height (HeightSmall)) {
7859 MessageDialog msg (_("There are too many tracks to fit in the current window"));
7860 /* too small to be displayed */
7864 undo_visual_stack.push_back (current_visual_state (true));
7865 PBD::Unwinder<bool> nsv (no_save_visual, true);
7867 /* build a list of all tracks, including children */
7870 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
7872 TimeAxisView::Children c = (*i)->get_child_list ();
7873 for (TimeAxisView::Children::iterator j = c.begin(); j != c.end(); ++j) {
7874 all.push_back (j->get());
7879 // find selection range.
7880 // if someone knows how to user TrackViewList::iterator for this
7882 int selected_top = -1;
7883 int selected_bottom = -1;
7885 for (TrackViewList::iterator t = all.begin(); t != all.end(); ++t, ++i) {
7886 if ((*t)->marked_for_display ()) {
7887 if (tracks.contains(*t)) {
7888 if (selected_top == -1) {
7891 selected_bottom = i;
7897 for (TrackViewList::iterator t = all.begin(); t != all.end(); ++t, ++i) {
7898 if ((*t)->marked_for_display ()) {
7899 if (tracks.contains(*t)) {
7900 (*t)->set_height (h);
7901 first_y_pos = std::min ((*t)->y_position (), first_y_pos);
7903 if (i > selected_top && i < selected_bottom) {
7904 hide_track_in_display (*t);
7911 set the controls_layout height now, because waiting for its size
7912 request signal handler will cause the vertical adjustment setting to fail
7915 controls_layout.property_height () = _full_canvas_height;
7916 vertical_adjustment.set_value (first_y_pos);
7918 redo_visual_stack.push_back (current_visual_state (true));
7920 visible_tracks_selector.set_text (_("Sel"));
7924 Editor::save_visual_state (uint32_t n)
7926 while (visual_states.size() <= n) {
7927 visual_states.push_back (0);
7930 if (visual_states[n] != 0) {
7931 delete visual_states[n];
7934 visual_states[n] = current_visual_state (true);
7939 Editor::goto_visual_state (uint32_t n)
7941 if (visual_states.size() <= n) {
7945 if (visual_states[n] == 0) {
7949 use_visual_state (*visual_states[n]);
7953 Editor::start_visual_state_op (uint32_t n)
7955 save_visual_state (n);
7957 PopUp* pup = new PopUp (WIN_POS_MOUSE, 1000, true);
7959 snprintf (buf, sizeof (buf), _("Saved view %u"), n+1);
7960 pup->set_text (buf);
7965 Editor::cancel_visual_state_op (uint32_t n)
7967 goto_visual_state (n);
7971 Editor::toggle_region_mute ()
7973 if (_ignore_region_action) {
7977 RegionSelection rs = get_regions_from_selection_and_entered ();
7983 if (rs.size() > 1) {
7984 begin_reversible_command (_("mute regions"));
7986 begin_reversible_command (_("mute region"));
7989 for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
7991 (*i)->region()->playlist()->clear_changes ();
7992 (*i)->region()->set_muted (!(*i)->region()->muted ());
7993 _session->add_command (new StatefulDiffCommand ((*i)->region()));
7997 commit_reversible_command ();
8001 Editor::combine_regions ()
8003 /* foreach track with selected regions, take all selected regions
8004 and join them into a new region containing the subregions (as a
8008 typedef set<RouteTimeAxisView*> RTVS;
8011 if (selection->regions.empty()) {
8015 for (RegionSelection::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
8016 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*>(&(*i)->get_time_axis_view());
8019 tracks.insert (rtv);
8023 begin_reversible_command (_("combine regions"));
8025 vector<RegionView*> new_selection;
8027 for (RTVS::iterator i = tracks.begin(); i != tracks.end(); ++i) {
8030 if ((rv = (*i)->combine_regions ()) != 0) {
8031 new_selection.push_back (rv);
8035 selection->clear_regions ();
8036 for (vector<RegionView*>::iterator i = new_selection.begin(); i != new_selection.end(); ++i) {
8037 selection->add (*i);
8040 commit_reversible_command ();
8044 Editor::uncombine_regions ()
8046 typedef set<RouteTimeAxisView*> RTVS;
8049 if (selection->regions.empty()) {
8053 for (RegionSelection::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
8054 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*>(&(*i)->get_time_axis_view());
8057 tracks.insert (rtv);
8061 begin_reversible_command (_("uncombine regions"));
8063 for (RTVS::iterator i = tracks.begin(); i != tracks.end(); ++i) {
8064 (*i)->uncombine_regions ();
8067 commit_reversible_command ();
8071 Editor::toggle_midi_input_active (bool flip_others)
8074 boost::shared_ptr<RouteList> rl (new RouteList);
8076 for (TrackSelection::iterator i = selection->tracks.begin(); i != selection->tracks.end(); ++i) {
8077 RouteTimeAxisView *rtav = dynamic_cast<RouteTimeAxisView *>(*i);
8083 boost::shared_ptr<MidiTrack> mt = rtav->midi_track();
8086 rl->push_back (rtav->route());
8087 onoff = !mt->input_active();
8091 _session->set_exclusive_input_active (rl, onoff, flip_others);
8094 static bool ok_fine (GdkEventAny*) { return true; }
8100 lock_dialog = new Gtk::Dialog (string_compose (_("%1: Locked"), PROGRAM_NAME), true);
8102 Gtk::Image* padlock = manage (new Gtk::Image (ARDOUR_UI_UTILS::get_icon ("padlock_closed")));
8103 lock_dialog->get_vbox()->pack_start (*padlock);
8104 lock_dialog->signal_delete_event ().connect (sigc::ptr_fun (ok_fine));
8106 ArdourButton* b = manage (new ArdourButton);
8107 b->set_name ("lock button");
8108 b->set_text (_("Click to unlock"));
8109 b->signal_clicked.connect (sigc::mem_fun (*this, &Editor::unlock));
8110 lock_dialog->get_vbox()->pack_start (*b);
8112 lock_dialog->get_vbox()->show_all ();
8113 lock_dialog->set_size_request (200, 200);
8116 delete _main_menu_disabler;
8117 _main_menu_disabler = new MainMenuDisabler;
8119 lock_dialog->present ();
8121 lock_dialog->get_window()->set_decorations (Gdk::WMDecoration (0));
8127 lock_dialog->hide ();
8129 delete _main_menu_disabler;
8130 _main_menu_disabler = 0;
8132 if (UIConfiguration::instance().get_lock_gui_after_seconds()) {
8133 start_lock_event_timing ();
8138 Editor::bring_in_callback (Gtk::Label* label, uint32_t n, uint32_t total, string name)
8140 Gtkmm2ext::UI::instance()->call_slot (invalidator (*this), boost::bind (&Editor::update_bring_in_message, this, label, n, total, name));
8144 Editor::update_bring_in_message (Gtk::Label* label, uint32_t n, uint32_t total, string name)
8146 Timers::TimerSuspender t;
8147 label->set_text (string_compose ("Copying %1, %2 of %3", name, n, total));
8148 Gtkmm2ext::UI::instance()->flush_pending (1);
8152 Editor::bring_all_sources_into_session ()
8159 ArdourDialog w (_("Moving embedded files into session folder"));
8160 w.get_vbox()->pack_start (msg);
8163 /* flush all pending GUI events because we're about to start copying
8167 Timers::TimerSuspender t;
8168 Gtkmm2ext::UI::instance()->flush_pending (3);
8172 _session->bring_all_sources_into_session (boost::bind (&Editor::bring_in_callback, this, &msg, _1, _2, _3));