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 bool maybe_first_frame = false;
726 switch (_snap_type) {
727 case SnapToRegionStart:
728 interesting_points.push_back (Start);
729 maybe_first_frame = true;
731 case SnapToRegionEnd:
732 interesting_points.push_back (End);
734 case SnapToRegionSync:
735 interesting_points.push_back (SyncPoint);
737 case SnapToRegionBoundary:
738 interesting_points.push_back (Start);
739 interesting_points.push_back (End);
740 maybe_first_frame = true;
743 fatal << string_compose (_("build_region_boundary_cache called with snap_type = %1"), _snap_type) << endmsg;
744 abort(); /*NOTREACHED*/
748 TimeAxisView *ontrack = 0;
751 if (!selection->tracks.empty()) {
752 tlist = selection->tracks.filter_to_unique_playlists ();
754 tlist = track_views.filter_to_unique_playlists ();
757 if (maybe_first_frame) {
758 TrackViewList::const_iterator i;
759 for (i = tlist.begin(); i != tlist.end(); ++i) {
760 boost::shared_ptr<Playlist> pl = (*i)->playlist();
761 if (pl && pl->count_regions_at (0)) {
762 region_boundary_cache.push_back (0);
768 while (pos < _session->current_end_frame() && !at_end) {
771 framepos_t lpos = max_framepos;
773 for (vector<RegionPoint>::iterator p = interesting_points.begin(); p != interesting_points.end(); ++p) {
775 if ((r = find_next_region (pos, *p, 1, tlist, &ontrack)) == 0) {
776 if (*p == interesting_points.back()) {
779 /* move to next point type */
785 rpos = r->first_frame();
789 rpos = r->last_frame();
793 rpos = r->sync_position ();
801 RouteTimeAxisView *rtav;
803 if (ontrack != 0 && (rtav = dynamic_cast<RouteTimeAxisView*>(ontrack)) != 0 ) {
804 if (rtav->track() != 0) {
805 speed = rtav->track()->speed();
809 rpos = track_frame_to_session_frame (rpos, speed);
815 /* prevent duplicates, but we don't use set<> because we want to be able
819 vector<framepos_t>::iterator ri;
821 for (ri = region_boundary_cache.begin(); ri != region_boundary_cache.end(); ++ri) {
827 if (ri == region_boundary_cache.end()) {
828 region_boundary_cache.push_back (rpos);
835 /* finally sort to be sure that the order is correct */
837 sort (region_boundary_cache.begin(), region_boundary_cache.end());
840 boost::shared_ptr<Region>
841 Editor::find_next_region (framepos_t frame, RegionPoint point, int32_t dir, TrackViewList& tracks, TimeAxisView **ontrack)
843 TrackViewList::iterator i;
844 framepos_t closest = max_framepos;
845 boost::shared_ptr<Region> ret;
849 framepos_t track_frame;
850 RouteTimeAxisView *rtav;
852 for (i = tracks.begin(); i != tracks.end(); ++i) {
855 boost::shared_ptr<Region> r;
858 if ( (rtav = dynamic_cast<RouteTimeAxisView*>(*i)) != 0 ) {
859 if (rtav->track()!=0)
860 track_speed = rtav->track()->speed();
863 track_frame = session_frame_to_track_frame(frame, track_speed);
865 if ((r = (*i)->find_next_region (track_frame, point, dir)) == 0) {
871 rpos = r->first_frame ();
875 rpos = r->last_frame ();
879 rpos = r->sync_position ();
883 // rpos is a "track frame", converting it to "_session frame"
884 rpos = track_frame_to_session_frame(rpos, track_speed);
887 distance = rpos - frame;
889 distance = frame - rpos;
892 if (distance < closest) {
904 Editor::find_next_region_boundary (framepos_t pos, int32_t dir, const TrackViewList& tracks)
906 framecnt_t distance = max_framepos;
907 framepos_t current_nearest = -1;
909 for (TrackViewList::const_iterator i = tracks.begin(); i != tracks.end(); ++i) {
910 framepos_t contender;
913 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (*i);
919 if ((contender = rtv->find_next_region_boundary (pos, dir)) < 0) {
923 d = ::llabs (pos - contender);
926 current_nearest = contender;
931 return current_nearest;
935 Editor::get_region_boundary (framepos_t pos, int32_t dir, bool with_selection, bool only_onscreen)
940 if (with_selection && Config->get_region_boundaries_from_selected_tracks()) {
942 if (!selection->tracks.empty()) {
944 target = find_next_region_boundary (pos, dir, selection->tracks);
948 if (only_onscreen || Config->get_region_boundaries_from_onscreen_tracks()) {
949 get_onscreen_tracks (tvl);
950 target = find_next_region_boundary (pos, dir, tvl);
952 target = find_next_region_boundary (pos, dir, track_views);
958 if (only_onscreen || Config->get_region_boundaries_from_onscreen_tracks()) {
959 get_onscreen_tracks (tvl);
960 target = find_next_region_boundary (pos, dir, tvl);
962 target = find_next_region_boundary (pos, dir, track_views);
970 Editor::cursor_to_region_boundary (bool with_selection, int32_t dir)
972 framepos_t pos = playhead_cursor->current_frame ();
979 // so we don't find the current region again..
980 if (dir > 0 || pos > 0) {
984 if ((target = get_region_boundary (pos, dir, with_selection, false)) < 0) {
988 _session->request_locate (target);
992 Editor::cursor_to_next_region_boundary (bool with_selection)
994 cursor_to_region_boundary (with_selection, 1);
998 Editor::cursor_to_previous_region_boundary (bool with_selection)
1000 cursor_to_region_boundary (with_selection, -1);
1004 Editor::cursor_to_region_point (EditorCursor* cursor, RegionPoint point, int32_t dir)
1006 boost::shared_ptr<Region> r;
1007 framepos_t pos = cursor->current_frame ();
1013 TimeAxisView *ontrack = 0;
1015 // so we don't find the current region again..
1019 if (!selection->tracks.empty()) {
1021 r = find_next_region (pos, point, dir, selection->tracks, &ontrack);
1023 } else if (clicked_axisview) {
1026 t.push_back (clicked_axisview);
1028 r = find_next_region (pos, point, dir, t, &ontrack);
1032 r = find_next_region (pos, point, dir, track_views, &ontrack);
1041 pos = r->first_frame ();
1045 pos = r->last_frame ();
1049 pos = r->sync_position ();
1054 RouteTimeAxisView *rtav;
1056 if ( ontrack != 0 && (rtav = dynamic_cast<RouteTimeAxisView*>(ontrack)) != 0 ) {
1057 if (rtav->track() != 0) {
1058 speed = rtav->track()->speed();
1062 pos = track_frame_to_session_frame(pos, speed);
1064 if (cursor == playhead_cursor) {
1065 _session->request_locate (pos);
1067 cursor->set_position (pos);
1072 Editor::cursor_to_next_region_point (EditorCursor* cursor, RegionPoint point)
1074 cursor_to_region_point (cursor, point, 1);
1078 Editor::cursor_to_previous_region_point (EditorCursor* cursor, RegionPoint point)
1080 cursor_to_region_point (cursor, point, -1);
1084 Editor::cursor_to_selection_start (EditorCursor *cursor)
1088 switch (mouse_mode) {
1090 if (!selection->regions.empty()) {
1091 pos = selection->regions.start();
1096 if (!selection->time.empty()) {
1097 pos = selection->time.start ();
1105 if (cursor == playhead_cursor) {
1106 _session->request_locate (pos);
1108 cursor->set_position (pos);
1113 Editor::cursor_to_selection_end (EditorCursor *cursor)
1117 switch (mouse_mode) {
1119 if (!selection->regions.empty()) {
1120 pos = selection->regions.end_frame();
1125 if (!selection->time.empty()) {
1126 pos = selection->time.end_frame ();
1134 if (cursor == playhead_cursor) {
1135 _session->request_locate (pos);
1137 cursor->set_position (pos);
1142 Editor::selected_marker_to_region_boundary (bool with_selection, int32_t dir)
1152 if (selection->markers.empty()) {
1156 if (!mouse_frame (mouse, ignored)) {
1160 add_location_mark (mouse);
1163 if ((loc = find_location_from_marker (selection->markers.front(), ignored)) == 0) {
1167 framepos_t pos = loc->start();
1169 // so we don't find the current region again..
1170 if (dir > 0 || pos > 0) {
1174 if ((target = get_region_boundary (pos, dir, with_selection, false)) < 0) {
1178 loc->move_to (target, 0);
1182 Editor::selected_marker_to_next_region_boundary (bool with_selection)
1184 selected_marker_to_region_boundary (with_selection, 1);
1188 Editor::selected_marker_to_previous_region_boundary (bool with_selection)
1190 selected_marker_to_region_boundary (with_selection, -1);
1194 Editor::selected_marker_to_region_point (RegionPoint point, int32_t dir)
1196 boost::shared_ptr<Region> r;
1201 if (!_session || selection->markers.empty()) {
1205 if ((loc = find_location_from_marker (selection->markers.front(), ignored)) == 0) {
1209 TimeAxisView *ontrack = 0;
1213 // so we don't find the current region again..
1217 if (!selection->tracks.empty()) {
1219 r = find_next_region (pos, point, dir, selection->tracks, &ontrack);
1223 r = find_next_region (pos, point, dir, track_views, &ontrack);
1232 pos = r->first_frame ();
1236 pos = r->last_frame ();
1240 pos = r->adjust_to_sync (r->first_frame());
1245 RouteTimeAxisView *rtav;
1247 if (ontrack != 0 && (rtav = dynamic_cast<RouteTimeAxisView*>(ontrack)) != 0) {
1248 if (rtav->track() != 0) {
1249 speed = rtav->track()->speed();
1253 pos = track_frame_to_session_frame(pos, speed);
1255 loc->move_to (pos, 0);
1259 Editor::selected_marker_to_next_region_point (RegionPoint point)
1261 selected_marker_to_region_point (point, 1);
1265 Editor::selected_marker_to_previous_region_point (RegionPoint point)
1267 selected_marker_to_region_point (point, -1);
1271 Editor::selected_marker_to_selection_start ()
1277 if (!_session || selection->markers.empty()) {
1281 if ((loc = find_location_from_marker (selection->markers.front(), ignored)) == 0) {
1285 switch (mouse_mode) {
1287 if (!selection->regions.empty()) {
1288 pos = selection->regions.start();
1293 if (!selection->time.empty()) {
1294 pos = selection->time.start ();
1302 loc->move_to (pos, 0);
1306 Editor::selected_marker_to_selection_end ()
1312 if (!_session || selection->markers.empty()) {
1316 if ((loc = find_location_from_marker (selection->markers.front(), ignored)) == 0) {
1320 switch (mouse_mode) {
1322 if (!selection->regions.empty()) {
1323 pos = selection->regions.end_frame();
1328 if (!selection->time.empty()) {
1329 pos = selection->time.end_frame ();
1337 loc->move_to (pos, 0);
1341 Editor::scroll_playhead (bool forward)
1343 framepos_t pos = playhead_cursor->current_frame ();
1344 framecnt_t delta = (framecnt_t) floor (current_page_samples() / 0.8);
1347 if (pos == max_framepos) {
1351 if (pos < max_framepos - delta) {
1370 _session->request_locate (pos);
1374 Editor::cursor_align (bool playhead_to_edit)
1380 if (playhead_to_edit) {
1382 if (selection->markers.empty()) {
1386 _session->request_locate (selection->markers.front()->position(), _session->transport_rolling());
1389 const int32_t divisions = get_grid_music_divisions (0);
1390 /* move selected markers to playhead */
1392 for (MarkerSelection::iterator i = selection->markers.begin(); i != selection->markers.end(); ++i) {
1395 Location* loc = find_location_from_marker (*i, ignored);
1397 if (loc->is_mark()) {
1398 loc->set_start (playhead_cursor->current_frame (), false, true, divisions);
1400 loc->set (playhead_cursor->current_frame (),
1401 playhead_cursor->current_frame () + loc->length(), true, divisions);
1408 Editor::scroll_backward (float pages)
1410 framepos_t const one_page = (framepos_t) rint (_visible_canvas_width * samples_per_pixel);
1411 framepos_t const cnt = (framepos_t) floor (pages * one_page);
1414 if (leftmost_frame < cnt) {
1417 frame = leftmost_frame - cnt;
1420 reset_x_origin (frame);
1424 Editor::scroll_forward (float pages)
1426 framepos_t const one_page = (framepos_t) rint (_visible_canvas_width * samples_per_pixel);
1427 framepos_t const cnt = (framepos_t) floor (pages * one_page);
1430 if (max_framepos - cnt < leftmost_frame) {
1431 frame = max_framepos - cnt;
1433 frame = leftmost_frame + cnt;
1436 reset_x_origin (frame);
1440 Editor::scroll_tracks_down ()
1442 double vert_value = vertical_adjustment.get_value() + vertical_adjustment.get_page_size();
1443 if (vert_value > vertical_adjustment.get_upper() - _visible_canvas_height) {
1444 vert_value = vertical_adjustment.get_upper() - _visible_canvas_height;
1447 vertical_adjustment.set_value (vert_value);
1451 Editor::scroll_tracks_up ()
1453 vertical_adjustment.set_value (vertical_adjustment.get_value() - vertical_adjustment.get_page_size());
1457 Editor::scroll_tracks_down_line ()
1459 double vert_value = vertical_adjustment.get_value() + 60;
1461 if (vert_value > vertical_adjustment.get_upper() - _visible_canvas_height) {
1462 vert_value = vertical_adjustment.get_upper() - _visible_canvas_height;
1465 vertical_adjustment.set_value (vert_value);
1469 Editor::scroll_tracks_up_line ()
1471 reset_y_origin (vertical_adjustment.get_value() - 60);
1475 Editor::select_topmost_track ()
1477 const double top_of_trackviews = vertical_adjustment.get_value();
1478 for (TrackViewList::iterator t = track_views.begin(); t != track_views.end(); ++t) {
1479 if ((*t)->hidden()) {
1482 std::pair<TimeAxisView*,double> res = (*t)->covers_y_position (top_of_trackviews);
1484 selection->set (*t);
1491 Editor::scroll_down_one_track (bool skip_child_views)
1493 TrackViewList::reverse_iterator next = track_views.rend();
1494 const double top_of_trackviews = vertical_adjustment.get_value();
1496 for (TrackViewList::reverse_iterator t = track_views.rbegin(); t != track_views.rend(); ++t) {
1497 if ((*t)->hidden()) {
1501 /* If this is the upper-most visible trackview, we want to display
1502 * the one above it (next)
1504 * Note that covers_y_position() is recursive and includes child views
1506 std::pair<TimeAxisView*,double> res = (*t)->covers_y_position (top_of_trackviews);
1509 if (skip_child_views) {
1512 /* automation lane (one level, non-recursive)
1514 * - if no automation lane exists -> move to next tack
1515 * - if the first (here: bottom-most) matches -> move to next tack
1516 * - if no y-axis match is found -> the current track is at the top
1517 * -> move to last (here: top-most) automation lane
1519 TimeAxisView::Children kids = (*t)->get_child_list();
1520 TimeAxisView::Children::reverse_iterator nkid = kids.rend();
1522 for (TimeAxisView::Children::reverse_iterator ci = kids.rbegin(); ci != kids.rend(); ++ci) {
1523 if ((*ci)->hidden()) {
1527 std::pair<TimeAxisView*,double> dev;
1528 dev = (*ci)->covers_y_position (top_of_trackviews);
1530 /* some automation lane is currently at the top */
1531 if (ci == kids.rbegin()) {
1532 /* first (bottom-most) autmation lane is at the top.
1533 * -> move to next track
1542 if (nkid != kids.rend()) {
1543 ensure_time_axis_view_is_visible (**nkid, true);
1551 /* move to the track below the first one that covers the */
1553 if (next != track_views.rend()) {
1554 ensure_time_axis_view_is_visible (**next, true);
1562 Editor::scroll_up_one_track (bool skip_child_views)
1564 TrackViewList::iterator prev = track_views.end();
1565 double top_of_trackviews = vertical_adjustment.get_value ();
1567 for (TrackViewList::iterator t = track_views.begin(); t != track_views.end(); ++t) {
1569 if ((*t)->hidden()) {
1573 /* find the trackview at the top of the trackview group
1575 * Note that covers_y_position() is recursive and includes child views
1577 std::pair<TimeAxisView*,double> res = (*t)->covers_y_position (top_of_trackviews);
1580 if (skip_child_views) {
1583 /* automation lane (one level, non-recursive)
1585 * - if no automation lane exists -> move to prev tack
1586 * - if no y-axis match is found -> the current track is at the top -> move to prev track
1587 * (actually last automation lane of previous track, see below)
1588 * - if first (top-most) lane is at the top -> move to this track
1589 * - else move up one lane
1591 TimeAxisView::Children kids = (*t)->get_child_list();
1592 TimeAxisView::Children::iterator pkid = kids.end();
1594 for (TimeAxisView::Children::iterator ci = kids.begin(); ci != kids.end(); ++ci) {
1595 if ((*ci)->hidden()) {
1599 std::pair<TimeAxisView*,double> dev;
1600 dev = (*ci)->covers_y_position (top_of_trackviews);
1602 /* some automation lane is currently at the top */
1603 if (ci == kids.begin()) {
1604 /* first (top-most) autmation lane is at the top.
1605 * jump directly to this track's top
1607 ensure_time_axis_view_is_visible (**t, true);
1610 else if (pkid != kids.end()) {
1611 /* some other automation lane is at the top.
1612 * move up to prev automation lane.
1614 ensure_time_axis_view_is_visible (**pkid, true);
1617 assert(0); // not reached
1628 if (prev != track_views.end()) {
1629 // move to bottom-most automation-lane of the previous track
1630 TimeAxisView::Children kids = (*prev)->get_child_list();
1631 TimeAxisView::Children::reverse_iterator pkid = kids.rend();
1632 if (!skip_child_views) {
1633 // find the last visible lane
1634 for (TimeAxisView::Children::reverse_iterator ci = kids.rbegin(); ci != kids.rend(); ++ci) {
1635 if (!(*ci)->hidden()) {
1641 if (pkid != kids.rend()) {
1642 ensure_time_axis_view_is_visible (**pkid, true);
1644 ensure_time_axis_view_is_visible (**prev, true);
1653 Editor::scroll_left_step ()
1655 framepos_t xdelta = (current_page_samples() / 8);
1657 if (leftmost_frame > xdelta) {
1658 reset_x_origin (leftmost_frame - xdelta);
1666 Editor::scroll_right_step ()
1668 framepos_t xdelta = (current_page_samples() / 8);
1670 if (max_framepos - xdelta > leftmost_frame) {
1671 reset_x_origin (leftmost_frame + xdelta);
1673 reset_x_origin (max_framepos - current_page_samples());
1678 Editor::scroll_left_half_page ()
1680 framepos_t xdelta = (current_page_samples() / 2);
1681 if (leftmost_frame > xdelta) {
1682 reset_x_origin (leftmost_frame - xdelta);
1689 Editor::scroll_right_half_page ()
1691 framepos_t xdelta = (current_page_samples() / 2);
1692 if (max_framepos - xdelta > leftmost_frame) {
1693 reset_x_origin (leftmost_frame + xdelta);
1695 reset_x_origin (max_framepos - current_page_samples());
1702 Editor::tav_zoom_step (bool coarser)
1704 DisplaySuspender ds;
1708 if (selection->tracks.empty()) {
1711 ts = &selection->tracks;
1714 for (TrackViewList::iterator i = ts->begin(); i != ts->end(); ++i) {
1715 TimeAxisView *tv = (static_cast<TimeAxisView*>(*i));
1716 tv->step_height (coarser);
1721 Editor::tav_zoom_smooth (bool coarser, bool force_all)
1723 DisplaySuspender ds;
1727 if (selection->tracks.empty() || force_all) {
1730 ts = &selection->tracks;
1733 for (TrackViewList::iterator i = ts->begin(); i != ts->end(); ++i) {
1734 TimeAxisView *tv = (static_cast<TimeAxisView*>(*i));
1735 uint32_t h = tv->current_height ();
1740 if (h >= TimeAxisView::preset_height (HeightSmall)) {
1745 tv->set_height (h + 5);
1751 Editor::temporal_zoom_step_mouse_focus_scale (bool zoom_out, double scale)
1753 Editing::ZoomFocus temp_focus = zoom_focus;
1754 zoom_focus = Editing::ZoomFocusMouse;
1755 temporal_zoom_step_scale (zoom_out, scale);
1756 zoom_focus = temp_focus;
1760 Editor::temporal_zoom_step_mouse_focus (bool zoom_out)
1762 temporal_zoom_step_mouse_focus_scale (zoom_out, 2.0);
1766 Editor::temporal_zoom_step (bool zoom_out)
1768 temporal_zoom_step_scale (zoom_out, 2.0);
1772 Editor::temporal_zoom_step_scale (bool zoom_out, double scale)
1774 ENSURE_GUI_THREAD (*this, &Editor::temporal_zoom_step, zoom_out, scale)
1776 framecnt_t nspp = samples_per_pixel;
1780 if (nspp == samples_per_pixel) {
1785 if (nspp == samples_per_pixel) {
1790 // ToDo: encapsulate all of this into something like editor::get_session_extents() or editor::leftmost(), rightmost()
1792 //ToDo: also incorporate automation regions (in case the session has no audio/midi but is just used for automating plugins or the like)
1794 //calculate the extents of all regions in every playlist
1795 framecnt_t session_extent_start = 0;
1796 framecnt_t session_extent_end = 0;
1798 boost::shared_ptr<RouteList> rl = _session->get_routes();
1799 for (RouteList::iterator r = rl->begin(); r != rl->end(); ++r) {
1800 boost::shared_ptr<Track> tr = boost::dynamic_pointer_cast<Track> (*r);
1802 boost::shared_ptr<Playlist> pl = tr->playlist();
1804 pair<framepos_t, framepos_t> e;
1805 e = pl->get_extent();
1806 if (e.first < session_extent_start) {
1807 session_extent_start = e.first;
1809 if (e.second > session_extent_end) {
1810 session_extent_end = e.second;
1816 framecnt_t session_extents = session_extent_end - session_extent_start;
1818 //in a session with no regions, use the start/end markers to set max zoom
1819 framecnt_t const session_length = _session->current_end_frame() - _session->current_start_frame ();
1820 if ( session_length > session_extents )
1821 session_extents = session_length;
1823 //in a session with no regions or start/end markers, use 2 minutes to set max zoom
1824 framecnt_t const min_length = _session->nominal_frame_rate()*60*2;
1825 if ( min_length > session_extents )
1826 session_extents = min_length;
1828 //convert to samples-per-pixel and limit our zoom to this value
1829 framecnt_t session_extents_pp = session_extents / _visible_canvas_width;
1830 if (nspp > session_extents_pp)
1831 nspp = session_extents_pp;
1834 temporal_zoom (nspp);
1838 Editor::temporal_zoom (framecnt_t fpp)
1844 framepos_t current_page = current_page_samples();
1845 framepos_t current_leftmost = leftmost_frame;
1846 framepos_t current_rightmost;
1847 framepos_t current_center;
1848 framepos_t new_page_size;
1849 framepos_t half_page_size;
1850 framepos_t leftmost_after_zoom = 0;
1852 bool in_track_canvas;
1853 bool use_mouse_frame = true;
1857 if (fpp == samples_per_pixel) {
1861 // Imposing an arbitrary limit to zoom out as too much zoom out produces
1862 // segfaults for lack of memory. If somebody decides this is not high enough I
1863 // believe it can be raisen to higher values but some limit must be in place.
1865 // This constant represents 1 day @ 48kHz on a 1600 pixel wide display
1866 // all of which is used for the editor track displays. The whole day
1867 // would be 4147200000 samples, so 2592000 samples per pixel.
1869 nfpp = min (fpp, (framecnt_t) 2592000);
1870 nfpp = max ((framecnt_t) 1, nfpp);
1872 new_page_size = (framepos_t) floor (_visible_canvas_width * nfpp);
1873 half_page_size = new_page_size / 2;
1875 switch (zoom_focus) {
1877 leftmost_after_zoom = current_leftmost;
1880 case ZoomFocusRight:
1881 current_rightmost = leftmost_frame + current_page;
1882 if (current_rightmost < new_page_size) {
1883 leftmost_after_zoom = 0;
1885 leftmost_after_zoom = current_rightmost - new_page_size;
1889 case ZoomFocusCenter:
1890 current_center = current_leftmost + (current_page/2);
1891 if (current_center < half_page_size) {
1892 leftmost_after_zoom = 0;
1894 leftmost_after_zoom = current_center - half_page_size;
1898 case ZoomFocusPlayhead:
1899 /* centre playhead */
1900 l = playhead_cursor->current_frame () - (new_page_size * 0.5);
1903 leftmost_after_zoom = 0;
1904 } else if (l > max_framepos) {
1905 leftmost_after_zoom = max_framepos - new_page_size;
1907 leftmost_after_zoom = (framepos_t) l;
1911 case ZoomFocusMouse:
1912 /* try to keep the mouse over the same point in the display */
1914 if (_drags->active()) {
1915 where = _drags->current_pointer_frame ();
1916 } else if (!mouse_frame (where, in_track_canvas)) {
1917 use_mouse_frame = false;
1920 if (use_mouse_frame) {
1921 l = - ((new_page_size * ((where - current_leftmost)/(double)current_page)) - where);
1924 leftmost_after_zoom = 0;
1925 } else if (l > max_framepos) {
1926 leftmost_after_zoom = max_framepos - new_page_size;
1928 leftmost_after_zoom = (framepos_t) l;
1931 /* use playhead instead */
1932 where = playhead_cursor->current_frame ();
1934 if (where < half_page_size) {
1935 leftmost_after_zoom = 0;
1937 leftmost_after_zoom = where - half_page_size;
1943 /* try to keep the edit point in the same place */
1944 where = get_preferred_edit_position ();
1948 double l = - ((new_page_size * ((where - current_leftmost)/(double)current_page)) - where);
1951 leftmost_after_zoom = 0;
1952 } else if (l > max_framepos) {
1953 leftmost_after_zoom = max_framepos - new_page_size;
1955 leftmost_after_zoom = (framepos_t) l;
1959 /* edit point not defined */
1966 // leftmost_after_zoom = min (leftmost_after_zoom, _session->current_end_frame());
1968 reposition_and_zoom (leftmost_after_zoom, nfpp);
1972 Editor::calc_extra_zoom_edges(framepos_t &start, framepos_t &end)
1974 /* this func helps make sure we leave a little space
1975 at each end of the editor so that the zoom doesn't fit the region
1976 precisely to the screen.
1979 GdkScreen* screen = gdk_screen_get_default ();
1980 const gint pixwidth = gdk_screen_get_width (screen);
1981 const gint mmwidth = gdk_screen_get_width_mm (screen);
1982 const double pix_per_mm = (double) pixwidth/ (double) mmwidth;
1983 const double one_centimeter_in_pixels = pix_per_mm * 10.0;
1985 const framepos_t range = end - start;
1986 const framecnt_t new_fpp = (framecnt_t) ceil ((double) range / (double) _visible_canvas_width);
1987 const framepos_t extra_samples = (framepos_t) floor (one_centimeter_in_pixels * new_fpp);
1989 if (start > extra_samples) {
1990 start -= extra_samples;
1995 if (max_framepos - extra_samples > end) {
1996 end += extra_samples;
2003 Editor::get_selection_extents (framepos_t &start, framepos_t &end) const
2005 start = max_framepos;
2009 //ToDo: if notes are selected, set extents to that selection
2011 //ToDo: if control points are selected, set extents to that selection
2013 if ( !selection->regions.empty() ) {
2014 RegionSelection rs = get_regions_from_selection_and_entered ();
2016 for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
2018 if ((*i)->region()->position() < start) {
2019 start = (*i)->region()->position();
2022 if ((*i)->region()->last_frame() + 1 > end) {
2023 end = (*i)->region()->last_frame() + 1;
2027 } else if (!selection->time.empty()) {
2028 start = selection->time.start();
2029 end = selection->time.end_frame();
2031 ret = false; //no selection found
2034 if ((start == 0 && end == 0) || end < start) {
2043 Editor::temporal_zoom_selection (Editing::ZoomAxis axes)
2045 if (!selection) return;
2047 //ToDo: if notes are selected, zoom to that
2049 //ToDo: if control points are selected, zoom to that
2051 if (axes == Horizontal || axes == Both) {
2053 framepos_t start, end;
2054 if (get_selection_extents (start, end)) {
2055 calc_extra_zoom_edges (start, end);
2056 temporal_zoom_by_frame (start, end);
2060 if (axes == Vertical || axes == Both) {
2066 Editor::temporal_zoom_session ()
2068 ENSURE_GUI_THREAD (*this, &Editor::temporal_zoom_session)
2071 framecnt_t start = _session->current_start_frame();
2072 framecnt_t end = _session->current_end_frame();
2074 if (_session->actively_recording () ) {
2075 framepos_t cur = playhead_cursor->current_frame ();
2077 /* recording beyond the end marker; zoom out
2078 * by 5 seconds more so that if 'follow
2079 * playhead' is active we don't immediately
2082 end = cur + _session->frame_rate() * 5;
2086 if ((start == 0 && end == 0) || end < start) {
2090 calc_extra_zoom_edges(start, end);
2092 temporal_zoom_by_frame (start, end);
2097 Editor::temporal_zoom_by_frame (framepos_t start, framepos_t end)
2099 if (!_session) return;
2101 if ((start == 0 && end == 0) || end < start) {
2105 framepos_t range = end - start;
2107 const framecnt_t new_fpp = (framecnt_t) ceil ((double) range / (double) _visible_canvas_width);
2109 framepos_t new_page = range;
2110 framepos_t middle = (framepos_t) floor ((double) start + ((double) range / 2.0f));
2111 framepos_t new_leftmost = (framepos_t) floor ((double) middle - ((double) new_page / 2.0f));
2113 if (new_leftmost > middle) {
2117 if (new_leftmost < 0) {
2121 reposition_and_zoom (new_leftmost, new_fpp);
2125 Editor::temporal_zoom_to_frame (bool coarser, framepos_t frame)
2131 framecnt_t range_before = frame - leftmost_frame;
2135 if (samples_per_pixel <= 1) {
2138 new_spp = samples_per_pixel + (samples_per_pixel/2);
2140 range_before += range_before/2;
2142 if (samples_per_pixel >= 1) {
2143 new_spp = samples_per_pixel - (samples_per_pixel/2);
2145 /* could bail out here since we cannot zoom any finer,
2146 but leave that to the equality test below
2148 new_spp = samples_per_pixel;
2151 range_before -= range_before/2;
2154 if (new_spp == samples_per_pixel) {
2158 /* zoom focus is automatically taken as @param frame when this
2162 framepos_t new_leftmost = frame - (framepos_t)range_before;
2164 if (new_leftmost > frame) {
2168 if (new_leftmost < 0) {
2172 reposition_and_zoom (new_leftmost, new_spp);
2177 Editor::choose_new_marker_name(string &name) {
2179 if (!UIConfiguration::instance().get_name_new_markers()) {
2180 /* don't prompt user for a new name */
2184 Prompter dialog (true);
2186 dialog.set_prompt (_("New Name:"));
2188 dialog.set_title (_("New Location Marker"));
2190 dialog.set_name ("MarkNameWindow");
2191 dialog.set_size_request (250, -1);
2192 dialog.set_position (Gtk::WIN_POS_MOUSE);
2194 dialog.add_button (Stock::OK, RESPONSE_ACCEPT);
2195 dialog.set_initial_text (name);
2199 switch (dialog.run ()) {
2200 case RESPONSE_ACCEPT:
2206 dialog.get_result(name);
2213 Editor::add_location_from_selection ()
2217 if (selection->time.empty()) {
2221 if (_session == 0 || clicked_axisview == 0) {
2225 framepos_t start = selection->time[clicked_selection].start;
2226 framepos_t end = selection->time[clicked_selection].end;
2228 _session->locations()->next_available_name(rangename,"selection");
2229 Location *location = new Location (*_session, start, end, rangename, Location::IsRangeMarker, get_grid_music_divisions(0));
2231 begin_reversible_command (_("add marker"));
2233 XMLNode &before = _session->locations()->get_state();
2234 _session->locations()->add (location, true);
2235 XMLNode &after = _session->locations()->get_state();
2236 _session->add_command(new MementoCommand<Locations>(*(_session->locations()), &before, &after));
2238 commit_reversible_command ();
2242 Editor::add_location_mark (framepos_t where)
2246 select_new_marker = true;
2248 _session->locations()->next_available_name(markername,"mark");
2249 if (!choose_new_marker_name(markername)) {
2252 Location *location = new Location (*_session, where, where, markername, Location::IsMark, get_grid_music_divisions (0));
2253 begin_reversible_command (_("add marker"));
2255 XMLNode &before = _session->locations()->get_state();
2256 _session->locations()->add (location, true);
2257 XMLNode &after = _session->locations()->get_state();
2258 _session->add_command(new MementoCommand<Locations>(*(_session->locations()), &before, &after));
2260 commit_reversible_command ();
2264 Editor::set_session_start_from_playhead ()
2270 if ((loc = _session->locations()->session_range_location()) == 0) { //should never happen
2271 _session->set_session_extents ( _session->audible_frame(), _session->audible_frame() );
2273 XMLNode &before = loc->get_state();
2275 _session->set_session_extents ( _session->audible_frame(), loc->end() );
2277 XMLNode &after = loc->get_state();
2279 begin_reversible_command (_("Set session start"));
2281 _session->add_command (new MementoCommand<Location>(*loc, &before, &after));
2283 commit_reversible_command ();
2288 Editor::set_session_end_from_playhead ()
2294 if ((loc = _session->locations()->session_range_location()) == 0) { //should never happen
2295 _session->set_session_extents ( _session->audible_frame(), _session->audible_frame() );
2297 XMLNode &before = loc->get_state();
2299 _session->set_session_extents ( loc->start(), _session->audible_frame() );
2301 XMLNode &after = loc->get_state();
2303 begin_reversible_command (_("Set session start"));
2305 _session->add_command (new MementoCommand<Location>(*loc, &before, &after));
2307 commit_reversible_command ();
2310 _session->set_end_is_free (false);
2315 Editor::toggle_location_at_playhead_cursor ()
2317 if (!do_remove_location_at_playhead_cursor())
2319 add_location_from_playhead_cursor();
2324 Editor::add_location_from_playhead_cursor ()
2326 add_location_mark (_session->audible_frame());
2330 Editor::do_remove_location_at_playhead_cursor ()
2332 bool removed = false;
2335 XMLNode &before = _session->locations()->get_state();
2337 //find location(s) at this time
2338 Locations::LocationList locs;
2339 _session->locations()->find_all_between (_session->audible_frame(), _session->audible_frame()+1, locs, Location::Flags(0));
2340 for (Locations::LocationList::iterator i = locs.begin(); i != locs.end(); ++i) {
2341 if ((*i)->is_mark()) {
2342 _session->locations()->remove (*i);
2349 begin_reversible_command (_("remove marker"));
2350 XMLNode &after = _session->locations()->get_state();
2351 _session->add_command(new MementoCommand<Locations>(*(_session->locations()), &before, &after));
2352 commit_reversible_command ();
2359 Editor::remove_location_at_playhead_cursor ()
2361 do_remove_location_at_playhead_cursor ();
2364 /** Add a range marker around each selected region */
2366 Editor::add_locations_from_region ()
2368 RegionSelection rs = get_regions_from_selection_and_entered ();
2373 bool commit = false;
2375 XMLNode &before = _session->locations()->get_state();
2377 for (RegionSelection::iterator i = rs.begin (); i != rs.end (); ++i) {
2379 boost::shared_ptr<Region> region = (*i)->region ();
2381 Location *location = new Location (*_session, region->position(), region->last_frame(), region->name(), Location::IsRangeMarker, 0);
2383 _session->locations()->add (location, true);
2388 begin_reversible_command (selection->regions.size () > 1 ? _("add markers") : _("add marker"));
2389 XMLNode &after = _session->locations()->get_state();
2390 _session->add_command (new MementoCommand<Locations>(*(_session->locations()), &before, &after));
2391 commit_reversible_command ();
2395 /** Add a single range marker around all selected regions */
2397 Editor::add_location_from_region ()
2399 RegionSelection rs = get_regions_from_selection_and_entered ();
2405 XMLNode &before = _session->locations()->get_state();
2409 if (rs.size() > 1) {
2410 _session->locations()->next_available_name(markername, "regions");
2412 RegionView* rv = *(rs.begin());
2413 boost::shared_ptr<Region> region = rv->region();
2414 markername = region->name();
2417 if (!choose_new_marker_name(markername)) {
2421 // single range spanning all selected
2422 Location *location = new Location (*_session, selection->regions.start(), selection->regions.end_frame(), markername, Location::IsRangeMarker, 0);
2423 _session->locations()->add (location, true);
2425 begin_reversible_command (_("add marker"));
2426 XMLNode &after = _session->locations()->get_state();
2427 _session->add_command (new MementoCommand<Locations>(*(_session->locations()), &before, &after));
2428 commit_reversible_command ();
2434 Editor::jump_forward_to_mark ()
2440 framepos_t pos = _session->locations()->first_mark_after (playhead_cursor->current_frame());
2446 _session->request_locate (pos, _session->transport_rolling());
2450 Editor::jump_backward_to_mark ()
2456 framepos_t pos = _session->locations()->first_mark_before (playhead_cursor->current_frame());
2458 //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...
2459 if ( _session->transport_rolling() ) {
2460 if ( (playhead_cursor->current_frame() - pos) < _session->frame_rate()/2 ) {
2461 framepos_t prior = _session->locations()->first_mark_before ( pos );
2470 _session->request_locate (pos, _session->transport_rolling());
2476 framepos_t const pos = _session->audible_frame ();
2479 _session->locations()->next_available_name (markername, "mark");
2481 if (!choose_new_marker_name (markername)) {
2485 _session->locations()->add (new Location (*_session, pos, 0, markername, Location::IsMark, 0), true);
2489 Editor::clear_markers ()
2492 begin_reversible_command (_("clear markers"));
2494 XMLNode &before = _session->locations()->get_state();
2495 _session->locations()->clear_markers ();
2496 XMLNode &after = _session->locations()->get_state();
2497 _session->add_command(new MementoCommand<Locations>(*(_session->locations()), &before, &after));
2499 commit_reversible_command ();
2504 Editor::clear_ranges ()
2507 begin_reversible_command (_("clear ranges"));
2509 XMLNode &before = _session->locations()->get_state();
2511 _session->locations()->clear_ranges ();
2513 XMLNode &after = _session->locations()->get_state();
2514 _session->add_command(new MementoCommand<Locations>(*(_session->locations()), &before, &after));
2516 commit_reversible_command ();
2521 Editor::clear_locations ()
2523 begin_reversible_command (_("clear locations"));
2525 XMLNode &before = _session->locations()->get_state();
2526 _session->locations()->clear ();
2527 XMLNode &after = _session->locations()->get_state();
2528 _session->add_command(new MementoCommand<Locations>(*(_session->locations()), &before, &after));
2530 commit_reversible_command ();
2534 Editor::unhide_markers ()
2536 for (LocationMarkerMap::iterator i = location_markers.begin(); i != location_markers.end(); ++i) {
2537 Location *l = (*i).first;
2538 if (l->is_hidden() && l->is_mark()) {
2539 l->set_hidden(false, this);
2545 Editor::unhide_ranges ()
2547 for (LocationMarkerMap::iterator i = location_markers.begin(); i != location_markers.end(); ++i) {
2548 Location *l = (*i).first;
2549 if (l->is_hidden() && l->is_range_marker()) {
2550 l->set_hidden(false, this);
2555 /* INSERT/REPLACE */
2558 Editor::insert_region_list_selection (float times)
2560 RouteTimeAxisView *tv = 0;
2561 boost::shared_ptr<Playlist> playlist;
2563 if (clicked_routeview != 0) {
2564 tv = clicked_routeview;
2565 } else if (!selection->tracks.empty()) {
2566 if ((tv = dynamic_cast<RouteTimeAxisView*>(selection->tracks.front())) == 0) {
2569 } else if (entered_track != 0) {
2570 if ((tv = dynamic_cast<RouteTimeAxisView*>(entered_track)) == 0) {
2577 if ((playlist = tv->playlist()) == 0) {
2581 boost::shared_ptr<Region> region = _regions->get_single_selection ();
2586 begin_reversible_command (_("insert region"));
2587 playlist->clear_changes ();
2588 playlist->add_region ((RegionFactory::create (region, true)), get_preferred_edit_position(), times);
2589 if (Config->get_edit_mode() == Ripple)
2590 playlist->ripple (get_preferred_edit_position(), region->length() * times, boost::shared_ptr<Region>());
2592 _session->add_command(new StatefulDiffCommand (playlist));
2593 commit_reversible_command ();
2596 /* BUILT-IN EFFECTS */
2599 Editor::reverse_selection ()
2604 /* GAIN ENVELOPE EDITING */
2607 Editor::edit_envelope ()
2614 Editor::transition_to_rolling (bool fwd)
2620 if (_session->config.get_external_sync()) {
2621 switch (Config->get_sync_source()) {
2625 /* transport controlled by the master */
2630 if (_session->is_auditioning()) {
2631 _session->cancel_audition ();
2635 _session->request_transport_speed (fwd ? 1.0f : -1.0f);
2639 Editor::play_from_start ()
2641 _session->request_locate (_session->current_start_frame(), true);
2645 Editor::play_from_edit_point ()
2647 _session->request_locate (get_preferred_edit_position(), true);
2651 Editor::play_from_edit_point_and_return ()
2653 framepos_t start_frame;
2654 framepos_t return_frame;
2656 start_frame = get_preferred_edit_position ( EDIT_IGNORE_PHEAD );
2658 if (_session->transport_rolling()) {
2659 _session->request_locate (start_frame, false);
2663 /* don't reset the return frame if its already set */
2665 if ((return_frame = _session->requested_return_frame()) < 0) {
2666 return_frame = _session->audible_frame();
2669 if (start_frame >= 0) {
2670 _session->request_roll_at_and_return (start_frame, return_frame);
2675 Editor::play_selection ()
2677 framepos_t start, end;
2678 if (!get_selection_extents ( start, end))
2681 AudioRange ar (start, end, 0);
2682 list<AudioRange> lar;
2685 _session->request_play_range (&lar, true);
2690 Editor::maybe_locate_with_edit_preroll (framepos_t location)
2692 if ( _session->transport_rolling() || !UIConfiguration::instance().get_follow_edits() || _session->config.get_external_sync() )
2695 location -= _session->preroll_samples (location);
2697 //don't try to locate before the beginning of time
2702 //if follow_playhead is on, keep the playhead on the screen
2703 if ( _follow_playhead )
2704 if ( location < leftmost_frame )
2705 location = leftmost_frame;
2707 _session->request_locate( location );
2711 Editor::play_with_preroll ()
2713 framepos_t start, end;
2714 if ( UIConfiguration::instance().get_follow_edits() && get_selection_extents ( start, end) ) {
2715 const framepos_t preroll = _session->preroll_samples (start);
2717 framepos_t ret = start;
2719 if (start > preroll) {
2720 start = start - preroll;
2723 end = end + preroll; //"post-roll"
2725 AudioRange ar (start, end, 0);
2726 list<AudioRange> lar;
2729 _session->request_play_range (&lar, true);
2730 _session->set_requested_return_frame (ret); //force auto-return to return to range start, without the preroll
2732 framepos_t ph = playhead_cursor->current_frame ();
2733 const framepos_t preroll = _session->preroll_samples (ph);
2736 start = ph - preroll;
2740 _session->request_locate (start, true);
2741 _session->set_requested_return_frame (ph); //force auto-return to return to playhead location, without the preroll
2746 Editor::rec_with_preroll ()
2748 framepos_t ph = playhead_cursor->current_frame ();
2749 framepos_t preroll = _session->preroll_samples (ph);
2750 _session->request_preroll_record_trim (ph, preroll);
2754 Editor::rec_with_count_in ()
2756 _session->request_count_in_record ();
2760 Editor::play_location (Location& location)
2762 if (location.start() <= location.end()) {
2766 _session->request_bounded_roll (location.start(), location.end());
2770 Editor::loop_location (Location& location)
2772 if (location.start() <= location.end()) {
2778 if ((tll = transport_loop_location()) != 0) {
2779 tll->set (location.start(), location.end());
2781 // enable looping, reposition and start rolling
2782 _session->request_locate (tll->start(), true);
2783 _session->request_play_loop (true);
2788 Editor::do_layer_operation (LayerOperation op)
2790 if (selection->regions.empty ()) {
2794 bool const multiple = selection->regions.size() > 1;
2798 begin_reversible_command (_("raise regions"));
2800 begin_reversible_command (_("raise region"));
2806 begin_reversible_command (_("raise regions to top"));
2808 begin_reversible_command (_("raise region to top"));
2814 begin_reversible_command (_("lower regions"));
2816 begin_reversible_command (_("lower region"));
2822 begin_reversible_command (_("lower regions to bottom"));
2824 begin_reversible_command (_("lower region"));
2829 set<boost::shared_ptr<Playlist> > playlists = selection->regions.playlists ();
2830 for (set<boost::shared_ptr<Playlist> >::iterator i = playlists.begin(); i != playlists.end(); ++i) {
2831 (*i)->clear_owned_changes ();
2834 for (RegionSelection::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
2835 boost::shared_ptr<Region> r = (*i)->region ();
2847 r->lower_to_bottom ();
2851 for (set<boost::shared_ptr<Playlist> >::iterator i = playlists.begin(); i != playlists.end(); ++i) {
2852 vector<Command*> cmds;
2854 _session->add_commands (cmds);
2857 commit_reversible_command ();
2861 Editor::raise_region ()
2863 do_layer_operation (Raise);
2867 Editor::raise_region_to_top ()
2869 do_layer_operation (RaiseToTop);
2873 Editor::lower_region ()
2875 do_layer_operation (Lower);
2879 Editor::lower_region_to_bottom ()
2881 do_layer_operation (LowerToBottom);
2884 /** Show the region editor for the selected regions */
2886 Editor::show_region_properties ()
2888 selection->foreach_regionview (&RegionView::show_region_editor);
2891 /** Show the midi list editor for the selected MIDI regions */
2893 Editor::show_midi_list_editor ()
2895 selection->foreach_midi_regionview (&MidiRegionView::show_list_editor);
2899 Editor::rename_region ()
2901 RegionSelection rs = get_regions_from_selection_and_entered ();
2907 ArdourDialog d (_("Rename Region"), true, false);
2909 Label label (_("New name:"));
2912 hbox.set_spacing (6);
2913 hbox.pack_start (label, false, false);
2914 hbox.pack_start (entry, true, true);
2916 d.get_vbox()->set_border_width (12);
2917 d.get_vbox()->pack_start (hbox, false, false);
2919 d.add_button(Gtk::Stock::CANCEL, Gtk::RESPONSE_CANCEL);
2920 d.add_button(Gtk::Stock::OK, Gtk::RESPONSE_OK);
2922 d.set_size_request (300, -1);
2924 entry.set_text (rs.front()->region()->name());
2925 entry.select_region (0, -1);
2927 entry.signal_activate().connect (sigc::bind (sigc::mem_fun (d, &Dialog::response), RESPONSE_OK));
2933 int const ret = d.run();
2937 if (ret != RESPONSE_OK) {
2941 std::string str = entry.get_text();
2942 strip_whitespace_edges (str);
2944 rs.front()->region()->set_name (str);
2945 _regions->redisplay ();
2949 /** Start an audition of the first selected region */
2951 Editor::play_edit_range ()
2953 framepos_t start, end;
2955 if (get_edit_op_range (start, end)) {
2956 _session->request_bounded_roll (start, end);
2961 Editor::play_selected_region ()
2963 framepos_t start = max_framepos;
2966 RegionSelection rs = get_regions_from_selection_and_entered ();
2972 for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
2973 if ((*i)->region()->position() < start) {
2974 start = (*i)->region()->position();
2976 if ((*i)->region()->last_frame() + 1 > end) {
2977 end = (*i)->region()->last_frame() + 1;
2981 _session->request_bounded_roll (start, end);
2985 Editor::audition_playlist_region_standalone (boost::shared_ptr<Region> region)
2987 _session->audition_region (region);
2991 Editor::region_from_selection ()
2993 if (clicked_axisview == 0) {
2997 if (selection->time.empty()) {
3001 framepos_t start = selection->time[clicked_selection].start;
3002 framepos_t end = selection->time[clicked_selection].end;
3004 TrackViewList tracks = get_tracks_for_range_action ();
3006 framepos_t selection_cnt = end - start + 1;
3008 for (TrackSelection::iterator i = tracks.begin(); i != tracks.end(); ++i) {
3009 boost::shared_ptr<Region> current;
3010 boost::shared_ptr<Playlist> pl;
3011 framepos_t internal_start;
3014 if ((pl = (*i)->playlist()) == 0) {
3018 if ((current = pl->top_region_at (start)) == 0) {
3022 internal_start = start - current->position();
3023 RegionFactory::region_name (new_name, current->name(), true);
3027 plist.add (ARDOUR::Properties::start, current->start() + internal_start);
3028 plist.add (ARDOUR::Properties::length, selection_cnt);
3029 plist.add (ARDOUR::Properties::name, new_name);
3030 plist.add (ARDOUR::Properties::layer, 0);
3032 boost::shared_ptr<Region> region (RegionFactory::create (current, plist));
3037 Editor::create_region_from_selection (vector<boost::shared_ptr<Region> >& new_regions)
3039 if (selection->time.empty() || selection->tracks.empty()) {
3043 framepos_t start, end;
3044 if (clicked_selection) {
3045 start = selection->time[clicked_selection].start;
3046 end = selection->time[clicked_selection].end;
3048 start = selection->time.start();
3049 end = selection->time.end_frame();
3052 TrackViewList ts = selection->tracks.filter_to_unique_playlists ();
3053 sort_track_selection (ts);
3055 for (TrackSelection::iterator i = ts.begin(); i != ts.end(); ++i) {
3056 boost::shared_ptr<Region> current;
3057 boost::shared_ptr<Playlist> playlist;
3058 framepos_t internal_start;
3061 if ((playlist = (*i)->playlist()) == 0) {
3065 if ((current = playlist->top_region_at(start)) == 0) {
3069 internal_start = start - current->position();
3070 RegionFactory::region_name (new_name, current->name(), true);
3074 plist.add (ARDOUR::Properties::start, current->start() + internal_start);
3075 plist.add (ARDOUR::Properties::length, end - start + 1);
3076 plist.add (ARDOUR::Properties::name, new_name);
3078 new_regions.push_back (RegionFactory::create (current, plist));
3083 Editor::split_multichannel_region ()
3085 RegionSelection rs = get_regions_from_selection_and_entered ();
3091 vector< boost::shared_ptr<Region> > v;
3093 for (list<RegionView*>::iterator x = rs.begin(); x != rs.end(); ++x) {
3094 (*x)->region()->separate_by_channel (*_session, v);
3099 Editor::new_region_from_selection ()
3101 region_from_selection ();
3102 cancel_selection ();
3106 add_if_covered (RegionView* rv, const AudioRange* ar, RegionSelection* rs)
3108 switch (rv->region()->coverage (ar->start, ar->end - 1)) {
3109 // n.b. -1 because AudioRange::end is one past the end, but coverage expects inclusive ranges
3110 case Evoral::OverlapNone:
3118 * - selected tracks, or if there are none...
3119 * - tracks containing selected regions, or if there are none...
3124 Editor::get_tracks_for_range_action () const
3128 if (selection->tracks.empty()) {
3130 /* use tracks with selected regions */
3132 RegionSelection rs = selection->regions;
3134 for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
3135 TimeAxisView* tv = &(*i)->get_time_axis_view();
3137 if (!t.contains (tv)) {
3143 /* no regions and no tracks: use all tracks */
3149 t = selection->tracks;
3152 return t.filter_to_unique_playlists();
3156 Editor::separate_regions_between (const TimeSelection& ts)
3158 bool in_command = false;
3159 boost::shared_ptr<Playlist> playlist;
3160 RegionSelection new_selection;
3162 TrackViewList tmptracks = get_tracks_for_range_action ();
3163 sort_track_selection (tmptracks);
3165 for (TrackSelection::iterator i = tmptracks.begin(); i != tmptracks.end(); ++i) {
3167 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> ((*i));
3173 if (!rtv->is_track()) {
3177 /* no edits to destructive tracks */
3179 if (rtv->track()->destructive()) {
3183 if ((playlist = rtv->playlist()) != 0) {
3185 playlist->clear_changes ();
3187 /* XXX need to consider musical time selections here at some point */
3189 double speed = rtv->track()->speed();
3191 for (list<AudioRange>::const_iterator t = ts.begin(); t != ts.end(); ++t) {
3193 sigc::connection c = rtv->view()->RegionViewAdded.connect (
3194 sigc::mem_fun(*this, &Editor::collect_new_region_view));
3196 latest_regionviews.clear ();
3198 playlist->partition ((framepos_t)((*t).start * speed),
3199 (framepos_t)((*t).end * speed), false);
3203 if (!latest_regionviews.empty()) {
3205 rtv->view()->foreach_regionview (sigc::bind (
3206 sigc::ptr_fun (add_if_covered),
3207 &(*t), &new_selection));
3210 begin_reversible_command (_("separate"));
3214 /* pick up changes to existing regions */
3216 vector<Command*> cmds;
3217 playlist->rdiff (cmds);
3218 _session->add_commands (cmds);
3220 /* pick up changes to the playlist itself (adds/removes)
3223 _session->add_command(new StatefulDiffCommand (playlist));
3230 // selection->set (new_selection);
3232 commit_reversible_command ();
3236 struct PlaylistState {
3237 boost::shared_ptr<Playlist> playlist;
3241 /** Take tracks from get_tracks_for_range_action and cut any regions
3242 * on those tracks so that the tracks are empty over the time
3246 Editor::separate_region_from_selection ()
3248 /* preferentially use *all* ranges in the time selection if we're in range mode
3249 to allow discontiguous operation, since get_edit_op_range() currently
3250 returns a single range.
3253 if (!selection->time.empty()) {
3255 separate_regions_between (selection->time);
3262 if (get_edit_op_range (start, end)) {
3264 AudioRange ar (start, end, 1);
3268 separate_regions_between (ts);
3274 Editor::separate_region_from_punch ()
3276 Location* loc = _session->locations()->auto_punch_location();
3278 separate_regions_using_location (*loc);
3283 Editor::separate_region_from_loop ()
3285 Location* loc = _session->locations()->auto_loop_location();
3287 separate_regions_using_location (*loc);
3292 Editor::separate_regions_using_location (Location& loc)
3294 if (loc.is_mark()) {
3298 AudioRange ar (loc.start(), loc.end(), 1);
3303 separate_regions_between (ts);
3306 /** Separate regions under the selected region */
3308 Editor::separate_under_selected_regions ()
3310 vector<PlaylistState> playlists;
3314 rs = get_regions_from_selection_and_entered();
3316 if (!_session || rs.empty()) {
3320 begin_reversible_command (_("separate region under"));
3322 list<boost::shared_ptr<Region> > regions_to_remove;
3324 for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
3325 // we can't just remove the region(s) in this loop because
3326 // this removes them from the RegionSelection, and they thus
3327 // disappear from underneath the iterator, and the ++i above
3328 // SEGVs in a puzzling fashion.
3330 // so, first iterate over the regions to be removed from rs and
3331 // add them to the regions_to_remove list, and then
3332 // iterate over the list to actually remove them.
3334 regions_to_remove.push_back ((*i)->region());
3337 for (list<boost::shared_ptr<Region> >::iterator rl = regions_to_remove.begin(); rl != regions_to_remove.end(); ++rl) {
3339 boost::shared_ptr<Playlist> playlist = (*rl)->playlist();
3342 // is this check necessary?
3346 vector<PlaylistState>::iterator i;
3348 //only take state if this is a new playlist.
3349 for (i = playlists.begin(); i != playlists.end(); ++i) {
3350 if ((*i).playlist == playlist) {
3355 if (i == playlists.end()) {
3357 PlaylistState before;
3358 before.playlist = playlist;
3359 before.before = &playlist->get_state();
3360 playlist->clear_changes ();
3361 playlist->freeze ();
3362 playlists.push_back(before);
3365 //Partition on the region bounds
3366 playlist->partition ((*rl)->first_frame() - 1, (*rl)->last_frame() + 1, true);
3368 //Re-add region that was just removed due to the partition operation
3369 playlist->add_region( (*rl), (*rl)->first_frame() );
3372 vector<PlaylistState>::iterator pl;
3374 for (pl = playlists.begin(); pl != playlists.end(); ++pl) {
3375 (*pl).playlist->thaw ();
3376 _session->add_command(new MementoCommand<Playlist>(*(*pl).playlist, (*pl).before, &(*pl).playlist->get_state()));
3379 commit_reversible_command ();
3383 Editor::crop_region_to_selection ()
3385 if (!selection->time.empty()) {
3387 begin_reversible_command (_("Crop Regions to Time Selection"));
3388 for (std::list<AudioRange>::iterator i = selection->time.begin(); i != selection->time.end(); ++i) {
3389 crop_region_to ((*i).start, (*i).end);
3391 commit_reversible_command();
3397 if (get_edit_op_range (start, end)) {
3398 begin_reversible_command (_("Crop Regions to Edit Range"));
3400 crop_region_to (start, end);
3402 commit_reversible_command();
3409 Editor::crop_region_to (framepos_t start, framepos_t end)
3411 vector<boost::shared_ptr<Playlist> > playlists;
3412 boost::shared_ptr<Playlist> playlist;
3415 if (selection->tracks.empty()) {
3416 ts = track_views.filter_to_unique_playlists();
3418 ts = selection->tracks.filter_to_unique_playlists ();
3421 sort_track_selection (ts);
3423 for (TrackSelection::iterator i = ts.begin(); i != ts.end(); ++i) {
3425 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> ((*i));
3431 boost::shared_ptr<Track> t = rtv->track();
3433 if (t != 0 && ! t->destructive()) {
3435 if ((playlist = rtv->playlist()) != 0) {
3436 playlists.push_back (playlist);
3441 if (playlists.empty()) {
3446 framepos_t new_start;
3448 framecnt_t new_length;
3450 for (vector<boost::shared_ptr<Playlist> >::iterator i = playlists.begin(); i != playlists.end(); ++i) {
3452 /* Only the top regions at start and end have to be cropped */
3453 boost::shared_ptr<Region> region_at_start = (*i)->top_region_at(start);
3454 boost::shared_ptr<Region> region_at_end = (*i)->top_region_at(end);
3456 vector<boost::shared_ptr<Region> > regions;
3458 if (region_at_start != 0) {
3459 regions.push_back (region_at_start);
3461 if (region_at_end != 0) {
3462 regions.push_back (region_at_end);
3465 /* now adjust lengths */
3466 for (vector<boost::shared_ptr<Region> >::iterator i = regions.begin(); i != regions.end(); ++i) {
3468 pos = (*i)->position();
3469 new_start = max (start, pos);
3470 if (max_framepos - pos > (*i)->length()) {
3471 new_end = pos + (*i)->length() - 1;
3473 new_end = max_framepos;
3475 new_end = min (end, new_end);
3476 new_length = new_end - new_start + 1;
3478 (*i)->clear_changes ();
3479 (*i)->trim_to (new_start, new_length);
3480 _session->add_command (new StatefulDiffCommand (*i));
3486 Editor::region_fill_track ()
3488 boost::shared_ptr<Playlist> playlist;
3489 RegionSelection regions = get_regions_from_selection_and_entered ();
3490 RegionSelection foo;
3492 framepos_t const end = _session->current_end_frame ();
3494 if (regions.empty () || regions.end_frame () + 1 >= end) {
3498 framepos_t const start_frame = regions.start ();
3499 framepos_t const end_frame = regions.end_frame ();
3500 framecnt_t const gap = end_frame - start_frame + 1;
3502 begin_reversible_command (Operations::region_fill);
3504 selection->clear_regions ();
3506 for (RegionSelection::iterator i = regions.begin(); i != regions.end(); ++i) {
3508 boost::shared_ptr<Region> r ((*i)->region());
3510 TimeAxisView& tv = (*i)->get_time_axis_view();
3511 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (&tv);
3512 latest_regionviews.clear ();
3513 sigc::connection c = rtv->view()->RegionViewAdded.connect (sigc::mem_fun(*this, &Editor::collect_new_region_view));
3515 framepos_t const position = end_frame + (r->first_frame() - start_frame + 1);
3516 playlist = (*i)->region()->playlist();
3517 playlist->clear_changes ();
3518 playlist->duplicate_until (r, position, gap, end);
3519 _session->add_command(new StatefulDiffCommand (playlist));
3523 foo.insert (foo.end(), latest_regionviews.begin(), latest_regionviews.end());
3527 selection->set (foo);
3530 commit_reversible_command ();
3534 Editor::set_region_sync_position ()
3536 set_sync_point (get_preferred_edit_position (), get_regions_from_selection_and_edit_point ());
3540 Editor::set_sync_point (framepos_t where, const RegionSelection& rs)
3542 bool in_command = false;
3544 for (RegionSelection::const_iterator r = rs.begin(); r != rs.end(); ++r) {
3546 if (!(*r)->region()->covers (where)) {
3550 boost::shared_ptr<Region> region ((*r)->region());
3553 begin_reversible_command (_("set sync point"));
3557 region->clear_changes ();
3558 region->set_sync_position (where);
3559 _session->add_command(new StatefulDiffCommand (region));
3563 commit_reversible_command ();
3567 /** Remove the sync positions of the selection */
3569 Editor::remove_region_sync ()
3571 RegionSelection rs = get_regions_from_selection_and_entered ();
3577 begin_reversible_command (_("remove region sync"));
3579 for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
3581 (*i)->region()->clear_changes ();
3582 (*i)->region()->clear_sync_position ();
3583 _session->add_command(new StatefulDiffCommand ((*i)->region()));
3586 commit_reversible_command ();
3590 Editor::naturalize_region ()
3592 RegionSelection rs = get_regions_from_selection_and_entered ();
3598 if (rs.size() > 1) {
3599 begin_reversible_command (_("move regions to original position"));
3601 begin_reversible_command (_("move region to original position"));
3604 for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
3605 (*i)->region()->clear_changes ();
3606 (*i)->region()->move_to_natural_position ();
3607 _session->add_command (new StatefulDiffCommand ((*i)->region()));
3610 commit_reversible_command ();
3614 Editor::align_regions (RegionPoint what)
3616 RegionSelection const rs = get_regions_from_selection_and_edit_point ();
3622 begin_reversible_command (_("align selection"));
3624 framepos_t const position = get_preferred_edit_position ();
3626 for (RegionSelection::const_iterator i = rs.begin(); i != rs.end(); ++i) {
3627 align_region_internal ((*i)->region(), what, position);
3630 commit_reversible_command ();
3633 struct RegionSortByTime {
3634 bool operator() (const RegionView* a, const RegionView* b) {
3635 return a->region()->position() < b->region()->position();
3640 Editor::align_regions_relative (RegionPoint point)
3642 RegionSelection const rs = get_regions_from_selection_and_edit_point ();
3648 framepos_t const position = get_preferred_edit_position ();
3650 framepos_t distance = 0;
3654 list<RegionView*> sorted;
3655 rs.by_position (sorted);
3657 boost::shared_ptr<Region> r ((*sorted.begin())->region());
3662 if (position > r->position()) {
3663 distance = position - r->position();
3665 distance = r->position() - position;
3671 if (position > r->last_frame()) {
3672 distance = position - r->last_frame();
3673 pos = r->position() + distance;
3675 distance = r->last_frame() - position;
3676 pos = r->position() - distance;
3682 pos = r->adjust_to_sync (position);
3683 if (pos > r->position()) {
3684 distance = pos - r->position();
3686 distance = r->position() - pos;
3692 if (pos == r->position()) {
3696 begin_reversible_command (_("align selection (relative)"));
3698 /* move first one specially */
3700 r->clear_changes ();
3701 r->set_position (pos);
3702 _session->add_command(new StatefulDiffCommand (r));
3704 /* move rest by the same amount */
3708 for (list<RegionView*>::iterator i = sorted.begin(); i != sorted.end(); ++i) {
3710 boost::shared_ptr<Region> region ((*i)->region());
3712 region->clear_changes ();
3715 region->set_position (region->position() + distance);
3717 region->set_position (region->position() - distance);
3720 _session->add_command(new StatefulDiffCommand (region));
3724 commit_reversible_command ();
3728 Editor::align_region (boost::shared_ptr<Region> region, RegionPoint point, framepos_t position)
3730 begin_reversible_command (_("align region"));
3731 align_region_internal (region, point, position);
3732 commit_reversible_command ();
3736 Editor::align_region_internal (boost::shared_ptr<Region> region, RegionPoint point, framepos_t position)
3738 region->clear_changes ();
3742 region->set_position (region->adjust_to_sync (position));
3746 if (position > region->length()) {
3747 region->set_position (position - region->length());
3752 region->set_position (position);
3756 _session->add_command(new StatefulDiffCommand (region));
3760 Editor::trim_region_front ()
3766 Editor::trim_region_back ()
3768 trim_region (false);
3772 Editor::trim_region (bool front)
3774 framepos_t where = get_preferred_edit_position();
3775 RegionSelection rs = get_regions_from_selection_and_edit_point ();
3781 begin_reversible_command (front ? _("trim front") : _("trim back"));
3783 for (list<RegionView*>::const_iterator i = rs.by_layer().begin(); i != rs.by_layer().end(); ++i) {
3784 if (!(*i)->region()->locked()) {
3786 (*i)->region()->clear_changes ();
3789 (*i)->region()->trim_front (where);
3791 (*i)->region()->trim_end (where);
3794 _session->add_command (new StatefulDiffCommand ((*i)->region()));
3798 commit_reversible_command ();
3801 /** Trim the end of the selected regions to the position of the edit cursor */
3803 Editor::trim_region_to_loop ()
3805 Location* loc = _session->locations()->auto_loop_location();
3809 trim_region_to_location (*loc, _("trim to loop"));
3813 Editor::trim_region_to_punch ()
3815 Location* loc = _session->locations()->auto_punch_location();
3819 trim_region_to_location (*loc, _("trim to punch"));
3823 Editor::trim_region_to_location (const Location& loc, const char* str)
3825 RegionSelection rs = get_regions_from_selection_and_entered ();
3826 bool in_command = false;
3828 for (RegionSelection::iterator x = rs.begin(); x != rs.end(); ++x) {
3829 RegionView* rv = (*x);
3831 /* require region to span proposed trim */
3832 switch (rv->region()->coverage (loc.start(), loc.end())) {
3833 case Evoral::OverlapInternal:
3839 RouteTimeAxisView* tav = dynamic_cast<RouteTimeAxisView*> (&rv->get_time_axis_view());
3848 if (tav->track() != 0) {
3849 speed = tav->track()->speed();
3852 start = session_frame_to_track_frame (loc.start(), speed);
3853 end = session_frame_to_track_frame (loc.end(), speed);
3855 rv->region()->clear_changes ();
3856 rv->region()->trim_to (start, (end - start));
3859 begin_reversible_command (str);
3862 _session->add_command(new StatefulDiffCommand (rv->region()));
3866 commit_reversible_command ();
3871 Editor::trim_region_to_previous_region_end ()
3873 return trim_to_region(false);
3877 Editor::trim_region_to_next_region_start ()
3879 return trim_to_region(true);
3883 Editor::trim_to_region(bool forward)
3885 RegionSelection rs = get_regions_from_selection_and_entered ();
3886 bool in_command = false;
3888 boost::shared_ptr<Region> next_region;
3890 for (RegionSelection::iterator x = rs.begin(); x != rs.end(); ++x) {
3892 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (*x);
3898 AudioTimeAxisView* atav = dynamic_cast<AudioTimeAxisView*> (&arv->get_time_axis_view());
3906 if (atav->track() != 0) {
3907 speed = atav->track()->speed();
3911 boost::shared_ptr<Region> region = arv->region();
3912 boost::shared_ptr<Playlist> playlist (region->playlist());
3914 region->clear_changes ();
3918 next_region = playlist->find_next_region (region->first_frame(), Start, 1);
3924 region->trim_end((framepos_t) ( (next_region->first_frame() - 1) * speed));
3925 arv->region_changed (PropertyChange (ARDOUR::Properties::length));
3929 next_region = playlist->find_next_region (region->first_frame(), Start, 0);
3935 region->trim_front((framepos_t) ((next_region->last_frame() + 1) * speed));
3937 arv->region_changed (ARDOUR::bounds_change);
3941 begin_reversible_command (_("trim to region"));
3944 _session->add_command(new StatefulDiffCommand (region));
3948 commit_reversible_command ();
3953 Editor::unfreeze_route ()
3955 if (clicked_routeview == 0 || !clicked_routeview->is_track()) {
3959 clicked_routeview->track()->unfreeze ();
3963 Editor::_freeze_thread (void* arg)
3965 return static_cast<Editor*>(arg)->freeze_thread ();
3969 Editor::freeze_thread ()
3971 /* create event pool because we may need to talk to the session */
3972 SessionEvent::create_per_thread_pool ("freeze events", 64);
3973 /* create per-thread buffers for process() tree to use */
3974 clicked_routeview->audio_track()->freeze_me (*current_interthread_info);
3975 current_interthread_info->done = true;
3980 Editor::freeze_route ()
3986 /* stop transport before we start. this is important */
3988 _session->request_transport_speed (0.0);
3990 /* wait for just a little while, because the above call is asynchronous */
3992 Glib::usleep (250000);
3994 if (clicked_routeview == 0 || !clicked_routeview->is_audio_track()) {
3998 if (!clicked_routeview->track()->bounceable (clicked_routeview->track()->main_outs(), true)) {
4000 _("This track/bus cannot be frozen because the signal adds or loses channels before reaching the outputs.\n"
4001 "This is typically caused by plugins that generate stereo output from mono input or vice versa.")
4003 d.set_title (_("Cannot freeze"));
4008 if (clicked_routeview->track()->has_external_redirects()) {
4009 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"
4010 "Freezing will only process the signal as far as the first send/insert/return."),
4011 clicked_routeview->track()->name()), true, MESSAGE_INFO, BUTTONS_NONE, true);
4013 d.add_button (_("Freeze anyway"), Gtk::RESPONSE_OK);
4014 d.add_button (_("Don't freeze"), Gtk::RESPONSE_CANCEL);
4015 d.set_title (_("Freeze Limits"));
4017 int response = d.run ();
4020 case Gtk::RESPONSE_CANCEL:
4027 InterThreadInfo itt;
4028 current_interthread_info = &itt;
4030 InterthreadProgressWindow ipw (current_interthread_info, _("Freeze"), _("Cancel Freeze"));
4032 pthread_create_and_store (X_("freezer"), &itt.thread, _freeze_thread, this);
4034 CursorContext::Handle cursor_ctx = CursorContext::create(*this, _cursors->wait);
4036 while (!itt.done && !itt.cancel) {
4037 gtk_main_iteration ();
4040 pthread_join (itt.thread, 0);
4041 current_interthread_info = 0;
4045 Editor::bounce_range_selection (bool replace, bool enable_processing)
4047 if (selection->time.empty()) {
4051 TrackSelection views = selection->tracks;
4053 for (TrackViewList::iterator i = views.begin(); i != views.end(); ++i) {
4055 if (enable_processing) {
4057 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (*i);
4059 if (rtv && rtv->track() && replace && enable_processing && !rtv->track()->bounceable (rtv->track()->main_outs(), false)) {
4061 _("You can't perform this operation because the processing of the signal "
4062 "will cause one or more of the tracks to end up with a region with more channels than this track has inputs.\n\n"
4063 "You can do this without processing, which is a different operation.")
4065 d.set_title (_("Cannot bounce"));
4072 framepos_t start = selection->time[clicked_selection].start;
4073 framepos_t end = selection->time[clicked_selection].end;
4074 framepos_t cnt = end - start + 1;
4075 bool in_command = false;
4077 for (TrackViewList::iterator i = views.begin(); i != views.end(); ++i) {
4079 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (*i);
4085 boost::shared_ptr<Playlist> playlist;
4087 if ((playlist = rtv->playlist()) == 0) {
4091 InterThreadInfo itt;
4093 playlist->clear_changes ();
4094 playlist->clear_owned_changes ();
4096 boost::shared_ptr<Region> r;
4098 if (enable_processing) {
4099 r = rtv->track()->bounce_range (start, start+cnt, itt, rtv->track()->main_outs(), false);
4101 r = rtv->track()->bounce_range (start, start+cnt, itt, boost::shared_ptr<Processor>(), false);
4109 list<AudioRange> ranges;
4110 ranges.push_back (AudioRange (start, start+cnt, 0));
4111 playlist->cut (ranges); // discard result
4112 playlist->add_region (r, start);
4116 begin_reversible_command (_("bounce range"));
4119 vector<Command*> cmds;
4120 playlist->rdiff (cmds);
4121 _session->add_commands (cmds);
4123 _session->add_command (new StatefulDiffCommand (playlist));
4127 commit_reversible_command ();
4131 /** Delete selected regions, automation points or a time range */
4135 //special case: if the user is pointing in the editor/mixer strip, they may be trying to delete a plugin.
4136 //we need this because the editor-mixer strip is in the editor window, so it doesn't get the bindings from the mix window
4137 bool deleted = false;
4138 if ( current_mixer_strip && current_mixer_strip == MixerStrip::entered_mixer_strip() )
4139 deleted = current_mixer_strip->delete_processors ();
4145 /** Cut selected regions, automation points or a time range */
4152 /** Copy selected regions, automation points or a time range */
4160 /** @return true if a Cut, Copy or Clear is possible */
4162 Editor::can_cut_copy () const
4164 if (!selection->time.empty() || !selection->regions.empty() || !selection->points.empty())
4171 /** Cut, copy or clear selected regions, automation points or a time range.
4172 * @param op Operation (Delete, Cut, Copy or Clear)
4175 Editor::cut_copy (CutCopyOp op)
4177 /* only cancel selection if cut/copy is successful.*/
4183 opname = _("delete");
4192 opname = _("clear");
4196 /* if we're deleting something, and the mouse is still pressed,
4197 the thing we started a drag for will be gone when we release
4198 the mouse button(s). avoid this. see part 2 at the end of
4202 if (op == Delete || op == Cut || op == Clear) {
4203 if (_drags->active ()) {
4208 if ( op != Delete ) { //"Delete" doesn't change copy/paste buf
4209 cut_buffer->clear ();
4212 if (entered_marker) {
4214 /* cut/delete op while pointing at a marker */
4217 Location* loc = find_location_from_marker (entered_marker, ignored);
4219 if (_session && loc) {
4220 entered_marker = NULL;
4221 Glib::signal_idle().connect (sigc::bind (sigc::mem_fun(*this, &Editor::really_remove_marker), loc));
4228 switch (mouse_mode) {
4231 begin_reversible_command (opname + ' ' + X_("MIDI"));
4233 commit_reversible_command ();
4239 bool did_edit = false;
4241 if (!selection->regions.empty() || !selection->points.empty()) {
4242 begin_reversible_command (opname + ' ' + _("objects"));
4245 if (!selection->regions.empty()) {
4246 cut_copy_regions (op, selection->regions);
4248 if (op == Cut || op == Delete) {
4249 selection->clear_regions ();
4253 if (!selection->points.empty()) {
4254 cut_copy_points (op);
4256 if (op == Cut || op == Delete) {
4257 selection->clear_points ();
4260 } else if (selection->time.empty()) {
4261 framepos_t start, end;
4262 /* no time selection, see if we can get an edit range
4265 if (get_edit_op_range (start, end)) {
4266 selection->set (start, end);
4268 } else if (!selection->time.empty()) {
4269 begin_reversible_command (opname + ' ' + _("range"));
4272 cut_copy_ranges (op);
4274 if (op == Cut || op == Delete) {
4275 selection->clear_time ();
4280 /* reset repeated paste state */
4282 last_paste_pos = -1;
4283 commit_reversible_command ();
4286 if (op == Delete || op == Cut || op == Clear) {
4292 struct AutomationRecord {
4293 AutomationRecord () : state (0) , line(NULL) {}
4294 AutomationRecord (XMLNode* s, const AutomationLine* l) : state (s) , line (l) {}
4296 XMLNode* state; ///< state before any operation
4297 const AutomationLine* line; ///< line this came from
4298 boost::shared_ptr<Evoral::ControlList> copy; ///< copied events for the cut buffer
4301 struct PointsSelectionPositionSorter {
4302 bool operator() (ControlPoint* a, ControlPoint* b) {
4303 return (*(a->model()))->when < (*(b->model()))->when;
4307 /** Cut, copy or clear selected automation points.
4308 * @param op Operation (Cut, Copy or Clear)
4311 Editor::cut_copy_points (Editing::CutCopyOp op, Evoral::Beats earliest, bool midi)
4313 if (selection->points.empty ()) {
4317 /* XXX: not ideal, as there may be more than one track involved in the point selection */
4318 _last_cut_copy_source_track = &selection->points.front()->line().trackview;
4320 /* Keep a record of the AutomationLists that we end up using in this operation */
4321 typedef std::map<boost::shared_ptr<AutomationList>, AutomationRecord> Lists;
4324 /* user could select points in any order */
4325 selection->points.sort(PointsSelectionPositionSorter ());
4327 /* Go through all selected points, making an AutomationRecord for each distinct AutomationList */
4328 for (PointSelection::iterator sel_point = selection->points.begin(); sel_point != selection->points.end(); ++sel_point) {
4329 const AutomationLine& line = (*sel_point)->line();
4330 const boost::shared_ptr<AutomationList> al = line.the_list();
4331 if (lists.find (al) == lists.end ()) {
4332 /* We haven't seen this list yet, so make a record for it. This includes
4333 taking a copy of its current state, in case this is needed for undo later.
4335 lists[al] = AutomationRecord (&al->get_state (), &line);
4339 if (op == Cut || op == Copy) {
4340 /* This operation will involve putting things in the cut buffer, so create an empty
4341 ControlList for each of our source lists to put the cut buffer data in.
4343 for (Lists::iterator i = lists.begin(); i != lists.end(); ++i) {
4344 i->second.copy = i->first->create (i->first->parameter (), i->first->descriptor());
4347 /* Add all selected points to the relevant copy ControlLists */
4348 MusicFrame start (std::numeric_limits<framepos_t>::max(), 0);
4349 for (PointSelection::iterator sel_point = selection->points.begin(); sel_point != selection->points.end(); ++sel_point) {
4350 boost::shared_ptr<AutomationList> al = (*sel_point)->line().the_list();
4351 AutomationList::const_iterator ctrl_evt = (*sel_point)->model ();
4353 lists[al].copy->fast_simple_add ((*ctrl_evt)->when, (*ctrl_evt)->value);
4355 /* Update earliest MIDI start time in beats */
4356 earliest = std::min(earliest, Evoral::Beats((*ctrl_evt)->when));
4358 /* Update earliest session start time in frames */
4359 start.frame = std::min(start.frame, (*sel_point)->line().session_position(ctrl_evt));
4363 /* Snap start time backwards, so copy/paste is snap aligned. */
4365 if (earliest == Evoral::Beats::max()) {
4366 earliest = Evoral::Beats(); // Weird... don't offset
4368 earliest.round_down_to_beat();
4370 if (start.frame == std::numeric_limits<double>::max()) {
4371 start.frame = 0; // Weird... don't offset
4373 snap_to(start, RoundDownMaybe);
4376 const double line_offset = midi ? earliest.to_double() : start.frame;
4377 for (Lists::iterator i = lists.begin(); i != lists.end(); ++i) {
4378 /* Correct this copy list so that it is relative to the earliest
4379 start time, so relative ordering between points is preserved
4380 when copying from several lists and the paste starts at the
4381 earliest copied piece of data. */
4382 boost::shared_ptr<Evoral::ControlList> &al_cpy = i->second.copy;
4383 for (AutomationList::iterator ctrl_evt = al_cpy->begin(); ctrl_evt != al_cpy->end(); ++ctrl_evt) {
4384 (*ctrl_evt)->when -= line_offset;
4387 /* And add it to the cut buffer */
4388 cut_buffer->add (al_cpy);
4392 if (op == Delete || op == Cut) {
4393 /* This operation needs to remove things from the main AutomationList, so do that now */
4395 for (Lists::iterator i = lists.begin(); i != lists.end(); ++i) {
4396 i->first->freeze ();
4399 /* Remove each selected point from its AutomationList */
4400 for (PointSelection::iterator sel_point = selection->points.begin(); sel_point != selection->points.end(); ++sel_point) {
4401 AutomationLine& line = (*sel_point)->line ();
4402 boost::shared_ptr<AutomationList> al = line.the_list();
4406 if (dynamic_cast<AudioRegionGainLine*> (&line)) {
4407 /* removing of first and last gain point in region gain lines is prohibited*/
4408 if (line.is_last_point (*(*sel_point)) || line.is_first_point (*(*sel_point))) {
4414 al->erase ((*sel_point)->model ());
4418 /* Thaw the lists and add undo records for them */
4419 for (Lists::iterator i = lists.begin(); i != lists.end(); ++i) {
4420 boost::shared_ptr<AutomationList> al = i->first;
4422 _session->add_command (new MementoCommand<AutomationList> (*al.get(), i->second.state, &(al->get_state ())));
4427 /** Cut, copy or clear selected automation points.
4428 * @param op Operation (Cut, Copy or Clear)
4431 Editor::cut_copy_midi (CutCopyOp op)
4433 Evoral::Beats earliest = Evoral::Beats::max();
4434 for (MidiRegionSelection::iterator i = selection->midi_regions.begin(); i != selection->midi_regions.end(); ++i) {
4435 MidiRegionView* mrv = dynamic_cast<MidiRegionView*>(*i);
4437 if (!mrv->selection().empty()) {
4438 earliest = std::min(earliest, (*mrv->selection().begin())->note()->time());
4440 mrv->cut_copy_clear (op);
4442 /* XXX: not ideal, as there may be more than one track involved in the selection */
4443 _last_cut_copy_source_track = &mrv->get_time_axis_view();
4447 if (!selection->points.empty()) {
4448 cut_copy_points (op, earliest, true);
4449 if (op == Cut || op == Delete) {
4450 selection->clear_points ();
4455 struct lt_playlist {
4456 bool operator () (const PlaylistState& a, const PlaylistState& b) {
4457 return a.playlist < b.playlist;
4461 struct PlaylistMapping {
4463 boost::shared_ptr<Playlist> pl;
4465 PlaylistMapping (TimeAxisView* tvp) : tv (tvp) {}
4468 /** Remove `clicked_regionview' */
4470 Editor::remove_clicked_region ()
4472 if (clicked_routeview == 0 || clicked_regionview == 0) {
4476 begin_reversible_command (_("remove region"));
4478 boost::shared_ptr<Playlist> playlist = clicked_routeview->playlist();
4480 playlist->clear_changes ();
4481 playlist->clear_owned_changes ();
4482 playlist->remove_region (clicked_regionview->region());
4483 if (Config->get_edit_mode() == Ripple)
4484 playlist->ripple (clicked_regionview->region()->position(), -clicked_regionview->region()->length(), boost::shared_ptr<Region>());
4486 /* We might have removed regions, which alters other regions' layering_index,
4487 so we need to do a recursive diff here.
4489 vector<Command*> cmds;
4490 playlist->rdiff (cmds);
4491 _session->add_commands (cmds);
4493 _session->add_command(new StatefulDiffCommand (playlist));
4494 commit_reversible_command ();
4498 /** Remove the selected regions */
4500 Editor::remove_selected_regions ()
4502 RegionSelection rs = get_regions_from_selection_and_entered ();
4504 if (!_session || rs.empty()) {
4508 list<boost::shared_ptr<Region> > regions_to_remove;
4510 for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
4511 // we can't just remove the region(s) in this loop because
4512 // this removes them from the RegionSelection, and they thus
4513 // disappear from underneath the iterator, and the ++i above
4514 // SEGVs in a puzzling fashion.
4516 // so, first iterate over the regions to be removed from rs and
4517 // add them to the regions_to_remove list, and then
4518 // iterate over the list to actually remove them.
4520 regions_to_remove.push_back ((*i)->region());
4523 vector<boost::shared_ptr<Playlist> > playlists;
4525 for (list<boost::shared_ptr<Region> >::iterator rl = regions_to_remove.begin(); rl != regions_to_remove.end(); ++rl) {
4527 boost::shared_ptr<Playlist> playlist = (*rl)->playlist();
4530 // is this check necessary?
4534 /* get_regions_from_selection_and_entered() guarantees that
4535 the playlists involved are unique, so there is no need
4539 playlists.push_back (playlist);
4541 playlist->clear_changes ();
4542 playlist->clear_owned_changes ();
4543 playlist->freeze ();
4544 playlist->remove_region (*rl);
4545 if (Config->get_edit_mode() == Ripple)
4546 playlist->ripple ((*rl)->position(), -(*rl)->length(), boost::shared_ptr<Region>());
4550 vector<boost::shared_ptr<Playlist> >::iterator pl;
4551 bool in_command = false;
4553 for (pl = playlists.begin(); pl != playlists.end(); ++pl) {
4556 /* We might have removed regions, which alters other regions' layering_index,
4557 so we need to do a recursive diff here.
4561 begin_reversible_command (_("remove region"));
4564 vector<Command*> cmds;
4565 (*pl)->rdiff (cmds);
4566 _session->add_commands (cmds);
4568 _session->add_command(new StatefulDiffCommand (*pl));
4572 commit_reversible_command ();
4576 /** Cut, copy or clear selected regions.
4577 * @param op Operation (Cut, Copy or Clear)
4580 Editor::cut_copy_regions (CutCopyOp op, RegionSelection& rs)
4582 /* we can't use a std::map here because the ordering is important, and we can't trivially sort
4583 a map when we want ordered access to both elements. i think.
4586 vector<PlaylistMapping> pmap;
4588 framepos_t first_position = max_framepos;
4590 typedef set<boost::shared_ptr<Playlist> > FreezeList;
4591 FreezeList freezelist;
4593 /* get ordering correct before we cut/copy */
4595 rs.sort_by_position_and_track ();
4597 for (RegionSelection::iterator x = rs.begin(); x != rs.end(); ++x) {
4599 first_position = min ((framepos_t) (*x)->region()->position(), first_position);
4601 if (op == Cut || op == Clear || op == Delete) {
4602 boost::shared_ptr<Playlist> pl = (*x)->region()->playlist();
4605 FreezeList::iterator fl;
4607 // only take state if this is a new playlist.
4608 for (fl = freezelist.begin(); fl != freezelist.end(); ++fl) {
4614 if (fl == freezelist.end()) {
4615 pl->clear_changes();
4616 pl->clear_owned_changes ();
4618 freezelist.insert (pl);
4623 TimeAxisView* tv = &(*x)->get_time_axis_view();
4624 vector<PlaylistMapping>::iterator z;
4626 for (z = pmap.begin(); z != pmap.end(); ++z) {
4627 if ((*z).tv == tv) {
4632 if (z == pmap.end()) {
4633 pmap.push_back (PlaylistMapping (tv));
4637 for (RegionSelection::iterator x = rs.begin(); x != rs.end(); ) {
4639 boost::shared_ptr<Playlist> pl = (*x)->region()->playlist();
4642 /* region not yet associated with a playlist (e.g. unfinished
4649 TimeAxisView& tv = (*x)->get_time_axis_view();
4650 boost::shared_ptr<Playlist> npl;
4651 RegionSelection::iterator tmp;
4658 vector<PlaylistMapping>::iterator z;
4660 for (z = pmap.begin(); z != pmap.end(); ++z) {
4661 if ((*z).tv == &tv) {
4666 assert (z != pmap.end());
4669 npl = PlaylistFactory::create (pl->data_type(), *_session, "cutlist", true);
4677 boost::shared_ptr<Region> r = (*x)->region();
4678 boost::shared_ptr<Region> _xx;
4684 pl->remove_region (r);
4685 if (Config->get_edit_mode() == Ripple)
4686 pl->ripple (r->position(), -r->length(), boost::shared_ptr<Region>());
4690 _xx = RegionFactory::create (r);
4691 npl->add_region (_xx, r->position() - first_position);
4692 pl->remove_region (r);
4693 if (Config->get_edit_mode() == Ripple)
4694 pl->ripple (r->position(), -r->length(), boost::shared_ptr<Region>());
4698 /* copy region before adding, so we're not putting same object into two different playlists */
4699 npl->add_region (RegionFactory::create (r), r->position() - first_position);
4703 pl->remove_region (r);
4704 if (Config->get_edit_mode() == Ripple)
4705 pl->ripple (r->position(), -r->length(), boost::shared_ptr<Region>());
4714 list<boost::shared_ptr<Playlist> > foo;
4716 /* the pmap is in the same order as the tracks in which selected regions occurred */
4718 for (vector<PlaylistMapping>::iterator i = pmap.begin(); i != pmap.end(); ++i) {
4721 foo.push_back ((*i).pl);
4726 cut_buffer->set (foo);
4730 _last_cut_copy_source_track = 0;
4732 _last_cut_copy_source_track = pmap.front().tv;
4736 for (FreezeList::iterator pl = freezelist.begin(); pl != freezelist.end(); ++pl) {
4739 /* We might have removed regions, which alters other regions' layering_index,
4740 so we need to do a recursive diff here.
4742 vector<Command*> cmds;
4743 (*pl)->rdiff (cmds);
4744 _session->add_commands (cmds);
4746 _session->add_command (new StatefulDiffCommand (*pl));
4751 Editor::cut_copy_ranges (CutCopyOp op)
4753 TrackViewList ts = selection->tracks.filter_to_unique_playlists ();
4755 /* Sort the track selection now, so that it if is used, the playlists
4756 selected by the calls below to cut_copy_clear are in the order that
4757 their tracks appear in the editor. This makes things like paste
4758 of ranges work properly.
4761 sort_track_selection (ts);
4764 if (!entered_track) {
4767 ts.push_back (entered_track);
4770 for (TrackViewList::iterator i = ts.begin(); i != ts.end(); ++i) {
4771 (*i)->cut_copy_clear (*selection, op);
4776 Editor::paste (float times, bool from_context)
4778 DEBUG_TRACE (DEBUG::CutNPaste, "paste to preferred edit pos\n");
4779 MusicFrame where (get_preferred_edit_position (EDIT_IGNORE_NONE, from_context), 0);
4780 paste_internal (where.frame, times, 0);
4784 Editor::mouse_paste ()
4786 MusicFrame where (0, 0);
4788 if (!mouse_frame (where.frame, ignored)) {
4793 paste_internal (where.frame, 1, where.division);
4797 Editor::paste_internal (framepos_t position, float times, const int32_t sub_num)
4799 DEBUG_TRACE (DEBUG::CutNPaste, string_compose ("apparent paste position is %1\n", position));
4801 if (cut_buffer->empty(internal_editing())) {
4805 if (position == max_framepos) {
4806 position = get_preferred_edit_position();
4807 DEBUG_TRACE (DEBUG::CutNPaste, string_compose ("preferred edit position is %1\n", position));
4810 if (position == last_paste_pos) {
4811 /* repeated paste in the same position */
4814 /* paste in new location, reset repeated paste state */
4816 last_paste_pos = position;
4819 /* get everything in the correct order */
4822 if (!selection->tracks.empty()) {
4823 /* If there is a track selection, paste into exactly those tracks and
4824 * only those tracks. This allows the user to be explicit and override
4825 * the below "do the reasonable thing" logic. */
4826 ts = selection->tracks.filter_to_unique_playlists ();
4827 sort_track_selection (ts);
4829 /* Figure out which track to base the paste at. */
4830 TimeAxisView* base_track = NULL;
4831 if (_edit_point == Editing::EditAtMouse && entered_track) {
4832 /* With the mouse edit point, paste onto the track under the mouse. */
4833 base_track = entered_track;
4834 } else if (_edit_point == Editing::EditAtMouse && entered_regionview) {
4835 /* With the mouse edit point, paste onto the track of the region under the mouse. */
4836 base_track = &entered_regionview->get_time_axis_view();
4837 } else if (_last_cut_copy_source_track) {
4838 /* Paste to the track that the cut/copy came from (see mantis #333). */
4839 base_track = _last_cut_copy_source_track;
4841 /* This is "impossible" since we've copied... well, do nothing. */
4845 /* Walk up to parent if necessary, so base track is a route. */
4846 while (base_track->get_parent()) {
4847 base_track = base_track->get_parent();
4850 /* Add base track and all tracks below it. The paste logic will select
4851 the appropriate object types from the cut buffer in relative order. */
4852 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
4853 if ((*i)->order() >= base_track->order()) {
4858 /* Sort tracks so the nth track of type T will pick the nth object of type T. */
4859 sort_track_selection (ts);
4861 /* Add automation children of each track in order, for pasting several lines. */
4862 for (TrackViewList::iterator i = ts.begin(); i != ts.end();) {
4863 /* Add any automation children for pasting several lines */
4864 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*>(*i++);
4869 typedef RouteTimeAxisView::AutomationTracks ATracks;
4870 const ATracks& atracks = rtv->automation_tracks();
4871 for (ATracks::const_iterator a = atracks.begin(); a != atracks.end(); ++a) {
4872 i = ts.insert(i, a->second.get());
4877 /* We now have a list of trackviews starting at base_track, including
4878 automation children, in the order shown in the editor, e.g. R1,
4879 R1.A1, R1.A2, R2, R2.A1, ... */
4882 begin_reversible_command (Operations::paste);
4884 if (ts.size() == 1 && cut_buffer->lines.size() == 1 &&
4885 dynamic_cast<AutomationTimeAxisView*>(ts.front())) {
4886 /* Only one line copied, and one automation track selected. Do a
4887 "greedy" paste from one automation type to another. */
4889 PasteContext ctx(paste_count, times, ItemCounts(), true);
4890 ts.front()->paste (position, *cut_buffer, ctx, sub_num);
4894 /* Paste into tracks */
4896 PasteContext ctx(paste_count, times, ItemCounts(), false);
4897 for (TrackViewList::iterator i = ts.begin(); i != ts.end(); ++i) {
4898 (*i)->paste (position, *cut_buffer, ctx, sub_num);
4902 commit_reversible_command ();
4906 Editor::duplicate_regions (float times)
4908 RegionSelection rs (get_regions_from_selection_and_entered());
4909 duplicate_some_regions (rs, times);
4913 Editor::duplicate_some_regions (RegionSelection& regions, float times)
4915 if (regions.empty ()) {
4919 boost::shared_ptr<Playlist> playlist;
4920 RegionSelection sel = regions; // clear (below) may clear the argument list if its the current region selection
4921 RegionSelection foo;
4923 framepos_t const start_frame = regions.start ();
4924 framepos_t const end_frame = regions.end_frame ();
4925 framecnt_t const gap = end_frame - start_frame + 1;
4927 begin_reversible_command (Operations::duplicate_region);
4929 selection->clear_regions ();
4931 for (RegionSelection::iterator i = sel.begin(); i != sel.end(); ++i) {
4933 boost::shared_ptr<Region> r ((*i)->region());
4935 TimeAxisView& tv = (*i)->get_time_axis_view();
4936 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (&tv);
4937 latest_regionviews.clear ();
4938 sigc::connection c = rtv->view()->RegionViewAdded.connect (sigc::mem_fun(*this, &Editor::collect_new_region_view));
4940 framepos_t const position = end_frame + (r->first_frame() - start_frame + 1);
4941 playlist = (*i)->region()->playlist();
4942 playlist->clear_changes ();
4943 playlist->duplicate (r, position, gap, times);
4944 _session->add_command(new StatefulDiffCommand (playlist));
4948 foo.insert (foo.end(), latest_regionviews.begin(), latest_regionviews.end());
4952 selection->set (foo);
4955 commit_reversible_command ();
4959 Editor::duplicate_selection (float times)
4961 if (selection->time.empty() || selection->tracks.empty()) {
4965 boost::shared_ptr<Playlist> playlist;
4967 TrackViewList ts = selection->tracks.filter_to_unique_playlists ();
4969 bool in_command = false;
4971 for (TrackViewList::iterator i = ts.begin(); i != ts.end(); ++i) {
4972 if ((playlist = (*i)->playlist()) == 0) {
4975 playlist->clear_changes ();
4977 if (clicked_selection) {
4978 playlist->duplicate_range (selection->time[clicked_selection], times);
4980 playlist->duplicate_ranges (selection->time, times);
4984 begin_reversible_command (_("duplicate range selection"));
4987 _session->add_command (new StatefulDiffCommand (playlist));
4992 if (times == 1.0f) {
4993 // now "move" range selection to after the current range selection
4994 framecnt_t distance = 0;
4996 if (clicked_selection) {
4998 selection->time[clicked_selection].end - selection->time[clicked_selection].start;
5000 distance = selection->time.end_frame () - selection->time.start ();
5003 selection->move_time (distance);
5005 commit_reversible_command ();
5009 /** Reset all selected points to the relevant default value */
5011 Editor::reset_point_selection ()
5013 for (PointSelection::iterator i = selection->points.begin(); i != selection->points.end(); ++i) {
5014 ARDOUR::AutomationList::iterator j = (*i)->model ();
5015 (*j)->value = (*i)->line().the_list()->descriptor ().normal;
5020 Editor::center_playhead ()
5022 float const page = _visible_canvas_width * samples_per_pixel;
5023 center_screen_internal (playhead_cursor->current_frame (), page);
5027 Editor::center_edit_point ()
5029 float const page = _visible_canvas_width * samples_per_pixel;
5030 center_screen_internal (get_preferred_edit_position(), page);
5033 /** Caller must begin and commit a reversible command */
5035 Editor::clear_playlist (boost::shared_ptr<Playlist> playlist)
5037 playlist->clear_changes ();
5039 _session->add_command (new StatefulDiffCommand (playlist));
5043 Editor::nudge_track (bool use_edit, bool forwards)
5045 boost::shared_ptr<Playlist> playlist;
5046 framepos_t distance;
5047 framepos_t next_distance;
5051 start = get_preferred_edit_position();
5056 if ((distance = get_nudge_distance (start, next_distance)) == 0) {
5060 if (selection->tracks.empty()) {
5064 TrackViewList ts = selection->tracks.filter_to_unique_playlists ();
5065 bool in_command = false;
5067 for (TrackViewList::iterator i = ts.begin(); i != ts.end(); ++i) {
5069 if ((playlist = (*i)->playlist()) == 0) {
5073 playlist->clear_changes ();
5074 playlist->clear_owned_changes ();
5076 playlist->nudge_after (start, distance, forwards);
5079 begin_reversible_command (_("nudge track"));
5082 vector<Command*> cmds;
5084 playlist->rdiff (cmds);
5085 _session->add_commands (cmds);
5087 _session->add_command (new StatefulDiffCommand (playlist));
5091 commit_reversible_command ();
5096 Editor::remove_last_capture ()
5098 vector<string> choices;
5105 if (Config->get_verify_remove_last_capture()) {
5106 prompt = _("Do you really want to destroy the last capture?"
5107 "\n(This is destructive and cannot be undone)");
5109 choices.push_back (_("No, do nothing."));
5110 choices.push_back (_("Yes, destroy it."));
5112 Choice prompter (_("Destroy last capture"), prompt, choices);
5114 if (prompter.run () == 1) {
5115 _session->remove_last_capture ();
5116 _regions->redisplay ();
5120 _session->remove_last_capture();
5121 _regions->redisplay ();
5126 Editor::normalize_region ()
5132 RegionSelection rs = get_regions_from_selection_and_entered ();
5138 NormalizeDialog dialog (rs.size() > 1);
5140 if (dialog.run () != RESPONSE_ACCEPT) {
5144 CursorContext::Handle cursor_ctx = CursorContext::create(*this, _cursors->wait);
5147 /* XXX: should really only count audio regions here */
5148 int const regions = rs.size ();
5150 /* Make a list of the selected audio regions' maximum amplitudes, and also
5151 obtain the maximum amplitude of them all.
5153 list<double> max_amps;
5154 list<double> rms_vals;
5157 bool use_rms = dialog.constrain_rms ();
5159 for (RegionSelection::const_iterator i = rs.begin(); i != rs.end(); ++i) {
5160 AudioRegionView const * arv = dynamic_cast<AudioRegionView const *> (*i);
5164 dialog.descend (1.0 / regions);
5165 double const a = arv->audio_region()->maximum_amplitude (&dialog);
5167 double r = arv->audio_region()->rms (&dialog);
5168 max_rms = max (max_rms, r);
5169 rms_vals.push_back (r);
5173 /* the user cancelled the operation */
5177 max_amps.push_back (a);
5178 max_amp = max (max_amp, a);
5182 list<double>::const_iterator a = max_amps.begin ();
5183 list<double>::const_iterator l = rms_vals.begin ();
5184 bool in_command = false;
5186 for (RegionSelection::iterator r = rs.begin(); r != rs.end(); ++r) {
5187 AudioRegionView* const arv = dynamic_cast<AudioRegionView*> (*r);
5192 arv->region()->clear_changes ();
5194 double amp = dialog.normalize_individually() ? *a : max_amp;
5195 double target = dialog.target_peak (); // dB
5198 double const amp_rms = dialog.normalize_individually() ? *l : max_rms;
5199 const double t_rms = dialog.target_rms ();
5200 const gain_t c_peak = dB_to_coefficient (target);
5201 const gain_t c_rms = dB_to_coefficient (t_rms);
5202 if ((amp_rms / c_rms) > (amp / c_peak)) {
5208 arv->audio_region()->normalize (amp, target);
5211 begin_reversible_command (_("normalize"));
5214 _session->add_command (new StatefulDiffCommand (arv->region()));
5221 commit_reversible_command ();
5227 Editor::reset_region_scale_amplitude ()
5233 RegionSelection rs = get_regions_from_selection_and_entered ();
5239 bool in_command = false;
5241 for (RegionSelection::iterator r = rs.begin(); r != rs.end(); ++r) {
5242 AudioRegionView* const arv = dynamic_cast<AudioRegionView*>(*r);
5245 arv->region()->clear_changes ();
5246 arv->audio_region()->set_scale_amplitude (1.0f);
5249 begin_reversible_command ("reset gain");
5252 _session->add_command (new StatefulDiffCommand (arv->region()));
5256 commit_reversible_command ();
5261 Editor::adjust_region_gain (bool up)
5263 RegionSelection rs = get_regions_from_selection_and_entered ();
5265 if (!_session || rs.empty()) {
5269 bool in_command = false;
5271 for (RegionSelection::iterator r = rs.begin(); r != rs.end(); ++r) {
5272 AudioRegionView* const arv = dynamic_cast<AudioRegionView*>(*r);
5277 arv->region()->clear_changes ();
5279 double dB = accurate_coefficient_to_dB (arv->audio_region()->scale_amplitude ());
5287 arv->audio_region()->set_scale_amplitude (dB_to_coefficient (dB));
5290 begin_reversible_command ("adjust region gain");
5293 _session->add_command (new StatefulDiffCommand (arv->region()));
5297 commit_reversible_command ();
5302 Editor::reset_region_gain ()
5304 RegionSelection rs = get_regions_from_selection_and_entered ();
5306 if (!_session || rs.empty()) {
5310 bool in_command = false;
5312 for (RegionSelection::iterator r = rs.begin(); r != rs.end(); ++r) {
5313 AudioRegionView* const arv = dynamic_cast<AudioRegionView*>(*r);
5318 arv->region()->clear_changes ();
5320 arv->audio_region()->set_scale_amplitude (1.0f);
5323 begin_reversible_command ("reset region gain");
5326 _session->add_command (new StatefulDiffCommand (arv->region()));
5330 commit_reversible_command ();
5335 Editor::reverse_region ()
5341 Reverse rev (*_session);
5342 apply_filter (rev, _("reverse regions"));
5346 Editor::strip_region_silence ()
5352 RegionSelection rs = get_regions_from_selection_and_entered ();
5358 std::list<RegionView*> audio_only;
5360 for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
5361 AudioRegionView* const arv = dynamic_cast<AudioRegionView*> (*i);
5363 audio_only.push_back (arv);
5367 assert (!audio_only.empty());
5369 StripSilenceDialog d (_session, audio_only);
5370 int const r = d.run ();
5374 if (r == Gtk::RESPONSE_OK) {
5375 ARDOUR::AudioIntervalMap silences;
5376 d.silences (silences);
5377 StripSilence s (*_session, silences, d.fade_length());
5379 apply_filter (s, _("strip silence"), &d);
5384 Editor::apply_midi_note_edit_op_to_region (MidiOperator& op, MidiRegionView& mrv)
5386 Evoral::Sequence<Evoral::Beats>::Notes selected;
5387 mrv.selection_as_notelist (selected, true);
5389 vector<Evoral::Sequence<Evoral::Beats>::Notes> v;
5390 v.push_back (selected);
5392 Evoral::Beats pos_beats = Evoral::Beats (mrv.midi_region()->beat()) - mrv.midi_region()->start_beats();
5394 return op (mrv.midi_region()->model(), pos_beats, v);
5398 Editor::apply_midi_note_edit_op (MidiOperator& op, const RegionSelection& rs)
5404 bool in_command = false;
5406 for (RegionSelection::const_iterator r = rs.begin(); r != rs.end(); ) {
5407 RegionSelection::const_iterator tmp = r;
5410 MidiRegionView* const mrv = dynamic_cast<MidiRegionView*> (*r);
5413 Command* cmd = apply_midi_note_edit_op_to_region (op, *mrv);
5416 begin_reversible_command (op.name ());
5420 _session->add_command (cmd);
5428 commit_reversible_command ();
5433 Editor::fork_region ()
5435 RegionSelection rs = get_regions_from_selection_and_entered ();
5441 CursorContext::Handle cursor_ctx = CursorContext::create(*this, _cursors->wait);
5442 bool in_command = false;
5446 for (RegionSelection::iterator r = rs.begin(); r != rs.end(); ) {
5447 RegionSelection::iterator tmp = r;
5450 MidiRegionView* const mrv = dynamic_cast<MidiRegionView*>(*r);
5454 boost::shared_ptr<Playlist> playlist = mrv->region()->playlist();
5455 boost::shared_ptr<MidiSource> new_source = _session->create_midi_source_by_stealing_name (mrv->midi_view()->track());
5456 boost::shared_ptr<MidiRegion> newregion = mrv->midi_region()->clone (new_source);
5459 begin_reversible_command (_("Fork Region(s)"));
5462 playlist->clear_changes ();
5463 playlist->replace_region (mrv->region(), newregion, mrv->region()->position());
5464 _session->add_command(new StatefulDiffCommand (playlist));
5466 error << string_compose (_("Could not unlink %1"), mrv->region()->name()) << endmsg;
5474 commit_reversible_command ();
5479 Editor::quantize_region ()
5482 quantize_regions(get_regions_from_selection_and_entered ());
5487 Editor::quantize_regions (const RegionSelection& rs)
5489 if (rs.n_midi_regions() == 0) {
5493 if (!quantize_dialog) {
5494 quantize_dialog = new QuantizeDialog (*this);
5497 if (quantize_dialog->is_mapped()) {
5498 /* in progress already */
5502 quantize_dialog->present ();
5503 const int r = quantize_dialog->run ();
5504 quantize_dialog->hide ();
5506 if (r == Gtk::RESPONSE_OK) {
5507 Quantize quant (quantize_dialog->snap_start(),
5508 quantize_dialog->snap_end(),
5509 quantize_dialog->start_grid_size(),
5510 quantize_dialog->end_grid_size(),
5511 quantize_dialog->strength(),
5512 quantize_dialog->swing(),
5513 quantize_dialog->threshold());
5515 apply_midi_note_edit_op (quant, rs);
5520 Editor::legatize_region (bool shrink_only)
5523 legatize_regions(get_regions_from_selection_and_entered (), shrink_only);
5528 Editor::legatize_regions (const RegionSelection& rs, bool shrink_only)
5530 if (rs.n_midi_regions() == 0) {
5534 Legatize legatize(shrink_only);
5535 apply_midi_note_edit_op (legatize, rs);
5539 Editor::transform_region ()
5542 transform_regions(get_regions_from_selection_and_entered ());
5547 Editor::transform_regions (const RegionSelection& rs)
5549 if (rs.n_midi_regions() == 0) {
5556 const int r = td.run();
5559 if (r == Gtk::RESPONSE_OK) {
5560 Transform transform(td.get());
5561 apply_midi_note_edit_op(transform, rs);
5566 Editor::transpose_region ()
5569 transpose_regions(get_regions_from_selection_and_entered ());
5574 Editor::transpose_regions (const RegionSelection& rs)
5576 if (rs.n_midi_regions() == 0) {
5581 int const r = d.run ();
5583 if (r == RESPONSE_ACCEPT) {
5584 Transpose transpose(d.semitones ());
5585 apply_midi_note_edit_op (transpose, rs);
5590 Editor::insert_patch_change (bool from_context)
5592 RegionSelection rs = get_regions_from_selection_and_entered ();
5598 const framepos_t p = get_preferred_edit_position (EDIT_IGNORE_NONE, from_context);
5600 /* XXX: bit of a hack; use the MIDNAM from the first selected region;
5601 there may be more than one, but the PatchChangeDialog can only offer
5602 one set of patch menus.
5604 MidiRegionView* first = dynamic_cast<MidiRegionView*> (rs.front ());
5606 Evoral::PatchChange<Evoral::Beats> empty (Evoral::Beats(), 0, 0, 0);
5607 PatchChangeDialog d (0, _session, empty, first->instrument_info(), Gtk::Stock::ADD);
5609 if (d.run() == RESPONSE_CANCEL) {
5613 for (RegionSelection::iterator i = rs.begin (); i != rs.end(); ++i) {
5614 MidiRegionView* const mrv = dynamic_cast<MidiRegionView*> (*i);
5616 if (p >= mrv->region()->first_frame() && p <= mrv->region()->last_frame()) {
5617 mrv->add_patch_change (p - mrv->region()->position(), d.patch ());
5624 Editor::apply_filter (Filter& filter, string command, ProgressReporter* progress)
5626 RegionSelection rs = get_regions_from_selection_and_entered ();
5632 CursorContext::Handle cursor_ctx = CursorContext::create(*this, _cursors->wait);
5633 bool in_command = false;
5638 int const N = rs.size ();
5640 for (RegionSelection::iterator r = rs.begin(); r != rs.end(); ) {
5641 RegionSelection::iterator tmp = r;
5644 AudioRegionView* const arv = dynamic_cast<AudioRegionView*>(*r);
5646 boost::shared_ptr<Playlist> playlist = arv->region()->playlist();
5649 progress->descend (1.0 / N);
5652 if (arv->audio_region()->apply (filter, progress) == 0) {
5654 playlist->clear_changes ();
5655 playlist->clear_owned_changes ();
5658 begin_reversible_command (command);
5662 if (filter.results.empty ()) {
5664 /* no regions returned; remove the old one */
5665 playlist->remove_region (arv->region ());
5669 std::vector<boost::shared_ptr<Region> >::iterator res = filter.results.begin ();
5671 /* first region replaces the old one */
5672 playlist->replace_region (arv->region(), *res, (*res)->position());
5676 while (res != filter.results.end()) {
5677 playlist->add_region (*res, (*res)->position());
5683 /* We might have removed regions, which alters other regions' layering_index,
5684 so we need to do a recursive diff here.
5686 vector<Command*> cmds;
5687 playlist->rdiff (cmds);
5688 _session->add_commands (cmds);
5690 _session->add_command(new StatefulDiffCommand (playlist));
5694 progress->ascend ();
5703 commit_reversible_command ();
5708 Editor::external_edit_region ()
5714 Editor::reset_region_gain_envelopes ()
5716 RegionSelection rs = get_regions_from_selection_and_entered ();
5718 if (!_session || rs.empty()) {
5722 bool in_command = false;
5724 for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
5725 AudioRegionView* const arv = dynamic_cast<AudioRegionView*>(*i);
5727 boost::shared_ptr<AutomationList> alist (arv->audio_region()->envelope());
5728 XMLNode& before (alist->get_state());
5730 arv->audio_region()->set_default_envelope ();
5733 begin_reversible_command (_("reset region gain"));
5736 _session->add_command (new MementoCommand<AutomationList>(*arv->audio_region()->envelope().get(), &before, &alist->get_state()));
5741 commit_reversible_command ();
5746 Editor::set_region_gain_visibility (RegionView* rv)
5748 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (rv);
5750 arv->update_envelope_visibility();
5755 Editor::set_gain_envelope_visibility ()
5761 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
5762 AudioTimeAxisView* v = dynamic_cast<AudioTimeAxisView*>(*i);
5764 v->audio_view()->foreach_regionview (sigc::mem_fun (this, &Editor::set_region_gain_visibility));
5770 Editor::toggle_gain_envelope_active ()
5772 if (_ignore_region_action) {
5776 RegionSelection rs = get_regions_from_selection_and_entered ();
5778 if (!_session || rs.empty()) {
5782 bool in_command = false;
5784 for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
5785 AudioRegionView* const arv = dynamic_cast<AudioRegionView*>(*i);
5787 arv->region()->clear_changes ();
5788 arv->audio_region()->set_envelope_active (!arv->audio_region()->envelope_active());
5791 begin_reversible_command (_("region gain envelope active"));
5794 _session->add_command (new StatefulDiffCommand (arv->region()));
5799 commit_reversible_command ();
5804 Editor::toggle_region_lock ()
5806 if (_ignore_region_action) {
5810 RegionSelection rs = get_regions_from_selection_and_entered ();
5812 if (!_session || rs.empty()) {
5816 begin_reversible_command (_("toggle region lock"));
5818 for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
5819 (*i)->region()->clear_changes ();
5820 (*i)->region()->set_locked (!(*i)->region()->locked());
5821 _session->add_command (new StatefulDiffCommand ((*i)->region()));
5824 commit_reversible_command ();
5828 Editor::toggle_region_video_lock ()
5830 if (_ignore_region_action) {
5834 RegionSelection rs = get_regions_from_selection_and_entered ();
5836 if (!_session || rs.empty()) {
5840 begin_reversible_command (_("Toggle Video Lock"));
5842 for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
5843 (*i)->region()->clear_changes ();
5844 (*i)->region()->set_video_locked (!(*i)->region()->video_locked());
5845 _session->add_command (new StatefulDiffCommand ((*i)->region()));
5848 commit_reversible_command ();
5852 Editor::toggle_region_lock_style ()
5854 if (_ignore_region_action) {
5858 RegionSelection rs = get_regions_from_selection_and_entered ();
5860 if (!_session || rs.empty()) {
5864 Glib::RefPtr<ToggleAction> a = Glib::RefPtr<ToggleAction>::cast_dynamic (_region_actions->get_action("toggle-region-lock-style"));
5865 vector<Widget*> proxies = a->get_proxies();
5866 Gtk::CheckMenuItem* cmi = dynamic_cast<Gtk::CheckMenuItem*> (proxies.front());
5870 begin_reversible_command (_("toggle region lock style"));
5872 for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
5873 (*i)->region()->clear_changes ();
5874 PositionLockStyle const ns = ((*i)->region()->position_lock_style() == AudioTime && !cmi->get_inconsistent()) ? MusicTime : AudioTime;
5875 (*i)->region()->set_position_lock_style (ns);
5876 _session->add_command (new StatefulDiffCommand ((*i)->region()));
5879 commit_reversible_command ();
5883 Editor::toggle_opaque_region ()
5885 if (_ignore_region_action) {
5889 RegionSelection rs = get_regions_from_selection_and_entered ();
5891 if (!_session || rs.empty()) {
5895 begin_reversible_command (_("change region opacity"));
5897 for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
5898 (*i)->region()->clear_changes ();
5899 (*i)->region()->set_opaque (!(*i)->region()->opaque());
5900 _session->add_command (new StatefulDiffCommand ((*i)->region()));
5903 commit_reversible_command ();
5907 Editor::toggle_record_enable ()
5909 bool new_state = false;
5911 for (TrackSelection::iterator i = selection->tracks.begin(); i != selection->tracks.end(); ++i) {
5912 RouteTimeAxisView *rtav = dynamic_cast<RouteTimeAxisView *>(*i);
5915 if (!rtav->is_track())
5919 new_state = !rtav->track()->rec_enable_control()->get_value();
5923 rtav->track()->rec_enable_control()->set_value (new_state, Controllable::UseGroup);
5928 Editor::toggle_solo ()
5930 bool new_state = false;
5932 boost::shared_ptr<ControlList> cl (new ControlList);
5934 for (TrackSelection::iterator i = selection->tracks.begin(); i != selection->tracks.end(); ++i) {
5935 RouteTimeAxisView *rtav = dynamic_cast<RouteTimeAxisView *>(*i);
5942 new_state = !rtav->route()->soloed ();
5946 cl->push_back (rtav->route()->solo_control());
5949 _session->set_controls (cl, new_state ? 1.0 : 0.0, Controllable::UseGroup);
5953 Editor::toggle_mute ()
5955 bool new_state = false;
5957 boost::shared_ptr<RouteList> rl (new RouteList);
5959 for (TrackSelection::iterator i = selection->tracks.begin(); i != selection->tracks.end(); ++i) {
5960 RouteTimeAxisView *rtav = dynamic_cast<RouteTimeAxisView *>(*i);
5967 new_state = !rtav->route()->muted();
5971 rl->push_back (rtav->route());
5974 _session->set_controls (route_list_to_control_list (rl, &Stripable::mute_control), new_state, Controllable::UseGroup);
5978 Editor::toggle_solo_isolate ()
5984 Editor::fade_range ()
5986 TrackViewList ts = selection->tracks.filter_to_unique_playlists ();
5988 begin_reversible_command (_("fade range"));
5990 for (TrackViewList::iterator i = ts.begin(); i != ts.end(); ++i) {
5991 (*i)->fade_range (selection->time);
5994 commit_reversible_command ();
5999 Editor::set_fade_length (bool in)
6001 RegionSelection rs = get_regions_from_selection_and_entered ();
6007 /* we need a region to measure the offset from the start */
6009 RegionView* rv = rs.front ();
6011 framepos_t pos = get_preferred_edit_position();
6015 if (pos > rv->region()->last_frame() || pos < rv->region()->first_frame()) {
6016 /* edit point is outside the relevant region */
6021 if (pos <= rv->region()->position()) {
6025 len = pos - rv->region()->position();
6026 cmd = _("set fade in length");
6028 if (pos >= rv->region()->last_frame()) {
6032 len = rv->region()->last_frame() - pos;
6033 cmd = _("set fade out length");
6036 bool in_command = false;
6038 for (RegionSelection::iterator x = rs.begin(); x != rs.end(); ++x) {
6039 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (*x);
6045 boost::shared_ptr<AutomationList> alist;
6047 alist = tmp->audio_region()->fade_in();
6049 alist = tmp->audio_region()->fade_out();
6052 XMLNode &before = alist->get_state();
6055 tmp->audio_region()->set_fade_in_length (len);
6056 tmp->audio_region()->set_fade_in_active (true);
6058 tmp->audio_region()->set_fade_out_length (len);
6059 tmp->audio_region()->set_fade_out_active (true);
6063 begin_reversible_command (cmd);
6066 XMLNode &after = alist->get_state();
6067 _session->add_command(new MementoCommand<AutomationList>(*alist, &before, &after));
6071 commit_reversible_command ();
6076 Editor::set_fade_in_shape (FadeShape shape)
6078 RegionSelection rs = get_regions_from_selection_and_entered ();
6083 bool in_command = false;
6085 for (RegionSelection::iterator x = rs.begin(); x != rs.end(); ++x) {
6086 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (*x);
6092 boost::shared_ptr<AutomationList> alist = tmp->audio_region()->fade_in();
6093 XMLNode &before = alist->get_state();
6095 tmp->audio_region()->set_fade_in_shape (shape);
6098 begin_reversible_command (_("set fade in shape"));
6101 XMLNode &after = alist->get_state();
6102 _session->add_command(new MementoCommand<AutomationList>(*alist.get(), &before, &after));
6106 commit_reversible_command ();
6111 Editor::set_fade_out_shape (FadeShape shape)
6113 RegionSelection rs = get_regions_from_selection_and_entered ();
6118 bool in_command = false;
6120 for (RegionSelection::iterator x = rs.begin(); x != rs.end(); ++x) {
6121 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (*x);
6127 boost::shared_ptr<AutomationList> alist = tmp->audio_region()->fade_out();
6128 XMLNode &before = alist->get_state();
6130 tmp->audio_region()->set_fade_out_shape (shape);
6133 begin_reversible_command (_("set fade out shape"));
6136 XMLNode &after = alist->get_state();
6137 _session->add_command(new MementoCommand<AutomationList>(*alist.get(), &before, &after));
6141 commit_reversible_command ();
6146 Editor::set_fade_in_active (bool yn)
6148 RegionSelection rs = get_regions_from_selection_and_entered ();
6153 bool in_command = false;
6155 for (RegionSelection::iterator x = rs.begin(); x != rs.end(); ++x) {
6156 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (*x);
6163 boost::shared_ptr<AudioRegion> ar (tmp->audio_region());
6165 ar->clear_changes ();
6166 ar->set_fade_in_active (yn);
6169 begin_reversible_command (_("set fade in active"));
6172 _session->add_command (new StatefulDiffCommand (ar));
6176 commit_reversible_command ();
6181 Editor::set_fade_out_active (bool yn)
6183 RegionSelection rs = get_regions_from_selection_and_entered ();
6188 bool in_command = false;
6190 for (RegionSelection::iterator x = rs.begin(); x != rs.end(); ++x) {
6191 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (*x);
6197 boost::shared_ptr<AudioRegion> ar (tmp->audio_region());
6199 ar->clear_changes ();
6200 ar->set_fade_out_active (yn);
6203 begin_reversible_command (_("set fade out active"));
6206 _session->add_command(new StatefulDiffCommand (ar));
6210 commit_reversible_command ();
6215 Editor::toggle_region_fades (int dir)
6217 if (_ignore_region_action) {
6221 boost::shared_ptr<AudioRegion> ar;
6224 RegionSelection rs = get_regions_from_selection_and_entered ();
6230 RegionSelection::iterator i;
6231 for (i = rs.begin(); i != rs.end(); ++i) {
6232 if ((ar = boost::dynamic_pointer_cast<AudioRegion>((*i)->region())) != 0) {
6234 yn = ar->fade_out_active ();
6236 yn = ar->fade_in_active ();
6242 if (i == rs.end()) {
6246 /* XXX should this undo-able? */
6247 bool in_command = false;
6249 for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
6250 if ((ar = boost::dynamic_pointer_cast<AudioRegion>((*i)->region())) == 0) {
6253 ar->clear_changes ();
6255 if (dir == 1 || dir == 0) {
6256 ar->set_fade_in_active (!yn);
6259 if (dir == -1 || dir == 0) {
6260 ar->set_fade_out_active (!yn);
6263 begin_reversible_command (_("toggle fade active"));
6266 _session->add_command(new StatefulDiffCommand (ar));
6270 commit_reversible_command ();
6275 /** Update region fade visibility after its configuration has been changed */
6277 Editor::update_region_fade_visibility ()
6279 bool _fade_visibility = _session->config.get_show_region_fades ();
6281 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
6282 AudioTimeAxisView* v = dynamic_cast<AudioTimeAxisView*>(*i);
6284 if (_fade_visibility) {
6285 v->audio_view()->show_all_fades ();
6287 v->audio_view()->hide_all_fades ();
6294 Editor::set_edit_point ()
6297 MusicFrame where (0, 0);
6299 if (!mouse_frame (where.frame, ignored)) {
6305 if (selection->markers.empty()) {
6307 mouse_add_new_marker (where.frame);
6312 Location* loc = find_location_from_marker (selection->markers.front(), ignored);
6315 loc->move_to (where.frame, where.division);
6321 Editor::set_playhead_cursor ()
6323 if (entered_marker) {
6324 _session->request_locate (entered_marker->position(), _session->transport_rolling());
6326 MusicFrame where (0, 0);
6329 if (!mouse_frame (where.frame, ignored)) {
6336 _session->request_locate (where.frame, _session->transport_rolling());
6340 //not sure what this was for; remove it for now.
6341 // if (UIConfiguration::instance().get_follow_edits() && (!_session || !_session->config.get_external_sync())) {
6342 // cancel_time_selection();
6348 Editor::split_region ()
6350 if (_drags->active ()) {
6354 //if a range is selected, separate it
6355 if ( !selection->time.empty()) {
6356 separate_regions_between (selection->time);
6360 //if no range was selected, try to find some regions to split
6361 if (current_mouse_mode() == MouseObject) { //don't try this for Internal Edit, Stretch, Draw, etc.
6363 RegionSelection rs = get_regions_from_selection_and_edit_point ();
6364 const framepos_t pos = get_preferred_edit_position();
6365 const int32_t division = get_grid_music_divisions (0);
6366 MusicFrame where (pos, division);
6372 split_regions_at (where, rs);
6378 Editor::select_next_route()
6380 if (selection->tracks.empty()) {
6381 selection->set (track_views.front());
6385 TimeAxisView* current = selection->tracks.front();
6389 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
6391 if (*i == current) {
6393 if (i != track_views.end()) {
6396 current = (*(track_views.begin()));
6397 //selection->set (*(track_views.begin()));
6403 rui = dynamic_cast<RouteUI *>(current);
6405 } while (current->hidden() || (rui == NULL) || !rui->route()->active());
6407 selection->set (current);
6409 ensure_time_axis_view_is_visible (*current, false);
6413 Editor::select_prev_route()
6415 if (selection->tracks.empty()) {
6416 selection->set (track_views.front());
6420 TimeAxisView* current = selection->tracks.front();
6424 for (TrackViewList::reverse_iterator i = track_views.rbegin(); i != track_views.rend(); ++i) {
6426 if (*i == current) {
6428 if (i != track_views.rend()) {
6431 current = *(track_views.rbegin());
6436 rui = dynamic_cast<RouteUI *>(current);
6438 } while (current->hidden() || (rui == NULL) || !rui->route()->active());
6440 selection->set (current);
6442 ensure_time_axis_view_is_visible (*current, false);
6446 Editor::set_loop_from_selection (bool play)
6448 if (_session == 0) {
6452 framepos_t start, end;
6453 if (!get_selection_extents ( start, end))
6456 set_loop_range (start, end, _("set loop range from selection"));
6459 _session->request_play_loop (true, true);
6464 Editor::set_loop_from_region (bool play)
6466 framepos_t start, end;
6467 if (!get_selection_extents ( start, end))
6470 set_loop_range (start, end, _("set loop range from region"));
6473 _session->request_locate (start, true);
6474 _session->request_play_loop (true);
6479 Editor::set_punch_from_selection ()
6481 if (_session == 0) {
6485 framepos_t start, end;
6486 if (!get_selection_extents ( start, end))
6489 set_punch_range (start, end, _("set punch range from selection"));
6493 Editor::set_auto_punch_range ()
6495 // auto punch in/out button from a single button
6496 // If Punch In is unset, set punch range from playhead to end, enable punch in
6497 // If Punch In is set, the next punch sets Punch Out, unless the playhead has been
6498 // rewound beyond the Punch In marker, in which case that marker will be moved back
6499 // to the current playhead position.
6500 // If punch out is set, it clears the punch range and Punch In/Out buttons
6502 if (_session == 0) {
6506 Location* tpl = transport_punch_location();
6507 framepos_t now = playhead_cursor->current_frame();
6508 framepos_t begin = now;
6509 framepos_t end = _session->current_end_frame();
6511 if (!_session->config.get_punch_in()) {
6512 // First Press - set punch in and create range from here to eternity
6513 set_punch_range (begin, end, _("Auto Punch In"));
6514 _session->config.set_punch_in(true);
6515 } else if (tpl && !_session->config.get_punch_out()) {
6516 // Second press - update end range marker and set punch_out
6517 if (now < tpl->start()) {
6518 // playhead has been rewound - move start back and pretend nothing happened
6520 set_punch_range (begin, end, _("Auto Punch In/Out"));
6522 // normal case for 2nd press - set the punch out
6523 end = playhead_cursor->current_frame ();
6524 set_punch_range (tpl->start(), now, _("Auto Punch In/Out"));
6525 _session->config.set_punch_out(true);
6528 if (_session->config.get_punch_out()) {
6529 _session->config.set_punch_out(false);
6532 if (_session->config.get_punch_in()) {
6533 _session->config.set_punch_in(false);
6538 // third press - unset punch in/out and remove range
6539 _session->locations()->remove(tpl);
6546 Editor::set_session_extents_from_selection ()
6548 if (_session == 0) {
6552 framepos_t start, end;
6553 if (!get_selection_extents ( start, end))
6557 if ((loc = _session->locations()->session_range_location()) == 0) {
6558 _session->set_session_extents (start, end); // this will create a new session range; no need for UNDO
6560 XMLNode &before = loc->get_state();
6562 _session->set_session_extents (start, end);
6564 XMLNode &after = loc->get_state();
6566 begin_reversible_command (_("set session start/end from selection"));
6568 _session->add_command (new MementoCommand<Location>(*loc, &before, &after));
6570 commit_reversible_command ();
6573 _session->set_end_is_free (false);
6577 Editor::set_punch_start_from_edit_point ()
6581 MusicFrame start (0, 0);
6582 framepos_t end = max_framepos;
6584 //use the existing punch end, if any
6585 Location* tpl = transport_punch_location();
6590 if ((_edit_point == EditAtPlayhead) && _session->transport_rolling()) {
6591 start.frame = _session->audible_frame();
6593 start.frame = get_preferred_edit_position();
6596 //snap the selection start/end
6599 //if there's not already a sensible selection endpoint, go "forever"
6600 if (start.frame > end ) {
6604 set_punch_range (start.frame, end, _("set punch start from EP"));
6610 Editor::set_punch_end_from_edit_point ()
6614 framepos_t start = 0;
6615 MusicFrame end (max_framepos, 0);
6617 //use the existing punch start, if any
6618 Location* tpl = transport_punch_location();
6620 start = tpl->start();
6623 if ((_edit_point == EditAtPlayhead) && _session->transport_rolling()) {
6624 end.frame = _session->audible_frame();
6626 end.frame = get_preferred_edit_position();
6629 //snap the selection start/end
6632 set_punch_range (start, end.frame, _("set punch end from EP"));
6638 Editor::set_loop_start_from_edit_point ()
6642 MusicFrame start (0, 0);
6643 framepos_t end = max_framepos;
6645 //use the existing loop end, if any
6646 Location* tpl = transport_loop_location();
6651 if ((_edit_point == EditAtPlayhead) && _session->transport_rolling()) {
6652 start.frame = _session->audible_frame();
6654 start.frame = get_preferred_edit_position();
6657 //snap the selection start/end
6660 //if there's not already a sensible selection endpoint, go "forever"
6661 if (start.frame > end ) {
6665 set_loop_range (start.frame, end, _("set loop start from EP"));
6671 Editor::set_loop_end_from_edit_point ()
6675 framepos_t start = 0;
6676 MusicFrame end (max_framepos, 0);
6678 //use the existing loop start, if any
6679 Location* tpl = transport_loop_location();
6681 start = tpl->start();
6684 if ((_edit_point == EditAtPlayhead) && _session->transport_rolling()) {
6685 end.frame = _session->audible_frame();
6687 end.frame = get_preferred_edit_position();
6690 //snap the selection start/end
6693 set_loop_range (start, end.frame, _("set loop end from EP"));
6698 Editor::set_punch_from_region ()
6700 framepos_t start, end;
6701 if (!get_selection_extents ( start, end))
6704 set_punch_range (start, end, _("set punch range from region"));
6708 Editor::pitch_shift_region ()
6710 RegionSelection rs = get_regions_from_selection_and_entered ();
6712 RegionSelection audio_rs;
6713 for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
6714 if (dynamic_cast<AudioRegionView*> (*i)) {
6715 audio_rs.push_back (*i);
6719 if (audio_rs.empty()) {
6723 pitch_shift (audio_rs, 1.2);
6727 Editor::set_tempo_from_region ()
6729 RegionSelection rs = get_regions_from_selection_and_entered ();
6731 if (!_session || rs.empty()) {
6735 RegionView* rv = rs.front();
6737 define_one_bar (rv->region()->position(), rv->region()->last_frame() + 1);
6741 Editor::use_range_as_bar ()
6743 framepos_t start, end;
6744 if (get_edit_op_range (start, end)) {
6745 define_one_bar (start, end);
6750 Editor::define_one_bar (framepos_t start, framepos_t end)
6752 framepos_t length = end - start;
6754 const Meter& m (_session->tempo_map().meter_at_frame (start));
6756 /* length = 1 bar */
6758 /* We're going to deliver a constant tempo here,
6759 so we can use frames per beat to determine length.
6760 now we want frames per beat.
6761 we have frames per bar, and beats per bar, so ...
6764 /* XXXX METER MATH */
6766 double frames_per_beat = length / m.divisions_per_bar();
6768 /* beats per minute = */
6770 double beats_per_minute = (_session->frame_rate() * 60.0) / frames_per_beat;
6772 /* now decide whether to:
6774 (a) set global tempo
6775 (b) add a new tempo marker
6779 const TempoSection& t (_session->tempo_map().tempo_section_at_frame (start));
6781 bool do_global = false;
6783 if ((_session->tempo_map().n_tempos() == 1) && (_session->tempo_map().n_meters() == 1)) {
6785 /* only 1 tempo & 1 meter: ask if the user wants to set the tempo
6786 at the start, or create a new marker
6789 vector<string> options;
6790 options.push_back (_("Cancel"));
6791 options.push_back (_("Add new marker"));
6792 options.push_back (_("Set global tempo"));
6795 _("Define one bar"),
6796 _("Do you want to set the global tempo or add a new tempo marker?"),
6800 c.set_default_response (2);
6816 /* more than 1 tempo and/or meter section already, go ahead do the "usual":
6817 if the marker is at the region starter, change it, otherwise add
6822 begin_reversible_command (_("set tempo from region"));
6823 XMLNode& before (_session->tempo_map().get_state());
6826 _session->tempo_map().change_initial_tempo (beats_per_minute, t.note_type(), t.end_note_types_per_minute());
6827 } else if (t.frame() == start) {
6828 _session->tempo_map().change_existing_tempo_at (start, beats_per_minute, t.note_type(), t.end_note_types_per_minute());
6830 /* constant tempo */
6831 const Tempo tempo (beats_per_minute, t.note_type());
6832 _session->tempo_map().add_tempo (tempo, 0.0, start, AudioTime);
6835 XMLNode& after (_session->tempo_map().get_state());
6837 _session->add_command (new MementoCommand<TempoMap>(_session->tempo_map(), &before, &after));
6838 commit_reversible_command ();
6842 Editor::split_region_at_transients ()
6844 AnalysisFeatureList positions;
6846 RegionSelection rs = get_regions_from_selection_and_entered ();
6848 if (!_session || rs.empty()) {
6852 begin_reversible_command (_("split regions"));
6854 for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ) {
6856 RegionSelection::iterator tmp;
6861 boost::shared_ptr<AudioRegion> ar = boost::dynamic_pointer_cast<AudioRegion> ((*i)->region());
6864 ar->transients (positions);
6865 split_region_at_points ((*i)->region(), positions, true);
6872 commit_reversible_command ();
6877 Editor::split_region_at_points (boost::shared_ptr<Region> r, AnalysisFeatureList& positions, bool can_ferret, bool select_new)
6879 bool use_rhythmic_rodent = false;
6881 boost::shared_ptr<Playlist> pl = r->playlist();
6883 list<boost::shared_ptr<Region> > new_regions;
6889 if (positions.empty()) {
6893 if (positions.size() > 20 && can_ferret) {
6894 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);
6895 MessageDialog msg (msgstr,
6898 Gtk::BUTTONS_OK_CANCEL);
6901 msg.add_button (_("Call for the Ferret!"), RESPONSE_APPLY);
6902 msg.set_secondary_text (_("Press OK to continue with this split operation\nor ask the Ferret dialog to tune the analysis"));
6904 msg.set_secondary_text (_("Press OK to continue with this split operation"));
6907 msg.set_title (_("Excessive split?"));
6910 int response = msg.run();
6916 case RESPONSE_APPLY:
6917 use_rhythmic_rodent = true;
6924 if (use_rhythmic_rodent) {
6925 show_rhythm_ferret ();
6929 AnalysisFeatureList::const_iterator x;
6931 pl->clear_changes ();
6932 pl->clear_owned_changes ();
6934 x = positions.begin();
6936 if (x == positions.end()) {
6941 pl->remove_region (r);
6945 framepos_t rstart = r->first_frame ();
6946 framepos_t rend = r->last_frame ();
6948 while (x != positions.end()) {
6950 /* deal with positons that are out of scope of present region bounds */
6951 if (*x <= rstart || *x > rend) {
6956 /* file start = original start + how far we from the initial position ? */
6958 framepos_t file_start = r->start() + pos;
6960 /* length = next position - current position */
6962 framepos_t len = (*x) - pos - rstart;
6964 /* XXX we do we really want to allow even single-sample regions?
6965 * shouldn't we have some kind of lower limit on region size?
6974 if (RegionFactory::region_name (new_name, r->name())) {
6978 /* do NOT announce new regions 1 by one, just wait till they are all done */
6982 plist.add (ARDOUR::Properties::start, file_start);
6983 plist.add (ARDOUR::Properties::length, len);
6984 plist.add (ARDOUR::Properties::name, new_name);
6985 plist.add (ARDOUR::Properties::layer, 0);
6986 // TODO set transients_offset
6988 boost::shared_ptr<Region> nr = RegionFactory::create (r->sources(), plist, false);
6989 /* because we set annouce to false, manually add the new region to the
6992 RegionFactory::map_add (nr);
6994 pl->add_region (nr, rstart + pos);
6997 new_regions.push_front(nr);
7006 RegionFactory::region_name (new_name, r->name());
7008 /* Add the final region */
7011 plist.add (ARDOUR::Properties::start, r->start() + pos);
7012 plist.add (ARDOUR::Properties::length, r->last_frame() - (r->position() + pos) + 1);
7013 plist.add (ARDOUR::Properties::name, new_name);
7014 plist.add (ARDOUR::Properties::layer, 0);
7016 boost::shared_ptr<Region> nr = RegionFactory::create (r->sources(), plist, false);
7017 /* because we set annouce to false, manually add the new region to the
7020 RegionFactory::map_add (nr);
7021 pl->add_region (nr, r->position() + pos);
7024 new_regions.push_front(nr);
7029 /* We might have removed regions, which alters other regions' layering_index,
7030 so we need to do a recursive diff here.
7032 vector<Command*> cmds;
7034 _session->add_commands (cmds);
7036 _session->add_command (new StatefulDiffCommand (pl));
7040 for (list<boost::shared_ptr<Region> >::iterator i = new_regions.begin(); i != new_regions.end(); ++i){
7041 set_selected_regionview_from_region_list ((*i), Selection::Add);
7047 Editor::place_transient()
7053 RegionSelection rs = get_regions_from_selection_and_edit_point ();
7059 framepos_t where = get_preferred_edit_position();
7061 begin_reversible_command (_("place transient"));
7063 for (RegionSelection::iterator r = rs.begin(); r != rs.end(); ++r) {
7064 (*r)->region()->add_transient(where);
7067 commit_reversible_command ();
7071 Editor::remove_transient(ArdourCanvas::Item* item)
7077 ArdourCanvas::Line* _line = reinterpret_cast<ArdourCanvas::Line*> (item);
7080 AudioRegionView* _arv = reinterpret_cast<AudioRegionView*> (item->get_data ("regionview"));
7081 _arv->remove_transient (*(float*) _line->get_data ("position"));
7085 Editor::snap_regions_to_grid ()
7087 list <boost::shared_ptr<Playlist > > used_playlists;
7089 RegionSelection rs = get_regions_from_selection_and_entered ();
7091 if (!_session || rs.empty()) {
7095 begin_reversible_command (_("snap regions to grid"));
7097 for (RegionSelection::iterator r = rs.begin(); r != rs.end(); ++r) {
7099 boost::shared_ptr<Playlist> pl = (*r)->region()->playlist();
7101 if (!pl->frozen()) {
7102 /* we haven't seen this playlist before */
7104 /* remember used playlists so we can thaw them later */
7105 used_playlists.push_back(pl);
7108 (*r)->region()->clear_changes ();
7110 MusicFrame start ((*r)->region()->first_frame (), 0);
7112 (*r)->region()->set_position (start.frame, start.division);
7113 _session->add_command(new StatefulDiffCommand ((*r)->region()));
7116 while (used_playlists.size() > 0) {
7117 list <boost::shared_ptr<Playlist > >::iterator i = used_playlists.begin();
7119 used_playlists.pop_front();
7122 commit_reversible_command ();
7126 Editor::close_region_gaps ()
7128 list <boost::shared_ptr<Playlist > > used_playlists;
7130 RegionSelection rs = get_regions_from_selection_and_entered ();
7132 if (!_session || rs.empty()) {
7136 Dialog dialog (_("Close Region Gaps"));
7139 table.set_spacings (12);
7140 table.set_border_width (12);
7141 Label* l = manage (left_aligned_label (_("Crossfade length")));
7142 table.attach (*l, 0, 1, 0, 1);
7144 SpinButton spin_crossfade (1, 0);
7145 spin_crossfade.set_range (0, 15);
7146 spin_crossfade.set_increments (1, 1);
7147 spin_crossfade.set_value (5);
7148 table.attach (spin_crossfade, 1, 2, 0, 1);
7150 table.attach (*manage (new Label (_("ms"))), 2, 3, 0, 1);
7152 l = manage (left_aligned_label (_("Pull-back length")));
7153 table.attach (*l, 0, 1, 1, 2);
7155 SpinButton spin_pullback (1, 0);
7156 spin_pullback.set_range (0, 100);
7157 spin_pullback.set_increments (1, 1);
7158 spin_pullback.set_value(30);
7159 table.attach (spin_pullback, 1, 2, 1, 2);
7161 table.attach (*manage (new Label (_("ms"))), 2, 3, 1, 2);
7163 dialog.get_vbox()->pack_start (table);
7164 dialog.add_button (Stock::CANCEL, RESPONSE_CANCEL);
7165 dialog.add_button (_("Ok"), RESPONSE_ACCEPT);
7168 if (dialog.run () == RESPONSE_CANCEL) {
7172 framepos_t crossfade_len = spin_crossfade.get_value();
7173 framepos_t pull_back_frames = spin_pullback.get_value();
7175 crossfade_len = lrintf (crossfade_len * _session->frame_rate()/1000);
7176 pull_back_frames = lrintf (pull_back_frames * _session->frame_rate()/1000);
7178 /* Iterate over the region list and make adjacent regions overlap by crossfade_len_ms */
7180 begin_reversible_command (_("close region gaps"));
7183 boost::shared_ptr<Region> last_region;
7185 rs.sort_by_position_and_track();
7187 for (RegionSelection::iterator r = rs.begin(); r != rs.end(); ++r) {
7189 boost::shared_ptr<Playlist> pl = (*r)->region()->playlist();
7191 if (!pl->frozen()) {
7192 /* we haven't seen this playlist before */
7194 /* remember used playlists so we can thaw them later */
7195 used_playlists.push_back(pl);
7199 framepos_t position = (*r)->region()->position();
7201 if (idx == 0 || position < last_region->position()){
7202 last_region = (*r)->region();
7207 (*r)->region()->clear_changes ();
7208 (*r)->region()->trim_front( (position - pull_back_frames));
7210 last_region->clear_changes ();
7211 last_region->trim_end( (position - pull_back_frames + crossfade_len));
7213 _session->add_command (new StatefulDiffCommand ((*r)->region()));
7214 _session->add_command (new StatefulDiffCommand (last_region));
7216 last_region = (*r)->region();
7220 while (used_playlists.size() > 0) {
7221 list <boost::shared_ptr<Playlist > >::iterator i = used_playlists.begin();
7223 used_playlists.pop_front();
7226 commit_reversible_command ();
7230 Editor::tab_to_transient (bool forward)
7232 AnalysisFeatureList positions;
7234 RegionSelection rs = get_regions_from_selection_and_entered ();
7240 framepos_t pos = _session->audible_frame ();
7242 if (!selection->tracks.empty()) {
7244 /* don't waste time searching for transients in duplicate playlists.
7247 TrackViewList ts = selection->tracks.filter_to_unique_playlists ();
7249 for (TrackViewList::iterator t = ts.begin(); t != ts.end(); ++t) {
7251 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (*t);
7254 boost::shared_ptr<Track> tr = rtv->track();
7256 boost::shared_ptr<Playlist> pl = tr->playlist ();
7258 framepos_t result = pl->find_next_transient (pos, forward ? 1 : -1);
7261 positions.push_back (result);
7274 for (RegionSelection::iterator r = rs.begin(); r != rs.end(); ++r) {
7275 (*r)->region()->get_transients (positions);
7279 TransientDetector::cleanup_transients (positions, _session->frame_rate(), 3.0);
7282 AnalysisFeatureList::iterator x;
7284 for (x = positions.begin(); x != positions.end(); ++x) {
7290 if (x != positions.end ()) {
7291 _session->request_locate (*x);
7295 AnalysisFeatureList::reverse_iterator x;
7297 for (x = positions.rbegin(); x != positions.rend(); ++x) {
7303 if (x != positions.rend ()) {
7304 _session->request_locate (*x);
7310 Editor::playhead_forward_to_grid ()
7316 MusicFrame pos (playhead_cursor->current_frame (), 0);
7318 if (pos.frame < max_framepos - 1) {
7320 snap_to_internal (pos, RoundUpAlways, false, true);
7321 _session->request_locate (pos.frame);
7327 Editor::playhead_backward_to_grid ()
7333 MusicFrame pos (playhead_cursor->current_frame (), 0);
7335 if (pos.frame > 2) {
7337 snap_to_internal (pos, RoundDownAlways, false, true);
7338 _session->request_locate (pos.frame);
7343 Editor::set_track_height (Height h)
7345 TrackSelection& ts (selection->tracks);
7347 for (TrackSelection::iterator x = ts.begin(); x != ts.end(); ++x) {
7348 (*x)->set_height_enum (h);
7353 Editor::toggle_tracks_active ()
7355 TrackSelection& ts (selection->tracks);
7357 bool target = false;
7363 for (TrackSelection::iterator x = ts.begin(); x != ts.end(); ++x) {
7364 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*>(*x);
7368 target = !rtv->_route->active();
7371 rtv->_route->set_active (target, this);
7377 Editor::remove_tracks ()
7379 /* this will delete GUI objects that may be the subject of an event
7380 handler in which this method is called. Defer actual deletion to the
7381 next idle callback, when all event handling is finished.
7383 Glib::signal_idle().connect (sigc::mem_fun (*this, &Editor::idle_remove_tracks));
7387 Editor::idle_remove_tracks ()
7389 Session::StateProtector sp (_session);
7391 return false; /* do not call again */
7395 Editor::_remove_tracks ()
7397 TrackSelection& ts (selection->tracks);
7403 vector<string> choices;
7407 const char* trackstr;
7409 vector<boost::shared_ptr<Route> > routes;
7410 bool special_bus = false;
7412 for (TrackSelection::iterator x = ts.begin(); x != ts.end(); ++x) {
7413 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (*x);
7417 if (rtv->is_track()) {
7422 routes.push_back (rtv->_route);
7424 if (rtv->route()->is_master() || rtv->route()->is_monitor()) {
7429 if (special_bus && !Config->get_allow_special_bus_removal()) {
7430 MessageDialog msg (_("That would be bad news ...."),
7434 msg.set_secondary_text (string_compose (_(
7435 "Removing the master or monitor bus is such a bad idea\n\
7436 that %1 is not going to allow it.\n\
7438 If you really want to do this sort of thing\n\
7439 edit your ardour.rc file to set the\n\
7440 \"allow-special-bus-removal\" option to be \"yes\""), PROGRAM_NAME));
7447 if (ntracks + nbusses == 0) {
7451 trackstr = P_("track", "tracks", ntracks);
7452 busstr = P_("bus", "busses", nbusses);
7456 prompt = string_compose (_("Do you really want to remove %1 %2 and %3 %4?\n"
7457 "(You may also lose the playlists associated with the %2)\n\n"
7458 "This action cannot be undone, and the session file will be overwritten!"),
7459 ntracks, trackstr, nbusses, busstr);
7461 prompt = string_compose (_("Do you really want to remove %1 %2?\n"
7462 "(You may also lose the playlists associated with the %2)\n\n"
7463 "This action cannot be undone, and the session file will be overwritten!"),
7466 } else if (nbusses) {
7467 prompt = string_compose (_("Do you really want to remove %1 %2?\n\n"
7468 "This action cannot be undone, and the session file will be overwritten"),
7472 choices.push_back (_("No, do nothing."));
7473 if (ntracks + nbusses > 1) {
7474 choices.push_back (_("Yes, remove them."));
7476 choices.push_back (_("Yes, remove it."));
7481 title = string_compose (_("Remove %1"), trackstr);
7483 title = string_compose (_("Remove %1"), busstr);
7486 Choice prompter (title, prompt, choices);
7488 if (prompter.run () != 1) {
7492 if (current_mixer_strip && routes.size () > 1 && std::find (routes.begin(), routes.end(), current_mixer_strip->route()) != routes.end ()) {
7493 /* Route deletion calls Editor::timeaxisview_deleted() iteratively (for each deleted
7494 * route). If the deleted route is currently displayed in the Editor-Mixer (highly
7495 * likely because deletion requires selection) this will call
7496 * Editor::set_selected_mixer_strip () which is expensive ( MixerStrip::set_route() ).
7497 * It's likewise likely that the route that has just been displayed in the
7498 * Editor-Mixer will be next in line for deletion.
7500 * So simply switch to the master-bus (if present)
7502 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
7503 if ((*i)->stripable ()->is_master ()) {
7504 set_selected_mixer_strip (*(*i));
7511 PresentationInfo::ChangeSuspender cs;
7512 DisplaySuspender ds;
7514 boost::shared_ptr<RouteList> rl (new RouteList);
7515 for (vector<boost::shared_ptr<Route> >::iterator x = routes.begin(); x != routes.end(); ++x) {
7518 _session->remove_routes (rl);
7520 /* TrackSelection and RouteList leave scope,
7521 * destructors are called,
7522 * diskstream drops references, save_state is called (again for every track)
7527 Editor::do_insert_time ()
7529 if (selection->tracks.empty()) {
7533 InsertRemoveTimeDialog d (*this);
7534 int response = d.run ();
7536 if (response != RESPONSE_OK) {
7540 if (d.distance() == 0) {
7547 d.intersected_region_action (),
7551 d.move_glued_markers(),
7552 d.move_locked_markers(),
7558 Editor::insert_time (
7559 framepos_t pos, framecnt_t frames, InsertTimeOption opt,
7560 bool all_playlists, bool ignore_music_glue, bool markers_too, bool glued_markers_too, bool locked_markers_too, bool tempo_too
7564 if (Config->get_edit_mode() == Lock) {
7567 bool in_command = false;
7569 TrackViewList ts = selection->tracks.filter_to_unique_playlists ();
7571 for (TrackViewList::iterator x = ts.begin(); x != ts.end(); ++x) {
7575 /* don't operate on any playlist more than once, which could
7576 * happen if "all playlists" is enabled, but there is more
7577 * than 1 track using playlists "from" a given track.
7580 set<boost::shared_ptr<Playlist> > pl;
7582 if (all_playlists) {
7583 RouteTimeAxisView* rtav = dynamic_cast<RouteTimeAxisView*> (*x);
7584 if (rtav && rtav->track ()) {
7585 vector<boost::shared_ptr<Playlist> > all = _session->playlists->playlists_for_track (rtav->track ());
7586 for (vector<boost::shared_ptr<Playlist> >::iterator p = all.begin(); p != all.end(); ++p) {
7591 if ((*x)->playlist ()) {
7592 pl.insert ((*x)->playlist ());
7596 for (set<boost::shared_ptr<Playlist> >::iterator i = pl.begin(); i != pl.end(); ++i) {
7598 (*i)->clear_changes ();
7599 (*i)->clear_owned_changes ();
7602 begin_reversible_command (_("insert time"));
7606 if (opt == SplitIntersected) {
7607 /* non musical split */
7608 (*i)->split (MusicFrame (pos, 0));
7611 (*i)->shift (pos, frames, (opt == MoveIntersected), ignore_music_glue);
7613 vector<Command*> cmds;
7615 _session->add_commands (cmds);
7617 _session->add_command (new StatefulDiffCommand (*i));
7621 RouteTimeAxisView* rtav = dynamic_cast<RouteTimeAxisView*> (*x);
7624 begin_reversible_command (_("insert time"));
7627 rtav->route ()->shift (pos, frames);
7634 const int32_t divisions = get_grid_music_divisions (0);
7635 XMLNode& before (_session->locations()->get_state());
7636 Locations::LocationList copy (_session->locations()->list());
7638 for (Locations::LocationList::iterator i = copy.begin(); i != copy.end(); ++i) {
7640 Locations::LocationList::const_iterator tmp;
7642 if ((*i)->position_lock_style() == AudioTime || glued_markers_too) {
7643 bool const was_locked = (*i)->locked ();
7644 if (locked_markers_too) {
7648 if ((*i)->start() >= pos) {
7649 // move end first, in case we're moving by more than the length of the range
7650 if (!(*i)->is_mark()) {
7651 (*i)->set_end ((*i)->end() + frames, false, true, divisions);
7653 (*i)->set_start ((*i)->start() + frames, false, true, divisions);
7665 begin_reversible_command (_("insert time"));
7668 XMLNode& after (_session->locations()->get_state());
7669 _session->add_command (new MementoCommand<Locations>(*_session->locations(), &before, &after));
7675 begin_reversible_command (_("insert time"));
7678 XMLNode& before (_session->tempo_map().get_state());
7679 _session->tempo_map().insert_time (pos, frames);
7680 XMLNode& after (_session->tempo_map().get_state());
7681 _session->add_command (new MementoCommand<TempoMap>(_session->tempo_map(), &before, &after));
7685 commit_reversible_command ();
7690 Editor::do_remove_time ()
7692 if (selection->tracks.empty()) {
7696 InsertRemoveTimeDialog d (*this, true);
7698 int response = d.run ();
7700 if (response != RESPONSE_OK) {
7704 framecnt_t distance = d.distance();
7706 if (distance == 0) {
7716 d.move_glued_markers(),
7717 d.move_locked_markers(),
7723 Editor::remove_time (framepos_t pos, framecnt_t frames, InsertTimeOption opt,
7724 bool ignore_music_glue, bool markers_too, bool glued_markers_too, bool locked_markers_too, bool tempo_too)
7726 if (Config->get_edit_mode() == Lock) {
7727 error << (_("Cannot insert or delete time when in Lock edit.")) << endmsg;
7730 bool in_command = false;
7732 for (TrackSelection::iterator x = selection->tracks.begin(); x != selection->tracks.end(); ++x) {
7734 boost::shared_ptr<Playlist> pl = (*x)->playlist();
7738 XMLNode &before = pl->get_state();
7741 begin_reversible_command (_("remove time"));
7745 std::list<AudioRange> rl;
7746 AudioRange ar(pos, pos+frames, 0);
7749 pl->shift (pos, -frames, true, ignore_music_glue);
7751 XMLNode &after = pl->get_state();
7753 _session->add_command (new MementoCommand<Playlist> (*pl, &before, &after));
7757 RouteTimeAxisView* rtav = dynamic_cast<RouteTimeAxisView*> (*x);
7760 begin_reversible_command (_("remove time"));
7763 rtav->route ()->shift (pos, -frames);
7767 const int32_t divisions = get_grid_music_divisions (0);
7768 std::list<Location*> loc_kill_list;
7773 XMLNode& before (_session->locations()->get_state());
7774 Locations::LocationList copy (_session->locations()->list());
7776 for (Locations::LocationList::iterator i = copy.begin(); i != copy.end(); ++i) {
7777 if ((*i)->position_lock_style() == AudioTime || glued_markers_too) {
7779 bool const was_locked = (*i)->locked ();
7780 if (locked_markers_too) {
7784 if (!(*i)->is_mark()) { // it's a range; have to handle both start and end
7785 if ((*i)->end() >= pos
7786 && (*i)->end() < pos+frames
7787 && (*i)->start() >= pos
7788 && (*i)->end() < pos+frames) { // range is completely enclosed; kill it
7790 loc_kill_list.push_back(*i);
7791 } else { // only start or end is included, try to do the right thing
7792 // move start before moving end, to avoid trying to move the end to before the start
7793 // if we're removing more time than the length of the range
7794 if ((*i)->start() >= pos && (*i)->start() < pos+frames) {
7795 // start is within cut
7796 (*i)->set_start (pos, false, true,divisions); // bring the start marker to the beginning of the cut
7798 } else if ((*i)->start() >= pos+frames) {
7799 // start (and thus entire range) lies beyond end of cut
7800 (*i)->set_start ((*i)->start() - frames, false, true, divisions); // slip the start marker back
7803 if ((*i)->end() >= pos && (*i)->end() < pos+frames) {
7804 // end is inside cut
7805 (*i)->set_end (pos, false, true, divisions); // bring the end to the cut
7807 } else if ((*i)->end() >= pos+frames) {
7808 // end is beyond end of cut
7809 (*i)->set_end ((*i)->end() - frames, false, true, divisions); // slip the end marker back
7814 } else if ((*i)->start() >= pos && (*i)->start() < pos+frames ) {
7815 loc_kill_list.push_back(*i);
7817 } else if ((*i)->start() >= pos) {
7818 (*i)->set_start ((*i)->start() -frames, false, true, divisions);
7828 for (list<Location*>::iterator i = loc_kill_list.begin(); i != loc_kill_list.end(); ++i) {
7829 _session->locations()->remove( *i );
7834 begin_reversible_command (_("remove time"));
7837 XMLNode& after (_session->locations()->get_state());
7838 _session->add_command (new MementoCommand<Locations>(*_session->locations(), &before, &after));
7843 XMLNode& before (_session->tempo_map().get_state());
7845 if (_session->tempo_map().remove_time (pos, frames) ) {
7847 begin_reversible_command (_("remove time"));
7850 XMLNode& after (_session->tempo_map().get_state());
7851 _session->add_command (new MementoCommand<TempoMap>(_session->tempo_map(), &before, &after));
7856 commit_reversible_command ();
7861 Editor::fit_selection ()
7863 if (!selection->tracks.empty()) {
7864 fit_tracks (selection->tracks);
7868 /* no selected tracks - use tracks with selected regions */
7870 if (!selection->regions.empty()) {
7871 for (RegionSelection::iterator r = selection->regions.begin(); r != selection->regions.end(); ++r) {
7872 tvl.push_back (&(*r)->get_time_axis_view ());
7878 } else if (internal_editing()) {
7879 /* no selected tracks, or regions, but in internal edit mode, so follow the mouse and use
7882 if (entered_track) {
7883 tvl.push_back (entered_track);
7891 Editor::fit_tracks (TrackViewList & tracks)
7893 if (tracks.empty()) {
7897 uint32_t child_heights = 0;
7898 int visible_tracks = 0;
7900 for (TrackSelection::iterator t = tracks.begin(); t != tracks.end(); ++t) {
7902 if (!(*t)->marked_for_display()) {
7906 child_heights += (*t)->effective_height() - (*t)->current_height();
7910 /* compute the per-track height from:
7912 * total canvas visible height
7913 * - height that will be taken by visible children of selected tracks
7914 * - height of the ruler/hscroll area
7916 uint32_t h = (uint32_t) floor ((trackviews_height() - child_heights) / visible_tracks);
7917 double first_y_pos = DBL_MAX;
7919 if (h < TimeAxisView::preset_height (HeightSmall)) {
7920 MessageDialog msg (_("There are too many tracks to fit in the current window"));
7921 /* too small to be displayed */
7925 undo_visual_stack.push_back (current_visual_state (true));
7926 PBD::Unwinder<bool> nsv (no_save_visual, true);
7928 /* build a list of all tracks, including children */
7931 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
7933 TimeAxisView::Children c = (*i)->get_child_list ();
7934 for (TimeAxisView::Children::iterator j = c.begin(); j != c.end(); ++j) {
7935 all.push_back (j->get());
7940 // find selection range.
7941 // if someone knows how to user TrackViewList::iterator for this
7943 int selected_top = -1;
7944 int selected_bottom = -1;
7946 for (TrackViewList::iterator t = all.begin(); t != all.end(); ++t, ++i) {
7947 if ((*t)->marked_for_display ()) {
7948 if (tracks.contains(*t)) {
7949 if (selected_top == -1) {
7952 selected_bottom = i;
7958 for (TrackViewList::iterator t = all.begin(); t != all.end(); ++t, ++i) {
7959 if ((*t)->marked_for_display ()) {
7960 if (tracks.contains(*t)) {
7961 (*t)->set_height (h);
7962 first_y_pos = std::min ((*t)->y_position (), first_y_pos);
7964 if (i > selected_top && i < selected_bottom) {
7965 hide_track_in_display (*t);
7972 set the controls_layout height now, because waiting for its size
7973 request signal handler will cause the vertical adjustment setting to fail
7976 controls_layout.property_height () = _full_canvas_height;
7977 vertical_adjustment.set_value (first_y_pos);
7979 redo_visual_stack.push_back (current_visual_state (true));
7981 visible_tracks_selector.set_text (_("Sel"));
7985 Editor::save_visual_state (uint32_t n)
7987 while (visual_states.size() <= n) {
7988 visual_states.push_back (0);
7991 if (visual_states[n] != 0) {
7992 delete visual_states[n];
7995 visual_states[n] = current_visual_state (true);
8000 Editor::goto_visual_state (uint32_t n)
8002 if (visual_states.size() <= n) {
8006 if (visual_states[n] == 0) {
8010 use_visual_state (*visual_states[n]);
8014 Editor::start_visual_state_op (uint32_t n)
8016 save_visual_state (n);
8018 PopUp* pup = new PopUp (WIN_POS_MOUSE, 1000, true);
8020 snprintf (buf, sizeof (buf), _("Saved view %u"), n+1);
8021 pup->set_text (buf);
8026 Editor::cancel_visual_state_op (uint32_t n)
8028 goto_visual_state (n);
8032 Editor::toggle_region_mute ()
8034 if (_ignore_region_action) {
8038 RegionSelection rs = get_regions_from_selection_and_entered ();
8044 if (rs.size() > 1) {
8045 begin_reversible_command (_("mute regions"));
8047 begin_reversible_command (_("mute region"));
8050 for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
8052 (*i)->region()->playlist()->clear_changes ();
8053 (*i)->region()->set_muted (!(*i)->region()->muted ());
8054 _session->add_command (new StatefulDiffCommand ((*i)->region()));
8058 commit_reversible_command ();
8062 Editor::combine_regions ()
8064 /* foreach track with selected regions, take all selected regions
8065 and join them into a new region containing the subregions (as a
8069 typedef set<RouteTimeAxisView*> RTVS;
8072 if (selection->regions.empty()) {
8076 for (RegionSelection::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
8077 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*>(&(*i)->get_time_axis_view());
8080 tracks.insert (rtv);
8084 begin_reversible_command (_("combine regions"));
8086 vector<RegionView*> new_selection;
8088 for (RTVS::iterator i = tracks.begin(); i != tracks.end(); ++i) {
8091 if ((rv = (*i)->combine_regions ()) != 0) {
8092 new_selection.push_back (rv);
8096 selection->clear_regions ();
8097 for (vector<RegionView*>::iterator i = new_selection.begin(); i != new_selection.end(); ++i) {
8098 selection->add (*i);
8101 commit_reversible_command ();
8105 Editor::uncombine_regions ()
8107 typedef set<RouteTimeAxisView*> RTVS;
8110 if (selection->regions.empty()) {
8114 for (RegionSelection::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
8115 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*>(&(*i)->get_time_axis_view());
8118 tracks.insert (rtv);
8122 begin_reversible_command (_("uncombine regions"));
8124 for (RTVS::iterator i = tracks.begin(); i != tracks.end(); ++i) {
8125 (*i)->uncombine_regions ();
8128 commit_reversible_command ();
8132 Editor::toggle_midi_input_active (bool flip_others)
8135 boost::shared_ptr<RouteList> rl (new RouteList);
8137 for (TrackSelection::iterator i = selection->tracks.begin(); i != selection->tracks.end(); ++i) {
8138 RouteTimeAxisView *rtav = dynamic_cast<RouteTimeAxisView *>(*i);
8144 boost::shared_ptr<MidiTrack> mt = rtav->midi_track();
8147 rl->push_back (rtav->route());
8148 onoff = !mt->input_active();
8152 _session->set_exclusive_input_active (rl, onoff, flip_others);
8155 static bool ok_fine (GdkEventAny*) { return true; }
8161 lock_dialog = new Gtk::Dialog (string_compose (_("%1: Locked"), PROGRAM_NAME), true);
8163 Gtk::Image* padlock = manage (new Gtk::Image (ARDOUR_UI_UTILS::get_icon ("padlock_closed")));
8164 lock_dialog->get_vbox()->pack_start (*padlock);
8165 lock_dialog->signal_delete_event ().connect (sigc::ptr_fun (ok_fine));
8167 ArdourButton* b = manage (new ArdourButton);
8168 b->set_name ("lock button");
8169 b->set_text (_("Click to unlock"));
8170 b->signal_clicked.connect (sigc::mem_fun (*this, &Editor::unlock));
8171 lock_dialog->get_vbox()->pack_start (*b);
8173 lock_dialog->get_vbox()->show_all ();
8174 lock_dialog->set_size_request (200, 200);
8177 delete _main_menu_disabler;
8178 _main_menu_disabler = new MainMenuDisabler;
8180 lock_dialog->present ();
8182 lock_dialog->get_window()->set_decorations (Gdk::WMDecoration (0));
8188 lock_dialog->hide ();
8190 delete _main_menu_disabler;
8191 _main_menu_disabler = 0;
8193 if (UIConfiguration::instance().get_lock_gui_after_seconds()) {
8194 start_lock_event_timing ();
8199 Editor::bring_in_callback (Gtk::Label* label, uint32_t n, uint32_t total, string name)
8201 Gtkmm2ext::UI::instance()->call_slot (invalidator (*this), boost::bind (&Editor::update_bring_in_message, this, label, n, total, name));
8205 Editor::update_bring_in_message (Gtk::Label* label, uint32_t n, uint32_t total, string name)
8207 Timers::TimerSuspender t;
8208 label->set_text (string_compose ("Copying %1, %2 of %3", name, n, total));
8209 Gtkmm2ext::UI::instance()->flush_pending (1);
8213 Editor::bring_all_sources_into_session ()
8220 ArdourDialog w (_("Moving embedded files into session folder"));
8221 w.get_vbox()->pack_start (msg);
8224 /* flush all pending GUI events because we're about to start copying
8228 Timers::TimerSuspender t;
8229 Gtkmm2ext::UI::instance()->flush_pending (3);
8233 _session->bring_all_sources_into_session (boost::bind (&Editor::bring_in_callback, this, &msg, _1, _2, _3));