2 Copyright (C) 2000-2004 Paul Davis
4 This program is free software; you can redistribute it and/or modify
5 it under the terms of the GNU General Public License as published by
6 the Free Software Foundation; either version 2 of the License, or
7 (at your option) any later version.
9 This program is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 GNU General Public License for more details.
14 You should have received a copy of the GNU General Public License
15 along with this program; if not, write to the Free Software
16 Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
20 /* Note: public Editor methods are documented in public_editor.h */
31 #include <gtkmm/messagedialog.h>
33 #include "pbd/error.h"
34 #include "pbd/basename.h"
35 #include "pbd/pthread_utils.h"
36 #include "pbd/memento_command.h"
37 #include "pbd/unwind.h"
38 #include "pbd/whitespace.h"
39 #include "pbd/stateful_diff_command.h"
41 #include "gtkmm2ext/utils.h"
43 #include "widgets/choice.h"
44 #include "widgets/popup.h"
45 #include "widgets/prompter.h"
47 #include "ardour/audio_track.h"
48 #include "ardour/audioregion.h"
49 #include "ardour/boost_debug.h"
50 #include "ardour/dB.h"
51 #include "ardour/location.h"
52 #include "ardour/midi_region.h"
53 #include "ardour/midi_track.h"
54 #include "ardour/operations.h"
55 #include "ardour/playlist_factory.h"
56 #include "ardour/profile.h"
57 #include "ardour/quantize.h"
58 #include "ardour/legatize.h"
59 #include "ardour/region_factory.h"
60 #include "ardour/reverse.h"
61 #include "ardour/session.h"
62 #include "ardour/session_playlists.h"
63 #include "ardour/strip_silence.h"
64 #include "ardour/transient_detector.h"
65 #include "ardour/transpose.h"
67 #include "canvas/canvas.h"
70 #include "audio_region_view.h"
71 #include "audio_streamview.h"
72 #include "audio_time_axis.h"
73 #include "automation_region_view.h"
74 #include "automation_time_axis.h"
75 #include "control_point.h"
79 #include "editor_cursors.h"
80 #include "editor_drag.h"
81 #include "editor_regions.h"
82 #include "editor_routes.h"
83 #include "gui_thread.h"
84 #include "insert_remove_time_dialog.h"
85 #include "interthread_progress_window.h"
86 #include "item_counts.h"
88 #include "midi_region_view.h"
90 #include "mixer_strip.h"
91 #include "mouse_cursors.h"
92 #include "normalize_dialog.h"
94 #include "paste_context.h"
95 #include "patch_change_dialog.h"
96 #include "quantize_dialog.h"
97 #include "region_gain_line.h"
98 #include "rgb_macros.h"
99 #include "route_time_axis.h"
100 #include "selection.h"
101 #include "selection_templates.h"
102 #include "streamview.h"
103 #include "strip_silence_dialog.h"
104 #include "time_axis_view.h"
106 #include "transpose_dialog.h"
107 #include "transform_dialog.h"
108 #include "ui_config.h"
110 #include "pbd/i18n.h"
113 using namespace ARDOUR;
116 using namespace Gtkmm2ext;
117 using namespace ArdourWidgets;
118 using namespace Editing;
119 using Gtkmm2ext::Keyboard;
121 /***********************************************************************
123 ***********************************************************************/
126 Editor::undo (uint32_t n)
128 if (_session && _session->actively_recording()) {
129 /* no undo allowed while recording. Session will check also,
130 but we don't even want to get to that.
135 if (_drags->active ()) {
141 if (_session->undo_depth() == 0) {
142 undo_action->set_sensitive(false);
144 redo_action->set_sensitive(true);
145 begin_selection_op_history ();
150 Editor::redo (uint32_t n)
152 if (_session && _session->actively_recording()) {
153 /* no redo allowed while recording. Session will check also,
154 but we don't even want to get to that.
159 if (_drags->active ()) {
165 if (_session->redo_depth() == 0) {
166 redo_action->set_sensitive(false);
168 undo_action->set_sensitive(true);
169 begin_selection_op_history ();
174 Editor::split_regions_at (MusicFrame where, RegionSelection& regions, bool snap_frame)
178 RegionSelection pre_selected_regions = selection->regions;
179 bool working_on_selection = !pre_selected_regions.empty();
181 list<boost::shared_ptr<Playlist> > used_playlists;
182 list<RouteTimeAxisView*> used_trackviews;
184 if (regions.empty()) {
188 begin_reversible_command (_("split"));
190 // if splitting a single region, and snap-to is using
191 // region boundaries, don't pay attention to them
193 if (regions.size() == 1) {
194 switch (_snap_type) {
195 case SnapToRegionStart:
196 case SnapToRegionSync:
197 case SnapToRegionEnd:
210 EditorFreeze(); /* Emit Signal */
213 for (RegionSelection::iterator a = regions.begin(); a != regions.end(); ) {
215 RegionSelection::iterator tmp;
217 /* XXX this test needs to be more complicated, to make sure we really
218 have something to split.
221 if (!(*a)->region()->covers (where.frame)) {
229 boost::shared_ptr<Playlist> pl = (*a)->region()->playlist();
237 /* we haven't seen this playlist before */
239 /* remember used playlists so we can thaw them later */
240 used_playlists.push_back(pl);
242 TimeAxisView& tv = (*a)->get_time_axis_view();
243 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (&tv);
245 used_trackviews.push_back (rtv);
252 pl->clear_changes ();
253 pl->split_region ((*a)->region(), where);
254 _session->add_command (new StatefulDiffCommand (pl));
260 latest_regionviews.clear ();
262 vector<sigc::connection> region_added_connections;
264 for (list<RouteTimeAxisView*>::iterator i = used_trackviews.begin(); i != used_trackviews.end(); ++i) {
265 region_added_connections.push_back ((*i)->view()->RegionViewAdded.connect (sigc::mem_fun(*this, &Editor::collect_new_region_view)));
268 while (used_playlists.size() > 0) {
269 list <boost::shared_ptr<Playlist > >::iterator i = used_playlists.begin();
271 used_playlists.pop_front();
274 for (vector<sigc::connection>::iterator c = region_added_connections.begin(); c != region_added_connections.end(); ++c) {
279 EditorThaw(); /* Emit Signal */
282 if (working_on_selection) {
283 // IFF we were working on selected regions, try to reinstate the other region selections that existed before the freeze/thaw.
285 RegionSelectionAfterSplit rsas = Config->get_region_selection_after_split();
286 /* There are three classes of regions that we might want selected after
287 splitting selected regions:
288 - regions selected before the split operation, and unaffected by it
289 - newly-created regions before the split
290 - newly-created regions after the split
293 if (rsas & Existing) {
294 // region selections that existed before the split.
295 selection->add ( pre_selected_regions );
298 for (RegionSelection::iterator ri = latest_regionviews.begin(); ri != latest_regionviews.end(); ri++) {
299 if ((*ri)->region()->position() < where.frame) {
300 // new regions created before the split
301 if (rsas & NewlyCreatedLeft) {
302 selection->add (*ri);
305 // new regions created after the split
306 if (rsas & NewlyCreatedRight) {
307 selection->add (*ri);
312 if( working_on_selection ) {
313 selection->add (latest_regionviews); //these are the new regions created after the split
317 commit_reversible_command ();
320 /** Move one extreme of the current range selection. If more than one range is selected,
321 * the start of the earliest range or the end of the latest range is moved.
323 * @param move_end true to move the end of the current range selection, false to move
325 * @param next true to move the extreme to the next region boundary, false to move to
329 Editor::move_range_selection_start_or_end_to_region_boundary (bool move_end, bool next)
331 if (selection->time.start() == selection->time.end_frame()) {
335 framepos_t start = selection->time.start ();
336 framepos_t end = selection->time.end_frame ();
338 /* the position of the thing we may move */
339 framepos_t pos = move_end ? end : start;
340 int dir = next ? 1 : -1;
342 /* so we don't find the current region again */
343 if (dir > 0 || pos > 0) {
347 framepos_t const target = get_region_boundary (pos, dir, true, false);
362 begin_reversible_selection_op (_("alter selection"));
363 selection->set_preserving_all_ranges (start, end);
364 commit_reversible_selection_op ();
368 Editor::nudge_forward_release (GdkEventButton* ev)
370 if (ev->state & Keyboard::PrimaryModifier) {
371 nudge_forward (false, true);
373 nudge_forward (false, false);
379 Editor::nudge_backward_release (GdkEventButton* ev)
381 if (ev->state & Keyboard::PrimaryModifier) {
382 nudge_backward (false, true);
384 nudge_backward (false, false);
391 Editor::nudge_forward (bool next, bool force_playhead)
394 framepos_t next_distance;
400 RegionSelection rs = get_regions_from_selection_and_entered ();
402 if (!force_playhead && !rs.empty()) {
404 begin_reversible_command (_("nudge regions forward"));
406 for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
407 boost::shared_ptr<Region> r ((*i)->region());
409 distance = get_nudge_distance (r->position(), next_distance);
412 distance = next_distance;
416 r->set_position (r->position() + distance);
417 _session->add_command (new StatefulDiffCommand (r));
420 commit_reversible_command ();
423 } else if (!force_playhead && !selection->markers.empty()) {
426 bool in_command = false;
427 const int32_t divisions = get_grid_music_divisions (0);
429 for (MarkerSelection::iterator i = selection->markers.begin(); i != selection->markers.end(); ++i) {
431 Location* loc = find_location_from_marker ((*i), is_start);
435 XMLNode& before (loc->get_state());
438 distance = get_nudge_distance (loc->start(), next_distance);
440 distance = next_distance;
442 if (max_framepos - distance > loc->start() + loc->length()) {
443 loc->set_start (loc->start() + distance, false, true, divisions);
445 loc->set_start (max_framepos - loc->length(), false, true, divisions);
448 distance = get_nudge_distance (loc->end(), next_distance);
450 distance = next_distance;
452 if (max_framepos - distance > loc->end()) {
453 loc->set_end (loc->end() + distance, false, true, divisions);
455 loc->set_end (max_framepos, false, true, divisions);
457 if (loc->is_session_range()) {
458 _session->set_end_is_free (false);
462 begin_reversible_command (_("nudge location forward"));
465 XMLNode& after (loc->get_state());
466 _session->add_command (new MementoCommand<Location>(*loc, &before, &after));
471 commit_reversible_command ();
474 distance = get_nudge_distance (playhead_cursor->current_frame (), next_distance);
475 _session->request_locate (playhead_cursor->current_frame () + distance);
480 Editor::nudge_backward (bool next, bool force_playhead)
483 framepos_t next_distance;
489 RegionSelection rs = get_regions_from_selection_and_entered ();
491 if (!force_playhead && !rs.empty()) {
493 begin_reversible_command (_("nudge regions backward"));
495 for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
496 boost::shared_ptr<Region> r ((*i)->region());
498 distance = get_nudge_distance (r->position(), next_distance);
501 distance = next_distance;
506 if (r->position() > distance) {
507 r->set_position (r->position() - distance);
511 _session->add_command (new StatefulDiffCommand (r));
514 commit_reversible_command ();
516 } else if (!force_playhead && !selection->markers.empty()) {
519 bool in_command = false;
521 for (MarkerSelection::iterator i = selection->markers.begin(); i != selection->markers.end(); ++i) {
523 Location* loc = find_location_from_marker ((*i), is_start);
527 XMLNode& before (loc->get_state());
530 distance = get_nudge_distance (loc->start(), next_distance);
532 distance = next_distance;
534 if (distance < loc->start()) {
535 loc->set_start (loc->start() - distance, false, true, get_grid_music_divisions(0));
537 loc->set_start (0, false, true, get_grid_music_divisions(0));
540 distance = get_nudge_distance (loc->end(), next_distance);
543 distance = next_distance;
546 if (distance < loc->end() - loc->length()) {
547 loc->set_end (loc->end() - distance, false, true, get_grid_music_divisions(0));
549 loc->set_end (loc->length(), false, true, get_grid_music_divisions(0));
551 if (loc->is_session_range()) {
552 _session->set_end_is_free (false);
556 begin_reversible_command (_("nudge location forward"));
559 XMLNode& after (loc->get_state());
560 _session->add_command (new MementoCommand<Location>(*loc, &before, &after));
564 commit_reversible_command ();
569 distance = get_nudge_distance (playhead_cursor->current_frame (), next_distance);
571 if (playhead_cursor->current_frame () > distance) {
572 _session->request_locate (playhead_cursor->current_frame () - distance);
574 _session->goto_start();
580 Editor::nudge_forward_capture_offset ()
582 RegionSelection rs = get_regions_from_selection_and_entered ();
584 if (!_session || rs.empty()) {
588 begin_reversible_command (_("nudge forward"));
590 framepos_t const distance = _session->worst_output_latency();
592 for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
593 boost::shared_ptr<Region> r ((*i)->region());
596 r->set_position (r->position() + distance);
597 _session->add_command(new StatefulDiffCommand (r));
600 commit_reversible_command ();
604 Editor::nudge_backward_capture_offset ()
606 RegionSelection rs = get_regions_from_selection_and_entered ();
608 if (!_session || rs.empty()) {
612 begin_reversible_command (_("nudge backward"));
614 framepos_t const distance = _session->worst_output_latency();
616 for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
617 boost::shared_ptr<Region> r ((*i)->region());
621 if (r->position() > distance) {
622 r->set_position (r->position() - distance);
626 _session->add_command(new StatefulDiffCommand (r));
629 commit_reversible_command ();
632 struct RegionSelectionPositionSorter {
633 bool operator() (RegionView* a, RegionView* b) {
634 return a->region()->position() < b->region()->position();
639 Editor::sequence_regions ()
642 framepos_t r_end_prev;
650 RegionSelection rs = get_regions_from_selection_and_entered ();
651 rs.sort(RegionSelectionPositionSorter());
655 bool in_command = false;
657 for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
658 boost::shared_ptr<Region> r ((*i)->region());
666 if(r->position_locked())
673 r->set_position(r_end_prev);
677 begin_reversible_command (_("sequence regions"));
680 _session->add_command (new StatefulDiffCommand (r));
682 r_end=r->position() + r->length();
688 commit_reversible_command ();
697 Editor::move_to_start ()
699 _session->goto_start ();
703 Editor::move_to_end ()
706 _session->request_locate (_session->current_end_frame());
710 Editor::build_region_boundary_cache ()
713 vector<RegionPoint> interesting_points;
714 boost::shared_ptr<Region> r;
715 TrackViewList tracks;
718 region_boundary_cache.clear ();
724 switch (_snap_type) {
725 case SnapToRegionStart:
726 interesting_points.push_back (Start);
728 case SnapToRegionEnd:
729 interesting_points.push_back (End);
731 case SnapToRegionSync:
732 interesting_points.push_back (SyncPoint);
734 case SnapToRegionBoundary:
735 interesting_points.push_back (Start);
736 interesting_points.push_back (End);
739 fatal << string_compose (_("build_region_boundary_cache called with snap_type = %1"), _snap_type) << endmsg;
740 abort(); /*NOTREACHED*/
744 TimeAxisView *ontrack = 0;
747 if (!selection->tracks.empty()) {
748 tlist = selection->tracks.filter_to_unique_playlists ();
750 tlist = track_views.filter_to_unique_playlists ();
753 while (pos < _session->current_end_frame() && !at_end) {
756 framepos_t lpos = max_framepos;
758 for (vector<RegionPoint>::iterator p = interesting_points.begin(); p != interesting_points.end(); ++p) {
760 if ((r = find_next_region (pos, *p, 1, tlist, &ontrack)) == 0) {
761 if (*p == interesting_points.back()) {
764 /* move to next point type */
770 rpos = r->first_frame();
774 rpos = r->last_frame();
778 rpos = r->sync_position ();
786 RouteTimeAxisView *rtav;
788 if (ontrack != 0 && (rtav = dynamic_cast<RouteTimeAxisView*>(ontrack)) != 0 ) {
789 if (rtav->track() != 0) {
790 speed = rtav->track()->speed();
794 rpos = track_frame_to_session_frame (rpos, speed);
800 /* prevent duplicates, but we don't use set<> because we want to be able
804 vector<framepos_t>::iterator ri;
806 for (ri = region_boundary_cache.begin(); ri != region_boundary_cache.end(); ++ri) {
812 if (ri == region_boundary_cache.end()) {
813 region_boundary_cache.push_back (rpos);
820 /* finally sort to be sure that the order is correct */
822 sort (region_boundary_cache.begin(), region_boundary_cache.end());
825 boost::shared_ptr<Region>
826 Editor::find_next_region (framepos_t frame, RegionPoint point, int32_t dir, TrackViewList& tracks, TimeAxisView **ontrack)
828 TrackViewList::iterator i;
829 framepos_t closest = max_framepos;
830 boost::shared_ptr<Region> ret;
834 framepos_t track_frame;
835 RouteTimeAxisView *rtav;
837 for (i = tracks.begin(); i != tracks.end(); ++i) {
840 boost::shared_ptr<Region> r;
843 if ( (rtav = dynamic_cast<RouteTimeAxisView*>(*i)) != 0 ) {
844 if (rtav->track()!=0)
845 track_speed = rtav->track()->speed();
848 track_frame = session_frame_to_track_frame(frame, track_speed);
850 if ((r = (*i)->find_next_region (track_frame, point, dir)) == 0) {
856 rpos = r->first_frame ();
860 rpos = r->last_frame ();
864 rpos = r->sync_position ();
868 // rpos is a "track frame", converting it to "_session frame"
869 rpos = track_frame_to_session_frame(rpos, track_speed);
872 distance = rpos - frame;
874 distance = frame - rpos;
877 if (distance < closest) {
889 Editor::find_next_region_boundary (framepos_t pos, int32_t dir, const TrackViewList& tracks)
891 framecnt_t distance = max_framepos;
892 framepos_t current_nearest = -1;
894 for (TrackViewList::const_iterator i = tracks.begin(); i != tracks.end(); ++i) {
895 framepos_t contender;
898 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (*i);
904 if ((contender = rtv->find_next_region_boundary (pos, dir)) < 0) {
908 d = ::llabs (pos - contender);
911 current_nearest = contender;
916 return current_nearest;
920 Editor::get_region_boundary (framepos_t pos, int32_t dir, bool with_selection, bool only_onscreen)
925 if (with_selection && Config->get_region_boundaries_from_selected_tracks()) {
927 if (!selection->tracks.empty()) {
929 target = find_next_region_boundary (pos, dir, selection->tracks);
933 if (only_onscreen || Config->get_region_boundaries_from_onscreen_tracks()) {
934 get_onscreen_tracks (tvl);
935 target = find_next_region_boundary (pos, dir, tvl);
937 target = find_next_region_boundary (pos, dir, track_views);
943 if (only_onscreen || Config->get_region_boundaries_from_onscreen_tracks()) {
944 get_onscreen_tracks (tvl);
945 target = find_next_region_boundary (pos, dir, tvl);
947 target = find_next_region_boundary (pos, dir, track_views);
955 Editor::cursor_to_region_boundary (bool with_selection, int32_t dir)
957 framepos_t pos = playhead_cursor->current_frame ();
964 // so we don't find the current region again..
965 if (dir > 0 || pos > 0) {
969 if ((target = get_region_boundary (pos, dir, with_selection, false)) < 0) {
973 _session->request_locate (target);
977 Editor::cursor_to_next_region_boundary (bool with_selection)
979 cursor_to_region_boundary (with_selection, 1);
983 Editor::cursor_to_previous_region_boundary (bool with_selection)
985 cursor_to_region_boundary (with_selection, -1);
989 Editor::cursor_to_region_point (EditorCursor* cursor, RegionPoint point, int32_t dir)
991 boost::shared_ptr<Region> r;
992 framepos_t pos = cursor->current_frame ();
998 TimeAxisView *ontrack = 0;
1000 // so we don't find the current region again..
1004 if (!selection->tracks.empty()) {
1006 r = find_next_region (pos, point, dir, selection->tracks, &ontrack);
1008 } else if (clicked_axisview) {
1011 t.push_back (clicked_axisview);
1013 r = find_next_region (pos, point, dir, t, &ontrack);
1017 r = find_next_region (pos, point, dir, track_views, &ontrack);
1026 pos = r->first_frame ();
1030 pos = r->last_frame ();
1034 pos = r->sync_position ();
1039 RouteTimeAxisView *rtav;
1041 if ( ontrack != 0 && (rtav = dynamic_cast<RouteTimeAxisView*>(ontrack)) != 0 ) {
1042 if (rtav->track() != 0) {
1043 speed = rtav->track()->speed();
1047 pos = track_frame_to_session_frame(pos, speed);
1049 if (cursor == playhead_cursor) {
1050 _session->request_locate (pos);
1052 cursor->set_position (pos);
1057 Editor::cursor_to_next_region_point (EditorCursor* cursor, RegionPoint point)
1059 cursor_to_region_point (cursor, point, 1);
1063 Editor::cursor_to_previous_region_point (EditorCursor* cursor, RegionPoint point)
1065 cursor_to_region_point (cursor, point, -1);
1069 Editor::cursor_to_selection_start (EditorCursor *cursor)
1073 switch (mouse_mode) {
1075 if (!selection->regions.empty()) {
1076 pos = selection->regions.start();
1081 if (!selection->time.empty()) {
1082 pos = selection->time.start ();
1090 if (cursor == playhead_cursor) {
1091 _session->request_locate (pos);
1093 cursor->set_position (pos);
1098 Editor::cursor_to_selection_end (EditorCursor *cursor)
1102 switch (mouse_mode) {
1104 if (!selection->regions.empty()) {
1105 pos = selection->regions.end_frame();
1110 if (!selection->time.empty()) {
1111 pos = selection->time.end_frame ();
1119 if (cursor == playhead_cursor) {
1120 _session->request_locate (pos);
1122 cursor->set_position (pos);
1127 Editor::selected_marker_to_region_boundary (bool with_selection, int32_t dir)
1137 if (selection->markers.empty()) {
1141 if (!mouse_frame (mouse, ignored)) {
1145 add_location_mark (mouse);
1148 if ((loc = find_location_from_marker (selection->markers.front(), ignored)) == 0) {
1152 framepos_t pos = loc->start();
1154 // so we don't find the current region again..
1155 if (dir > 0 || pos > 0) {
1159 if ((target = get_region_boundary (pos, dir, with_selection, false)) < 0) {
1163 loc->move_to (target, 0);
1167 Editor::selected_marker_to_next_region_boundary (bool with_selection)
1169 selected_marker_to_region_boundary (with_selection, 1);
1173 Editor::selected_marker_to_previous_region_boundary (bool with_selection)
1175 selected_marker_to_region_boundary (with_selection, -1);
1179 Editor::selected_marker_to_region_point (RegionPoint point, int32_t dir)
1181 boost::shared_ptr<Region> r;
1186 if (!_session || selection->markers.empty()) {
1190 if ((loc = find_location_from_marker (selection->markers.front(), ignored)) == 0) {
1194 TimeAxisView *ontrack = 0;
1198 // so we don't find the current region again..
1202 if (!selection->tracks.empty()) {
1204 r = find_next_region (pos, point, dir, selection->tracks, &ontrack);
1208 r = find_next_region (pos, point, dir, track_views, &ontrack);
1217 pos = r->first_frame ();
1221 pos = r->last_frame ();
1225 pos = r->adjust_to_sync (r->first_frame());
1230 RouteTimeAxisView *rtav;
1232 if (ontrack != 0 && (rtav = dynamic_cast<RouteTimeAxisView*>(ontrack)) != 0) {
1233 if (rtav->track() != 0) {
1234 speed = rtav->track()->speed();
1238 pos = track_frame_to_session_frame(pos, speed);
1240 loc->move_to (pos, 0);
1244 Editor::selected_marker_to_next_region_point (RegionPoint point)
1246 selected_marker_to_region_point (point, 1);
1250 Editor::selected_marker_to_previous_region_point (RegionPoint point)
1252 selected_marker_to_region_point (point, -1);
1256 Editor::selected_marker_to_selection_start ()
1262 if (!_session || selection->markers.empty()) {
1266 if ((loc = find_location_from_marker (selection->markers.front(), ignored)) == 0) {
1270 switch (mouse_mode) {
1272 if (!selection->regions.empty()) {
1273 pos = selection->regions.start();
1278 if (!selection->time.empty()) {
1279 pos = selection->time.start ();
1287 loc->move_to (pos, 0);
1291 Editor::selected_marker_to_selection_end ()
1297 if (!_session || selection->markers.empty()) {
1301 if ((loc = find_location_from_marker (selection->markers.front(), ignored)) == 0) {
1305 switch (mouse_mode) {
1307 if (!selection->regions.empty()) {
1308 pos = selection->regions.end_frame();
1313 if (!selection->time.empty()) {
1314 pos = selection->time.end_frame ();
1322 loc->move_to (pos, 0);
1326 Editor::scroll_playhead (bool forward)
1328 framepos_t pos = playhead_cursor->current_frame ();
1329 framecnt_t delta = (framecnt_t) floor (current_page_samples() / 0.8);
1332 if (pos == max_framepos) {
1336 if (pos < max_framepos - delta) {
1355 _session->request_locate (pos);
1359 Editor::cursor_align (bool playhead_to_edit)
1365 if (playhead_to_edit) {
1367 if (selection->markers.empty()) {
1371 _session->request_locate (selection->markers.front()->position(), _session->transport_rolling());
1374 const int32_t divisions = get_grid_music_divisions (0);
1375 /* move selected markers to playhead */
1377 for (MarkerSelection::iterator i = selection->markers.begin(); i != selection->markers.end(); ++i) {
1380 Location* loc = find_location_from_marker (*i, ignored);
1382 if (loc->is_mark()) {
1383 loc->set_start (playhead_cursor->current_frame (), false, true, divisions);
1385 loc->set (playhead_cursor->current_frame (),
1386 playhead_cursor->current_frame () + loc->length(), true, divisions);
1393 Editor::scroll_backward (float pages)
1395 framepos_t const one_page = (framepos_t) rint (_visible_canvas_width * samples_per_pixel);
1396 framepos_t const cnt = (framepos_t) floor (pages * one_page);
1399 if (leftmost_frame < cnt) {
1402 frame = leftmost_frame - cnt;
1405 reset_x_origin (frame);
1409 Editor::scroll_forward (float pages)
1411 framepos_t const one_page = (framepos_t) rint (_visible_canvas_width * samples_per_pixel);
1412 framepos_t const cnt = (framepos_t) floor (pages * one_page);
1415 if (max_framepos - cnt < leftmost_frame) {
1416 frame = max_framepos - cnt;
1418 frame = leftmost_frame + cnt;
1421 reset_x_origin (frame);
1425 Editor::scroll_tracks_down ()
1427 double vert_value = vertical_adjustment.get_value() + vertical_adjustment.get_page_size();
1428 if (vert_value > vertical_adjustment.get_upper() - _visible_canvas_height) {
1429 vert_value = vertical_adjustment.get_upper() - _visible_canvas_height;
1432 vertical_adjustment.set_value (vert_value);
1436 Editor::scroll_tracks_up ()
1438 vertical_adjustment.set_value (vertical_adjustment.get_value() - vertical_adjustment.get_page_size());
1442 Editor::scroll_tracks_down_line ()
1444 double vert_value = vertical_adjustment.get_value() + 60;
1446 if (vert_value > vertical_adjustment.get_upper() - _visible_canvas_height) {
1447 vert_value = vertical_adjustment.get_upper() - _visible_canvas_height;
1450 vertical_adjustment.set_value (vert_value);
1454 Editor::scroll_tracks_up_line ()
1456 reset_y_origin (vertical_adjustment.get_value() - 60);
1460 Editor::select_topmost_track ()
1462 const double top_of_trackviews = vertical_adjustment.get_value();
1463 for (TrackViewList::iterator t = track_views.begin(); t != track_views.end(); ++t) {
1464 if ((*t)->hidden()) {
1467 std::pair<TimeAxisView*,double> res = (*t)->covers_y_position (top_of_trackviews);
1469 selection->set (*t);
1476 Editor::scroll_down_one_track (bool skip_child_views)
1478 TrackViewList::reverse_iterator next = track_views.rend();
1479 const double top_of_trackviews = vertical_adjustment.get_value();
1481 for (TrackViewList::reverse_iterator t = track_views.rbegin(); t != track_views.rend(); ++t) {
1482 if ((*t)->hidden()) {
1486 /* If this is the upper-most visible trackview, we want to display
1487 * the one above it (next)
1489 * Note that covers_y_position() is recursive and includes child views
1491 std::pair<TimeAxisView*,double> res = (*t)->covers_y_position (top_of_trackviews);
1494 if (skip_child_views) {
1497 /* automation lane (one level, non-recursive)
1499 * - if no automation lane exists -> move to next tack
1500 * - if the first (here: bottom-most) matches -> move to next tack
1501 * - if no y-axis match is found -> the current track is at the top
1502 * -> move to last (here: top-most) automation lane
1504 TimeAxisView::Children kids = (*t)->get_child_list();
1505 TimeAxisView::Children::reverse_iterator nkid = kids.rend();
1507 for (TimeAxisView::Children::reverse_iterator ci = kids.rbegin(); ci != kids.rend(); ++ci) {
1508 if ((*ci)->hidden()) {
1512 std::pair<TimeAxisView*,double> dev;
1513 dev = (*ci)->covers_y_position (top_of_trackviews);
1515 /* some automation lane is currently at the top */
1516 if (ci == kids.rbegin()) {
1517 /* first (bottom-most) autmation lane is at the top.
1518 * -> move to next track
1527 if (nkid != kids.rend()) {
1528 ensure_time_axis_view_is_visible (**nkid, true);
1536 /* move to the track below the first one that covers the */
1538 if (next != track_views.rend()) {
1539 ensure_time_axis_view_is_visible (**next, true);
1547 Editor::scroll_up_one_track (bool skip_child_views)
1549 TrackViewList::iterator prev = track_views.end();
1550 double top_of_trackviews = vertical_adjustment.get_value ();
1552 for (TrackViewList::iterator t = track_views.begin(); t != track_views.end(); ++t) {
1554 if ((*t)->hidden()) {
1558 /* find the trackview at the top of the trackview group
1560 * Note that covers_y_position() is recursive and includes child views
1562 std::pair<TimeAxisView*,double> res = (*t)->covers_y_position (top_of_trackviews);
1565 if (skip_child_views) {
1568 /* automation lane (one level, non-recursive)
1570 * - if no automation lane exists -> move to prev tack
1571 * - if no y-axis match is found -> the current track is at the top -> move to prev track
1572 * (actually last automation lane of previous track, see below)
1573 * - if first (top-most) lane is at the top -> move to this track
1574 * - else move up one lane
1576 TimeAxisView::Children kids = (*t)->get_child_list();
1577 TimeAxisView::Children::iterator pkid = kids.end();
1579 for (TimeAxisView::Children::iterator ci = kids.begin(); ci != kids.end(); ++ci) {
1580 if ((*ci)->hidden()) {
1584 std::pair<TimeAxisView*,double> dev;
1585 dev = (*ci)->covers_y_position (top_of_trackviews);
1587 /* some automation lane is currently at the top */
1588 if (ci == kids.begin()) {
1589 /* first (top-most) autmation lane is at the top.
1590 * jump directly to this track's top
1592 ensure_time_axis_view_is_visible (**t, true);
1595 else if (pkid != kids.end()) {
1596 /* some other automation lane is at the top.
1597 * move up to prev automation lane.
1599 ensure_time_axis_view_is_visible (**pkid, true);
1602 assert(0); // not reached
1613 if (prev != track_views.end()) {
1614 // move to bottom-most automation-lane of the previous track
1615 TimeAxisView::Children kids = (*prev)->get_child_list();
1616 TimeAxisView::Children::reverse_iterator pkid = kids.rend();
1617 if (!skip_child_views) {
1618 // find the last visible lane
1619 for (TimeAxisView::Children::reverse_iterator ci = kids.rbegin(); ci != kids.rend(); ++ci) {
1620 if (!(*ci)->hidden()) {
1626 if (pkid != kids.rend()) {
1627 ensure_time_axis_view_is_visible (**pkid, true);
1629 ensure_time_axis_view_is_visible (**prev, true);
1638 Editor::scroll_left_step ()
1640 framepos_t xdelta = (current_page_samples() / 8);
1642 if (leftmost_frame > xdelta) {
1643 reset_x_origin (leftmost_frame - xdelta);
1651 Editor::scroll_right_step ()
1653 framepos_t xdelta = (current_page_samples() / 8);
1655 if (max_framepos - xdelta > leftmost_frame) {
1656 reset_x_origin (leftmost_frame + xdelta);
1658 reset_x_origin (max_framepos - current_page_samples());
1663 Editor::scroll_left_half_page ()
1665 framepos_t xdelta = (current_page_samples() / 2);
1666 if (leftmost_frame > xdelta) {
1667 reset_x_origin (leftmost_frame - xdelta);
1674 Editor::scroll_right_half_page ()
1676 framepos_t xdelta = (current_page_samples() / 2);
1677 if (max_framepos - xdelta > leftmost_frame) {
1678 reset_x_origin (leftmost_frame + xdelta);
1680 reset_x_origin (max_framepos - current_page_samples());
1687 Editor::tav_zoom_step (bool coarser)
1689 DisplaySuspender ds;
1693 if (selection->tracks.empty()) {
1696 ts = &selection->tracks;
1699 for (TrackViewList::iterator i = ts->begin(); i != ts->end(); ++i) {
1700 TimeAxisView *tv = (static_cast<TimeAxisView*>(*i));
1701 tv->step_height (coarser);
1706 Editor::tav_zoom_smooth (bool coarser, bool force_all)
1708 DisplaySuspender ds;
1712 if (selection->tracks.empty() || force_all) {
1715 ts = &selection->tracks;
1718 for (TrackViewList::iterator i = ts->begin(); i != ts->end(); ++i) {
1719 TimeAxisView *tv = (static_cast<TimeAxisView*>(*i));
1720 uint32_t h = tv->current_height ();
1725 if (h >= TimeAxisView::preset_height (HeightSmall)) {
1730 tv->set_height (h + 5);
1736 Editor::temporal_zoom_step_mouse_focus_scale (bool zoom_out, double scale)
1738 Editing::ZoomFocus temp_focus = zoom_focus;
1739 zoom_focus = Editing::ZoomFocusMouse;
1740 temporal_zoom_step_scale (zoom_out, scale);
1741 zoom_focus = temp_focus;
1745 Editor::temporal_zoom_step_mouse_focus (bool zoom_out)
1747 temporal_zoom_step_mouse_focus_scale (zoom_out, 2.0);
1751 Editor::temporal_zoom_step (bool zoom_out)
1753 temporal_zoom_step_scale (zoom_out, 2.0);
1757 Editor::temporal_zoom_step_scale (bool zoom_out, double scale)
1759 ENSURE_GUI_THREAD (*this, &Editor::temporal_zoom_step, zoom_out, scale)
1761 framecnt_t nspp = samples_per_pixel;
1765 if (nspp == samples_per_pixel) {
1770 if (nspp == samples_per_pixel) {
1775 // ToDo: encapsulate all of this into something like editor::get_session_extents() or editor::leftmost(), rightmost()
1777 //ToDo: also incorporate automation regions (in case the session has no audio/midi but is just used for automating plugins or the like)
1779 //calculate the extents of all regions in every playlist
1780 framecnt_t session_extent_start = 0;
1781 framecnt_t session_extent_end = 0;
1783 boost::shared_ptr<RouteList> rl = _session->get_routes();
1784 for (RouteList::iterator r = rl->begin(); r != rl->end(); ++r) {
1785 boost::shared_ptr<Track> tr = boost::dynamic_pointer_cast<Track> (*r);
1787 boost::shared_ptr<Playlist> pl = tr->playlist();
1789 pair<framepos_t, framepos_t> e;
1790 e = pl->get_extent();
1791 if (e.first < session_extent_start) {
1792 session_extent_start = e.first;
1794 if (e.second > session_extent_end) {
1795 session_extent_end = e.second;
1801 framecnt_t session_extents = session_extent_end - session_extent_start;
1803 //in a session with no regions, use the start/end markers to set max zoom
1804 framecnt_t const session_length = _session->current_end_frame() - _session->current_start_frame ();
1805 if ( session_length > session_extents )
1806 session_extents = session_length;
1808 //in a session with no regions or start/end markers, use 2 minutes to set max zoom
1809 framecnt_t const min_length = _session->nominal_frame_rate()*60*2;
1810 if ( min_length > session_extents )
1811 session_extents = min_length;
1813 //convert to samples-per-pixel and limit our zoom to this value
1814 framecnt_t session_extents_pp = session_extents / _visible_canvas_width;
1815 if (nspp > session_extents_pp)
1816 nspp = session_extents_pp;
1819 temporal_zoom (nspp);
1823 Editor::temporal_zoom (framecnt_t fpp)
1829 framepos_t current_page = current_page_samples();
1830 framepos_t current_leftmost = leftmost_frame;
1831 framepos_t current_rightmost;
1832 framepos_t current_center;
1833 framepos_t new_page_size;
1834 framepos_t half_page_size;
1835 framepos_t leftmost_after_zoom = 0;
1837 bool in_track_canvas;
1838 bool use_mouse_frame = true;
1842 if (fpp == samples_per_pixel) {
1846 // Imposing an arbitrary limit to zoom out as too much zoom out produces
1847 // segfaults for lack of memory. If somebody decides this is not high enough I
1848 // believe it can be raisen to higher values but some limit must be in place.
1850 // This constant represents 1 day @ 48kHz on a 1600 pixel wide display
1851 // all of which is used for the editor track displays. The whole day
1852 // would be 4147200000 samples, so 2592000 samples per pixel.
1854 nfpp = min (fpp, (framecnt_t) 2592000);
1855 nfpp = max ((framecnt_t) 1, nfpp);
1857 new_page_size = (framepos_t) floor (_visible_canvas_width * nfpp);
1858 half_page_size = new_page_size / 2;
1860 switch (zoom_focus) {
1862 leftmost_after_zoom = current_leftmost;
1865 case ZoomFocusRight:
1866 current_rightmost = leftmost_frame + current_page;
1867 if (current_rightmost < new_page_size) {
1868 leftmost_after_zoom = 0;
1870 leftmost_after_zoom = current_rightmost - new_page_size;
1874 case ZoomFocusCenter:
1875 current_center = current_leftmost + (current_page/2);
1876 if (current_center < half_page_size) {
1877 leftmost_after_zoom = 0;
1879 leftmost_after_zoom = current_center - half_page_size;
1883 case ZoomFocusPlayhead:
1884 /* centre playhead */
1885 l = playhead_cursor->current_frame () - (new_page_size * 0.5);
1888 leftmost_after_zoom = 0;
1889 } else if (l > max_framepos) {
1890 leftmost_after_zoom = max_framepos - new_page_size;
1892 leftmost_after_zoom = (framepos_t) l;
1896 case ZoomFocusMouse:
1897 /* try to keep the mouse over the same point in the display */
1899 if (_drags->active()) {
1900 where = _drags->current_pointer_frame ();
1901 } else if (!mouse_frame (where, in_track_canvas)) {
1902 use_mouse_frame = false;
1905 if (use_mouse_frame) {
1906 l = - ((new_page_size * ((where - current_leftmost)/(double)current_page)) - where);
1909 leftmost_after_zoom = 0;
1910 } else if (l > max_framepos) {
1911 leftmost_after_zoom = max_framepos - new_page_size;
1913 leftmost_after_zoom = (framepos_t) l;
1916 /* use playhead instead */
1917 where = playhead_cursor->current_frame ();
1919 if (where < half_page_size) {
1920 leftmost_after_zoom = 0;
1922 leftmost_after_zoom = where - half_page_size;
1928 /* try to keep the edit point in the same place */
1929 where = get_preferred_edit_position ();
1933 double l = - ((new_page_size * ((where - current_leftmost)/(double)current_page)) - where);
1936 leftmost_after_zoom = 0;
1937 } else if (l > max_framepos) {
1938 leftmost_after_zoom = max_framepos - new_page_size;
1940 leftmost_after_zoom = (framepos_t) l;
1944 /* edit point not defined */
1951 // leftmost_after_zoom = min (leftmost_after_zoom, _session->current_end_frame());
1953 reposition_and_zoom (leftmost_after_zoom, nfpp);
1957 Editor::calc_extra_zoom_edges(framepos_t &start, framepos_t &end)
1959 /* this func helps make sure we leave a little space
1960 at each end of the editor so that the zoom doesn't fit the region
1961 precisely to the screen.
1964 GdkScreen* screen = gdk_screen_get_default ();
1965 const gint pixwidth = gdk_screen_get_width (screen);
1966 const gint mmwidth = gdk_screen_get_width_mm (screen);
1967 const double pix_per_mm = (double) pixwidth/ (double) mmwidth;
1968 const double one_centimeter_in_pixels = pix_per_mm * 10.0;
1970 const framepos_t range = end - start;
1971 const framecnt_t new_fpp = (framecnt_t) ceil ((double) range / (double) _visible_canvas_width);
1972 const framepos_t extra_samples = (framepos_t) floor (one_centimeter_in_pixels * new_fpp);
1974 if (start > extra_samples) {
1975 start -= extra_samples;
1980 if (max_framepos - extra_samples > end) {
1981 end += extra_samples;
1988 Editor::get_selection_extents (framepos_t &start, framepos_t &end) const
1990 start = max_framepos;
1994 //ToDo: if notes are selected, set extents to that selection
1996 //ToDo: if control points are selected, set extents to that selection
1998 if ( !selection->regions.empty() ) {
1999 RegionSelection rs = get_regions_from_selection_and_entered ();
2001 for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
2003 if ((*i)->region()->position() < start) {
2004 start = (*i)->region()->position();
2007 if ((*i)->region()->last_frame() + 1 > end) {
2008 end = (*i)->region()->last_frame() + 1;
2012 } else if (!selection->time.empty()) {
2013 start = selection->time.start();
2014 end = selection->time.end_frame();
2016 ret = false; //no selection found
2019 if ((start == 0 && end == 0) || end < start) {
2028 Editor::temporal_zoom_selection (Editing::ZoomAxis axes)
2030 if (!selection) return;
2032 //ToDo: if notes are selected, zoom to that
2034 //ToDo: if control points are selected, zoom to that
2036 if (axes == Horizontal || axes == Both) {
2038 framepos_t start, end;
2039 if (get_selection_extents (start, end)) {
2040 calc_extra_zoom_edges (start, end);
2041 temporal_zoom_by_frame (start, end);
2045 if (axes == Vertical || axes == Both) {
2051 Editor::temporal_zoom_session ()
2053 ENSURE_GUI_THREAD (*this, &Editor::temporal_zoom_session)
2056 framecnt_t start = _session->current_start_frame();
2057 framecnt_t end = _session->current_end_frame();
2059 if (_session->actively_recording () ) {
2060 framepos_t cur = playhead_cursor->current_frame ();
2062 /* recording beyond the end marker; zoom out
2063 * by 5 seconds more so that if 'follow
2064 * playhead' is active we don't immediately
2067 end = cur + _session->frame_rate() * 5;
2071 if ((start == 0 && end == 0) || end < start) {
2075 calc_extra_zoom_edges(start, end);
2077 temporal_zoom_by_frame (start, end);
2082 Editor::temporal_zoom_by_frame (framepos_t start, framepos_t end)
2084 if (!_session) return;
2086 if ((start == 0 && end == 0) || end < start) {
2090 framepos_t range = end - start;
2092 const framecnt_t new_fpp = (framecnt_t) ceil ((double) range / (double) _visible_canvas_width);
2094 framepos_t new_page = range;
2095 framepos_t middle = (framepos_t) floor ((double) start + ((double) range / 2.0f));
2096 framepos_t new_leftmost = (framepos_t) floor ((double) middle - ((double) new_page / 2.0f));
2098 if (new_leftmost > middle) {
2102 if (new_leftmost < 0) {
2106 reposition_and_zoom (new_leftmost, new_fpp);
2110 Editor::temporal_zoom_to_frame (bool coarser, framepos_t frame)
2116 framecnt_t range_before = frame - leftmost_frame;
2120 if (samples_per_pixel <= 1) {
2123 new_spp = samples_per_pixel + (samples_per_pixel/2);
2125 range_before += range_before/2;
2127 if (samples_per_pixel >= 1) {
2128 new_spp = samples_per_pixel - (samples_per_pixel/2);
2130 /* could bail out here since we cannot zoom any finer,
2131 but leave that to the equality test below
2133 new_spp = samples_per_pixel;
2136 range_before -= range_before/2;
2139 if (new_spp == samples_per_pixel) {
2143 /* zoom focus is automatically taken as @param frame when this
2147 framepos_t new_leftmost = frame - (framepos_t)range_before;
2149 if (new_leftmost > frame) {
2153 if (new_leftmost < 0) {
2157 reposition_and_zoom (new_leftmost, new_spp);
2162 Editor::choose_new_marker_name(string &name) {
2164 if (!UIConfiguration::instance().get_name_new_markers()) {
2165 /* don't prompt user for a new name */
2169 Prompter dialog (true);
2171 dialog.set_prompt (_("New Name:"));
2173 dialog.set_title (_("New Location Marker"));
2175 dialog.set_name ("MarkNameWindow");
2176 dialog.set_size_request (250, -1);
2177 dialog.set_position (Gtk::WIN_POS_MOUSE);
2179 dialog.add_button (Stock::OK, RESPONSE_ACCEPT);
2180 dialog.set_initial_text (name);
2184 switch (dialog.run ()) {
2185 case RESPONSE_ACCEPT:
2191 dialog.get_result(name);
2198 Editor::add_location_from_selection ()
2202 if (selection->time.empty()) {
2206 if (_session == 0 || clicked_axisview == 0) {
2210 framepos_t start = selection->time[clicked_selection].start;
2211 framepos_t end = selection->time[clicked_selection].end;
2213 _session->locations()->next_available_name(rangename,"selection");
2214 Location *location = new Location (*_session, start, end, rangename, Location::IsRangeMarker, get_grid_music_divisions(0));
2216 begin_reversible_command (_("add marker"));
2218 XMLNode &before = _session->locations()->get_state();
2219 _session->locations()->add (location, true);
2220 XMLNode &after = _session->locations()->get_state();
2221 _session->add_command(new MementoCommand<Locations>(*(_session->locations()), &before, &after));
2223 commit_reversible_command ();
2227 Editor::add_location_mark (framepos_t where)
2231 select_new_marker = true;
2233 _session->locations()->next_available_name(markername,"mark");
2234 if (!choose_new_marker_name(markername)) {
2237 Location *location = new Location (*_session, where, where, markername, Location::IsMark, get_grid_music_divisions (0));
2238 begin_reversible_command (_("add marker"));
2240 XMLNode &before = _session->locations()->get_state();
2241 _session->locations()->add (location, true);
2242 XMLNode &after = _session->locations()->get_state();
2243 _session->add_command(new MementoCommand<Locations>(*(_session->locations()), &before, &after));
2245 commit_reversible_command ();
2249 Editor::set_session_start_from_playhead ()
2255 if ((loc = _session->locations()->session_range_location()) == 0) { //should never happen
2256 _session->set_session_extents ( _session->audible_frame(), _session->audible_frame() );
2258 XMLNode &before = loc->get_state();
2260 _session->set_session_extents ( _session->audible_frame(), loc->end() );
2262 XMLNode &after = loc->get_state();
2264 begin_reversible_command (_("Set session start"));
2266 _session->add_command (new MementoCommand<Location>(*loc, &before, &after));
2268 commit_reversible_command ();
2273 Editor::set_session_end_from_playhead ()
2279 if ((loc = _session->locations()->session_range_location()) == 0) { //should never happen
2280 _session->set_session_extents ( _session->audible_frame(), _session->audible_frame() );
2282 XMLNode &before = loc->get_state();
2284 _session->set_session_extents ( loc->start(), _session->audible_frame() );
2286 XMLNode &after = loc->get_state();
2288 begin_reversible_command (_("Set session start"));
2290 _session->add_command (new MementoCommand<Location>(*loc, &before, &after));
2292 commit_reversible_command ();
2295 _session->set_end_is_free (false);
2300 Editor::toggle_location_at_playhead_cursor ()
2302 if (!do_remove_location_at_playhead_cursor())
2304 add_location_from_playhead_cursor();
2309 Editor::add_location_from_playhead_cursor ()
2311 add_location_mark (_session->audible_frame());
2315 Editor::do_remove_location_at_playhead_cursor ()
2317 bool removed = false;
2320 XMLNode &before = _session->locations()->get_state();
2322 //find location(s) at this time
2323 Locations::LocationList locs;
2324 _session->locations()->find_all_between (_session->audible_frame(), _session->audible_frame()+1, locs, Location::Flags(0));
2325 for (Locations::LocationList::iterator i = locs.begin(); i != locs.end(); ++i) {
2326 if ((*i)->is_mark()) {
2327 _session->locations()->remove (*i);
2334 begin_reversible_command (_("remove marker"));
2335 XMLNode &after = _session->locations()->get_state();
2336 _session->add_command(new MementoCommand<Locations>(*(_session->locations()), &before, &after));
2337 commit_reversible_command ();
2344 Editor::remove_location_at_playhead_cursor ()
2346 do_remove_location_at_playhead_cursor ();
2349 /** Add a range marker around each selected region */
2351 Editor::add_locations_from_region ()
2353 RegionSelection rs = get_regions_from_selection_and_entered ();
2358 bool commit = false;
2360 XMLNode &before = _session->locations()->get_state();
2362 for (RegionSelection::iterator i = rs.begin (); i != rs.end (); ++i) {
2364 boost::shared_ptr<Region> region = (*i)->region ();
2366 Location *location = new Location (*_session, region->position(), region->last_frame(), region->name(), Location::IsRangeMarker, 0);
2368 _session->locations()->add (location, true);
2373 begin_reversible_command (selection->regions.size () > 1 ? _("add markers") : _("add marker"));
2374 XMLNode &after = _session->locations()->get_state();
2375 _session->add_command (new MementoCommand<Locations>(*(_session->locations()), &before, &after));
2376 commit_reversible_command ();
2380 /** Add a single range marker around all selected regions */
2382 Editor::add_location_from_region ()
2384 RegionSelection rs = get_regions_from_selection_and_entered ();
2390 XMLNode &before = _session->locations()->get_state();
2394 if (rs.size() > 1) {
2395 _session->locations()->next_available_name(markername, "regions");
2397 RegionView* rv = *(rs.begin());
2398 boost::shared_ptr<Region> region = rv->region();
2399 markername = region->name();
2402 if (!choose_new_marker_name(markername)) {
2406 // single range spanning all selected
2407 Location *location = new Location (*_session, selection->regions.start(), selection->regions.end_frame(), markername, Location::IsRangeMarker, 0);
2408 _session->locations()->add (location, true);
2410 begin_reversible_command (_("add marker"));
2411 XMLNode &after = _session->locations()->get_state();
2412 _session->add_command (new MementoCommand<Locations>(*(_session->locations()), &before, &after));
2413 commit_reversible_command ();
2419 Editor::jump_forward_to_mark ()
2425 framepos_t pos = _session->locations()->first_mark_after (playhead_cursor->current_frame());
2431 _session->request_locate (pos, _session->transport_rolling());
2435 Editor::jump_backward_to_mark ()
2441 framepos_t pos = _session->locations()->first_mark_before (playhead_cursor->current_frame());
2443 //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...
2444 if ( _session->transport_rolling() ) {
2445 if ( (playhead_cursor->current_frame() - pos) < _session->frame_rate()/2 ) {
2446 framepos_t prior = _session->locations()->first_mark_before ( pos );
2455 _session->request_locate (pos, _session->transport_rolling());
2461 framepos_t const pos = _session->audible_frame ();
2464 _session->locations()->next_available_name (markername, "mark");
2466 if (!choose_new_marker_name (markername)) {
2470 _session->locations()->add (new Location (*_session, pos, 0, markername, Location::IsMark, 0), true);
2474 Editor::clear_markers ()
2477 begin_reversible_command (_("clear markers"));
2479 XMLNode &before = _session->locations()->get_state();
2480 _session->locations()->clear_markers ();
2481 XMLNode &after = _session->locations()->get_state();
2482 _session->add_command(new MementoCommand<Locations>(*(_session->locations()), &before, &after));
2484 commit_reversible_command ();
2489 Editor::clear_ranges ()
2492 begin_reversible_command (_("clear ranges"));
2494 XMLNode &before = _session->locations()->get_state();
2496 _session->locations()->clear_ranges ();
2498 XMLNode &after = _session->locations()->get_state();
2499 _session->add_command(new MementoCommand<Locations>(*(_session->locations()), &before, &after));
2501 commit_reversible_command ();
2506 Editor::clear_locations ()
2508 begin_reversible_command (_("clear locations"));
2510 XMLNode &before = _session->locations()->get_state();
2511 _session->locations()->clear ();
2512 XMLNode &after = _session->locations()->get_state();
2513 _session->add_command(new MementoCommand<Locations>(*(_session->locations()), &before, &after));
2515 commit_reversible_command ();
2519 Editor::unhide_markers ()
2521 for (LocationMarkerMap::iterator i = location_markers.begin(); i != location_markers.end(); ++i) {
2522 Location *l = (*i).first;
2523 if (l->is_hidden() && l->is_mark()) {
2524 l->set_hidden(false, this);
2530 Editor::unhide_ranges ()
2532 for (LocationMarkerMap::iterator i = location_markers.begin(); i != location_markers.end(); ++i) {
2533 Location *l = (*i).first;
2534 if (l->is_hidden() && l->is_range_marker()) {
2535 l->set_hidden(false, this);
2540 /* INSERT/REPLACE */
2543 Editor::insert_region_list_selection (float times)
2545 RouteTimeAxisView *tv = 0;
2546 boost::shared_ptr<Playlist> playlist;
2548 if (clicked_routeview != 0) {
2549 tv = clicked_routeview;
2550 } else if (!selection->tracks.empty()) {
2551 if ((tv = dynamic_cast<RouteTimeAxisView*>(selection->tracks.front())) == 0) {
2554 } else if (entered_track != 0) {
2555 if ((tv = dynamic_cast<RouteTimeAxisView*>(entered_track)) == 0) {
2562 if ((playlist = tv->playlist()) == 0) {
2566 boost::shared_ptr<Region> region = _regions->get_single_selection ();
2571 begin_reversible_command (_("insert region"));
2572 playlist->clear_changes ();
2573 playlist->add_region ((RegionFactory::create (region, true)), get_preferred_edit_position(), times);
2574 if (Config->get_edit_mode() == Ripple)
2575 playlist->ripple (get_preferred_edit_position(), region->length() * times, boost::shared_ptr<Region>());
2577 _session->add_command(new StatefulDiffCommand (playlist));
2578 commit_reversible_command ();
2581 /* BUILT-IN EFFECTS */
2584 Editor::reverse_selection ()
2589 /* GAIN ENVELOPE EDITING */
2592 Editor::edit_envelope ()
2599 Editor::transition_to_rolling (bool fwd)
2605 if (_session->config.get_external_sync()) {
2606 switch (Config->get_sync_source()) {
2610 /* transport controlled by the master */
2615 if (_session->is_auditioning()) {
2616 _session->cancel_audition ();
2620 _session->request_transport_speed (fwd ? 1.0f : -1.0f);
2624 Editor::play_from_start ()
2626 _session->request_locate (_session->current_start_frame(), true);
2630 Editor::play_from_edit_point ()
2632 _session->request_locate (get_preferred_edit_position(), true);
2636 Editor::play_from_edit_point_and_return ()
2638 framepos_t start_frame;
2639 framepos_t return_frame;
2641 start_frame = get_preferred_edit_position ( EDIT_IGNORE_PHEAD );
2643 if (_session->transport_rolling()) {
2644 _session->request_locate (start_frame, false);
2648 /* don't reset the return frame if its already set */
2650 if ((return_frame = _session->requested_return_frame()) < 0) {
2651 return_frame = _session->audible_frame();
2654 if (start_frame >= 0) {
2655 _session->request_roll_at_and_return (start_frame, return_frame);
2660 Editor::play_selection ()
2662 framepos_t start, end;
2663 if (!get_selection_extents ( start, end))
2666 AudioRange ar (start, end, 0);
2667 list<AudioRange> lar;
2670 _session->request_play_range (&lar, true);
2675 Editor::maybe_locate_with_edit_preroll (framepos_t location)
2677 if ( _session->transport_rolling() || !UIConfiguration::instance().get_follow_edits() || _session->config.get_external_sync() )
2680 location -= _session->preroll_samples (location);
2682 //don't try to locate before the beginning of time
2687 //if follow_playhead is on, keep the playhead on the screen
2688 if ( _follow_playhead )
2689 if ( location < leftmost_frame )
2690 location = leftmost_frame;
2692 _session->request_locate( location );
2696 Editor::play_with_preroll ()
2698 framepos_t start, end;
2699 if ( UIConfiguration::instance().get_follow_edits() && get_selection_extents ( start, end) ) {
2700 const framepos_t preroll = _session->preroll_samples (start);
2702 framepos_t ret = start;
2704 if (start > preroll) {
2705 start = start - preroll;
2708 end = end + preroll; //"post-roll"
2710 AudioRange ar (start, end, 0);
2711 list<AudioRange> lar;
2714 _session->request_play_range (&lar, true);
2715 _session->set_requested_return_frame (ret); //force auto-return to return to range start, without the preroll
2717 framepos_t ph = playhead_cursor->current_frame ();
2718 const framepos_t preroll = _session->preroll_samples (ph);
2721 start = ph - preroll;
2725 _session->request_locate (start, true);
2726 _session->set_requested_return_frame (ph); //force auto-return to return to playhead location, without the preroll
2731 Editor::rec_with_preroll ()
2733 framepos_t ph = playhead_cursor->current_frame ();
2734 framepos_t preroll = _session->preroll_samples (ph);
2735 _session->request_preroll_record_trim (ph, preroll);
2739 Editor::rec_with_count_in ()
2741 _session->request_count_in_record ();
2745 Editor::play_location (Location& location)
2747 if (location.start() <= location.end()) {
2751 _session->request_bounded_roll (location.start(), location.end());
2755 Editor::loop_location (Location& location)
2757 if (location.start() <= location.end()) {
2763 if ((tll = transport_loop_location()) != 0) {
2764 tll->set (location.start(), location.end());
2766 // enable looping, reposition and start rolling
2767 _session->request_locate (tll->start(), true);
2768 _session->request_play_loop (true);
2773 Editor::do_layer_operation (LayerOperation op)
2775 if (selection->regions.empty ()) {
2779 bool const multiple = selection->regions.size() > 1;
2783 begin_reversible_command (_("raise regions"));
2785 begin_reversible_command (_("raise region"));
2791 begin_reversible_command (_("raise regions to top"));
2793 begin_reversible_command (_("raise region to top"));
2799 begin_reversible_command (_("lower regions"));
2801 begin_reversible_command (_("lower region"));
2807 begin_reversible_command (_("lower regions to bottom"));
2809 begin_reversible_command (_("lower region"));
2814 set<boost::shared_ptr<Playlist> > playlists = selection->regions.playlists ();
2815 for (set<boost::shared_ptr<Playlist> >::iterator i = playlists.begin(); i != playlists.end(); ++i) {
2816 (*i)->clear_owned_changes ();
2819 for (RegionSelection::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
2820 boost::shared_ptr<Region> r = (*i)->region ();
2832 r->lower_to_bottom ();
2836 for (set<boost::shared_ptr<Playlist> >::iterator i = playlists.begin(); i != playlists.end(); ++i) {
2837 vector<Command*> cmds;
2839 _session->add_commands (cmds);
2842 commit_reversible_command ();
2846 Editor::raise_region ()
2848 do_layer_operation (Raise);
2852 Editor::raise_region_to_top ()
2854 do_layer_operation (RaiseToTop);
2858 Editor::lower_region ()
2860 do_layer_operation (Lower);
2864 Editor::lower_region_to_bottom ()
2866 do_layer_operation (LowerToBottom);
2869 /** Show the region editor for the selected regions */
2871 Editor::show_region_properties ()
2873 selection->foreach_regionview (&RegionView::show_region_editor);
2876 /** Show the midi list editor for the selected MIDI regions */
2878 Editor::show_midi_list_editor ()
2880 selection->foreach_midi_regionview (&MidiRegionView::show_list_editor);
2884 Editor::rename_region ()
2886 RegionSelection rs = get_regions_from_selection_and_entered ();
2892 ArdourDialog d (_("Rename Region"), true, false);
2894 Label label (_("New name:"));
2897 hbox.set_spacing (6);
2898 hbox.pack_start (label, false, false);
2899 hbox.pack_start (entry, true, true);
2901 d.get_vbox()->set_border_width (12);
2902 d.get_vbox()->pack_start (hbox, false, false);
2904 d.add_button(Gtk::Stock::CANCEL, Gtk::RESPONSE_CANCEL);
2905 d.add_button(Gtk::Stock::OK, Gtk::RESPONSE_OK);
2907 d.set_size_request (300, -1);
2909 entry.set_text (rs.front()->region()->name());
2910 entry.select_region (0, -1);
2912 entry.signal_activate().connect (sigc::bind (sigc::mem_fun (d, &Dialog::response), RESPONSE_OK));
2918 int const ret = d.run();
2922 if (ret != RESPONSE_OK) {
2926 std::string str = entry.get_text();
2927 strip_whitespace_edges (str);
2929 rs.front()->region()->set_name (str);
2930 _regions->redisplay ();
2934 /** Start an audition of the first selected region */
2936 Editor::play_edit_range ()
2938 framepos_t start, end;
2940 if (get_edit_op_range (start, end)) {
2941 _session->request_bounded_roll (start, end);
2946 Editor::play_selected_region ()
2948 framepos_t start = max_framepos;
2951 RegionSelection rs = get_regions_from_selection_and_entered ();
2957 for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
2958 if ((*i)->region()->position() < start) {
2959 start = (*i)->region()->position();
2961 if ((*i)->region()->last_frame() + 1 > end) {
2962 end = (*i)->region()->last_frame() + 1;
2966 _session->request_bounded_roll (start, end);
2970 Editor::audition_playlist_region_standalone (boost::shared_ptr<Region> region)
2972 _session->audition_region (region);
2976 Editor::region_from_selection ()
2978 if (clicked_axisview == 0) {
2982 if (selection->time.empty()) {
2986 framepos_t start = selection->time[clicked_selection].start;
2987 framepos_t end = selection->time[clicked_selection].end;
2989 TrackViewList tracks = get_tracks_for_range_action ();
2991 framepos_t selection_cnt = end - start + 1;
2993 for (TrackSelection::iterator i = tracks.begin(); i != tracks.end(); ++i) {
2994 boost::shared_ptr<Region> current;
2995 boost::shared_ptr<Playlist> pl;
2996 framepos_t internal_start;
2999 if ((pl = (*i)->playlist()) == 0) {
3003 if ((current = pl->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, selection_cnt);
3014 plist.add (ARDOUR::Properties::name, new_name);
3015 plist.add (ARDOUR::Properties::layer, 0);
3017 boost::shared_ptr<Region> region (RegionFactory::create (current, plist));
3022 Editor::create_region_from_selection (vector<boost::shared_ptr<Region> >& new_regions)
3024 if (selection->time.empty() || selection->tracks.empty()) {
3028 framepos_t start, end;
3029 if (clicked_selection) {
3030 start = selection->time[clicked_selection].start;
3031 end = selection->time[clicked_selection].end;
3033 start = selection->time.start();
3034 end = selection->time.end_frame();
3037 TrackViewList ts = selection->tracks.filter_to_unique_playlists ();
3038 sort_track_selection (ts);
3040 for (TrackSelection::iterator i = ts.begin(); i != ts.end(); ++i) {
3041 boost::shared_ptr<Region> current;
3042 boost::shared_ptr<Playlist> playlist;
3043 framepos_t internal_start;
3046 if ((playlist = (*i)->playlist()) == 0) {
3050 if ((current = playlist->top_region_at(start)) == 0) {
3054 internal_start = start - current->position();
3055 RegionFactory::region_name (new_name, current->name(), true);
3059 plist.add (ARDOUR::Properties::start, current->start() + internal_start);
3060 plist.add (ARDOUR::Properties::length, end - start + 1);
3061 plist.add (ARDOUR::Properties::name, new_name);
3063 new_regions.push_back (RegionFactory::create (current, plist));
3068 Editor::split_multichannel_region ()
3070 RegionSelection rs = get_regions_from_selection_and_entered ();
3076 vector< boost::shared_ptr<Region> > v;
3078 for (list<RegionView*>::iterator x = rs.begin(); x != rs.end(); ++x) {
3079 (*x)->region()->separate_by_channel (*_session, v);
3084 Editor::new_region_from_selection ()
3086 region_from_selection ();
3087 cancel_selection ();
3091 add_if_covered (RegionView* rv, const AudioRange* ar, RegionSelection* rs)
3093 switch (rv->region()->coverage (ar->start, ar->end - 1)) {
3094 // n.b. -1 because AudioRange::end is one past the end, but coverage expects inclusive ranges
3095 case Evoral::OverlapNone:
3103 * - selected tracks, or if there are none...
3104 * - tracks containing selected regions, or if there are none...
3109 Editor::get_tracks_for_range_action () const
3113 if (selection->tracks.empty()) {
3115 /* use tracks with selected regions */
3117 RegionSelection rs = selection->regions;
3119 for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
3120 TimeAxisView* tv = &(*i)->get_time_axis_view();
3122 if (!t.contains (tv)) {
3128 /* no regions and no tracks: use all tracks */
3134 t = selection->tracks;
3137 return t.filter_to_unique_playlists();
3141 Editor::separate_regions_between (const TimeSelection& ts)
3143 bool in_command = false;
3144 boost::shared_ptr<Playlist> playlist;
3145 RegionSelection new_selection;
3147 TrackViewList tmptracks = get_tracks_for_range_action ();
3148 sort_track_selection (tmptracks);
3150 for (TrackSelection::iterator i = tmptracks.begin(); i != tmptracks.end(); ++i) {
3152 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> ((*i));
3158 if (!rtv->is_track()) {
3162 /* no edits to destructive tracks */
3164 if (rtv->track()->destructive()) {
3168 if ((playlist = rtv->playlist()) != 0) {
3170 playlist->clear_changes ();
3172 /* XXX need to consider musical time selections here at some point */
3174 double speed = rtv->track()->speed();
3176 for (list<AudioRange>::const_iterator t = ts.begin(); t != ts.end(); ++t) {
3178 sigc::connection c = rtv->view()->RegionViewAdded.connect (
3179 sigc::mem_fun(*this, &Editor::collect_new_region_view));
3181 latest_regionviews.clear ();
3183 playlist->partition ((framepos_t)((*t).start * speed),
3184 (framepos_t)((*t).end * speed), false);
3188 if (!latest_regionviews.empty()) {
3190 rtv->view()->foreach_regionview (sigc::bind (
3191 sigc::ptr_fun (add_if_covered),
3192 &(*t), &new_selection));
3195 begin_reversible_command (_("separate"));
3199 /* pick up changes to existing regions */
3201 vector<Command*> cmds;
3202 playlist->rdiff (cmds);
3203 _session->add_commands (cmds);
3205 /* pick up changes to the playlist itself (adds/removes)
3208 _session->add_command(new StatefulDiffCommand (playlist));
3215 // selection->set (new_selection);
3217 commit_reversible_command ();
3221 struct PlaylistState {
3222 boost::shared_ptr<Playlist> playlist;
3226 /** Take tracks from get_tracks_for_range_action and cut any regions
3227 * on those tracks so that the tracks are empty over the time
3231 Editor::separate_region_from_selection ()
3233 /* preferentially use *all* ranges in the time selection if we're in range mode
3234 to allow discontiguous operation, since get_edit_op_range() currently
3235 returns a single range.
3238 if (!selection->time.empty()) {
3240 separate_regions_between (selection->time);
3247 if (get_edit_op_range (start, end)) {
3249 AudioRange ar (start, end, 1);
3253 separate_regions_between (ts);
3259 Editor::separate_region_from_punch ()
3261 Location* loc = _session->locations()->auto_punch_location();
3263 separate_regions_using_location (*loc);
3268 Editor::separate_region_from_loop ()
3270 Location* loc = _session->locations()->auto_loop_location();
3272 separate_regions_using_location (*loc);
3277 Editor::separate_regions_using_location (Location& loc)
3279 if (loc.is_mark()) {
3283 AudioRange ar (loc.start(), loc.end(), 1);
3288 separate_regions_between (ts);
3291 /** Separate regions under the selected region */
3293 Editor::separate_under_selected_regions ()
3295 vector<PlaylistState> playlists;
3299 rs = get_regions_from_selection_and_entered();
3301 if (!_session || rs.empty()) {
3305 begin_reversible_command (_("separate region under"));
3307 list<boost::shared_ptr<Region> > regions_to_remove;
3309 for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
3310 // we can't just remove the region(s) in this loop because
3311 // this removes them from the RegionSelection, and they thus
3312 // disappear from underneath the iterator, and the ++i above
3313 // SEGVs in a puzzling fashion.
3315 // so, first iterate over the regions to be removed from rs and
3316 // add them to the regions_to_remove list, and then
3317 // iterate over the list to actually remove them.
3319 regions_to_remove.push_back ((*i)->region());
3322 for (list<boost::shared_ptr<Region> >::iterator rl = regions_to_remove.begin(); rl != regions_to_remove.end(); ++rl) {
3324 boost::shared_ptr<Playlist> playlist = (*rl)->playlist();
3327 // is this check necessary?
3331 vector<PlaylistState>::iterator i;
3333 //only take state if this is a new playlist.
3334 for (i = playlists.begin(); i != playlists.end(); ++i) {
3335 if ((*i).playlist == playlist) {
3340 if (i == playlists.end()) {
3342 PlaylistState before;
3343 before.playlist = playlist;
3344 before.before = &playlist->get_state();
3345 playlist->clear_changes ();
3346 playlist->freeze ();
3347 playlists.push_back(before);
3350 //Partition on the region bounds
3351 playlist->partition ((*rl)->first_frame() - 1, (*rl)->last_frame() + 1, true);
3353 //Re-add region that was just removed due to the partition operation
3354 playlist->add_region( (*rl), (*rl)->first_frame() );
3357 vector<PlaylistState>::iterator pl;
3359 for (pl = playlists.begin(); pl != playlists.end(); ++pl) {
3360 (*pl).playlist->thaw ();
3361 _session->add_command(new MementoCommand<Playlist>(*(*pl).playlist, (*pl).before, &(*pl).playlist->get_state()));
3364 commit_reversible_command ();
3368 Editor::crop_region_to_selection ()
3370 if (!selection->time.empty()) {
3372 begin_reversible_command (_("Crop Regions to Time Selection"));
3373 for (std::list<AudioRange>::iterator i = selection->time.begin(); i != selection->time.end(); ++i) {
3374 crop_region_to ((*i).start, (*i).end);
3376 commit_reversible_command();
3382 if (get_edit_op_range (start, end)) {
3383 begin_reversible_command (_("Crop Regions to Edit Range"));
3385 crop_region_to (start, end);
3387 commit_reversible_command();
3394 Editor::crop_region_to (framepos_t start, framepos_t end)
3396 vector<boost::shared_ptr<Playlist> > playlists;
3397 boost::shared_ptr<Playlist> playlist;
3400 if (selection->tracks.empty()) {
3401 ts = track_views.filter_to_unique_playlists();
3403 ts = selection->tracks.filter_to_unique_playlists ();
3406 sort_track_selection (ts);
3408 for (TrackSelection::iterator i = ts.begin(); i != ts.end(); ++i) {
3410 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> ((*i));
3416 boost::shared_ptr<Track> t = rtv->track();
3418 if (t != 0 && ! t->destructive()) {
3420 if ((playlist = rtv->playlist()) != 0) {
3421 playlists.push_back (playlist);
3426 if (playlists.empty()) {
3431 framepos_t new_start;
3433 framecnt_t new_length;
3435 for (vector<boost::shared_ptr<Playlist> >::iterator i = playlists.begin(); i != playlists.end(); ++i) {
3437 /* Only the top regions at start and end have to be cropped */
3438 boost::shared_ptr<Region> region_at_start = (*i)->top_region_at(start);
3439 boost::shared_ptr<Region> region_at_end = (*i)->top_region_at(end);
3441 vector<boost::shared_ptr<Region> > regions;
3443 if (region_at_start != 0) {
3444 regions.push_back (region_at_start);
3446 if (region_at_end != 0) {
3447 regions.push_back (region_at_end);
3450 /* now adjust lengths */
3451 for (vector<boost::shared_ptr<Region> >::iterator i = regions.begin(); i != regions.end(); ++i) {
3453 pos = (*i)->position();
3454 new_start = max (start, pos);
3455 if (max_framepos - pos > (*i)->length()) {
3456 new_end = pos + (*i)->length() - 1;
3458 new_end = max_framepos;
3460 new_end = min (end, new_end);
3461 new_length = new_end - new_start + 1;
3463 (*i)->clear_changes ();
3464 (*i)->trim_to (new_start, new_length);
3465 _session->add_command (new StatefulDiffCommand (*i));
3471 Editor::region_fill_track ()
3473 boost::shared_ptr<Playlist> playlist;
3474 RegionSelection regions = get_regions_from_selection_and_entered ();
3475 RegionSelection foo;
3477 framepos_t const end = _session->current_end_frame ();
3479 if (regions.empty () || regions.end_frame () + 1 >= end) {
3483 framepos_t const start_frame = regions.start ();
3484 framepos_t const end_frame = regions.end_frame ();
3485 framecnt_t const gap = end_frame - start_frame + 1;
3487 begin_reversible_command (Operations::region_fill);
3489 selection->clear_regions ();
3491 for (RegionSelection::iterator i = regions.begin(); i != regions.end(); ++i) {
3493 boost::shared_ptr<Region> r ((*i)->region());
3495 TimeAxisView& tv = (*i)->get_time_axis_view();
3496 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (&tv);
3497 latest_regionviews.clear ();
3498 sigc::connection c = rtv->view()->RegionViewAdded.connect (sigc::mem_fun(*this, &Editor::collect_new_region_view));
3500 framepos_t const position = end_frame + (r->first_frame() - start_frame + 1);
3501 playlist = (*i)->region()->playlist();
3502 playlist->clear_changes ();
3503 playlist->duplicate_until (r, position, gap, end);
3504 _session->add_command(new StatefulDiffCommand (playlist));
3508 foo.insert (foo.end(), latest_regionviews.begin(), latest_regionviews.end());
3512 selection->set (foo);
3515 commit_reversible_command ();
3519 Editor::set_region_sync_position ()
3521 set_sync_point (get_preferred_edit_position (), get_regions_from_selection_and_edit_point ());
3525 Editor::set_sync_point (framepos_t where, const RegionSelection& rs)
3527 bool in_command = false;
3529 for (RegionSelection::const_iterator r = rs.begin(); r != rs.end(); ++r) {
3531 if (!(*r)->region()->covers (where)) {
3535 boost::shared_ptr<Region> region ((*r)->region());
3538 begin_reversible_command (_("set sync point"));
3542 region->clear_changes ();
3543 region->set_sync_position (where);
3544 _session->add_command(new StatefulDiffCommand (region));
3548 commit_reversible_command ();
3552 /** Remove the sync positions of the selection */
3554 Editor::remove_region_sync ()
3556 RegionSelection rs = get_regions_from_selection_and_entered ();
3562 begin_reversible_command (_("remove region sync"));
3564 for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
3566 (*i)->region()->clear_changes ();
3567 (*i)->region()->clear_sync_position ();
3568 _session->add_command(new StatefulDiffCommand ((*i)->region()));
3571 commit_reversible_command ();
3575 Editor::naturalize_region ()
3577 RegionSelection rs = get_regions_from_selection_and_entered ();
3583 if (rs.size() > 1) {
3584 begin_reversible_command (_("move regions to original position"));
3586 begin_reversible_command (_("move region to original position"));
3589 for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
3590 (*i)->region()->clear_changes ();
3591 (*i)->region()->move_to_natural_position ();
3592 _session->add_command (new StatefulDiffCommand ((*i)->region()));
3595 commit_reversible_command ();
3599 Editor::align_regions (RegionPoint what)
3601 RegionSelection const rs = get_regions_from_selection_and_edit_point ();
3607 begin_reversible_command (_("align selection"));
3609 framepos_t const position = get_preferred_edit_position ();
3611 for (RegionSelection::const_iterator i = rs.begin(); i != rs.end(); ++i) {
3612 align_region_internal ((*i)->region(), what, position);
3615 commit_reversible_command ();
3618 struct RegionSortByTime {
3619 bool operator() (const RegionView* a, const RegionView* b) {
3620 return a->region()->position() < b->region()->position();
3625 Editor::align_regions_relative (RegionPoint point)
3627 RegionSelection const rs = get_regions_from_selection_and_edit_point ();
3633 framepos_t const position = get_preferred_edit_position ();
3635 framepos_t distance = 0;
3639 list<RegionView*> sorted;
3640 rs.by_position (sorted);
3642 boost::shared_ptr<Region> r ((*sorted.begin())->region());
3647 if (position > r->position()) {
3648 distance = position - r->position();
3650 distance = r->position() - position;
3656 if (position > r->last_frame()) {
3657 distance = position - r->last_frame();
3658 pos = r->position() + distance;
3660 distance = r->last_frame() - position;
3661 pos = r->position() - distance;
3667 pos = r->adjust_to_sync (position);
3668 if (pos > r->position()) {
3669 distance = pos - r->position();
3671 distance = r->position() - pos;
3677 if (pos == r->position()) {
3681 begin_reversible_command (_("align selection (relative)"));
3683 /* move first one specially */
3685 r->clear_changes ();
3686 r->set_position (pos);
3687 _session->add_command(new StatefulDiffCommand (r));
3689 /* move rest by the same amount */
3693 for (list<RegionView*>::iterator i = sorted.begin(); i != sorted.end(); ++i) {
3695 boost::shared_ptr<Region> region ((*i)->region());
3697 region->clear_changes ();
3700 region->set_position (region->position() + distance);
3702 region->set_position (region->position() - distance);
3705 _session->add_command(new StatefulDiffCommand (region));
3709 commit_reversible_command ();
3713 Editor::align_region (boost::shared_ptr<Region> region, RegionPoint point, framepos_t position)
3715 begin_reversible_command (_("align region"));
3716 align_region_internal (region, point, position);
3717 commit_reversible_command ();
3721 Editor::align_region_internal (boost::shared_ptr<Region> region, RegionPoint point, framepos_t position)
3723 region->clear_changes ();
3727 region->set_position (region->adjust_to_sync (position));
3731 if (position > region->length()) {
3732 region->set_position (position - region->length());
3737 region->set_position (position);
3741 _session->add_command(new StatefulDiffCommand (region));
3745 Editor::trim_region_front ()
3751 Editor::trim_region_back ()
3753 trim_region (false);
3757 Editor::trim_region (bool front)
3759 framepos_t where = get_preferred_edit_position();
3760 RegionSelection rs = get_regions_from_selection_and_edit_point ();
3766 begin_reversible_command (front ? _("trim front") : _("trim back"));
3768 for (list<RegionView*>::const_iterator i = rs.by_layer().begin(); i != rs.by_layer().end(); ++i) {
3769 if (!(*i)->region()->locked()) {
3771 (*i)->region()->clear_changes ();
3774 (*i)->region()->trim_front (where);
3776 (*i)->region()->trim_end (where);
3779 _session->add_command (new StatefulDiffCommand ((*i)->region()));
3783 commit_reversible_command ();
3786 /** Trim the end of the selected regions to the position of the edit cursor */
3788 Editor::trim_region_to_loop ()
3790 Location* loc = _session->locations()->auto_loop_location();
3794 trim_region_to_location (*loc, _("trim to loop"));
3798 Editor::trim_region_to_punch ()
3800 Location* loc = _session->locations()->auto_punch_location();
3804 trim_region_to_location (*loc, _("trim to punch"));
3808 Editor::trim_region_to_location (const Location& loc, const char* str)
3810 RegionSelection rs = get_regions_from_selection_and_entered ();
3811 bool in_command = false;
3813 for (RegionSelection::iterator x = rs.begin(); x != rs.end(); ++x) {
3814 RegionView* rv = (*x);
3816 /* require region to span proposed trim */
3817 switch (rv->region()->coverage (loc.start(), loc.end())) {
3818 case Evoral::OverlapInternal:
3824 RouteTimeAxisView* tav = dynamic_cast<RouteTimeAxisView*> (&rv->get_time_axis_view());
3833 if (tav->track() != 0) {
3834 speed = tav->track()->speed();
3837 start = session_frame_to_track_frame (loc.start(), speed);
3838 end = session_frame_to_track_frame (loc.end(), speed);
3840 rv->region()->clear_changes ();
3841 rv->region()->trim_to (start, (end - start));
3844 begin_reversible_command (str);
3847 _session->add_command(new StatefulDiffCommand (rv->region()));
3851 commit_reversible_command ();
3856 Editor::trim_region_to_previous_region_end ()
3858 return trim_to_region(false);
3862 Editor::trim_region_to_next_region_start ()
3864 return trim_to_region(true);
3868 Editor::trim_to_region(bool forward)
3870 RegionSelection rs = get_regions_from_selection_and_entered ();
3871 bool in_command = false;
3873 boost::shared_ptr<Region> next_region;
3875 for (RegionSelection::iterator x = rs.begin(); x != rs.end(); ++x) {
3877 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (*x);
3883 AudioTimeAxisView* atav = dynamic_cast<AudioTimeAxisView*> (&arv->get_time_axis_view());
3891 if (atav->track() != 0) {
3892 speed = atav->track()->speed();
3896 boost::shared_ptr<Region> region = arv->region();
3897 boost::shared_ptr<Playlist> playlist (region->playlist());
3899 region->clear_changes ();
3903 next_region = playlist->find_next_region (region->first_frame(), Start, 1);
3909 region->trim_end((framepos_t) ( (next_region->first_frame() - 1) * speed));
3910 arv->region_changed (PropertyChange (ARDOUR::Properties::length));
3914 next_region = playlist->find_next_region (region->first_frame(), Start, 0);
3920 region->trim_front((framepos_t) ((next_region->last_frame() + 1) * speed));
3922 arv->region_changed (ARDOUR::bounds_change);
3926 begin_reversible_command (_("trim to region"));
3929 _session->add_command(new StatefulDiffCommand (region));
3933 commit_reversible_command ();
3938 Editor::unfreeze_route ()
3940 if (clicked_routeview == 0 || !clicked_routeview->is_track()) {
3944 clicked_routeview->track()->unfreeze ();
3948 Editor::_freeze_thread (void* arg)
3950 return static_cast<Editor*>(arg)->freeze_thread ();
3954 Editor::freeze_thread ()
3956 /* create event pool because we may need to talk to the session */
3957 SessionEvent::create_per_thread_pool ("freeze events", 64);
3958 /* create per-thread buffers for process() tree to use */
3959 clicked_routeview->audio_track()->freeze_me (*current_interthread_info);
3960 current_interthread_info->done = true;
3965 Editor::freeze_route ()
3971 /* stop transport before we start. this is important */
3973 _session->request_transport_speed (0.0);
3975 /* wait for just a little while, because the above call is asynchronous */
3977 Glib::usleep (250000);
3979 if (clicked_routeview == 0 || !clicked_routeview->is_audio_track()) {
3983 if (!clicked_routeview->track()->bounceable (clicked_routeview->track()->main_outs(), true)) {
3985 _("This track/bus cannot be frozen because the signal adds or loses channels before reaching the outputs.\n"
3986 "This is typically caused by plugins that generate stereo output from mono input or vice versa.")
3988 d.set_title (_("Cannot freeze"));
3993 if (clicked_routeview->track()->has_external_redirects()) {
3994 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"
3995 "Freezing will only process the signal as far as the first send/insert/return."),
3996 clicked_routeview->track()->name()), true, MESSAGE_INFO, BUTTONS_NONE, true);
3998 d.add_button (_("Freeze anyway"), Gtk::RESPONSE_OK);
3999 d.add_button (_("Don't freeze"), Gtk::RESPONSE_CANCEL);
4000 d.set_title (_("Freeze Limits"));
4002 int response = d.run ();
4005 case Gtk::RESPONSE_CANCEL:
4012 InterThreadInfo itt;
4013 current_interthread_info = &itt;
4015 InterthreadProgressWindow ipw (current_interthread_info, _("Freeze"), _("Cancel Freeze"));
4017 pthread_create_and_store (X_("freezer"), &itt.thread, _freeze_thread, this);
4019 CursorContext::Handle cursor_ctx = CursorContext::create(*this, _cursors->wait);
4021 while (!itt.done && !itt.cancel) {
4022 gtk_main_iteration ();
4025 pthread_join (itt.thread, 0);
4026 current_interthread_info = 0;
4030 Editor::bounce_range_selection (bool replace, bool enable_processing)
4032 if (selection->time.empty()) {
4036 TrackSelection views = selection->tracks;
4038 for (TrackViewList::iterator i = views.begin(); i != views.end(); ++i) {
4040 if (enable_processing) {
4042 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (*i);
4044 if (rtv && rtv->track() && replace && enable_processing && !rtv->track()->bounceable (rtv->track()->main_outs(), false)) {
4046 _("You can't perform this operation because the processing of the signal "
4047 "will cause one or more of the tracks to end up with a region with more channels than this track has inputs.\n\n"
4048 "You can do this without processing, which is a different operation.")
4050 d.set_title (_("Cannot bounce"));
4057 framepos_t start = selection->time[clicked_selection].start;
4058 framepos_t end = selection->time[clicked_selection].end;
4059 framepos_t cnt = end - start + 1;
4060 bool in_command = false;
4062 for (TrackViewList::iterator i = views.begin(); i != views.end(); ++i) {
4064 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (*i);
4070 boost::shared_ptr<Playlist> playlist;
4072 if ((playlist = rtv->playlist()) == 0) {
4076 InterThreadInfo itt;
4078 playlist->clear_changes ();
4079 playlist->clear_owned_changes ();
4081 boost::shared_ptr<Region> r;
4083 if (enable_processing) {
4084 r = rtv->track()->bounce_range (start, start+cnt, itt, rtv->track()->main_outs(), false);
4086 r = rtv->track()->bounce_range (start, start+cnt, itt, boost::shared_ptr<Processor>(), false);
4094 list<AudioRange> ranges;
4095 ranges.push_back (AudioRange (start, start+cnt, 0));
4096 playlist->cut (ranges); // discard result
4097 playlist->add_region (r, start);
4101 begin_reversible_command (_("bounce range"));
4104 vector<Command*> cmds;
4105 playlist->rdiff (cmds);
4106 _session->add_commands (cmds);
4108 _session->add_command (new StatefulDiffCommand (playlist));
4112 commit_reversible_command ();
4116 /** Delete selected regions, automation points or a time range */
4120 //special case: if the user is pointing in the editor/mixer strip, they may be trying to delete a plugin.
4121 //we need this because the editor-mixer strip is in the editor window, so it doesn't get the bindings from the mix window
4122 bool deleted = false;
4123 if ( current_mixer_strip && current_mixer_strip == MixerStrip::entered_mixer_strip() )
4124 deleted = current_mixer_strip->delete_processors ();
4130 /** Cut selected regions, automation points or a time range */
4137 /** Copy selected regions, automation points or a time range */
4145 /** @return true if a Cut, Copy or Clear is possible */
4147 Editor::can_cut_copy () const
4149 if (!selection->time.empty() || !selection->regions.empty() || !selection->points.empty())
4156 /** Cut, copy or clear selected regions, automation points or a time range.
4157 * @param op Operation (Delete, Cut, Copy or Clear)
4160 Editor::cut_copy (CutCopyOp op)
4162 /* only cancel selection if cut/copy is successful.*/
4168 opname = _("delete");
4177 opname = _("clear");
4181 /* if we're deleting something, and the mouse is still pressed,
4182 the thing we started a drag for will be gone when we release
4183 the mouse button(s). avoid this. see part 2 at the end of
4187 if (op == Delete || op == Cut || op == Clear) {
4188 if (_drags->active ()) {
4193 if ( op != Delete ) { //"Delete" doesn't change copy/paste buf
4194 cut_buffer->clear ();
4197 if (entered_marker) {
4199 /* cut/delete op while pointing at a marker */
4202 Location* loc = find_location_from_marker (entered_marker, ignored);
4204 if (_session && loc) {
4205 entered_marker = NULL;
4206 Glib::signal_idle().connect (sigc::bind (sigc::mem_fun(*this, &Editor::really_remove_marker), loc));
4213 switch (mouse_mode) {
4216 begin_reversible_command (opname + ' ' + X_("MIDI"));
4218 commit_reversible_command ();
4224 bool did_edit = false;
4226 if (!selection->regions.empty() || !selection->points.empty()) {
4227 begin_reversible_command (opname + ' ' + _("objects"));
4230 if (!selection->regions.empty()) {
4231 cut_copy_regions (op, selection->regions);
4233 if (op == Cut || op == Delete) {
4234 selection->clear_regions ();
4238 if (!selection->points.empty()) {
4239 cut_copy_points (op);
4241 if (op == Cut || op == Delete) {
4242 selection->clear_points ();
4245 } else if (selection->time.empty()) {
4246 framepos_t start, end;
4247 /* no time selection, see if we can get an edit range
4250 if (get_edit_op_range (start, end)) {
4251 selection->set (start, end);
4253 } else if (!selection->time.empty()) {
4254 begin_reversible_command (opname + ' ' + _("range"));
4257 cut_copy_ranges (op);
4259 if (op == Cut || op == Delete) {
4260 selection->clear_time ();
4265 /* reset repeated paste state */
4268 commit_reversible_command ();
4271 if (op == Delete || op == Cut || op == Clear) {
4277 struct AutomationRecord {
4278 AutomationRecord () : state (0) , line(NULL) {}
4279 AutomationRecord (XMLNode* s, const AutomationLine* l) : state (s) , line (l) {}
4281 XMLNode* state; ///< state before any operation
4282 const AutomationLine* line; ///< line this came from
4283 boost::shared_ptr<Evoral::ControlList> copy; ///< copied events for the cut buffer
4286 struct PointsSelectionPositionSorter {
4287 bool operator() (ControlPoint* a, ControlPoint* b) {
4288 return (*(a->model()))->when < (*(b->model()))->when;
4292 /** Cut, copy or clear selected automation points.
4293 * @param op Operation (Cut, Copy or Clear)
4296 Editor::cut_copy_points (Editing::CutCopyOp op, Evoral::Beats earliest, bool midi)
4298 if (selection->points.empty ()) {
4302 /* XXX: not ideal, as there may be more than one track involved in the point selection */
4303 _last_cut_copy_source_track = &selection->points.front()->line().trackview;
4305 /* Keep a record of the AutomationLists that we end up using in this operation */
4306 typedef std::map<boost::shared_ptr<AutomationList>, AutomationRecord> Lists;
4309 /* user could select points in any order */
4310 selection->points.sort(PointsSelectionPositionSorter ());
4312 /* Go through all selected points, making an AutomationRecord for each distinct AutomationList */
4313 for (PointSelection::iterator sel_point = selection->points.begin(); sel_point != selection->points.end(); ++sel_point) {
4314 const AutomationLine& line = (*sel_point)->line();
4315 const boost::shared_ptr<AutomationList> al = line.the_list();
4316 if (lists.find (al) == lists.end ()) {
4317 /* We haven't seen this list yet, so make a record for it. This includes
4318 taking a copy of its current state, in case this is needed for undo later.
4320 lists[al] = AutomationRecord (&al->get_state (), &line);
4324 if (op == Cut || op == Copy) {
4325 /* This operation will involve putting things in the cut buffer, so create an empty
4326 ControlList for each of our source lists to put the cut buffer data in.
4328 for (Lists::iterator i = lists.begin(); i != lists.end(); ++i) {
4329 i->second.copy = i->first->create (i->first->parameter (), i->first->descriptor());
4332 /* Add all selected points to the relevant copy ControlLists */
4333 MusicFrame start (std::numeric_limits<framepos_t>::max(), 0);
4334 for (PointSelection::iterator sel_point = selection->points.begin(); sel_point != selection->points.end(); ++sel_point) {
4335 boost::shared_ptr<AutomationList> al = (*sel_point)->line().the_list();
4336 AutomationList::const_iterator ctrl_evt = (*sel_point)->model ();
4338 lists[al].copy->fast_simple_add ((*ctrl_evt)->when, (*ctrl_evt)->value);
4340 /* Update earliest MIDI start time in beats */
4341 earliest = std::min(earliest, Evoral::Beats((*ctrl_evt)->when));
4343 /* Update earliest session start time in frames */
4344 start.frame = std::min(start.frame, (*sel_point)->line().session_position(ctrl_evt));
4348 /* Snap start time backwards, so copy/paste is snap aligned. */
4350 if (earliest == Evoral::Beats::max()) {
4351 earliest = Evoral::Beats(); // Weird... don't offset
4353 earliest.round_down_to_beat();
4355 if (start.frame == std::numeric_limits<double>::max()) {
4356 start.frame = 0; // Weird... don't offset
4358 snap_to(start, RoundDownMaybe);
4361 const double line_offset = midi ? earliest.to_double() : start.frame;
4362 for (Lists::iterator i = lists.begin(); i != lists.end(); ++i) {
4363 /* Correct this copy list so that it is relative to the earliest
4364 start time, so relative ordering between points is preserved
4365 when copying from several lists and the paste starts at the
4366 earliest copied piece of data. */
4367 boost::shared_ptr<Evoral::ControlList> &al_cpy = i->second.copy;
4368 for (AutomationList::iterator ctrl_evt = al_cpy->begin(); ctrl_evt != al_cpy->end(); ++ctrl_evt) {
4369 (*ctrl_evt)->when -= line_offset;
4372 /* And add it to the cut buffer */
4373 cut_buffer->add (al_cpy);
4377 if (op == Delete || op == Cut) {
4378 /* This operation needs to remove things from the main AutomationList, so do that now */
4380 for (Lists::iterator i = lists.begin(); i != lists.end(); ++i) {
4381 i->first->freeze ();
4384 /* Remove each selected point from its AutomationList */
4385 for (PointSelection::iterator sel_point = selection->points.begin(); sel_point != selection->points.end(); ++sel_point) {
4386 AutomationLine& line = (*sel_point)->line ();
4387 boost::shared_ptr<AutomationList> al = line.the_list();
4391 if (dynamic_cast<AudioRegionGainLine*> (&line)) {
4392 /* removing of first and last gain point in region gain lines is prohibited*/
4393 if (line.is_last_point (*(*sel_point)) || line.is_first_point (*(*sel_point))) {
4399 al->erase ((*sel_point)->model ());
4403 /* Thaw the lists and add undo records for them */
4404 for (Lists::iterator i = lists.begin(); i != lists.end(); ++i) {
4405 boost::shared_ptr<AutomationList> al = i->first;
4407 _session->add_command (new MementoCommand<AutomationList> (*al.get(), i->second.state, &(al->get_state ())));
4412 /** Cut, copy or clear selected automation points.
4413 * @param op Operation (Cut, Copy or Clear)
4416 Editor::cut_copy_midi (CutCopyOp op)
4418 Evoral::Beats earliest = Evoral::Beats::max();
4419 for (MidiRegionSelection::iterator i = selection->midi_regions.begin(); i != selection->midi_regions.end(); ++i) {
4420 MidiRegionView* mrv = dynamic_cast<MidiRegionView*>(*i);
4422 if (!mrv->selection().empty()) {
4423 earliest = std::min(earliest, (*mrv->selection().begin())->note()->time());
4425 mrv->cut_copy_clear (op);
4427 /* XXX: not ideal, as there may be more than one track involved in the selection */
4428 _last_cut_copy_source_track = &mrv->get_time_axis_view();
4432 if (!selection->points.empty()) {
4433 cut_copy_points (op, earliest, true);
4434 if (op == Cut || op == Delete) {
4435 selection->clear_points ();
4440 struct lt_playlist {
4441 bool operator () (const PlaylistState& a, const PlaylistState& b) {
4442 return a.playlist < b.playlist;
4446 struct PlaylistMapping {
4448 boost::shared_ptr<Playlist> pl;
4450 PlaylistMapping (TimeAxisView* tvp) : tv (tvp) {}
4453 /** Remove `clicked_regionview' */
4455 Editor::remove_clicked_region ()
4457 if (clicked_routeview == 0 || clicked_regionview == 0) {
4461 begin_reversible_command (_("remove region"));
4463 boost::shared_ptr<Playlist> playlist = clicked_routeview->playlist();
4465 playlist->clear_changes ();
4466 playlist->clear_owned_changes ();
4467 playlist->remove_region (clicked_regionview->region());
4468 if (Config->get_edit_mode() == Ripple)
4469 playlist->ripple (clicked_regionview->region()->position(), -clicked_regionview->region()->length(), boost::shared_ptr<Region>());
4471 /* We might have removed regions, which alters other regions' layering_index,
4472 so we need to do a recursive diff here.
4474 vector<Command*> cmds;
4475 playlist->rdiff (cmds);
4476 _session->add_commands (cmds);
4478 _session->add_command(new StatefulDiffCommand (playlist));
4479 commit_reversible_command ();
4483 /** Remove the selected regions */
4485 Editor::remove_selected_regions ()
4487 RegionSelection rs = get_regions_from_selection_and_entered ();
4489 if (!_session || rs.empty()) {
4493 list<boost::shared_ptr<Region> > regions_to_remove;
4495 for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
4496 // we can't just remove the region(s) in this loop because
4497 // this removes them from the RegionSelection, and they thus
4498 // disappear from underneath the iterator, and the ++i above
4499 // SEGVs in a puzzling fashion.
4501 // so, first iterate over the regions to be removed from rs and
4502 // add them to the regions_to_remove list, and then
4503 // iterate over the list to actually remove them.
4505 regions_to_remove.push_back ((*i)->region());
4508 vector<boost::shared_ptr<Playlist> > playlists;
4510 for (list<boost::shared_ptr<Region> >::iterator rl = regions_to_remove.begin(); rl != regions_to_remove.end(); ++rl) {
4512 boost::shared_ptr<Playlist> playlist = (*rl)->playlist();
4515 // is this check necessary?
4519 /* get_regions_from_selection_and_entered() guarantees that
4520 the playlists involved are unique, so there is no need
4524 playlists.push_back (playlist);
4526 playlist->clear_changes ();
4527 playlist->clear_owned_changes ();
4528 playlist->freeze ();
4529 playlist->remove_region (*rl);
4530 if (Config->get_edit_mode() == Ripple)
4531 playlist->ripple ((*rl)->position(), -(*rl)->length(), boost::shared_ptr<Region>());
4535 vector<boost::shared_ptr<Playlist> >::iterator pl;
4536 bool in_command = false;
4538 for (pl = playlists.begin(); pl != playlists.end(); ++pl) {
4541 /* We might have removed regions, which alters other regions' layering_index,
4542 so we need to do a recursive diff here.
4546 begin_reversible_command (_("remove region"));
4549 vector<Command*> cmds;
4550 (*pl)->rdiff (cmds);
4551 _session->add_commands (cmds);
4553 _session->add_command(new StatefulDiffCommand (*pl));
4557 commit_reversible_command ();
4561 /** Cut, copy or clear selected regions.
4562 * @param op Operation (Cut, Copy or Clear)
4565 Editor::cut_copy_regions (CutCopyOp op, RegionSelection& rs)
4567 /* we can't use a std::map here because the ordering is important, and we can't trivially sort
4568 a map when we want ordered access to both elements. i think.
4571 vector<PlaylistMapping> pmap;
4573 framepos_t first_position = max_framepos;
4575 typedef set<boost::shared_ptr<Playlist> > FreezeList;
4576 FreezeList freezelist;
4578 /* get ordering correct before we cut/copy */
4580 rs.sort_by_position_and_track ();
4582 for (RegionSelection::iterator x = rs.begin(); x != rs.end(); ++x) {
4584 first_position = min ((framepos_t) (*x)->region()->position(), first_position);
4586 if (op == Cut || op == Clear || op == Delete) {
4587 boost::shared_ptr<Playlist> pl = (*x)->region()->playlist();
4590 FreezeList::iterator fl;
4592 // only take state if this is a new playlist.
4593 for (fl = freezelist.begin(); fl != freezelist.end(); ++fl) {
4599 if (fl == freezelist.end()) {
4600 pl->clear_changes();
4601 pl->clear_owned_changes ();
4603 freezelist.insert (pl);
4608 TimeAxisView* tv = &(*x)->get_time_axis_view();
4609 vector<PlaylistMapping>::iterator z;
4611 for (z = pmap.begin(); z != pmap.end(); ++z) {
4612 if ((*z).tv == tv) {
4617 if (z == pmap.end()) {
4618 pmap.push_back (PlaylistMapping (tv));
4622 for (RegionSelection::iterator x = rs.begin(); x != rs.end(); ) {
4624 boost::shared_ptr<Playlist> pl = (*x)->region()->playlist();
4627 /* region not yet associated with a playlist (e.g. unfinished
4634 TimeAxisView& tv = (*x)->get_time_axis_view();
4635 boost::shared_ptr<Playlist> npl;
4636 RegionSelection::iterator tmp;
4643 vector<PlaylistMapping>::iterator z;
4645 for (z = pmap.begin(); z != pmap.end(); ++z) {
4646 if ((*z).tv == &tv) {
4651 assert (z != pmap.end());
4654 npl = PlaylistFactory::create (pl->data_type(), *_session, "cutlist", true);
4662 boost::shared_ptr<Region> r = (*x)->region();
4663 boost::shared_ptr<Region> _xx;
4669 pl->remove_region (r);
4670 if (Config->get_edit_mode() == Ripple)
4671 pl->ripple (r->position(), -r->length(), boost::shared_ptr<Region>());
4675 _xx = RegionFactory::create (r);
4676 npl->add_region (_xx, r->position() - first_position);
4677 pl->remove_region (r);
4678 if (Config->get_edit_mode() == Ripple)
4679 pl->ripple (r->position(), -r->length(), boost::shared_ptr<Region>());
4683 /* copy region before adding, so we're not putting same object into two different playlists */
4684 npl->add_region (RegionFactory::create (r), r->position() - first_position);
4688 pl->remove_region (r);
4689 if (Config->get_edit_mode() == Ripple)
4690 pl->ripple (r->position(), -r->length(), boost::shared_ptr<Region>());
4699 list<boost::shared_ptr<Playlist> > foo;
4701 /* the pmap is in the same order as the tracks in which selected regions occurred */
4703 for (vector<PlaylistMapping>::iterator i = pmap.begin(); i != pmap.end(); ++i) {
4706 foo.push_back ((*i).pl);
4711 cut_buffer->set (foo);
4715 _last_cut_copy_source_track = 0;
4717 _last_cut_copy_source_track = pmap.front().tv;
4721 for (FreezeList::iterator pl = freezelist.begin(); pl != freezelist.end(); ++pl) {
4724 /* We might have removed regions, which alters other regions' layering_index,
4725 so we need to do a recursive diff here.
4727 vector<Command*> cmds;
4728 (*pl)->rdiff (cmds);
4729 _session->add_commands (cmds);
4731 _session->add_command (new StatefulDiffCommand (*pl));
4736 Editor::cut_copy_ranges (CutCopyOp op)
4738 TrackViewList ts = selection->tracks.filter_to_unique_playlists ();
4740 /* Sort the track selection now, so that it if is used, the playlists
4741 selected by the calls below to cut_copy_clear are in the order that
4742 their tracks appear in the editor. This makes things like paste
4743 of ranges work properly.
4746 sort_track_selection (ts);
4749 if (!entered_track) {
4752 ts.push_back (entered_track);
4755 for (TrackViewList::iterator i = ts.begin(); i != ts.end(); ++i) {
4756 (*i)->cut_copy_clear (*selection, op);
4761 Editor::paste (float times, bool from_context)
4763 DEBUG_TRACE (DEBUG::CutNPaste, "paste to preferred edit pos\n");
4764 MusicFrame where (get_preferred_edit_position (EDIT_IGNORE_NONE, from_context), 0);
4765 paste_internal (where.frame, times, 0);
4769 Editor::mouse_paste ()
4771 MusicFrame where (0, 0);
4773 if (!mouse_frame (where.frame, ignored)) {
4778 paste_internal (where.frame, 1, where.division);
4782 Editor::paste_internal (framepos_t position, float times, const int32_t sub_num)
4784 DEBUG_TRACE (DEBUG::CutNPaste, string_compose ("apparent paste position is %1\n", position));
4786 if (cut_buffer->empty(internal_editing())) {
4790 if (position == max_framepos) {
4791 position = get_preferred_edit_position();
4792 DEBUG_TRACE (DEBUG::CutNPaste, string_compose ("preferred edit position is %1\n", position));
4795 if (position == last_paste_pos) {
4796 /* repeated paste in the same position */
4799 /* paste in new location, reset repeated paste state */
4801 last_paste_pos = position;
4804 /* get everything in the correct order */
4807 if (!selection->tracks.empty()) {
4808 /* If there is a track selection, paste into exactly those tracks and
4809 * only those tracks. This allows the user to be explicit and override
4810 * the below "do the reasonable thing" logic. */
4811 ts = selection->tracks.filter_to_unique_playlists ();
4812 sort_track_selection (ts);
4814 /* Figure out which track to base the paste at. */
4815 TimeAxisView* base_track = NULL;
4816 if (_edit_point == Editing::EditAtMouse && entered_track) {
4817 /* With the mouse edit point, paste onto the track under the mouse. */
4818 base_track = entered_track;
4819 } else if (_edit_point == Editing::EditAtMouse && entered_regionview) {
4820 /* With the mouse edit point, paste onto the track of the region under the mouse. */
4821 base_track = &entered_regionview->get_time_axis_view();
4822 } else if (_last_cut_copy_source_track) {
4823 /* Paste to the track that the cut/copy came from (see mantis #333). */
4824 base_track = _last_cut_copy_source_track;
4826 /* This is "impossible" since we've copied... well, do nothing. */
4830 /* Walk up to parent if necessary, so base track is a route. */
4831 while (base_track->get_parent()) {
4832 base_track = base_track->get_parent();
4835 /* Add base track and all tracks below it. The paste logic will select
4836 the appropriate object types from the cut buffer in relative order. */
4837 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
4838 if ((*i)->order() >= base_track->order()) {
4843 /* Sort tracks so the nth track of type T will pick the nth object of type T. */
4844 sort_track_selection (ts);
4846 /* Add automation children of each track in order, for pasting several lines. */
4847 for (TrackViewList::iterator i = ts.begin(); i != ts.end();) {
4848 /* Add any automation children for pasting several lines */
4849 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*>(*i++);
4854 typedef RouteTimeAxisView::AutomationTracks ATracks;
4855 const ATracks& atracks = rtv->automation_tracks();
4856 for (ATracks::const_iterator a = atracks.begin(); a != atracks.end(); ++a) {
4857 i = ts.insert(i, a->second.get());
4862 /* We now have a list of trackviews starting at base_track, including
4863 automation children, in the order shown in the editor, e.g. R1,
4864 R1.A1, R1.A2, R2, R2.A1, ... */
4867 begin_reversible_command (Operations::paste);
4869 if (ts.size() == 1 && cut_buffer->lines.size() == 1 &&
4870 dynamic_cast<AutomationTimeAxisView*>(ts.front())) {
4871 /* Only one line copied, and one automation track selected. Do a
4872 "greedy" paste from one automation type to another. */
4874 PasteContext ctx(paste_count, times, ItemCounts(), true);
4875 ts.front()->paste (position, *cut_buffer, ctx, sub_num);
4879 /* Paste into tracks */
4881 PasteContext ctx(paste_count, times, ItemCounts(), false);
4882 for (TrackViewList::iterator i = ts.begin(); i != ts.end(); ++i) {
4883 (*i)->paste (position, *cut_buffer, ctx, sub_num);
4887 commit_reversible_command ();
4891 Editor::duplicate_regions (float times)
4893 RegionSelection rs (get_regions_from_selection_and_entered());
4894 duplicate_some_regions (rs, times);
4898 Editor::duplicate_some_regions (RegionSelection& regions, float times)
4900 if (regions.empty ()) {
4904 boost::shared_ptr<Playlist> playlist;
4905 RegionSelection sel = regions; // clear (below) may clear the argument list if its the current region selection
4906 RegionSelection foo;
4908 framepos_t const start_frame = regions.start ();
4909 framepos_t const end_frame = regions.end_frame ();
4910 framecnt_t const gap = end_frame - start_frame + 1;
4912 begin_reversible_command (Operations::duplicate_region);
4914 selection->clear_regions ();
4916 for (RegionSelection::iterator i = sel.begin(); i != sel.end(); ++i) {
4918 boost::shared_ptr<Region> r ((*i)->region());
4920 TimeAxisView& tv = (*i)->get_time_axis_view();
4921 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (&tv);
4922 latest_regionviews.clear ();
4923 sigc::connection c = rtv->view()->RegionViewAdded.connect (sigc::mem_fun(*this, &Editor::collect_new_region_view));
4925 framepos_t const position = end_frame + (r->first_frame() - start_frame + 1);
4926 playlist = (*i)->region()->playlist();
4927 playlist->clear_changes ();
4928 playlist->duplicate (r, position, gap, times);
4929 _session->add_command(new StatefulDiffCommand (playlist));
4933 foo.insert (foo.end(), latest_regionviews.begin(), latest_regionviews.end());
4937 selection->set (foo);
4940 commit_reversible_command ();
4944 Editor::duplicate_selection (float times)
4946 if (selection->time.empty() || selection->tracks.empty()) {
4950 boost::shared_ptr<Playlist> playlist;
4952 TrackViewList ts = selection->tracks.filter_to_unique_playlists ();
4954 bool in_command = false;
4956 for (TrackViewList::iterator i = ts.begin(); i != ts.end(); ++i) {
4957 if ((playlist = (*i)->playlist()) == 0) {
4960 playlist->clear_changes ();
4962 if (clicked_selection) {
4963 playlist->duplicate_range (selection->time[clicked_selection], times);
4965 playlist->duplicate_ranges (selection->time, times);
4969 begin_reversible_command (_("duplicate range selection"));
4972 _session->add_command (new StatefulDiffCommand (playlist));
4977 if (times == 1.0f) {
4978 // now "move" range selection to after the current range selection
4979 framecnt_t distance = 0;
4981 if (clicked_selection) {
4983 selection->time[clicked_selection].end - selection->time[clicked_selection].start;
4985 distance = selection->time.end_frame () - selection->time.start ();
4988 selection->move_time (distance);
4990 commit_reversible_command ();
4994 /** Reset all selected points to the relevant default value */
4996 Editor::reset_point_selection ()
4998 for (PointSelection::iterator i = selection->points.begin(); i != selection->points.end(); ++i) {
4999 ARDOUR::AutomationList::iterator j = (*i)->model ();
5000 (*j)->value = (*i)->line().the_list()->descriptor ().normal;
5005 Editor::center_playhead ()
5007 float const page = _visible_canvas_width * samples_per_pixel;
5008 center_screen_internal (playhead_cursor->current_frame (), page);
5012 Editor::center_edit_point ()
5014 float const page = _visible_canvas_width * samples_per_pixel;
5015 center_screen_internal (get_preferred_edit_position(), page);
5018 /** Caller must begin and commit a reversible command */
5020 Editor::clear_playlist (boost::shared_ptr<Playlist> playlist)
5022 playlist->clear_changes ();
5024 _session->add_command (new StatefulDiffCommand (playlist));
5028 Editor::nudge_track (bool use_edit, bool forwards)
5030 boost::shared_ptr<Playlist> playlist;
5031 framepos_t distance;
5032 framepos_t next_distance;
5036 start = get_preferred_edit_position();
5041 if ((distance = get_nudge_distance (start, next_distance)) == 0) {
5045 if (selection->tracks.empty()) {
5049 TrackViewList ts = selection->tracks.filter_to_unique_playlists ();
5050 bool in_command = false;
5052 for (TrackViewList::iterator i = ts.begin(); i != ts.end(); ++i) {
5054 if ((playlist = (*i)->playlist()) == 0) {
5058 playlist->clear_changes ();
5059 playlist->clear_owned_changes ();
5061 playlist->nudge_after (start, distance, forwards);
5064 begin_reversible_command (_("nudge track"));
5067 vector<Command*> cmds;
5069 playlist->rdiff (cmds);
5070 _session->add_commands (cmds);
5072 _session->add_command (new StatefulDiffCommand (playlist));
5076 commit_reversible_command ();
5081 Editor::remove_last_capture ()
5083 vector<string> choices;
5090 if (Config->get_verify_remove_last_capture()) {
5091 prompt = _("Do you really want to destroy the last capture?"
5092 "\n(This is destructive and cannot be undone)");
5094 choices.push_back (_("No, do nothing."));
5095 choices.push_back (_("Yes, destroy it."));
5097 Choice prompter (_("Destroy last capture"), prompt, choices);
5099 if (prompter.run () == 1) {
5100 _session->remove_last_capture ();
5101 _regions->redisplay ();
5105 _session->remove_last_capture();
5106 _regions->redisplay ();
5111 Editor::normalize_region ()
5117 RegionSelection rs = get_regions_from_selection_and_entered ();
5123 NormalizeDialog dialog (rs.size() > 1);
5125 if (dialog.run () != RESPONSE_ACCEPT) {
5129 CursorContext::Handle cursor_ctx = CursorContext::create(*this, _cursors->wait);
5132 /* XXX: should really only count audio regions here */
5133 int const regions = rs.size ();
5135 /* Make a list of the selected audio regions' maximum amplitudes, and also
5136 obtain the maximum amplitude of them all.
5138 list<double> max_amps;
5139 list<double> rms_vals;
5142 bool use_rms = dialog.constrain_rms ();
5144 for (RegionSelection::const_iterator i = rs.begin(); i != rs.end(); ++i) {
5145 AudioRegionView const * arv = dynamic_cast<AudioRegionView const *> (*i);
5149 dialog.descend (1.0 / regions);
5150 double const a = arv->audio_region()->maximum_amplitude (&dialog);
5152 double r = arv->audio_region()->rms (&dialog);
5153 max_rms = max (max_rms, r);
5154 rms_vals.push_back (r);
5158 /* the user cancelled the operation */
5162 max_amps.push_back (a);
5163 max_amp = max (max_amp, a);
5167 list<double>::const_iterator a = max_amps.begin ();
5168 list<double>::const_iterator l = rms_vals.begin ();
5169 bool in_command = false;
5171 for (RegionSelection::iterator r = rs.begin(); r != rs.end(); ++r) {
5172 AudioRegionView* const arv = dynamic_cast<AudioRegionView*> (*r);
5177 arv->region()->clear_changes ();
5179 double amp = dialog.normalize_individually() ? *a : max_amp;
5180 double target = dialog.target_peak (); // dB
5183 double const amp_rms = dialog.normalize_individually() ? *l : max_rms;
5184 const double t_rms = dialog.target_rms ();
5185 const gain_t c_peak = dB_to_coefficient (target);
5186 const gain_t c_rms = dB_to_coefficient (t_rms);
5187 if ((amp_rms / c_rms) > (amp / c_peak)) {
5193 arv->audio_region()->normalize (amp, target);
5196 begin_reversible_command (_("normalize"));
5199 _session->add_command (new StatefulDiffCommand (arv->region()));
5206 commit_reversible_command ();
5212 Editor::reset_region_scale_amplitude ()
5218 RegionSelection rs = get_regions_from_selection_and_entered ();
5224 bool in_command = false;
5226 for (RegionSelection::iterator r = rs.begin(); r != rs.end(); ++r) {
5227 AudioRegionView* const arv = dynamic_cast<AudioRegionView*>(*r);
5230 arv->region()->clear_changes ();
5231 arv->audio_region()->set_scale_amplitude (1.0f);
5234 begin_reversible_command ("reset gain");
5237 _session->add_command (new StatefulDiffCommand (arv->region()));
5241 commit_reversible_command ();
5246 Editor::adjust_region_gain (bool up)
5248 RegionSelection rs = get_regions_from_selection_and_entered ();
5250 if (!_session || rs.empty()) {
5254 bool in_command = false;
5256 for (RegionSelection::iterator r = rs.begin(); r != rs.end(); ++r) {
5257 AudioRegionView* const arv = dynamic_cast<AudioRegionView*>(*r);
5262 arv->region()->clear_changes ();
5264 double dB = accurate_coefficient_to_dB (arv->audio_region()->scale_amplitude ());
5272 arv->audio_region()->set_scale_amplitude (dB_to_coefficient (dB));
5275 begin_reversible_command ("adjust region gain");
5278 _session->add_command (new StatefulDiffCommand (arv->region()));
5282 commit_reversible_command ();
5287 Editor::reset_region_gain ()
5289 RegionSelection rs = get_regions_from_selection_and_entered ();
5291 if (!_session || rs.empty()) {
5295 bool in_command = false;
5297 for (RegionSelection::iterator r = rs.begin(); r != rs.end(); ++r) {
5298 AudioRegionView* const arv = dynamic_cast<AudioRegionView*>(*r);
5303 arv->region()->clear_changes ();
5305 arv->audio_region()->set_scale_amplitude (1.0f);
5308 begin_reversible_command ("reset region gain");
5311 _session->add_command (new StatefulDiffCommand (arv->region()));
5315 commit_reversible_command ();
5320 Editor::reverse_region ()
5326 Reverse rev (*_session);
5327 apply_filter (rev, _("reverse regions"));
5331 Editor::strip_region_silence ()
5337 RegionSelection rs = get_regions_from_selection_and_entered ();
5343 std::list<RegionView*> audio_only;
5345 for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
5346 AudioRegionView* const arv = dynamic_cast<AudioRegionView*> (*i);
5348 audio_only.push_back (arv);
5352 assert (!audio_only.empty());
5354 StripSilenceDialog d (_session, audio_only);
5355 int const r = d.run ();
5359 if (r == Gtk::RESPONSE_OK) {
5360 ARDOUR::AudioIntervalMap silences;
5361 d.silences (silences);
5362 StripSilence s (*_session, silences, d.fade_length());
5364 apply_filter (s, _("strip silence"), &d);
5369 Editor::apply_midi_note_edit_op_to_region (MidiOperator& op, MidiRegionView& mrv)
5371 Evoral::Sequence<Evoral::Beats>::Notes selected;
5372 mrv.selection_as_notelist (selected, true);
5374 vector<Evoral::Sequence<Evoral::Beats>::Notes> v;
5375 v.push_back (selected);
5377 Evoral::Beats pos_beats = Evoral::Beats (mrv.midi_region()->beat()) - mrv.midi_region()->start_beats();
5379 return op (mrv.midi_region()->model(), pos_beats, v);
5383 Editor::apply_midi_note_edit_op (MidiOperator& op, const RegionSelection& rs)
5389 bool in_command = false;
5391 for (RegionSelection::const_iterator r = rs.begin(); r != rs.end(); ) {
5392 RegionSelection::const_iterator tmp = r;
5395 MidiRegionView* const mrv = dynamic_cast<MidiRegionView*> (*r);
5398 Command* cmd = apply_midi_note_edit_op_to_region (op, *mrv);
5401 begin_reversible_command (op.name ());
5405 _session->add_command (cmd);
5413 commit_reversible_command ();
5418 Editor::fork_region ()
5420 RegionSelection rs = get_regions_from_selection_and_entered ();
5426 CursorContext::Handle cursor_ctx = CursorContext::create(*this, _cursors->wait);
5427 bool in_command = false;
5431 for (RegionSelection::iterator r = rs.begin(); r != rs.end(); ) {
5432 RegionSelection::iterator tmp = r;
5435 MidiRegionView* const mrv = dynamic_cast<MidiRegionView*>(*r);
5439 boost::shared_ptr<Playlist> playlist = mrv->region()->playlist();
5440 boost::shared_ptr<MidiSource> new_source = _session->create_midi_source_by_stealing_name (mrv->midi_view()->track());
5441 boost::shared_ptr<MidiRegion> newregion = mrv->midi_region()->clone (new_source);
5444 begin_reversible_command (_("Fork Region(s)"));
5447 playlist->clear_changes ();
5448 playlist->replace_region (mrv->region(), newregion, mrv->region()->position());
5449 _session->add_command(new StatefulDiffCommand (playlist));
5451 error << string_compose (_("Could not unlink %1"), mrv->region()->name()) << endmsg;
5459 commit_reversible_command ();
5464 Editor::quantize_region ()
5467 quantize_regions(get_regions_from_selection_and_entered ());
5472 Editor::quantize_regions (const RegionSelection& rs)
5474 if (rs.n_midi_regions() == 0) {
5478 if (!quantize_dialog) {
5479 quantize_dialog = new QuantizeDialog (*this);
5482 if (quantize_dialog->is_mapped()) {
5483 /* in progress already */
5487 quantize_dialog->present ();
5488 const int r = quantize_dialog->run ();
5489 quantize_dialog->hide ();
5491 if (r == Gtk::RESPONSE_OK) {
5492 Quantize quant (quantize_dialog->snap_start(),
5493 quantize_dialog->snap_end(),
5494 quantize_dialog->start_grid_size(),
5495 quantize_dialog->end_grid_size(),
5496 quantize_dialog->strength(),
5497 quantize_dialog->swing(),
5498 quantize_dialog->threshold());
5500 apply_midi_note_edit_op (quant, rs);
5505 Editor::legatize_region (bool shrink_only)
5508 legatize_regions(get_regions_from_selection_and_entered (), shrink_only);
5513 Editor::legatize_regions (const RegionSelection& rs, bool shrink_only)
5515 if (rs.n_midi_regions() == 0) {
5519 Legatize legatize(shrink_only);
5520 apply_midi_note_edit_op (legatize, rs);
5524 Editor::transform_region ()
5527 transform_regions(get_regions_from_selection_and_entered ());
5532 Editor::transform_regions (const RegionSelection& rs)
5534 if (rs.n_midi_regions() == 0) {
5541 const int r = td.run();
5544 if (r == Gtk::RESPONSE_OK) {
5545 Transform transform(td.get());
5546 apply_midi_note_edit_op(transform, rs);
5551 Editor::transpose_region ()
5554 transpose_regions(get_regions_from_selection_and_entered ());
5559 Editor::transpose_regions (const RegionSelection& rs)
5561 if (rs.n_midi_regions() == 0) {
5566 int const r = d.run ();
5568 if (r == RESPONSE_ACCEPT) {
5569 Transpose transpose(d.semitones ());
5570 apply_midi_note_edit_op (transpose, rs);
5575 Editor::insert_patch_change (bool from_context)
5577 RegionSelection rs = get_regions_from_selection_and_entered ();
5583 const framepos_t p = get_preferred_edit_position (EDIT_IGNORE_NONE, from_context);
5585 /* XXX: bit of a hack; use the MIDNAM from the first selected region;
5586 there may be more than one, but the PatchChangeDialog can only offer
5587 one set of patch menus.
5589 MidiRegionView* first = dynamic_cast<MidiRegionView*> (rs.front ());
5591 Evoral::PatchChange<Evoral::Beats> empty (Evoral::Beats(), 0, 0, 0);
5592 PatchChangeDialog d (0, _session, empty, first->instrument_info(), Gtk::Stock::ADD);
5594 if (d.run() == RESPONSE_CANCEL) {
5598 for (RegionSelection::iterator i = rs.begin (); i != rs.end(); ++i) {
5599 MidiRegionView* const mrv = dynamic_cast<MidiRegionView*> (*i);
5601 if (p >= mrv->region()->first_frame() && p <= mrv->region()->last_frame()) {
5602 mrv->add_patch_change (p - mrv->region()->position(), d.patch ());
5609 Editor::apply_filter (Filter& filter, string command, ProgressReporter* progress)
5611 RegionSelection rs = get_regions_from_selection_and_entered ();
5617 CursorContext::Handle cursor_ctx = CursorContext::create(*this, _cursors->wait);
5618 bool in_command = false;
5623 int const N = rs.size ();
5625 for (RegionSelection::iterator r = rs.begin(); r != rs.end(); ) {
5626 RegionSelection::iterator tmp = r;
5629 AudioRegionView* const arv = dynamic_cast<AudioRegionView*>(*r);
5631 boost::shared_ptr<Playlist> playlist = arv->region()->playlist();
5634 progress->descend (1.0 / N);
5637 if (arv->audio_region()->apply (filter, progress) == 0) {
5639 playlist->clear_changes ();
5640 playlist->clear_owned_changes ();
5643 begin_reversible_command (command);
5647 if (filter.results.empty ()) {
5649 /* no regions returned; remove the old one */
5650 playlist->remove_region (arv->region ());
5654 std::vector<boost::shared_ptr<Region> >::iterator res = filter.results.begin ();
5656 /* first region replaces the old one */
5657 playlist->replace_region (arv->region(), *res, (*res)->position());
5661 while (res != filter.results.end()) {
5662 playlist->add_region (*res, (*res)->position());
5668 /* We might have removed regions, which alters other regions' layering_index,
5669 so we need to do a recursive diff here.
5671 vector<Command*> cmds;
5672 playlist->rdiff (cmds);
5673 _session->add_commands (cmds);
5675 _session->add_command(new StatefulDiffCommand (playlist));
5679 progress->ascend ();
5688 commit_reversible_command ();
5693 Editor::external_edit_region ()
5699 Editor::reset_region_gain_envelopes ()
5701 RegionSelection rs = get_regions_from_selection_and_entered ();
5703 if (!_session || rs.empty()) {
5707 bool in_command = false;
5709 for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
5710 AudioRegionView* const arv = dynamic_cast<AudioRegionView*>(*i);
5712 boost::shared_ptr<AutomationList> alist (arv->audio_region()->envelope());
5713 XMLNode& before (alist->get_state());
5715 arv->audio_region()->set_default_envelope ();
5718 begin_reversible_command (_("reset region gain"));
5721 _session->add_command (new MementoCommand<AutomationList>(*arv->audio_region()->envelope().get(), &before, &alist->get_state()));
5726 commit_reversible_command ();
5731 Editor::set_region_gain_visibility (RegionView* rv)
5733 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (rv);
5735 arv->update_envelope_visibility();
5740 Editor::set_gain_envelope_visibility ()
5746 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
5747 AudioTimeAxisView* v = dynamic_cast<AudioTimeAxisView*>(*i);
5749 v->audio_view()->foreach_regionview (sigc::mem_fun (this, &Editor::set_region_gain_visibility));
5755 Editor::toggle_gain_envelope_active ()
5757 if (_ignore_region_action) {
5761 RegionSelection rs = get_regions_from_selection_and_entered ();
5763 if (!_session || rs.empty()) {
5767 bool in_command = false;
5769 for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
5770 AudioRegionView* const arv = dynamic_cast<AudioRegionView*>(*i);
5772 arv->region()->clear_changes ();
5773 arv->audio_region()->set_envelope_active (!arv->audio_region()->envelope_active());
5776 begin_reversible_command (_("region gain envelope active"));
5779 _session->add_command (new StatefulDiffCommand (arv->region()));
5784 commit_reversible_command ();
5789 Editor::toggle_region_lock ()
5791 if (_ignore_region_action) {
5795 RegionSelection rs = get_regions_from_selection_and_entered ();
5797 if (!_session || rs.empty()) {
5801 begin_reversible_command (_("toggle region lock"));
5803 for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
5804 (*i)->region()->clear_changes ();
5805 (*i)->region()->set_locked (!(*i)->region()->locked());
5806 _session->add_command (new StatefulDiffCommand ((*i)->region()));
5809 commit_reversible_command ();
5813 Editor::toggle_region_video_lock ()
5815 if (_ignore_region_action) {
5819 RegionSelection rs = get_regions_from_selection_and_entered ();
5821 if (!_session || rs.empty()) {
5825 begin_reversible_command (_("Toggle Video Lock"));
5827 for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
5828 (*i)->region()->clear_changes ();
5829 (*i)->region()->set_video_locked (!(*i)->region()->video_locked());
5830 _session->add_command (new StatefulDiffCommand ((*i)->region()));
5833 commit_reversible_command ();
5837 Editor::toggle_region_lock_style ()
5839 if (_ignore_region_action) {
5843 RegionSelection rs = get_regions_from_selection_and_entered ();
5845 if (!_session || rs.empty()) {
5849 Glib::RefPtr<ToggleAction> a = Glib::RefPtr<ToggleAction>::cast_dynamic (_region_actions->get_action("toggle-region-lock-style"));
5850 vector<Widget*> proxies = a->get_proxies();
5851 Gtk::CheckMenuItem* cmi = dynamic_cast<Gtk::CheckMenuItem*> (proxies.front());
5855 begin_reversible_command (_("toggle region lock style"));
5857 for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
5858 (*i)->region()->clear_changes ();
5859 PositionLockStyle const ns = ((*i)->region()->position_lock_style() == AudioTime && !cmi->get_inconsistent()) ? MusicTime : AudioTime;
5860 (*i)->region()->set_position_lock_style (ns);
5861 _session->add_command (new StatefulDiffCommand ((*i)->region()));
5864 commit_reversible_command ();
5868 Editor::toggle_opaque_region ()
5870 if (_ignore_region_action) {
5874 RegionSelection rs = get_regions_from_selection_and_entered ();
5876 if (!_session || rs.empty()) {
5880 begin_reversible_command (_("change region opacity"));
5882 for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
5883 (*i)->region()->clear_changes ();
5884 (*i)->region()->set_opaque (!(*i)->region()->opaque());
5885 _session->add_command (new StatefulDiffCommand ((*i)->region()));
5888 commit_reversible_command ();
5892 Editor::toggle_record_enable ()
5894 bool new_state = false;
5896 for (TrackSelection::iterator i = selection->tracks.begin(); i != selection->tracks.end(); ++i) {
5897 RouteTimeAxisView *rtav = dynamic_cast<RouteTimeAxisView *>(*i);
5900 if (!rtav->is_track())
5904 new_state = !rtav->track()->rec_enable_control()->get_value();
5908 rtav->track()->rec_enable_control()->set_value (new_state, Controllable::UseGroup);
5913 Editor::toggle_solo ()
5915 bool new_state = false;
5917 boost::shared_ptr<ControlList> cl (new ControlList);
5919 for (TrackSelection::iterator i = selection->tracks.begin(); i != selection->tracks.end(); ++i) {
5920 RouteTimeAxisView *rtav = dynamic_cast<RouteTimeAxisView *>(*i);
5927 new_state = !rtav->route()->soloed ();
5931 cl->push_back (rtav->route()->solo_control());
5934 _session->set_controls (cl, new_state ? 1.0 : 0.0, Controllable::UseGroup);
5938 Editor::toggle_mute ()
5940 bool new_state = false;
5942 boost::shared_ptr<RouteList> rl (new RouteList);
5944 for (TrackSelection::iterator i = selection->tracks.begin(); i != selection->tracks.end(); ++i) {
5945 RouteTimeAxisView *rtav = dynamic_cast<RouteTimeAxisView *>(*i);
5952 new_state = !rtav->route()->muted();
5956 rl->push_back (rtav->route());
5959 _session->set_controls (route_list_to_control_list (rl, &Stripable::mute_control), new_state, Controllable::UseGroup);
5963 Editor::toggle_solo_isolate ()
5969 Editor::fade_range ()
5971 TrackViewList ts = selection->tracks.filter_to_unique_playlists ();
5973 begin_reversible_command (_("fade range"));
5975 for (TrackViewList::iterator i = ts.begin(); i != ts.end(); ++i) {
5976 (*i)->fade_range (selection->time);
5979 commit_reversible_command ();
5984 Editor::set_fade_length (bool in)
5986 RegionSelection rs = get_regions_from_selection_and_entered ();
5992 /* we need a region to measure the offset from the start */
5994 RegionView* rv = rs.front ();
5996 framepos_t pos = get_preferred_edit_position();
6000 if (pos > rv->region()->last_frame() || pos < rv->region()->first_frame()) {
6001 /* edit point is outside the relevant region */
6006 if (pos <= rv->region()->position()) {
6010 len = pos - rv->region()->position();
6011 cmd = _("set fade in length");
6013 if (pos >= rv->region()->last_frame()) {
6017 len = rv->region()->last_frame() - pos;
6018 cmd = _("set fade out length");
6021 bool in_command = false;
6023 for (RegionSelection::iterator x = rs.begin(); x != rs.end(); ++x) {
6024 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (*x);
6030 boost::shared_ptr<AutomationList> alist;
6032 alist = tmp->audio_region()->fade_in();
6034 alist = tmp->audio_region()->fade_out();
6037 XMLNode &before = alist->get_state();
6040 tmp->audio_region()->set_fade_in_length (len);
6041 tmp->audio_region()->set_fade_in_active (true);
6043 tmp->audio_region()->set_fade_out_length (len);
6044 tmp->audio_region()->set_fade_out_active (true);
6048 begin_reversible_command (cmd);
6051 XMLNode &after = alist->get_state();
6052 _session->add_command(new MementoCommand<AutomationList>(*alist, &before, &after));
6056 commit_reversible_command ();
6061 Editor::set_fade_in_shape (FadeShape shape)
6063 RegionSelection rs = get_regions_from_selection_and_entered ();
6068 bool in_command = false;
6070 for (RegionSelection::iterator x = rs.begin(); x != rs.end(); ++x) {
6071 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (*x);
6077 boost::shared_ptr<AutomationList> alist = tmp->audio_region()->fade_in();
6078 XMLNode &before = alist->get_state();
6080 tmp->audio_region()->set_fade_in_shape (shape);
6083 begin_reversible_command (_("set fade in shape"));
6086 XMLNode &after = alist->get_state();
6087 _session->add_command(new MementoCommand<AutomationList>(*alist.get(), &before, &after));
6091 commit_reversible_command ();
6096 Editor::set_fade_out_shape (FadeShape shape)
6098 RegionSelection rs = get_regions_from_selection_and_entered ();
6103 bool in_command = false;
6105 for (RegionSelection::iterator x = rs.begin(); x != rs.end(); ++x) {
6106 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (*x);
6112 boost::shared_ptr<AutomationList> alist = tmp->audio_region()->fade_out();
6113 XMLNode &before = alist->get_state();
6115 tmp->audio_region()->set_fade_out_shape (shape);
6118 begin_reversible_command (_("set fade out shape"));
6121 XMLNode &after = alist->get_state();
6122 _session->add_command(new MementoCommand<AutomationList>(*alist.get(), &before, &after));
6126 commit_reversible_command ();
6131 Editor::set_fade_in_active (bool yn)
6133 RegionSelection rs = get_regions_from_selection_and_entered ();
6138 bool in_command = false;
6140 for (RegionSelection::iterator x = rs.begin(); x != rs.end(); ++x) {
6141 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (*x);
6148 boost::shared_ptr<AudioRegion> ar (tmp->audio_region());
6150 ar->clear_changes ();
6151 ar->set_fade_in_active (yn);
6154 begin_reversible_command (_("set fade in active"));
6157 _session->add_command (new StatefulDiffCommand (ar));
6161 commit_reversible_command ();
6166 Editor::set_fade_out_active (bool yn)
6168 RegionSelection rs = get_regions_from_selection_and_entered ();
6173 bool in_command = false;
6175 for (RegionSelection::iterator x = rs.begin(); x != rs.end(); ++x) {
6176 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (*x);
6182 boost::shared_ptr<AudioRegion> ar (tmp->audio_region());
6184 ar->clear_changes ();
6185 ar->set_fade_out_active (yn);
6188 begin_reversible_command (_("set fade out active"));
6191 _session->add_command(new StatefulDiffCommand (ar));
6195 commit_reversible_command ();
6200 Editor::toggle_region_fades (int dir)
6202 if (_ignore_region_action) {
6206 boost::shared_ptr<AudioRegion> ar;
6209 RegionSelection rs = get_regions_from_selection_and_entered ();
6215 RegionSelection::iterator i;
6216 for (i = rs.begin(); i != rs.end(); ++i) {
6217 if ((ar = boost::dynamic_pointer_cast<AudioRegion>((*i)->region())) != 0) {
6219 yn = ar->fade_out_active ();
6221 yn = ar->fade_in_active ();
6227 if (i == rs.end()) {
6231 /* XXX should this undo-able? */
6232 bool in_command = false;
6234 for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
6235 if ((ar = boost::dynamic_pointer_cast<AudioRegion>((*i)->region())) == 0) {
6238 ar->clear_changes ();
6240 if (dir == 1 || dir == 0) {
6241 ar->set_fade_in_active (!yn);
6244 if (dir == -1 || dir == 0) {
6245 ar->set_fade_out_active (!yn);
6248 begin_reversible_command (_("toggle fade active"));
6251 _session->add_command(new StatefulDiffCommand (ar));
6255 commit_reversible_command ();
6260 /** Update region fade visibility after its configuration has been changed */
6262 Editor::update_region_fade_visibility ()
6264 bool _fade_visibility = _session->config.get_show_region_fades ();
6266 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
6267 AudioTimeAxisView* v = dynamic_cast<AudioTimeAxisView*>(*i);
6269 if (_fade_visibility) {
6270 v->audio_view()->show_all_fades ();
6272 v->audio_view()->hide_all_fades ();
6279 Editor::set_edit_point ()
6282 MusicFrame where (0, 0);
6284 if (!mouse_frame (where.frame, ignored)) {
6290 if (selection->markers.empty()) {
6292 mouse_add_new_marker (where.frame);
6297 Location* loc = find_location_from_marker (selection->markers.front(), ignored);
6300 loc->move_to (where.frame, where.division);
6306 Editor::set_playhead_cursor ()
6308 if (entered_marker) {
6309 _session->request_locate (entered_marker->position(), _session->transport_rolling());
6311 MusicFrame where (0, 0);
6314 if (!mouse_frame (where.frame, ignored)) {
6321 _session->request_locate (where.frame, _session->transport_rolling());
6325 //not sure what this was for; remove it for now.
6326 // if (UIConfiguration::instance().get_follow_edits() && (!_session || !_session->config.get_external_sync())) {
6327 // cancel_time_selection();
6333 Editor::split_region ()
6335 if (_drags->active ()) {
6339 //if a range is selected, separate it
6340 if ( !selection->time.empty()) {
6341 separate_regions_between (selection->time);
6345 //if no range was selected, try to find some regions to split
6346 if (current_mouse_mode() == MouseObject) { //don't try this for Internal Edit, Stretch, Draw, etc.
6348 RegionSelection rs = get_regions_from_selection_and_edit_point ();
6349 const framepos_t pos = get_preferred_edit_position();
6350 const int32_t division = get_grid_music_divisions (0);
6351 MusicFrame where (pos, division);
6357 split_regions_at (where, rs);
6363 Editor::select_next_route()
6365 if (selection->tracks.empty()) {
6366 selection->set (track_views.front());
6370 TimeAxisView* current = selection->tracks.front();
6374 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
6376 if (*i == current) {
6378 if (i != track_views.end()) {
6381 current = (*(track_views.begin()));
6382 //selection->set (*(track_views.begin()));
6388 rui = dynamic_cast<RouteUI *>(current);
6390 } while (current->hidden() || (rui == NULL) || !rui->route()->active());
6392 selection->set (current);
6394 ensure_time_axis_view_is_visible (*current, false);
6398 Editor::select_prev_route()
6400 if (selection->tracks.empty()) {
6401 selection->set (track_views.front());
6405 TimeAxisView* current = selection->tracks.front();
6409 for (TrackViewList::reverse_iterator i = track_views.rbegin(); i != track_views.rend(); ++i) {
6411 if (*i == current) {
6413 if (i != track_views.rend()) {
6416 current = *(track_views.rbegin());
6421 rui = dynamic_cast<RouteUI *>(current);
6423 } while (current->hidden() || (rui == NULL) || !rui->route()->active());
6425 selection->set (current);
6427 ensure_time_axis_view_is_visible (*current, false);
6431 Editor::set_loop_from_selection (bool play)
6433 if (_session == 0) {
6437 framepos_t start, end;
6438 if (!get_selection_extents ( start, end))
6441 set_loop_range (start, end, _("set loop range from selection"));
6444 _session->request_play_loop (true, true);
6449 Editor::set_loop_from_region (bool play)
6451 framepos_t start, end;
6452 if (!get_selection_extents ( start, end))
6455 set_loop_range (start, end, _("set loop range from region"));
6458 _session->request_locate (start, true);
6459 _session->request_play_loop (true);
6464 Editor::set_punch_from_selection ()
6466 if (_session == 0) {
6470 framepos_t start, end;
6471 if (!get_selection_extents ( start, end))
6474 set_punch_range (start, end, _("set punch range from selection"));
6478 Editor::set_auto_punch_range ()
6480 // auto punch in/out button from a single button
6481 // If Punch In is unset, set punch range from playhead to end, enable punch in
6482 // If Punch In is set, the next punch sets Punch Out, unless the playhead has been
6483 // rewound beyond the Punch In marker, in which case that marker will be moved back
6484 // to the current playhead position.
6485 // If punch out is set, it clears the punch range and Punch In/Out buttons
6487 if (_session == 0) {
6491 Location* tpl = transport_punch_location();
6492 framepos_t now = playhead_cursor->current_frame();
6493 framepos_t begin = now;
6494 framepos_t end = _session->current_end_frame();
6496 if (!_session->config.get_punch_in()) {
6497 // First Press - set punch in and create range from here to eternity
6498 set_punch_range (begin, end, _("Auto Punch In"));
6499 _session->config.set_punch_in(true);
6500 } else if (tpl && !_session->config.get_punch_out()) {
6501 // Second press - update end range marker and set punch_out
6502 if (now < tpl->start()) {
6503 // playhead has been rewound - move start back and pretend nothing happened
6505 set_punch_range (begin, end, _("Auto Punch In/Out"));
6507 // normal case for 2nd press - set the punch out
6508 end = playhead_cursor->current_frame ();
6509 set_punch_range (tpl->start(), now, _("Auto Punch In/Out"));
6510 _session->config.set_punch_out(true);
6513 if (_session->config.get_punch_out()) {
6514 _session->config.set_punch_out(false);
6517 if (_session->config.get_punch_in()) {
6518 _session->config.set_punch_in(false);
6523 // third press - unset punch in/out and remove range
6524 _session->locations()->remove(tpl);
6531 Editor::set_session_extents_from_selection ()
6533 if (_session == 0) {
6537 framepos_t start, end;
6538 if (!get_selection_extents ( start, end))
6542 if ((loc = _session->locations()->session_range_location()) == 0) {
6543 _session->set_session_extents (start, end); // this will create a new session range; no need for UNDO
6545 XMLNode &before = loc->get_state();
6547 _session->set_session_extents (start, end);
6549 XMLNode &after = loc->get_state();
6551 begin_reversible_command (_("set session start/end from selection"));
6553 _session->add_command (new MementoCommand<Location>(*loc, &before, &after));
6555 commit_reversible_command ();
6558 _session->set_end_is_free (false);
6562 Editor::set_punch_start_from_edit_point ()
6566 MusicFrame start (0, 0);
6567 framepos_t end = max_framepos;
6569 //use the existing punch end, if any
6570 Location* tpl = transport_punch_location();
6575 if ((_edit_point == EditAtPlayhead) && _session->transport_rolling()) {
6576 start.frame = _session->audible_frame();
6578 start.frame = get_preferred_edit_position();
6581 //snap the selection start/end
6584 //if there's not already a sensible selection endpoint, go "forever"
6585 if (start.frame > end ) {
6589 set_punch_range (start.frame, end, _("set punch start from EP"));
6595 Editor::set_punch_end_from_edit_point ()
6599 framepos_t start = 0;
6600 MusicFrame end (max_framepos, 0);
6602 //use the existing punch start, if any
6603 Location* tpl = transport_punch_location();
6605 start = tpl->start();
6608 if ((_edit_point == EditAtPlayhead) && _session->transport_rolling()) {
6609 end.frame = _session->audible_frame();
6611 end.frame = get_preferred_edit_position();
6614 //snap the selection start/end
6617 set_punch_range (start, end.frame, _("set punch end from EP"));
6623 Editor::set_loop_start_from_edit_point ()
6627 MusicFrame start (0, 0);
6628 framepos_t end = max_framepos;
6630 //use the existing loop end, if any
6631 Location* tpl = transport_loop_location();
6636 if ((_edit_point == EditAtPlayhead) && _session->transport_rolling()) {
6637 start.frame = _session->audible_frame();
6639 start.frame = get_preferred_edit_position();
6642 //snap the selection start/end
6645 //if there's not already a sensible selection endpoint, go "forever"
6646 if (start.frame > end ) {
6650 set_loop_range (start.frame, end, _("set loop start from EP"));
6656 Editor::set_loop_end_from_edit_point ()
6660 framepos_t start = 0;
6661 MusicFrame end (max_framepos, 0);
6663 //use the existing loop start, if any
6664 Location* tpl = transport_loop_location();
6666 start = tpl->start();
6669 if ((_edit_point == EditAtPlayhead) && _session->transport_rolling()) {
6670 end.frame = _session->audible_frame();
6672 end.frame = get_preferred_edit_position();
6675 //snap the selection start/end
6678 set_loop_range (start, end.frame, _("set loop end from EP"));
6683 Editor::set_punch_from_region ()
6685 framepos_t start, end;
6686 if (!get_selection_extents ( start, end))
6689 set_punch_range (start, end, _("set punch range from region"));
6693 Editor::pitch_shift_region ()
6695 RegionSelection rs = get_regions_from_selection_and_entered ();
6697 RegionSelection audio_rs;
6698 for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
6699 if (dynamic_cast<AudioRegionView*> (*i)) {
6700 audio_rs.push_back (*i);
6704 if (audio_rs.empty()) {
6708 pitch_shift (audio_rs, 1.2);
6712 Editor::set_tempo_from_region ()
6714 RegionSelection rs = get_regions_from_selection_and_entered ();
6716 if (!_session || rs.empty()) {
6720 RegionView* rv = rs.front();
6722 define_one_bar (rv->region()->position(), rv->region()->last_frame() + 1);
6726 Editor::use_range_as_bar ()
6728 framepos_t start, end;
6729 if (get_edit_op_range (start, end)) {
6730 define_one_bar (start, end);
6735 Editor::define_one_bar (framepos_t start, framepos_t end)
6737 framepos_t length = end - start;
6739 const Meter& m (_session->tempo_map().meter_at_frame (start));
6741 /* length = 1 bar */
6743 /* We're going to deliver a constant tempo here,
6744 so we can use frames per beat to determine length.
6745 now we want frames per beat.
6746 we have frames per bar, and beats per bar, so ...
6749 /* XXXX METER MATH */
6751 double frames_per_beat = length / m.divisions_per_bar();
6753 /* beats per minute = */
6755 double beats_per_minute = (_session->frame_rate() * 60.0) / frames_per_beat;
6757 /* now decide whether to:
6759 (a) set global tempo
6760 (b) add a new tempo marker
6764 const TempoSection& t (_session->tempo_map().tempo_section_at_frame (start));
6766 bool do_global = false;
6768 if ((_session->tempo_map().n_tempos() == 1) && (_session->tempo_map().n_meters() == 1)) {
6770 /* only 1 tempo & 1 meter: ask if the user wants to set the tempo
6771 at the start, or create a new marker
6774 vector<string> options;
6775 options.push_back (_("Cancel"));
6776 options.push_back (_("Add new marker"));
6777 options.push_back (_("Set global tempo"));
6780 _("Define one bar"),
6781 _("Do you want to set the global tempo or add a new tempo marker?"),
6785 c.set_default_response (2);
6801 /* more than 1 tempo and/or meter section already, go ahead do the "usual":
6802 if the marker is at the region starter, change it, otherwise add
6807 begin_reversible_command (_("set tempo from region"));
6808 XMLNode& before (_session->tempo_map().get_state());
6811 _session->tempo_map().change_initial_tempo (beats_per_minute, t.note_type(), t.end_note_types_per_minute());
6812 } else if (t.frame() == start) {
6813 _session->tempo_map().change_existing_tempo_at (start, beats_per_minute, t.note_type(), t.end_note_types_per_minute());
6815 /* constant tempo */
6816 const Tempo tempo (beats_per_minute, t.note_type());
6817 _session->tempo_map().add_tempo (tempo, 0.0, start, AudioTime);
6820 XMLNode& after (_session->tempo_map().get_state());
6822 _session->add_command (new MementoCommand<TempoMap>(_session->tempo_map(), &before, &after));
6823 commit_reversible_command ();
6827 Editor::split_region_at_transients ()
6829 AnalysisFeatureList positions;
6831 RegionSelection rs = get_regions_from_selection_and_entered ();
6833 if (!_session || rs.empty()) {
6837 begin_reversible_command (_("split regions"));
6839 for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ) {
6841 RegionSelection::iterator tmp;
6846 boost::shared_ptr<AudioRegion> ar = boost::dynamic_pointer_cast<AudioRegion> ((*i)->region());
6849 ar->transients (positions);
6850 split_region_at_points ((*i)->region(), positions, true);
6857 commit_reversible_command ();
6862 Editor::split_region_at_points (boost::shared_ptr<Region> r, AnalysisFeatureList& positions, bool can_ferret, bool select_new)
6864 bool use_rhythmic_rodent = false;
6866 boost::shared_ptr<Playlist> pl = r->playlist();
6868 list<boost::shared_ptr<Region> > new_regions;
6874 if (positions.empty()) {
6878 if (positions.size() > 20 && can_ferret) {
6879 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);
6880 MessageDialog msg (msgstr,
6883 Gtk::BUTTONS_OK_CANCEL);
6886 msg.add_button (_("Call for the Ferret!"), RESPONSE_APPLY);
6887 msg.set_secondary_text (_("Press OK to continue with this split operation\nor ask the Ferret dialog to tune the analysis"));
6889 msg.set_secondary_text (_("Press OK to continue with this split operation"));
6892 msg.set_title (_("Excessive split?"));
6895 int response = msg.run();
6901 case RESPONSE_APPLY:
6902 use_rhythmic_rodent = true;
6909 if (use_rhythmic_rodent) {
6910 show_rhythm_ferret ();
6914 AnalysisFeatureList::const_iterator x;
6916 pl->clear_changes ();
6917 pl->clear_owned_changes ();
6919 x = positions.begin();
6921 if (x == positions.end()) {
6926 pl->remove_region (r);
6930 framepos_t rstart = r->first_frame ();
6931 framepos_t rend = r->last_frame ();
6933 while (x != positions.end()) {
6935 /* deal with positons that are out of scope of present region bounds */
6936 if (*x <= rstart || *x > rend) {
6941 /* file start = original start + how far we from the initial position ? */
6943 framepos_t file_start = r->start() + pos;
6945 /* length = next position - current position */
6947 framepos_t len = (*x) - pos - rstart;
6949 /* XXX we do we really want to allow even single-sample regions?
6950 * shouldn't we have some kind of lower limit on region size?
6959 if (RegionFactory::region_name (new_name, r->name())) {
6963 /* do NOT announce new regions 1 by one, just wait till they are all done */
6967 plist.add (ARDOUR::Properties::start, file_start);
6968 plist.add (ARDOUR::Properties::length, len);
6969 plist.add (ARDOUR::Properties::name, new_name);
6970 plist.add (ARDOUR::Properties::layer, 0);
6971 // TODO set transients_offset
6973 boost::shared_ptr<Region> nr = RegionFactory::create (r->sources(), plist, false);
6974 /* because we set annouce to false, manually add the new region to the
6977 RegionFactory::map_add (nr);
6979 pl->add_region (nr, rstart + pos);
6982 new_regions.push_front(nr);
6991 RegionFactory::region_name (new_name, r->name());
6993 /* Add the final region */
6996 plist.add (ARDOUR::Properties::start, r->start() + pos);
6997 plist.add (ARDOUR::Properties::length, r->last_frame() - (r->position() + pos) + 1);
6998 plist.add (ARDOUR::Properties::name, new_name);
6999 plist.add (ARDOUR::Properties::layer, 0);
7001 boost::shared_ptr<Region> nr = RegionFactory::create (r->sources(), plist, false);
7002 /* because we set annouce to false, manually add the new region to the
7005 RegionFactory::map_add (nr);
7006 pl->add_region (nr, r->position() + pos);
7009 new_regions.push_front(nr);
7014 /* We might have removed regions, which alters other regions' layering_index,
7015 so we need to do a recursive diff here.
7017 vector<Command*> cmds;
7019 _session->add_commands (cmds);
7021 _session->add_command (new StatefulDiffCommand (pl));
7025 for (list<boost::shared_ptr<Region> >::iterator i = new_regions.begin(); i != new_regions.end(); ++i){
7026 set_selected_regionview_from_region_list ((*i), Selection::Add);
7032 Editor::place_transient()
7038 RegionSelection rs = get_regions_from_selection_and_edit_point ();
7044 framepos_t where = get_preferred_edit_position();
7046 begin_reversible_command (_("place transient"));
7048 for (RegionSelection::iterator r = rs.begin(); r != rs.end(); ++r) {
7049 (*r)->region()->add_transient(where);
7052 commit_reversible_command ();
7056 Editor::remove_transient(ArdourCanvas::Item* item)
7062 ArdourCanvas::Line* _line = reinterpret_cast<ArdourCanvas::Line*> (item);
7065 AudioRegionView* _arv = reinterpret_cast<AudioRegionView*> (item->get_data ("regionview"));
7066 _arv->remove_transient (*(float*) _line->get_data ("position"));
7070 Editor::snap_regions_to_grid ()
7072 list <boost::shared_ptr<Playlist > > used_playlists;
7074 RegionSelection rs = get_regions_from_selection_and_entered ();
7076 if (!_session || rs.empty()) {
7080 begin_reversible_command (_("snap regions to grid"));
7082 for (RegionSelection::iterator r = rs.begin(); r != rs.end(); ++r) {
7084 boost::shared_ptr<Playlist> pl = (*r)->region()->playlist();
7086 if (!pl->frozen()) {
7087 /* we haven't seen this playlist before */
7089 /* remember used playlists so we can thaw them later */
7090 used_playlists.push_back(pl);
7093 (*r)->region()->clear_changes ();
7095 MusicFrame start ((*r)->region()->first_frame (), 0);
7097 (*r)->region()->set_position (start.frame, start.division);
7098 _session->add_command(new StatefulDiffCommand ((*r)->region()));
7101 while (used_playlists.size() > 0) {
7102 list <boost::shared_ptr<Playlist > >::iterator i = used_playlists.begin();
7104 used_playlists.pop_front();
7107 commit_reversible_command ();
7111 Editor::close_region_gaps ()
7113 list <boost::shared_ptr<Playlist > > used_playlists;
7115 RegionSelection rs = get_regions_from_selection_and_entered ();
7117 if (!_session || rs.empty()) {
7121 Dialog dialog (_("Close Region Gaps"));
7124 table.set_spacings (12);
7125 table.set_border_width (12);
7126 Label* l = manage (left_aligned_label (_("Crossfade length")));
7127 table.attach (*l, 0, 1, 0, 1);
7129 SpinButton spin_crossfade (1, 0);
7130 spin_crossfade.set_range (0, 15);
7131 spin_crossfade.set_increments (1, 1);
7132 spin_crossfade.set_value (5);
7133 table.attach (spin_crossfade, 1, 2, 0, 1);
7135 table.attach (*manage (new Label (_("ms"))), 2, 3, 0, 1);
7137 l = manage (left_aligned_label (_("Pull-back length")));
7138 table.attach (*l, 0, 1, 1, 2);
7140 SpinButton spin_pullback (1, 0);
7141 spin_pullback.set_range (0, 100);
7142 spin_pullback.set_increments (1, 1);
7143 spin_pullback.set_value(30);
7144 table.attach (spin_pullback, 1, 2, 1, 2);
7146 table.attach (*manage (new Label (_("ms"))), 2, 3, 1, 2);
7148 dialog.get_vbox()->pack_start (table);
7149 dialog.add_button (Stock::CANCEL, RESPONSE_CANCEL);
7150 dialog.add_button (_("Ok"), RESPONSE_ACCEPT);
7153 if (dialog.run () == RESPONSE_CANCEL) {
7157 framepos_t crossfade_len = spin_crossfade.get_value();
7158 framepos_t pull_back_frames = spin_pullback.get_value();
7160 crossfade_len = lrintf (crossfade_len * _session->frame_rate()/1000);
7161 pull_back_frames = lrintf (pull_back_frames * _session->frame_rate()/1000);
7163 /* Iterate over the region list and make adjacent regions overlap by crossfade_len_ms */
7165 begin_reversible_command (_("close region gaps"));
7168 boost::shared_ptr<Region> last_region;
7170 rs.sort_by_position_and_track();
7172 for (RegionSelection::iterator r = rs.begin(); r != rs.end(); ++r) {
7174 boost::shared_ptr<Playlist> pl = (*r)->region()->playlist();
7176 if (!pl->frozen()) {
7177 /* we haven't seen this playlist before */
7179 /* remember used playlists so we can thaw them later */
7180 used_playlists.push_back(pl);
7184 framepos_t position = (*r)->region()->position();
7186 if (idx == 0 || position < last_region->position()){
7187 last_region = (*r)->region();
7192 (*r)->region()->clear_changes ();
7193 (*r)->region()->trim_front( (position - pull_back_frames));
7195 last_region->clear_changes ();
7196 last_region->trim_end( (position - pull_back_frames + crossfade_len));
7198 _session->add_command (new StatefulDiffCommand ((*r)->region()));
7199 _session->add_command (new StatefulDiffCommand (last_region));
7201 last_region = (*r)->region();
7205 while (used_playlists.size() > 0) {
7206 list <boost::shared_ptr<Playlist > >::iterator i = used_playlists.begin();
7208 used_playlists.pop_front();
7211 commit_reversible_command ();
7215 Editor::tab_to_transient (bool forward)
7217 AnalysisFeatureList positions;
7219 RegionSelection rs = get_regions_from_selection_and_entered ();
7225 framepos_t pos = _session->audible_frame ();
7227 if (!selection->tracks.empty()) {
7229 /* don't waste time searching for transients in duplicate playlists.
7232 TrackViewList ts = selection->tracks.filter_to_unique_playlists ();
7234 for (TrackViewList::iterator t = ts.begin(); t != ts.end(); ++t) {
7236 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (*t);
7239 boost::shared_ptr<Track> tr = rtv->track();
7241 boost::shared_ptr<Playlist> pl = tr->playlist ();
7243 framepos_t result = pl->find_next_transient (pos, forward ? 1 : -1);
7246 positions.push_back (result);
7259 for (RegionSelection::iterator r = rs.begin(); r != rs.end(); ++r) {
7260 (*r)->region()->get_transients (positions);
7264 TransientDetector::cleanup_transients (positions, _session->frame_rate(), 3.0);
7267 AnalysisFeatureList::iterator x;
7269 for (x = positions.begin(); x != positions.end(); ++x) {
7275 if (x != positions.end ()) {
7276 _session->request_locate (*x);
7280 AnalysisFeatureList::reverse_iterator x;
7282 for (x = positions.rbegin(); x != positions.rend(); ++x) {
7288 if (x != positions.rend ()) {
7289 _session->request_locate (*x);
7295 Editor::playhead_forward_to_grid ()
7301 MusicFrame pos (playhead_cursor->current_frame (), 0);
7303 if (pos.frame < max_framepos - 1) {
7305 snap_to_internal (pos, RoundUpAlways, false, true);
7306 _session->request_locate (pos.frame);
7312 Editor::playhead_backward_to_grid ()
7318 MusicFrame pos (playhead_cursor->current_frame (), 0);
7320 if (pos.frame > 2) {
7322 snap_to_internal (pos, RoundDownAlways, false, true);
7323 _session->request_locate (pos.frame);
7328 Editor::set_track_height (Height h)
7330 TrackSelection& ts (selection->tracks);
7332 for (TrackSelection::iterator x = ts.begin(); x != ts.end(); ++x) {
7333 (*x)->set_height_enum (h);
7338 Editor::toggle_tracks_active ()
7340 TrackSelection& ts (selection->tracks);
7342 bool target = false;
7348 for (TrackSelection::iterator x = ts.begin(); x != ts.end(); ++x) {
7349 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*>(*x);
7353 target = !rtv->_route->active();
7356 rtv->_route->set_active (target, this);
7362 Editor::remove_tracks ()
7364 /* this will delete GUI objects that may be the subject of an event
7365 handler in which this method is called. Defer actual deletion to the
7366 next idle callback, when all event handling is finished.
7368 Glib::signal_idle().connect (sigc::mem_fun (*this, &Editor::idle_remove_tracks));
7372 Editor::idle_remove_tracks ()
7374 Session::StateProtector sp (_session);
7376 return false; /* do not call again */
7380 Editor::_remove_tracks ()
7382 TrackSelection& ts (selection->tracks);
7388 vector<string> choices;
7392 const char* trackstr;
7394 vector<boost::shared_ptr<Route> > routes;
7395 bool special_bus = false;
7397 for (TrackSelection::iterator x = ts.begin(); x != ts.end(); ++x) {
7398 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (*x);
7402 if (rtv->is_track()) {
7407 routes.push_back (rtv->_route);
7409 if (rtv->route()->is_master() || rtv->route()->is_monitor()) {
7414 if (special_bus && !Config->get_allow_special_bus_removal()) {
7415 MessageDialog msg (_("That would be bad news ...."),
7419 msg.set_secondary_text (string_compose (_(
7420 "Removing the master or monitor bus is such a bad idea\n\
7421 that %1 is not going to allow it.\n\
7423 If you really want to do this sort of thing\n\
7424 edit your ardour.rc file to set the\n\
7425 \"allow-special-bus-removal\" option to be \"yes\""), PROGRAM_NAME));
7432 if (ntracks + nbusses == 0) {
7436 trackstr = P_("track", "tracks", ntracks);
7437 busstr = P_("bus", "busses", nbusses);
7441 prompt = string_compose (_("Do you really want to remove %1 %2 and %3 %4?\n"
7442 "(You may also lose the playlists associated with the %2)\n\n"
7443 "This action cannot be undone, and the session file will be overwritten!"),
7444 ntracks, trackstr, nbusses, busstr);
7446 prompt = string_compose (_("Do you really want to remove %1 %2?\n"
7447 "(You may also lose the playlists associated with the %2)\n\n"
7448 "This action cannot be undone, and the session file will be overwritten!"),
7451 } else if (nbusses) {
7452 prompt = string_compose (_("Do you really want to remove %1 %2?\n\n"
7453 "This action cannot be undone, and the session file will be overwritten"),
7457 choices.push_back (_("No, do nothing."));
7458 if (ntracks + nbusses > 1) {
7459 choices.push_back (_("Yes, remove them."));
7461 choices.push_back (_("Yes, remove it."));
7466 title = string_compose (_("Remove %1"), trackstr);
7468 title = string_compose (_("Remove %1"), busstr);
7471 Choice prompter (title, prompt, choices);
7473 if (prompter.run () != 1) {
7477 if (current_mixer_strip && routes.size () > 1 && std::find (routes.begin(), routes.end(), current_mixer_strip->route()) != routes.end ()) {
7478 /* Route deletion calls Editor::timeaxisview_deleted() iteratively (for each deleted
7479 * route). If the deleted route is currently displayed in the Editor-Mixer (highly
7480 * likely because deletion requires selection) this will call
7481 * Editor::set_selected_mixer_strip () which is expensive ( MixerStrip::set_route() ).
7482 * It's likewise likely that the route that has just been displayed in the
7483 * Editor-Mixer will be next in line for deletion.
7485 * So simply switch to the master-bus (if present)
7487 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
7488 if ((*i)->stripable ()->is_master ()) {
7489 set_selected_mixer_strip (*(*i));
7496 PresentationInfo::ChangeSuspender cs;
7497 DisplaySuspender ds;
7499 boost::shared_ptr<RouteList> rl (new RouteList);
7500 for (vector<boost::shared_ptr<Route> >::iterator x = routes.begin(); x != routes.end(); ++x) {
7503 _session->remove_routes (rl);
7505 /* TrackSelection and RouteList leave scope,
7506 * destructors are called,
7507 * diskstream drops references, save_state is called (again for every track)
7512 Editor::do_insert_time ()
7514 if (selection->tracks.empty()) {
7518 InsertRemoveTimeDialog d (*this);
7519 int response = d.run ();
7521 if (response != RESPONSE_OK) {
7525 if (d.distance() == 0) {
7532 d.intersected_region_action (),
7536 d.move_glued_markers(),
7537 d.move_locked_markers(),
7543 Editor::insert_time (
7544 framepos_t pos, framecnt_t frames, InsertTimeOption opt,
7545 bool all_playlists, bool ignore_music_glue, bool markers_too, bool glued_markers_too, bool locked_markers_too, bool tempo_too
7549 if (Config->get_edit_mode() == Lock) {
7552 bool in_command = false;
7554 TrackViewList ts = selection->tracks.filter_to_unique_playlists ();
7556 for (TrackViewList::iterator x = ts.begin(); x != ts.end(); ++x) {
7560 /* don't operate on any playlist more than once, which could
7561 * happen if "all playlists" is enabled, but there is more
7562 * than 1 track using playlists "from" a given track.
7565 set<boost::shared_ptr<Playlist> > pl;
7567 if (all_playlists) {
7568 RouteTimeAxisView* rtav = dynamic_cast<RouteTimeAxisView*> (*x);
7569 if (rtav && rtav->track ()) {
7570 vector<boost::shared_ptr<Playlist> > all = _session->playlists->playlists_for_track (rtav->track ());
7571 for (vector<boost::shared_ptr<Playlist> >::iterator p = all.begin(); p != all.end(); ++p) {
7576 if ((*x)->playlist ()) {
7577 pl.insert ((*x)->playlist ());
7581 for (set<boost::shared_ptr<Playlist> >::iterator i = pl.begin(); i != pl.end(); ++i) {
7583 (*i)->clear_changes ();
7584 (*i)->clear_owned_changes ();
7587 begin_reversible_command (_("insert time"));
7591 if (opt == SplitIntersected) {
7592 /* non musical split */
7593 (*i)->split (MusicFrame (pos, 0));
7596 (*i)->shift (pos, frames, (opt == MoveIntersected), ignore_music_glue);
7598 vector<Command*> cmds;
7600 _session->add_commands (cmds);
7602 _session->add_command (new StatefulDiffCommand (*i));
7606 RouteTimeAxisView* rtav = dynamic_cast<RouteTimeAxisView*> (*x);
7609 begin_reversible_command (_("insert time"));
7612 rtav->route ()->shift (pos, frames);
7619 const int32_t divisions = get_grid_music_divisions (0);
7620 XMLNode& before (_session->locations()->get_state());
7621 Locations::LocationList copy (_session->locations()->list());
7623 for (Locations::LocationList::iterator i = copy.begin(); i != copy.end(); ++i) {
7625 Locations::LocationList::const_iterator tmp;
7627 if ((*i)->position_lock_style() == AudioTime || glued_markers_too) {
7628 bool const was_locked = (*i)->locked ();
7629 if (locked_markers_too) {
7633 if ((*i)->start() >= pos) {
7634 // move end first, in case we're moving by more than the length of the range
7635 if (!(*i)->is_mark()) {
7636 (*i)->set_end ((*i)->end() + frames, false, true, divisions);
7638 (*i)->set_start ((*i)->start() + frames, false, true, divisions);
7650 begin_reversible_command (_("insert time"));
7653 XMLNode& after (_session->locations()->get_state());
7654 _session->add_command (new MementoCommand<Locations>(*_session->locations(), &before, &after));
7660 begin_reversible_command (_("insert time"));
7663 XMLNode& before (_session->tempo_map().get_state());
7664 _session->tempo_map().insert_time (pos, frames);
7665 XMLNode& after (_session->tempo_map().get_state());
7666 _session->add_command (new MementoCommand<TempoMap>(_session->tempo_map(), &before, &after));
7670 commit_reversible_command ();
7675 Editor::do_remove_time ()
7677 if (selection->tracks.empty()) {
7681 InsertRemoveTimeDialog d (*this, true);
7683 int response = d.run ();
7685 if (response != RESPONSE_OK) {
7689 framecnt_t distance = d.distance();
7691 if (distance == 0) {
7701 d.move_glued_markers(),
7702 d.move_locked_markers(),
7708 Editor::remove_time (framepos_t pos, framecnt_t frames, InsertTimeOption opt,
7709 bool ignore_music_glue, bool markers_too, bool glued_markers_too, bool locked_markers_too, bool tempo_too)
7711 if (Config->get_edit_mode() == Lock) {
7712 error << (_("Cannot insert or delete time when in Lock edit.")) << endmsg;
7715 bool in_command = false;
7717 for (TrackSelection::iterator x = selection->tracks.begin(); x != selection->tracks.end(); ++x) {
7719 boost::shared_ptr<Playlist> pl = (*x)->playlist();
7723 XMLNode &before = pl->get_state();
7726 begin_reversible_command (_("remove time"));
7730 std::list<AudioRange> rl;
7731 AudioRange ar(pos, pos+frames, 0);
7734 pl->shift (pos, -frames, true, ignore_music_glue);
7736 XMLNode &after = pl->get_state();
7738 _session->add_command (new MementoCommand<Playlist> (*pl, &before, &after));
7742 RouteTimeAxisView* rtav = dynamic_cast<RouteTimeAxisView*> (*x);
7745 begin_reversible_command (_("remove time"));
7748 rtav->route ()->shift (pos, -frames);
7752 const int32_t divisions = get_grid_music_divisions (0);
7753 std::list<Location*> loc_kill_list;
7758 XMLNode& before (_session->locations()->get_state());
7759 Locations::LocationList copy (_session->locations()->list());
7761 for (Locations::LocationList::iterator i = copy.begin(); i != copy.end(); ++i) {
7762 if ((*i)->position_lock_style() == AudioTime || glued_markers_too) {
7764 bool const was_locked = (*i)->locked ();
7765 if (locked_markers_too) {
7769 if (!(*i)->is_mark()) { // it's a range; have to handle both start and end
7770 if ((*i)->end() >= pos
7771 && (*i)->end() < pos+frames
7772 && (*i)->start() >= pos
7773 && (*i)->end() < pos+frames) { // range is completely enclosed; kill it
7775 loc_kill_list.push_back(*i);
7776 } else { // only start or end is included, try to do the right thing
7777 // move start before moving end, to avoid trying to move the end to before the start
7778 // if we're removing more time than the length of the range
7779 if ((*i)->start() >= pos && (*i)->start() < pos+frames) {
7780 // start is within cut
7781 (*i)->set_start (pos, false, true,divisions); // bring the start marker to the beginning of the cut
7783 } else if ((*i)->start() >= pos+frames) {
7784 // start (and thus entire range) lies beyond end of cut
7785 (*i)->set_start ((*i)->start() - frames, false, true, divisions); // slip the start marker back
7788 if ((*i)->end() >= pos && (*i)->end() < pos+frames) {
7789 // end is inside cut
7790 (*i)->set_end (pos, false, true, divisions); // bring the end to the cut
7792 } else if ((*i)->end() >= pos+frames) {
7793 // end is beyond end of cut
7794 (*i)->set_end ((*i)->end() - frames, false, true, divisions); // slip the end marker back
7799 } else if ((*i)->start() >= pos && (*i)->start() < pos+frames ) {
7800 loc_kill_list.push_back(*i);
7802 } else if ((*i)->start() >= pos) {
7803 (*i)->set_start ((*i)->start() -frames, false, true, divisions);
7813 for (list<Location*>::iterator i = loc_kill_list.begin(); i != loc_kill_list.end(); ++i) {
7814 _session->locations()->remove( *i );
7819 begin_reversible_command (_("remove time"));
7822 XMLNode& after (_session->locations()->get_state());
7823 _session->add_command (new MementoCommand<Locations>(*_session->locations(), &before, &after));
7828 XMLNode& before (_session->tempo_map().get_state());
7830 if (_session->tempo_map().remove_time (pos, frames) ) {
7832 begin_reversible_command (_("remove time"));
7835 XMLNode& after (_session->tempo_map().get_state());
7836 _session->add_command (new MementoCommand<TempoMap>(_session->tempo_map(), &before, &after));
7841 commit_reversible_command ();
7846 Editor::fit_selection ()
7848 if (!selection->tracks.empty()) {
7849 fit_tracks (selection->tracks);
7853 /* no selected tracks - use tracks with selected regions */
7855 if (!selection->regions.empty()) {
7856 for (RegionSelection::iterator r = selection->regions.begin(); r != selection->regions.end(); ++r) {
7857 tvl.push_back (&(*r)->get_time_axis_view ());
7863 } else if (internal_editing()) {
7864 /* no selected tracks, or regions, but in internal edit mode, so follow the mouse and use
7867 if (entered_track) {
7868 tvl.push_back (entered_track);
7876 Editor::fit_tracks (TrackViewList & tracks)
7878 if (tracks.empty()) {
7882 uint32_t child_heights = 0;
7883 int visible_tracks = 0;
7885 for (TrackSelection::iterator t = tracks.begin(); t != tracks.end(); ++t) {
7887 if (!(*t)->marked_for_display()) {
7891 child_heights += (*t)->effective_height() - (*t)->current_height();
7895 /* compute the per-track height from:
7897 * total canvas visible height
7898 * - height that will be taken by visible children of selected tracks
7899 * - height of the ruler/hscroll area
7901 uint32_t h = (uint32_t) floor ((trackviews_height() - child_heights) / visible_tracks);
7902 double first_y_pos = DBL_MAX;
7904 if (h < TimeAxisView::preset_height (HeightSmall)) {
7905 MessageDialog msg (_("There are too many tracks to fit in the current window"));
7906 /* too small to be displayed */
7910 undo_visual_stack.push_back (current_visual_state (true));
7911 PBD::Unwinder<bool> nsv (no_save_visual, true);
7913 /* build a list of all tracks, including children */
7916 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
7918 TimeAxisView::Children c = (*i)->get_child_list ();
7919 for (TimeAxisView::Children::iterator j = c.begin(); j != c.end(); ++j) {
7920 all.push_back (j->get());
7925 // find selection range.
7926 // if someone knows how to user TrackViewList::iterator for this
7928 int selected_top = -1;
7929 int selected_bottom = -1;
7931 for (TrackViewList::iterator t = all.begin(); t != all.end(); ++t, ++i) {
7932 if ((*t)->marked_for_display ()) {
7933 if (tracks.contains(*t)) {
7934 if (selected_top == -1) {
7937 selected_bottom = i;
7943 for (TrackViewList::iterator t = all.begin(); t != all.end(); ++t, ++i) {
7944 if ((*t)->marked_for_display ()) {
7945 if (tracks.contains(*t)) {
7946 (*t)->set_height (h);
7947 first_y_pos = std::min ((*t)->y_position (), first_y_pos);
7949 if (i > selected_top && i < selected_bottom) {
7950 hide_track_in_display (*t);
7957 set the controls_layout height now, because waiting for its size
7958 request signal handler will cause the vertical adjustment setting to fail
7961 controls_layout.property_height () = _full_canvas_height;
7962 vertical_adjustment.set_value (first_y_pos);
7964 redo_visual_stack.push_back (current_visual_state (true));
7966 visible_tracks_selector.set_text (_("Sel"));
7970 Editor::save_visual_state (uint32_t n)
7972 while (visual_states.size() <= n) {
7973 visual_states.push_back (0);
7976 if (visual_states[n] != 0) {
7977 delete visual_states[n];
7980 visual_states[n] = current_visual_state (true);
7985 Editor::goto_visual_state (uint32_t n)
7987 if (visual_states.size() <= n) {
7991 if (visual_states[n] == 0) {
7995 use_visual_state (*visual_states[n]);
7999 Editor::start_visual_state_op (uint32_t n)
8001 save_visual_state (n);
8003 PopUp* pup = new PopUp (WIN_POS_MOUSE, 1000, true);
8005 snprintf (buf, sizeof (buf), _("Saved view %u"), n+1);
8006 pup->set_text (buf);
8011 Editor::cancel_visual_state_op (uint32_t n)
8013 goto_visual_state (n);
8017 Editor::toggle_region_mute ()
8019 if (_ignore_region_action) {
8023 RegionSelection rs = get_regions_from_selection_and_entered ();
8029 if (rs.size() > 1) {
8030 begin_reversible_command (_("mute regions"));
8032 begin_reversible_command (_("mute region"));
8035 for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
8037 (*i)->region()->playlist()->clear_changes ();
8038 (*i)->region()->set_muted (!(*i)->region()->muted ());
8039 _session->add_command (new StatefulDiffCommand ((*i)->region()));
8043 commit_reversible_command ();
8047 Editor::combine_regions ()
8049 /* foreach track with selected regions, take all selected regions
8050 and join them into a new region containing the subregions (as a
8054 typedef set<RouteTimeAxisView*> RTVS;
8057 if (selection->regions.empty()) {
8061 for (RegionSelection::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
8062 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*>(&(*i)->get_time_axis_view());
8065 tracks.insert (rtv);
8069 begin_reversible_command (_("combine regions"));
8071 vector<RegionView*> new_selection;
8073 for (RTVS::iterator i = tracks.begin(); i != tracks.end(); ++i) {
8076 if ((rv = (*i)->combine_regions ()) != 0) {
8077 new_selection.push_back (rv);
8081 selection->clear_regions ();
8082 for (vector<RegionView*>::iterator i = new_selection.begin(); i != new_selection.end(); ++i) {
8083 selection->add (*i);
8086 commit_reversible_command ();
8090 Editor::uncombine_regions ()
8092 typedef set<RouteTimeAxisView*> RTVS;
8095 if (selection->regions.empty()) {
8099 for (RegionSelection::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
8100 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*>(&(*i)->get_time_axis_view());
8103 tracks.insert (rtv);
8107 begin_reversible_command (_("uncombine regions"));
8109 for (RTVS::iterator i = tracks.begin(); i != tracks.end(); ++i) {
8110 (*i)->uncombine_regions ();
8113 commit_reversible_command ();
8117 Editor::toggle_midi_input_active (bool flip_others)
8120 boost::shared_ptr<RouteList> rl (new RouteList);
8122 for (TrackSelection::iterator i = selection->tracks.begin(); i != selection->tracks.end(); ++i) {
8123 RouteTimeAxisView *rtav = dynamic_cast<RouteTimeAxisView *>(*i);
8129 boost::shared_ptr<MidiTrack> mt = rtav->midi_track();
8132 rl->push_back (rtav->route());
8133 onoff = !mt->input_active();
8137 _session->set_exclusive_input_active (rl, onoff, flip_others);
8140 static bool ok_fine (GdkEventAny*) { return true; }
8146 lock_dialog = new Gtk::Dialog (string_compose (_("%1: Locked"), PROGRAM_NAME), true);
8148 Gtk::Image* padlock = manage (new Gtk::Image (ARDOUR_UI_UTILS::get_icon ("padlock_closed")));
8149 lock_dialog->get_vbox()->pack_start (*padlock);
8150 lock_dialog->signal_delete_event ().connect (sigc::ptr_fun (ok_fine));
8152 ArdourButton* b = manage (new ArdourButton);
8153 b->set_name ("lock button");
8154 b->set_text (_("Click to unlock"));
8155 b->signal_clicked.connect (sigc::mem_fun (*this, &Editor::unlock));
8156 lock_dialog->get_vbox()->pack_start (*b);
8158 lock_dialog->get_vbox()->show_all ();
8159 lock_dialog->set_size_request (200, 200);
8162 delete _main_menu_disabler;
8163 _main_menu_disabler = new MainMenuDisabler;
8165 lock_dialog->present ();
8167 lock_dialog->get_window()->set_decorations (Gdk::WMDecoration (0));
8173 lock_dialog->hide ();
8175 delete _main_menu_disabler;
8176 _main_menu_disabler = 0;
8178 if (UIConfiguration::instance().get_lock_gui_after_seconds()) {
8179 start_lock_event_timing ();
8184 Editor::bring_in_callback (Gtk::Label* label, uint32_t n, uint32_t total, string name)
8186 Gtkmm2ext::UI::instance()->call_slot (invalidator (*this), boost::bind (&Editor::update_bring_in_message, this, label, n, total, name));
8190 Editor::update_bring_in_message (Gtk::Label* label, uint32_t n, uint32_t total, string name)
8192 Timers::TimerSuspender t;
8193 label->set_text (string_compose ("Copying %1, %2 of %3", name, n, total));
8194 Gtkmm2ext::UI::instance()->flush_pending (1);
8198 Editor::bring_all_sources_into_session ()
8205 ArdourDialog w (_("Moving embedded files into session folder"));
8206 w.get_vbox()->pack_start (msg);
8209 /* flush all pending GUI events because we're about to start copying
8213 Timers::TimerSuspender t;
8214 Gtkmm2ext::UI::instance()->flush_pending (3);
8218 _session->bring_all_sources_into_session (boost::bind (&Editor::bring_in_callback, this, &msg, _1, _2, _3));