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"
66 #include "ardour/vca_manager.h"
68 #include "canvas/canvas.h"
71 #include "audio_region_view.h"
72 #include "audio_streamview.h"
73 #include "audio_time_axis.h"
74 #include "automation_region_view.h"
75 #include "automation_time_axis.h"
76 #include "control_point.h"
80 #include "editor_cursors.h"
81 #include "editor_drag.h"
82 #include "editor_regions.h"
83 #include "editor_routes.h"
84 #include "gui_thread.h"
85 #include "insert_remove_time_dialog.h"
86 #include "interthread_progress_window.h"
87 #include "item_counts.h"
89 #include "midi_region_view.h"
91 #include "mixer_strip.h"
92 #include "mouse_cursors.h"
93 #include "normalize_dialog.h"
95 #include "paste_context.h"
96 #include "patch_change_dialog.h"
97 #include "quantize_dialog.h"
98 #include "region_gain_line.h"
99 #include "rgb_macros.h"
100 #include "route_time_axis.h"
101 #include "selection.h"
102 #include "selection_templates.h"
103 #include "streamview.h"
104 #include "strip_silence_dialog.h"
105 #include "time_axis_view.h"
107 #include "transpose_dialog.h"
108 #include "transform_dialog.h"
109 #include "ui_config.h"
110 #include "vca_time_axis.h"
112 #include "pbd/i18n.h"
115 using namespace ARDOUR;
118 using namespace Gtkmm2ext;
119 using namespace ArdourWidgets;
120 using namespace Editing;
121 using Gtkmm2ext::Keyboard;
123 /***********************************************************************
125 ***********************************************************************/
128 Editor::undo (uint32_t n)
130 if (_session && _session->actively_recording()) {
131 /* no undo allowed while recording. Session will check also,
132 but we don't even want to get to that.
137 if (_drags->active ()) {
143 if (_session->undo_depth() == 0) {
144 undo_action->set_sensitive(false);
146 redo_action->set_sensitive(true);
147 begin_selection_op_history ();
152 Editor::redo (uint32_t n)
154 if (_session && _session->actively_recording()) {
155 /* no redo allowed while recording. Session will check also,
156 but we don't even want to get to that.
161 if (_drags->active ()) {
167 if (_session->redo_depth() == 0) {
168 redo_action->set_sensitive(false);
170 undo_action->set_sensitive(true);
171 begin_selection_op_history ();
176 Editor::split_regions_at (MusicFrame where, RegionSelection& regions, bool snap_frame)
180 RegionSelection pre_selected_regions = selection->regions;
181 bool working_on_selection = !pre_selected_regions.empty();
183 list<boost::shared_ptr<Playlist> > used_playlists;
184 list<RouteTimeAxisView*> used_trackviews;
186 if (regions.empty()) {
190 begin_reversible_command (_("split"));
192 // if splitting a single region, and snap-to is using
193 // region boundaries, don't pay attention to them
195 if (regions.size() == 1) {
196 switch (_snap_type) {
197 case SnapToRegionStart:
198 case SnapToRegionSync:
199 case SnapToRegionEnd:
212 EditorFreeze(); /* Emit Signal */
215 for (RegionSelection::iterator a = regions.begin(); a != regions.end(); ) {
217 RegionSelection::iterator tmp;
219 /* XXX this test needs to be more complicated, to make sure we really
220 have something to split.
223 if (!(*a)->region()->covers (where.frame)) {
231 boost::shared_ptr<Playlist> pl = (*a)->region()->playlist();
239 /* we haven't seen this playlist before */
241 /* remember used playlists so we can thaw them later */
242 used_playlists.push_back(pl);
244 TimeAxisView& tv = (*a)->get_time_axis_view();
245 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (&tv);
247 used_trackviews.push_back (rtv);
254 pl->clear_changes ();
255 pl->split_region ((*a)->region(), where);
256 _session->add_command (new StatefulDiffCommand (pl));
262 latest_regionviews.clear ();
264 vector<sigc::connection> region_added_connections;
266 for (list<RouteTimeAxisView*>::iterator i = used_trackviews.begin(); i != used_trackviews.end(); ++i) {
267 region_added_connections.push_back ((*i)->view()->RegionViewAdded.connect (sigc::mem_fun(*this, &Editor::collect_new_region_view)));
270 while (used_playlists.size() > 0) {
271 list <boost::shared_ptr<Playlist > >::iterator i = used_playlists.begin();
273 used_playlists.pop_front();
276 for (vector<sigc::connection>::iterator c = region_added_connections.begin(); c != region_added_connections.end(); ++c) {
281 EditorThaw(); /* Emit Signal */
284 if (working_on_selection) {
285 // IFF we were working on selected regions, try to reinstate the other region selections that existed before the freeze/thaw.
287 RegionSelectionAfterSplit rsas = Config->get_region_selection_after_split();
288 /* There are three classes of regions that we might want selected after
289 splitting selected regions:
290 - regions selected before the split operation, and unaffected by it
291 - newly-created regions before the split
292 - newly-created regions after the split
295 if (rsas & Existing) {
296 // region selections that existed before the split.
297 selection->add ( pre_selected_regions );
300 for (RegionSelection::iterator ri = latest_regionviews.begin(); ri != latest_regionviews.end(); ri++) {
301 if ((*ri)->region()->position() < where.frame) {
302 // new regions created before the split
303 if (rsas & NewlyCreatedLeft) {
304 selection->add (*ri);
307 // new regions created after the split
308 if (rsas & NewlyCreatedRight) {
309 selection->add (*ri);
314 if( working_on_selection ) {
315 selection->add (latest_regionviews); //these are the new regions created after the split
319 commit_reversible_command ();
322 /** Move one extreme of the current range selection. If more than one range is selected,
323 * the start of the earliest range or the end of the latest range is moved.
325 * @param move_end true to move the end of the current range selection, false to move
327 * @param next true to move the extreme to the next region boundary, false to move to
331 Editor::move_range_selection_start_or_end_to_region_boundary (bool move_end, bool next)
333 if (selection->time.start() == selection->time.end_frame()) {
337 framepos_t start = selection->time.start ();
338 framepos_t end = selection->time.end_frame ();
340 /* the position of the thing we may move */
341 framepos_t pos = move_end ? end : start;
342 int dir = next ? 1 : -1;
344 /* so we don't find the current region again */
345 if (dir > 0 || pos > 0) {
349 framepos_t const target = get_region_boundary (pos, dir, true, false);
364 begin_reversible_selection_op (_("alter selection"));
365 selection->set_preserving_all_ranges (start, end);
366 commit_reversible_selection_op ();
370 Editor::nudge_forward_release (GdkEventButton* ev)
372 if (ev->state & Keyboard::PrimaryModifier) {
373 nudge_forward (false, true);
375 nudge_forward (false, false);
381 Editor::nudge_backward_release (GdkEventButton* ev)
383 if (ev->state & Keyboard::PrimaryModifier) {
384 nudge_backward (false, true);
386 nudge_backward (false, false);
393 Editor::nudge_forward (bool next, bool force_playhead)
396 framepos_t next_distance;
402 RegionSelection rs = get_regions_from_selection_and_entered ();
404 if (!force_playhead && !rs.empty()) {
406 begin_reversible_command (_("nudge regions forward"));
408 for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
409 boost::shared_ptr<Region> r ((*i)->region());
411 distance = get_nudge_distance (r->position(), next_distance);
414 distance = next_distance;
418 r->set_position (r->position() + distance);
419 _session->add_command (new StatefulDiffCommand (r));
422 commit_reversible_command ();
425 } else if (!force_playhead && !selection->markers.empty()) {
428 bool in_command = false;
429 const int32_t divisions = get_grid_music_divisions (0);
431 for (MarkerSelection::iterator i = selection->markers.begin(); i != selection->markers.end(); ++i) {
433 Location* loc = find_location_from_marker ((*i), is_start);
437 XMLNode& before (loc->get_state());
440 distance = get_nudge_distance (loc->start(), next_distance);
442 distance = next_distance;
444 if (max_framepos - distance > loc->start() + loc->length()) {
445 loc->set_start (loc->start() + distance, false, true, divisions);
447 loc->set_start (max_framepos - loc->length(), false, true, divisions);
450 distance = get_nudge_distance (loc->end(), next_distance);
452 distance = next_distance;
454 if (max_framepos - distance > loc->end()) {
455 loc->set_end (loc->end() + distance, false, true, divisions);
457 loc->set_end (max_framepos, false, true, divisions);
459 if (loc->is_session_range()) {
460 _session->set_end_is_free (false);
464 begin_reversible_command (_("nudge location forward"));
467 XMLNode& after (loc->get_state());
468 _session->add_command (new MementoCommand<Location>(*loc, &before, &after));
473 commit_reversible_command ();
476 distance = get_nudge_distance (playhead_cursor->current_frame (), next_distance);
477 _session->request_locate (playhead_cursor->current_frame () + distance);
482 Editor::nudge_backward (bool next, bool force_playhead)
485 framepos_t next_distance;
491 RegionSelection rs = get_regions_from_selection_and_entered ();
493 if (!force_playhead && !rs.empty()) {
495 begin_reversible_command (_("nudge regions backward"));
497 for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
498 boost::shared_ptr<Region> r ((*i)->region());
500 distance = get_nudge_distance (r->position(), next_distance);
503 distance = next_distance;
508 if (r->position() > distance) {
509 r->set_position (r->position() - distance);
513 _session->add_command (new StatefulDiffCommand (r));
516 commit_reversible_command ();
518 } else if (!force_playhead && !selection->markers.empty()) {
521 bool in_command = false;
523 for (MarkerSelection::iterator i = selection->markers.begin(); i != selection->markers.end(); ++i) {
525 Location* loc = find_location_from_marker ((*i), is_start);
529 XMLNode& before (loc->get_state());
532 distance = get_nudge_distance (loc->start(), next_distance);
534 distance = next_distance;
536 if (distance < loc->start()) {
537 loc->set_start (loc->start() - distance, false, true, get_grid_music_divisions(0));
539 loc->set_start (0, false, true, get_grid_music_divisions(0));
542 distance = get_nudge_distance (loc->end(), next_distance);
545 distance = next_distance;
548 if (distance < loc->end() - loc->length()) {
549 loc->set_end (loc->end() - distance, false, true, get_grid_music_divisions(0));
551 loc->set_end (loc->length(), false, true, get_grid_music_divisions(0));
553 if (loc->is_session_range()) {
554 _session->set_end_is_free (false);
558 begin_reversible_command (_("nudge location forward"));
561 XMLNode& after (loc->get_state());
562 _session->add_command (new MementoCommand<Location>(*loc, &before, &after));
566 commit_reversible_command ();
571 distance = get_nudge_distance (playhead_cursor->current_frame (), next_distance);
573 if (playhead_cursor->current_frame () > distance) {
574 _session->request_locate (playhead_cursor->current_frame () - distance);
576 _session->goto_start();
582 Editor::nudge_forward_capture_offset ()
584 RegionSelection rs = get_regions_from_selection_and_entered ();
586 if (!_session || rs.empty()) {
590 begin_reversible_command (_("nudge forward"));
592 framepos_t const distance = _session->worst_output_latency();
594 for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
595 boost::shared_ptr<Region> r ((*i)->region());
598 r->set_position (r->position() + distance);
599 _session->add_command(new StatefulDiffCommand (r));
602 commit_reversible_command ();
606 Editor::nudge_backward_capture_offset ()
608 RegionSelection rs = get_regions_from_selection_and_entered ();
610 if (!_session || rs.empty()) {
614 begin_reversible_command (_("nudge backward"));
616 framepos_t const distance = _session->worst_output_latency();
618 for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
619 boost::shared_ptr<Region> r ((*i)->region());
623 if (r->position() > distance) {
624 r->set_position (r->position() - distance);
628 _session->add_command(new StatefulDiffCommand (r));
631 commit_reversible_command ();
634 struct RegionSelectionPositionSorter {
635 bool operator() (RegionView* a, RegionView* b) {
636 return a->region()->position() < b->region()->position();
641 Editor::sequence_regions ()
644 framepos_t r_end_prev;
652 RegionSelection rs = get_regions_from_selection_and_entered ();
653 rs.sort(RegionSelectionPositionSorter());
657 bool in_command = false;
659 for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
660 boost::shared_ptr<Region> r ((*i)->region());
668 if(r->position_locked())
675 r->set_position(r_end_prev);
679 begin_reversible_command (_("sequence regions"));
682 _session->add_command (new StatefulDiffCommand (r));
684 r_end=r->position() + r->length();
690 commit_reversible_command ();
699 Editor::move_to_start ()
701 _session->goto_start ();
705 Editor::move_to_end ()
708 _session->request_locate (_session->current_end_frame());
712 Editor::build_region_boundary_cache ()
715 vector<RegionPoint> interesting_points;
716 boost::shared_ptr<Region> r;
717 TrackViewList tracks;
720 region_boundary_cache.clear ();
726 bool maybe_first_frame = false;
728 switch (_snap_type) {
729 case SnapToRegionStart:
730 interesting_points.push_back (Start);
731 maybe_first_frame = true;
733 case SnapToRegionEnd:
734 interesting_points.push_back (End);
736 case SnapToRegionSync:
737 interesting_points.push_back (SyncPoint);
739 case SnapToRegionBoundary:
740 interesting_points.push_back (Start);
741 interesting_points.push_back (End);
742 maybe_first_frame = true;
745 fatal << string_compose (_("build_region_boundary_cache called with snap_type = %1"), _snap_type) << endmsg;
746 abort(); /*NOTREACHED*/
750 TimeAxisView *ontrack = 0;
753 if (!selection->tracks.empty()) {
754 tlist = selection->tracks.filter_to_unique_playlists ();
756 tlist = track_views.filter_to_unique_playlists ();
759 if (maybe_first_frame) {
760 TrackViewList::const_iterator i;
761 for (i = tlist.begin(); i != tlist.end(); ++i) {
762 boost::shared_ptr<Playlist> pl = (*i)->playlist();
763 if (pl && pl->count_regions_at (0)) {
764 region_boundary_cache.push_back (0);
770 while (pos < _session->current_end_frame() && !at_end) {
773 framepos_t lpos = max_framepos;
775 for (vector<RegionPoint>::iterator p = interesting_points.begin(); p != interesting_points.end(); ++p) {
777 if ((r = find_next_region (pos, *p, 1, tlist, &ontrack)) == 0) {
778 if (*p == interesting_points.back()) {
781 /* move to next point type */
787 rpos = r->first_frame();
791 rpos = r->last_frame();
795 rpos = r->sync_position ();
803 RouteTimeAxisView *rtav;
805 if (ontrack != 0 && (rtav = dynamic_cast<RouteTimeAxisView*>(ontrack)) != 0 ) {
806 if (rtav->track() != 0) {
807 speed = rtav->track()->speed();
811 rpos = track_frame_to_session_frame (rpos, speed);
817 /* prevent duplicates, but we don't use set<> because we want to be able
821 vector<framepos_t>::iterator ri;
823 for (ri = region_boundary_cache.begin(); ri != region_boundary_cache.end(); ++ri) {
829 if (ri == region_boundary_cache.end()) {
830 region_boundary_cache.push_back (rpos);
837 /* finally sort to be sure that the order is correct */
839 sort (region_boundary_cache.begin(), region_boundary_cache.end());
842 boost::shared_ptr<Region>
843 Editor::find_next_region (framepos_t frame, RegionPoint point, int32_t dir, TrackViewList& tracks, TimeAxisView **ontrack)
845 TrackViewList::iterator i;
846 framepos_t closest = max_framepos;
847 boost::shared_ptr<Region> ret;
851 framepos_t track_frame;
852 RouteTimeAxisView *rtav;
854 for (i = tracks.begin(); i != tracks.end(); ++i) {
857 boost::shared_ptr<Region> r;
860 if ( (rtav = dynamic_cast<RouteTimeAxisView*>(*i)) != 0 ) {
861 if (rtav->track()!=0)
862 track_speed = rtav->track()->speed();
865 track_frame = session_frame_to_track_frame(frame, track_speed);
867 if ((r = (*i)->find_next_region (track_frame, point, dir)) == 0) {
873 rpos = r->first_frame ();
877 rpos = r->last_frame ();
881 rpos = r->sync_position ();
885 // rpos is a "track frame", converting it to "_session frame"
886 rpos = track_frame_to_session_frame(rpos, track_speed);
889 distance = rpos - frame;
891 distance = frame - rpos;
894 if (distance < closest) {
906 Editor::find_next_region_boundary (framepos_t pos, int32_t dir, const TrackViewList& tracks)
908 framecnt_t distance = max_framepos;
909 framepos_t current_nearest = -1;
911 for (TrackViewList::const_iterator i = tracks.begin(); i != tracks.end(); ++i) {
912 framepos_t contender;
915 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (*i);
921 if ((contender = rtv->find_next_region_boundary (pos, dir)) < 0) {
925 d = ::llabs (pos - contender);
928 current_nearest = contender;
933 return current_nearest;
937 Editor::get_region_boundary (framepos_t pos, int32_t dir, bool with_selection, bool only_onscreen)
942 if (with_selection && Config->get_region_boundaries_from_selected_tracks()) {
944 if (!selection->tracks.empty()) {
946 target = find_next_region_boundary (pos, dir, selection->tracks);
950 if (only_onscreen || Config->get_region_boundaries_from_onscreen_tracks()) {
951 get_onscreen_tracks (tvl);
952 target = find_next_region_boundary (pos, dir, tvl);
954 target = find_next_region_boundary (pos, dir, track_views);
960 if (only_onscreen || Config->get_region_boundaries_from_onscreen_tracks()) {
961 get_onscreen_tracks (tvl);
962 target = find_next_region_boundary (pos, dir, tvl);
964 target = find_next_region_boundary (pos, dir, track_views);
972 Editor::cursor_to_region_boundary (bool with_selection, int32_t dir)
974 framepos_t pos = playhead_cursor->current_frame ();
981 // so we don't find the current region again..
982 if (dir > 0 || pos > 0) {
986 if ((target = get_region_boundary (pos, dir, with_selection, false)) < 0) {
990 _session->request_locate (target);
994 Editor::cursor_to_next_region_boundary (bool with_selection)
996 cursor_to_region_boundary (with_selection, 1);
1000 Editor::cursor_to_previous_region_boundary (bool with_selection)
1002 cursor_to_region_boundary (with_selection, -1);
1006 Editor::cursor_to_region_point (EditorCursor* cursor, RegionPoint point, int32_t dir)
1008 boost::shared_ptr<Region> r;
1009 framepos_t pos = cursor->current_frame ();
1015 TimeAxisView *ontrack = 0;
1017 // so we don't find the current region again..
1021 if (!selection->tracks.empty()) {
1023 r = find_next_region (pos, point, dir, selection->tracks, &ontrack);
1025 } else if (clicked_axisview) {
1028 t.push_back (clicked_axisview);
1030 r = find_next_region (pos, point, dir, t, &ontrack);
1034 r = find_next_region (pos, point, dir, track_views, &ontrack);
1043 pos = r->first_frame ();
1047 pos = r->last_frame ();
1051 pos = r->sync_position ();
1056 RouteTimeAxisView *rtav;
1058 if ( ontrack != 0 && (rtav = dynamic_cast<RouteTimeAxisView*>(ontrack)) != 0 ) {
1059 if (rtav->track() != 0) {
1060 speed = rtav->track()->speed();
1064 pos = track_frame_to_session_frame(pos, speed);
1066 if (cursor == playhead_cursor) {
1067 _session->request_locate (pos);
1069 cursor->set_position (pos);
1074 Editor::cursor_to_next_region_point (EditorCursor* cursor, RegionPoint point)
1076 cursor_to_region_point (cursor, point, 1);
1080 Editor::cursor_to_previous_region_point (EditorCursor* cursor, RegionPoint point)
1082 cursor_to_region_point (cursor, point, -1);
1086 Editor::cursor_to_selection_start (EditorCursor *cursor)
1090 switch (mouse_mode) {
1092 if (!selection->regions.empty()) {
1093 pos = selection->regions.start();
1098 if (!selection->time.empty()) {
1099 pos = selection->time.start ();
1107 if (cursor == playhead_cursor) {
1108 _session->request_locate (pos);
1110 cursor->set_position (pos);
1115 Editor::cursor_to_selection_end (EditorCursor *cursor)
1119 switch (mouse_mode) {
1121 if (!selection->regions.empty()) {
1122 pos = selection->regions.end_frame();
1127 if (!selection->time.empty()) {
1128 pos = selection->time.end_frame ();
1136 if (cursor == playhead_cursor) {
1137 _session->request_locate (pos);
1139 cursor->set_position (pos);
1144 Editor::selected_marker_to_region_boundary (bool with_selection, int32_t dir)
1154 if (selection->markers.empty()) {
1158 if (!mouse_frame (mouse, ignored)) {
1162 add_location_mark (mouse);
1165 if ((loc = find_location_from_marker (selection->markers.front(), ignored)) == 0) {
1169 framepos_t pos = loc->start();
1171 // so we don't find the current region again..
1172 if (dir > 0 || pos > 0) {
1176 if ((target = get_region_boundary (pos, dir, with_selection, false)) < 0) {
1180 loc->move_to (target, 0);
1184 Editor::selected_marker_to_next_region_boundary (bool with_selection)
1186 selected_marker_to_region_boundary (with_selection, 1);
1190 Editor::selected_marker_to_previous_region_boundary (bool with_selection)
1192 selected_marker_to_region_boundary (with_selection, -1);
1196 Editor::selected_marker_to_region_point (RegionPoint point, int32_t dir)
1198 boost::shared_ptr<Region> r;
1203 if (!_session || selection->markers.empty()) {
1207 if ((loc = find_location_from_marker (selection->markers.front(), ignored)) == 0) {
1211 TimeAxisView *ontrack = 0;
1215 // so we don't find the current region again..
1219 if (!selection->tracks.empty()) {
1221 r = find_next_region (pos, point, dir, selection->tracks, &ontrack);
1225 r = find_next_region (pos, point, dir, track_views, &ontrack);
1234 pos = r->first_frame ();
1238 pos = r->last_frame ();
1242 pos = r->adjust_to_sync (r->first_frame());
1247 RouteTimeAxisView *rtav;
1249 if (ontrack != 0 && (rtav = dynamic_cast<RouteTimeAxisView*>(ontrack)) != 0) {
1250 if (rtav->track() != 0) {
1251 speed = rtav->track()->speed();
1255 pos = track_frame_to_session_frame(pos, speed);
1257 loc->move_to (pos, 0);
1261 Editor::selected_marker_to_next_region_point (RegionPoint point)
1263 selected_marker_to_region_point (point, 1);
1267 Editor::selected_marker_to_previous_region_point (RegionPoint point)
1269 selected_marker_to_region_point (point, -1);
1273 Editor::selected_marker_to_selection_start ()
1279 if (!_session || selection->markers.empty()) {
1283 if ((loc = find_location_from_marker (selection->markers.front(), ignored)) == 0) {
1287 switch (mouse_mode) {
1289 if (!selection->regions.empty()) {
1290 pos = selection->regions.start();
1295 if (!selection->time.empty()) {
1296 pos = selection->time.start ();
1304 loc->move_to (pos, 0);
1308 Editor::selected_marker_to_selection_end ()
1314 if (!_session || selection->markers.empty()) {
1318 if ((loc = find_location_from_marker (selection->markers.front(), ignored)) == 0) {
1322 switch (mouse_mode) {
1324 if (!selection->regions.empty()) {
1325 pos = selection->regions.end_frame();
1330 if (!selection->time.empty()) {
1331 pos = selection->time.end_frame ();
1339 loc->move_to (pos, 0);
1343 Editor::scroll_playhead (bool forward)
1345 framepos_t pos = playhead_cursor->current_frame ();
1346 framecnt_t delta = (framecnt_t) floor (current_page_samples() / 0.8);
1349 if (pos == max_framepos) {
1353 if (pos < max_framepos - delta) {
1372 _session->request_locate (pos);
1376 Editor::cursor_align (bool playhead_to_edit)
1382 if (playhead_to_edit) {
1384 if (selection->markers.empty()) {
1388 _session->request_locate (selection->markers.front()->position(), _session->transport_rolling());
1391 const int32_t divisions = get_grid_music_divisions (0);
1392 /* move selected markers to playhead */
1394 for (MarkerSelection::iterator i = selection->markers.begin(); i != selection->markers.end(); ++i) {
1397 Location* loc = find_location_from_marker (*i, ignored);
1399 if (loc->is_mark()) {
1400 loc->set_start (playhead_cursor->current_frame (), false, true, divisions);
1402 loc->set (playhead_cursor->current_frame (),
1403 playhead_cursor->current_frame () + loc->length(), true, divisions);
1410 Editor::scroll_backward (float pages)
1412 framepos_t const one_page = (framepos_t) rint (_visible_canvas_width * samples_per_pixel);
1413 framepos_t const cnt = (framepos_t) floor (pages * one_page);
1416 if (leftmost_frame < cnt) {
1419 frame = leftmost_frame - cnt;
1422 reset_x_origin (frame);
1426 Editor::scroll_forward (float pages)
1428 framepos_t const one_page = (framepos_t) rint (_visible_canvas_width * samples_per_pixel);
1429 framepos_t const cnt = (framepos_t) floor (pages * one_page);
1432 if (max_framepos - cnt < leftmost_frame) {
1433 frame = max_framepos - cnt;
1435 frame = leftmost_frame + cnt;
1438 reset_x_origin (frame);
1442 Editor::scroll_tracks_down ()
1444 double vert_value = vertical_adjustment.get_value() + vertical_adjustment.get_page_size();
1445 if (vert_value > vertical_adjustment.get_upper() - _visible_canvas_height) {
1446 vert_value = vertical_adjustment.get_upper() - _visible_canvas_height;
1449 vertical_adjustment.set_value (vert_value);
1453 Editor::scroll_tracks_up ()
1455 vertical_adjustment.set_value (vertical_adjustment.get_value() - vertical_adjustment.get_page_size());
1459 Editor::scroll_tracks_down_line ()
1461 double vert_value = vertical_adjustment.get_value() + 60;
1463 if (vert_value > vertical_adjustment.get_upper() - _visible_canvas_height) {
1464 vert_value = vertical_adjustment.get_upper() - _visible_canvas_height;
1467 vertical_adjustment.set_value (vert_value);
1471 Editor::scroll_tracks_up_line ()
1473 reset_y_origin (vertical_adjustment.get_value() - 60);
1477 Editor::select_topmost_track ()
1479 const double top_of_trackviews = vertical_adjustment.get_value();
1480 for (TrackViewList::iterator t = track_views.begin(); t != track_views.end(); ++t) {
1481 if ((*t)->hidden()) {
1484 std::pair<TimeAxisView*,double> res = (*t)->covers_y_position (top_of_trackviews);
1486 selection->set (*t);
1493 Editor::scroll_down_one_track (bool skip_child_views)
1495 TrackViewList::reverse_iterator next = track_views.rend();
1496 const double top_of_trackviews = vertical_adjustment.get_value();
1498 for (TrackViewList::reverse_iterator t = track_views.rbegin(); t != track_views.rend(); ++t) {
1499 if ((*t)->hidden()) {
1503 /* If this is the upper-most visible trackview, we want to display
1504 * the one above it (next)
1506 * Note that covers_y_position() is recursive and includes child views
1508 std::pair<TimeAxisView*,double> res = (*t)->covers_y_position (top_of_trackviews);
1511 if (skip_child_views) {
1514 /* automation lane (one level, non-recursive)
1516 * - if no automation lane exists -> move to next tack
1517 * - if the first (here: bottom-most) matches -> move to next tack
1518 * - if no y-axis match is found -> the current track is at the top
1519 * -> move to last (here: top-most) automation lane
1521 TimeAxisView::Children kids = (*t)->get_child_list();
1522 TimeAxisView::Children::reverse_iterator nkid = kids.rend();
1524 for (TimeAxisView::Children::reverse_iterator ci = kids.rbegin(); ci != kids.rend(); ++ci) {
1525 if ((*ci)->hidden()) {
1529 std::pair<TimeAxisView*,double> dev;
1530 dev = (*ci)->covers_y_position (top_of_trackviews);
1532 /* some automation lane is currently at the top */
1533 if (ci == kids.rbegin()) {
1534 /* first (bottom-most) autmation lane is at the top.
1535 * -> move to next track
1544 if (nkid != kids.rend()) {
1545 ensure_time_axis_view_is_visible (**nkid, true);
1553 /* move to the track below the first one that covers the */
1555 if (next != track_views.rend()) {
1556 ensure_time_axis_view_is_visible (**next, true);
1564 Editor::scroll_up_one_track (bool skip_child_views)
1566 TrackViewList::iterator prev = track_views.end();
1567 double top_of_trackviews = vertical_adjustment.get_value ();
1569 for (TrackViewList::iterator t = track_views.begin(); t != track_views.end(); ++t) {
1571 if ((*t)->hidden()) {
1575 /* find the trackview at the top of the trackview group
1577 * Note that covers_y_position() is recursive and includes child views
1579 std::pair<TimeAxisView*,double> res = (*t)->covers_y_position (top_of_trackviews);
1582 if (skip_child_views) {
1585 /* automation lane (one level, non-recursive)
1587 * - if no automation lane exists -> move to prev tack
1588 * - if no y-axis match is found -> the current track is at the top -> move to prev track
1589 * (actually last automation lane of previous track, see below)
1590 * - if first (top-most) lane is at the top -> move to this track
1591 * - else move up one lane
1593 TimeAxisView::Children kids = (*t)->get_child_list();
1594 TimeAxisView::Children::iterator pkid = kids.end();
1596 for (TimeAxisView::Children::iterator ci = kids.begin(); ci != kids.end(); ++ci) {
1597 if ((*ci)->hidden()) {
1601 std::pair<TimeAxisView*,double> dev;
1602 dev = (*ci)->covers_y_position (top_of_trackviews);
1604 /* some automation lane is currently at the top */
1605 if (ci == kids.begin()) {
1606 /* first (top-most) autmation lane is at the top.
1607 * jump directly to this track's top
1609 ensure_time_axis_view_is_visible (**t, true);
1612 else if (pkid != kids.end()) {
1613 /* some other automation lane is at the top.
1614 * move up to prev automation lane.
1616 ensure_time_axis_view_is_visible (**pkid, true);
1619 assert(0); // not reached
1630 if (prev != track_views.end()) {
1631 // move to bottom-most automation-lane of the previous track
1632 TimeAxisView::Children kids = (*prev)->get_child_list();
1633 TimeAxisView::Children::reverse_iterator pkid = kids.rend();
1634 if (!skip_child_views) {
1635 // find the last visible lane
1636 for (TimeAxisView::Children::reverse_iterator ci = kids.rbegin(); ci != kids.rend(); ++ci) {
1637 if (!(*ci)->hidden()) {
1643 if (pkid != kids.rend()) {
1644 ensure_time_axis_view_is_visible (**pkid, true);
1646 ensure_time_axis_view_is_visible (**prev, true);
1655 Editor::scroll_left_step ()
1657 framepos_t xdelta = (current_page_samples() / 8);
1659 if (leftmost_frame > xdelta) {
1660 reset_x_origin (leftmost_frame - xdelta);
1668 Editor::scroll_right_step ()
1670 framepos_t xdelta = (current_page_samples() / 8);
1672 if (max_framepos - xdelta > leftmost_frame) {
1673 reset_x_origin (leftmost_frame + xdelta);
1675 reset_x_origin (max_framepos - current_page_samples());
1680 Editor::scroll_left_half_page ()
1682 framepos_t xdelta = (current_page_samples() / 2);
1683 if (leftmost_frame > xdelta) {
1684 reset_x_origin (leftmost_frame - xdelta);
1691 Editor::scroll_right_half_page ()
1693 framepos_t xdelta = (current_page_samples() / 2);
1694 if (max_framepos - xdelta > leftmost_frame) {
1695 reset_x_origin (leftmost_frame + xdelta);
1697 reset_x_origin (max_framepos - current_page_samples());
1704 Editor::tav_zoom_step (bool coarser)
1706 DisplaySuspender ds;
1710 if (selection->tracks.empty()) {
1713 ts = &selection->tracks;
1716 for (TrackViewList::iterator i = ts->begin(); i != ts->end(); ++i) {
1717 TimeAxisView *tv = (static_cast<TimeAxisView*>(*i));
1718 tv->step_height (coarser);
1723 Editor::tav_zoom_smooth (bool coarser, bool force_all)
1725 DisplaySuspender ds;
1729 if (selection->tracks.empty() || force_all) {
1732 ts = &selection->tracks;
1735 for (TrackViewList::iterator i = ts->begin(); i != ts->end(); ++i) {
1736 TimeAxisView *tv = (static_cast<TimeAxisView*>(*i));
1737 uint32_t h = tv->current_height ();
1742 if (h >= TimeAxisView::preset_height (HeightSmall)) {
1747 tv->set_height (h + 5);
1753 Editor::temporal_zoom_step_mouse_focus_scale (bool zoom_out, double scale)
1755 Editing::ZoomFocus temp_focus = zoom_focus;
1756 zoom_focus = Editing::ZoomFocusMouse;
1757 temporal_zoom_step_scale (zoom_out, scale);
1758 zoom_focus = temp_focus;
1762 Editor::temporal_zoom_step_mouse_focus (bool zoom_out)
1764 temporal_zoom_step_mouse_focus_scale (zoom_out, 2.0);
1768 Editor::temporal_zoom_step (bool zoom_out)
1770 temporal_zoom_step_scale (zoom_out, 2.0);
1774 Editor::temporal_zoom_step_scale (bool zoom_out, double scale)
1776 ENSURE_GUI_THREAD (*this, &Editor::temporal_zoom_step, zoom_out, scale)
1778 framecnt_t nspp = samples_per_pixel;
1782 if (nspp == samples_per_pixel) {
1787 if (nspp == samples_per_pixel) {
1792 // ToDo: encapsulate all of this into something like editor::get_session_extents() or editor::leftmost(), rightmost()
1794 //ToDo: also incorporate automation regions (in case the session has no audio/midi but is just used for automating plugins or the like)
1796 //calculate the extents of all regions in every playlist
1797 framecnt_t session_extent_start = 0;
1798 framecnt_t session_extent_end = 0;
1800 boost::shared_ptr<RouteList> rl = _session->get_routes();
1801 for (RouteList::iterator r = rl->begin(); r != rl->end(); ++r) {
1802 boost::shared_ptr<Track> tr = boost::dynamic_pointer_cast<Track> (*r);
1804 boost::shared_ptr<Playlist> pl = tr->playlist();
1806 pair<framepos_t, framepos_t> e;
1807 e = pl->get_extent();
1808 if (e.first < session_extent_start) {
1809 session_extent_start = e.first;
1811 if (e.second > session_extent_end) {
1812 session_extent_end = e.second;
1818 framecnt_t session_extents = session_extent_end - session_extent_start;
1820 //in a session with no regions, use the start/end markers to set max zoom
1821 framecnt_t const session_length = _session->current_end_frame() - _session->current_start_frame ();
1822 if ( session_length > session_extents )
1823 session_extents = session_length;
1825 //in a session with no regions or start/end markers, use 2 minutes to set max zoom
1826 framecnt_t const min_length = _session->nominal_frame_rate()*60*2;
1827 if ( min_length > session_extents )
1828 session_extents = min_length;
1830 //convert to samples-per-pixel and limit our zoom to this value
1831 framecnt_t session_extents_pp = session_extents / _visible_canvas_width;
1832 if (nspp > session_extents_pp)
1833 nspp = session_extents_pp;
1836 temporal_zoom (nspp);
1840 Editor::temporal_zoom (framecnt_t fpp)
1846 framepos_t current_page = current_page_samples();
1847 framepos_t current_leftmost = leftmost_frame;
1848 framepos_t current_rightmost;
1849 framepos_t current_center;
1850 framepos_t new_page_size;
1851 framepos_t half_page_size;
1852 framepos_t leftmost_after_zoom = 0;
1854 bool in_track_canvas;
1855 bool use_mouse_frame = true;
1859 if (fpp == samples_per_pixel) {
1863 // Imposing an arbitrary limit to zoom out as too much zoom out produces
1864 // segfaults for lack of memory. If somebody decides this is not high enough I
1865 // believe it can be raisen to higher values but some limit must be in place.
1867 // This constant represents 1 day @ 48kHz on a 1600 pixel wide display
1868 // all of which is used for the editor track displays. The whole day
1869 // would be 4147200000 samples, so 2592000 samples per pixel.
1871 nfpp = min (fpp, (framecnt_t) 2592000);
1872 nfpp = max ((framecnt_t) 1, nfpp);
1874 new_page_size = (framepos_t) floor (_visible_canvas_width * nfpp);
1875 half_page_size = new_page_size / 2;
1877 switch (zoom_focus) {
1879 leftmost_after_zoom = current_leftmost;
1882 case ZoomFocusRight:
1883 current_rightmost = leftmost_frame + current_page;
1884 if (current_rightmost < new_page_size) {
1885 leftmost_after_zoom = 0;
1887 leftmost_after_zoom = current_rightmost - new_page_size;
1891 case ZoomFocusCenter:
1892 current_center = current_leftmost + (current_page/2);
1893 if (current_center < half_page_size) {
1894 leftmost_after_zoom = 0;
1896 leftmost_after_zoom = current_center - half_page_size;
1900 case ZoomFocusPlayhead:
1901 /* centre playhead */
1902 l = playhead_cursor->current_frame () - (new_page_size * 0.5);
1905 leftmost_after_zoom = 0;
1906 } else if (l > max_framepos) {
1907 leftmost_after_zoom = max_framepos - new_page_size;
1909 leftmost_after_zoom = (framepos_t) l;
1913 case ZoomFocusMouse:
1914 /* try to keep the mouse over the same point in the display */
1916 if (_drags->active()) {
1917 where = _drags->current_pointer_frame ();
1918 } else if (!mouse_frame (where, in_track_canvas)) {
1919 use_mouse_frame = false;
1922 if (use_mouse_frame) {
1923 l = - ((new_page_size * ((where - current_leftmost)/(double)current_page)) - where);
1926 leftmost_after_zoom = 0;
1927 } else if (l > max_framepos) {
1928 leftmost_after_zoom = max_framepos - new_page_size;
1930 leftmost_after_zoom = (framepos_t) l;
1933 /* use playhead instead */
1934 where = playhead_cursor->current_frame ();
1936 if (where < half_page_size) {
1937 leftmost_after_zoom = 0;
1939 leftmost_after_zoom = where - half_page_size;
1945 /* try to keep the edit point in the same place */
1946 where = get_preferred_edit_position ();
1950 double l = - ((new_page_size * ((where - current_leftmost)/(double)current_page)) - where);
1953 leftmost_after_zoom = 0;
1954 } else if (l > max_framepos) {
1955 leftmost_after_zoom = max_framepos - new_page_size;
1957 leftmost_after_zoom = (framepos_t) l;
1961 /* edit point not defined */
1968 // leftmost_after_zoom = min (leftmost_after_zoom, _session->current_end_frame());
1970 reposition_and_zoom (leftmost_after_zoom, nfpp);
1974 Editor::calc_extra_zoom_edges(framepos_t &start, framepos_t &end)
1976 /* this func helps make sure we leave a little space
1977 at each end of the editor so that the zoom doesn't fit the region
1978 precisely to the screen.
1981 GdkScreen* screen = gdk_screen_get_default ();
1982 const gint pixwidth = gdk_screen_get_width (screen);
1983 const gint mmwidth = gdk_screen_get_width_mm (screen);
1984 const double pix_per_mm = (double) pixwidth/ (double) mmwidth;
1985 const double one_centimeter_in_pixels = pix_per_mm * 10.0;
1987 const framepos_t range = end - start;
1988 const framecnt_t new_fpp = (framecnt_t) ceil ((double) range / (double) _visible_canvas_width);
1989 const framepos_t extra_samples = (framepos_t) floor (one_centimeter_in_pixels * new_fpp);
1991 if (start > extra_samples) {
1992 start -= extra_samples;
1997 if (max_framepos - extra_samples > end) {
1998 end += extra_samples;
2005 Editor::get_selection_extents (framepos_t &start, framepos_t &end) const
2007 start = max_framepos;
2011 //ToDo: if notes are selected, set extents to that selection
2013 //ToDo: if control points are selected, set extents to that selection
2015 if ( !selection->regions.empty() ) {
2016 RegionSelection rs = get_regions_from_selection_and_entered ();
2018 for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
2020 if ((*i)->region()->position() < start) {
2021 start = (*i)->region()->position();
2024 if ((*i)->region()->last_frame() + 1 > end) {
2025 end = (*i)->region()->last_frame() + 1;
2029 } else if (!selection->time.empty()) {
2030 start = selection->time.start();
2031 end = selection->time.end_frame();
2033 ret = false; //no selection found
2036 if ((start == 0 && end == 0) || end < start) {
2045 Editor::temporal_zoom_selection (Editing::ZoomAxis axes)
2047 if (!selection) return;
2049 //ToDo: if notes are selected, zoom to that
2051 //ToDo: if control points are selected, zoom to that
2053 if (axes == Horizontal || axes == Both) {
2055 framepos_t start, end;
2056 if (get_selection_extents (start, end)) {
2057 calc_extra_zoom_edges (start, end);
2058 temporal_zoom_by_frame (start, end);
2062 if (axes == Vertical || axes == Both) {
2068 Editor::temporal_zoom_session ()
2070 ENSURE_GUI_THREAD (*this, &Editor::temporal_zoom_session)
2073 framecnt_t start = _session->current_start_frame();
2074 framecnt_t end = _session->current_end_frame();
2076 if (_session->actively_recording () ) {
2077 framepos_t cur = playhead_cursor->current_frame ();
2079 /* recording beyond the end marker; zoom out
2080 * by 5 seconds more so that if 'follow
2081 * playhead' is active we don't immediately
2084 end = cur + _session->frame_rate() * 5;
2088 if ((start == 0 && end == 0) || end < start) {
2092 calc_extra_zoom_edges(start, end);
2094 temporal_zoom_by_frame (start, end);
2099 Editor::temporal_zoom_by_frame (framepos_t start, framepos_t end)
2101 if (!_session) return;
2103 if ((start == 0 && end == 0) || end < start) {
2107 framepos_t range = end - start;
2109 const framecnt_t new_fpp = (framecnt_t) ceil ((double) range / (double) _visible_canvas_width);
2111 framepos_t new_page = range;
2112 framepos_t middle = (framepos_t) floor ((double) start + ((double) range / 2.0f));
2113 framepos_t new_leftmost = (framepos_t) floor ((double) middle - ((double) new_page / 2.0f));
2115 if (new_leftmost > middle) {
2119 if (new_leftmost < 0) {
2123 reposition_and_zoom (new_leftmost, new_fpp);
2127 Editor::temporal_zoom_to_frame (bool coarser, framepos_t frame)
2133 framecnt_t range_before = frame - leftmost_frame;
2137 if (samples_per_pixel <= 1) {
2140 new_spp = samples_per_pixel + (samples_per_pixel/2);
2142 range_before += range_before/2;
2144 if (samples_per_pixel >= 1) {
2145 new_spp = samples_per_pixel - (samples_per_pixel/2);
2147 /* could bail out here since we cannot zoom any finer,
2148 but leave that to the equality test below
2150 new_spp = samples_per_pixel;
2153 range_before -= range_before/2;
2156 if (new_spp == samples_per_pixel) {
2160 /* zoom focus is automatically taken as @param frame when this
2164 framepos_t new_leftmost = frame - (framepos_t)range_before;
2166 if (new_leftmost > frame) {
2170 if (new_leftmost < 0) {
2174 reposition_and_zoom (new_leftmost, new_spp);
2179 Editor::choose_new_marker_name(string &name) {
2181 if (!UIConfiguration::instance().get_name_new_markers()) {
2182 /* don't prompt user for a new name */
2186 Prompter dialog (true);
2188 dialog.set_prompt (_("New Name:"));
2190 dialog.set_title (_("New Location Marker"));
2192 dialog.set_name ("MarkNameWindow");
2193 dialog.set_size_request (250, -1);
2194 dialog.set_position (Gtk::WIN_POS_MOUSE);
2196 dialog.add_button (Stock::OK, RESPONSE_ACCEPT);
2197 dialog.set_initial_text (name);
2201 switch (dialog.run ()) {
2202 case RESPONSE_ACCEPT:
2208 dialog.get_result(name);
2215 Editor::add_location_from_selection ()
2219 if (selection->time.empty()) {
2223 if (_session == 0 || clicked_axisview == 0) {
2227 framepos_t start = selection->time[clicked_selection].start;
2228 framepos_t end = selection->time[clicked_selection].end;
2230 _session->locations()->next_available_name(rangename,"selection");
2231 Location *location = new Location (*_session, start, end, rangename, Location::IsRangeMarker, get_grid_music_divisions(0));
2233 begin_reversible_command (_("add marker"));
2235 XMLNode &before = _session->locations()->get_state();
2236 _session->locations()->add (location, true);
2237 XMLNode &after = _session->locations()->get_state();
2238 _session->add_command(new MementoCommand<Locations>(*(_session->locations()), &before, &after));
2240 commit_reversible_command ();
2244 Editor::add_location_mark (framepos_t where)
2248 select_new_marker = true;
2250 _session->locations()->next_available_name(markername,"mark");
2251 if (!choose_new_marker_name(markername)) {
2254 Location *location = new Location (*_session, where, where, markername, Location::IsMark, get_grid_music_divisions (0));
2255 begin_reversible_command (_("add marker"));
2257 XMLNode &before = _session->locations()->get_state();
2258 _session->locations()->add (location, true);
2259 XMLNode &after = _session->locations()->get_state();
2260 _session->add_command(new MementoCommand<Locations>(*(_session->locations()), &before, &after));
2262 commit_reversible_command ();
2266 Editor::set_session_start_from_playhead ()
2272 if ((loc = _session->locations()->session_range_location()) == 0) { //should never happen
2273 _session->set_session_extents ( _session->audible_frame(), _session->audible_frame() );
2275 XMLNode &before = loc->get_state();
2277 _session->set_session_extents ( _session->audible_frame(), loc->end() );
2279 XMLNode &after = loc->get_state();
2281 begin_reversible_command (_("Set session start"));
2283 _session->add_command (new MementoCommand<Location>(*loc, &before, &after));
2285 commit_reversible_command ();
2290 Editor::set_session_end_from_playhead ()
2296 if ((loc = _session->locations()->session_range_location()) == 0) { //should never happen
2297 _session->set_session_extents ( _session->audible_frame(), _session->audible_frame() );
2299 XMLNode &before = loc->get_state();
2301 _session->set_session_extents ( loc->start(), _session->audible_frame() );
2303 XMLNode &after = loc->get_state();
2305 begin_reversible_command (_("Set session start"));
2307 _session->add_command (new MementoCommand<Location>(*loc, &before, &after));
2309 commit_reversible_command ();
2312 _session->set_end_is_free (false);
2317 Editor::toggle_location_at_playhead_cursor ()
2319 if (!do_remove_location_at_playhead_cursor())
2321 add_location_from_playhead_cursor();
2326 Editor::add_location_from_playhead_cursor ()
2328 add_location_mark (_session->audible_frame());
2332 Editor::do_remove_location_at_playhead_cursor ()
2334 bool removed = false;
2337 XMLNode &before = _session->locations()->get_state();
2339 //find location(s) at this time
2340 Locations::LocationList locs;
2341 _session->locations()->find_all_between (_session->audible_frame(), _session->audible_frame()+1, locs, Location::Flags(0));
2342 for (Locations::LocationList::iterator i = locs.begin(); i != locs.end(); ++i) {
2343 if ((*i)->is_mark()) {
2344 _session->locations()->remove (*i);
2351 begin_reversible_command (_("remove marker"));
2352 XMLNode &after = _session->locations()->get_state();
2353 _session->add_command(new MementoCommand<Locations>(*(_session->locations()), &before, &after));
2354 commit_reversible_command ();
2361 Editor::remove_location_at_playhead_cursor ()
2363 do_remove_location_at_playhead_cursor ();
2366 /** Add a range marker around each selected region */
2368 Editor::add_locations_from_region ()
2370 RegionSelection rs = get_regions_from_selection_and_entered ();
2375 bool commit = false;
2377 XMLNode &before = _session->locations()->get_state();
2379 for (RegionSelection::iterator i = rs.begin (); i != rs.end (); ++i) {
2381 boost::shared_ptr<Region> region = (*i)->region ();
2383 Location *location = new Location (*_session, region->position(), region->last_frame(), region->name(), Location::IsRangeMarker, 0);
2385 _session->locations()->add (location, true);
2390 begin_reversible_command (selection->regions.size () > 1 ? _("add markers") : _("add marker"));
2391 XMLNode &after = _session->locations()->get_state();
2392 _session->add_command (new MementoCommand<Locations>(*(_session->locations()), &before, &after));
2393 commit_reversible_command ();
2397 /** Add a single range marker around all selected regions */
2399 Editor::add_location_from_region ()
2401 RegionSelection rs = get_regions_from_selection_and_entered ();
2407 XMLNode &before = _session->locations()->get_state();
2411 if (rs.size() > 1) {
2412 _session->locations()->next_available_name(markername, "regions");
2414 RegionView* rv = *(rs.begin());
2415 boost::shared_ptr<Region> region = rv->region();
2416 markername = region->name();
2419 if (!choose_new_marker_name(markername)) {
2423 // single range spanning all selected
2424 Location *location = new Location (*_session, selection->regions.start(), selection->regions.end_frame(), markername, Location::IsRangeMarker, 0);
2425 _session->locations()->add (location, true);
2427 begin_reversible_command (_("add marker"));
2428 XMLNode &after = _session->locations()->get_state();
2429 _session->add_command (new MementoCommand<Locations>(*(_session->locations()), &before, &after));
2430 commit_reversible_command ();
2436 Editor::jump_forward_to_mark ()
2442 framepos_t pos = _session->locations()->first_mark_after (playhead_cursor->current_frame());
2448 _session->request_locate (pos, _session->transport_rolling());
2452 Editor::jump_backward_to_mark ()
2458 framepos_t pos = _session->locations()->first_mark_before (playhead_cursor->current_frame());
2460 //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...
2461 if ( _session->transport_rolling() ) {
2462 if ( (playhead_cursor->current_frame() - pos) < _session->frame_rate()/2 ) {
2463 framepos_t prior = _session->locations()->first_mark_before ( pos );
2472 _session->request_locate (pos, _session->transport_rolling());
2478 framepos_t const pos = _session->audible_frame ();
2481 _session->locations()->next_available_name (markername, "mark");
2483 if (!choose_new_marker_name (markername)) {
2487 _session->locations()->add (new Location (*_session, pos, 0, markername, Location::IsMark, 0), true);
2491 Editor::clear_markers ()
2494 begin_reversible_command (_("clear markers"));
2496 XMLNode &before = _session->locations()->get_state();
2497 _session->locations()->clear_markers ();
2498 XMLNode &after = _session->locations()->get_state();
2499 _session->add_command(new MementoCommand<Locations>(*(_session->locations()), &before, &after));
2501 commit_reversible_command ();
2506 Editor::clear_ranges ()
2509 begin_reversible_command (_("clear ranges"));
2511 XMLNode &before = _session->locations()->get_state();
2513 _session->locations()->clear_ranges ();
2515 XMLNode &after = _session->locations()->get_state();
2516 _session->add_command(new MementoCommand<Locations>(*(_session->locations()), &before, &after));
2518 commit_reversible_command ();
2523 Editor::clear_locations ()
2525 begin_reversible_command (_("clear locations"));
2527 XMLNode &before = _session->locations()->get_state();
2528 _session->locations()->clear ();
2529 XMLNode &after = _session->locations()->get_state();
2530 _session->add_command(new MementoCommand<Locations>(*(_session->locations()), &before, &after));
2532 commit_reversible_command ();
2536 Editor::unhide_markers ()
2538 for (LocationMarkerMap::iterator i = location_markers.begin(); i != location_markers.end(); ++i) {
2539 Location *l = (*i).first;
2540 if (l->is_hidden() && l->is_mark()) {
2541 l->set_hidden(false, this);
2547 Editor::unhide_ranges ()
2549 for (LocationMarkerMap::iterator i = location_markers.begin(); i != location_markers.end(); ++i) {
2550 Location *l = (*i).first;
2551 if (l->is_hidden() && l->is_range_marker()) {
2552 l->set_hidden(false, this);
2557 /* INSERT/REPLACE */
2560 Editor::insert_region_list_selection (float times)
2562 RouteTimeAxisView *tv = 0;
2563 boost::shared_ptr<Playlist> playlist;
2565 if (clicked_routeview != 0) {
2566 tv = clicked_routeview;
2567 } else if (!selection->tracks.empty()) {
2568 if ((tv = dynamic_cast<RouteTimeAxisView*>(selection->tracks.front())) == 0) {
2571 } else if (entered_track != 0) {
2572 if ((tv = dynamic_cast<RouteTimeAxisView*>(entered_track)) == 0) {
2579 if ((playlist = tv->playlist()) == 0) {
2583 boost::shared_ptr<Region> region = _regions->get_single_selection ();
2588 begin_reversible_command (_("insert region"));
2589 playlist->clear_changes ();
2590 playlist->add_region ((RegionFactory::create (region, true)), get_preferred_edit_position(), times);
2591 if (Config->get_edit_mode() == Ripple)
2592 playlist->ripple (get_preferred_edit_position(), region->length() * times, boost::shared_ptr<Region>());
2594 _session->add_command(new StatefulDiffCommand (playlist));
2595 commit_reversible_command ();
2598 /* BUILT-IN EFFECTS */
2601 Editor::reverse_selection ()
2606 /* GAIN ENVELOPE EDITING */
2609 Editor::edit_envelope ()
2616 Editor::transition_to_rolling (bool fwd)
2622 if (_session->config.get_external_sync()) {
2623 switch (Config->get_sync_source()) {
2627 /* transport controlled by the master */
2632 if (_session->is_auditioning()) {
2633 _session->cancel_audition ();
2637 _session->request_transport_speed (fwd ? 1.0f : -1.0f);
2641 Editor::play_from_start ()
2643 _session->request_locate (_session->current_start_frame(), true);
2647 Editor::play_from_edit_point ()
2649 _session->request_locate (get_preferred_edit_position(), true);
2653 Editor::play_from_edit_point_and_return ()
2655 framepos_t start_frame;
2656 framepos_t return_frame;
2658 start_frame = get_preferred_edit_position ( EDIT_IGNORE_PHEAD );
2660 if (_session->transport_rolling()) {
2661 _session->request_locate (start_frame, false);
2665 /* don't reset the return frame if its already set */
2667 if ((return_frame = _session->requested_return_frame()) < 0) {
2668 return_frame = _session->audible_frame();
2671 if (start_frame >= 0) {
2672 _session->request_roll_at_and_return (start_frame, return_frame);
2677 Editor::play_selection ()
2679 framepos_t start, end;
2680 if (!get_selection_extents ( start, end))
2683 AudioRange ar (start, end, 0);
2684 list<AudioRange> lar;
2687 _session->request_play_range (&lar, true);
2692 Editor::maybe_locate_with_edit_preroll (framepos_t location)
2694 if ( _session->transport_rolling() || !UIConfiguration::instance().get_follow_edits() || _session->config.get_external_sync() )
2697 location -= _session->preroll_samples (location);
2699 //don't try to locate before the beginning of time
2704 //if follow_playhead is on, keep the playhead on the screen
2705 if ( _follow_playhead )
2706 if ( location < leftmost_frame )
2707 location = leftmost_frame;
2709 _session->request_locate( location );
2713 Editor::play_with_preroll ()
2715 framepos_t start, end;
2716 if ( UIConfiguration::instance().get_follow_edits() && get_selection_extents ( start, end) ) {
2717 const framepos_t preroll = _session->preroll_samples (start);
2719 framepos_t ret = start;
2721 if (start > preroll) {
2722 start = start - preroll;
2725 end = end + preroll; //"post-roll"
2727 AudioRange ar (start, end, 0);
2728 list<AudioRange> lar;
2731 _session->request_play_range (&lar, true);
2732 _session->set_requested_return_frame (ret); //force auto-return to return to range start, without the preroll
2734 framepos_t ph = playhead_cursor->current_frame ();
2735 const framepos_t preroll = _session->preroll_samples (ph);
2738 start = ph - preroll;
2742 _session->request_locate (start, true);
2743 _session->set_requested_return_frame (ph); //force auto-return to return to playhead location, without the preroll
2748 Editor::rec_with_preroll ()
2750 framepos_t ph = playhead_cursor->current_frame ();
2751 framepos_t preroll = _session->preroll_samples (ph);
2752 _session->request_preroll_record_trim (ph, preroll);
2756 Editor::rec_with_count_in ()
2758 _session->request_count_in_record ();
2762 Editor::play_location (Location& location)
2764 if (location.start() <= location.end()) {
2768 _session->request_bounded_roll (location.start(), location.end());
2772 Editor::loop_location (Location& location)
2774 if (location.start() <= location.end()) {
2780 if ((tll = transport_loop_location()) != 0) {
2781 tll->set (location.start(), location.end());
2783 // enable looping, reposition and start rolling
2784 _session->request_locate (tll->start(), true);
2785 _session->request_play_loop (true);
2790 Editor::do_layer_operation (LayerOperation op)
2792 if (selection->regions.empty ()) {
2796 bool const multiple = selection->regions.size() > 1;
2800 begin_reversible_command (_("raise regions"));
2802 begin_reversible_command (_("raise region"));
2808 begin_reversible_command (_("raise regions to top"));
2810 begin_reversible_command (_("raise region to top"));
2816 begin_reversible_command (_("lower regions"));
2818 begin_reversible_command (_("lower region"));
2824 begin_reversible_command (_("lower regions to bottom"));
2826 begin_reversible_command (_("lower region"));
2831 set<boost::shared_ptr<Playlist> > playlists = selection->regions.playlists ();
2832 for (set<boost::shared_ptr<Playlist> >::iterator i = playlists.begin(); i != playlists.end(); ++i) {
2833 (*i)->clear_owned_changes ();
2836 for (RegionSelection::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
2837 boost::shared_ptr<Region> r = (*i)->region ();
2849 r->lower_to_bottom ();
2853 for (set<boost::shared_ptr<Playlist> >::iterator i = playlists.begin(); i != playlists.end(); ++i) {
2854 vector<Command*> cmds;
2856 _session->add_commands (cmds);
2859 commit_reversible_command ();
2863 Editor::raise_region ()
2865 do_layer_operation (Raise);
2869 Editor::raise_region_to_top ()
2871 do_layer_operation (RaiseToTop);
2875 Editor::lower_region ()
2877 do_layer_operation (Lower);
2881 Editor::lower_region_to_bottom ()
2883 do_layer_operation (LowerToBottom);
2886 /** Show the region editor for the selected regions */
2888 Editor::show_region_properties ()
2890 selection->foreach_regionview (&RegionView::show_region_editor);
2893 /** Show the midi list editor for the selected MIDI regions */
2895 Editor::show_midi_list_editor ()
2897 selection->foreach_midi_regionview (&MidiRegionView::show_list_editor);
2901 Editor::rename_region ()
2903 RegionSelection rs = get_regions_from_selection_and_entered ();
2909 ArdourDialog d (_("Rename Region"), true, false);
2911 Label label (_("New name:"));
2914 hbox.set_spacing (6);
2915 hbox.pack_start (label, false, false);
2916 hbox.pack_start (entry, true, true);
2918 d.get_vbox()->set_border_width (12);
2919 d.get_vbox()->pack_start (hbox, false, false);
2921 d.add_button(Gtk::Stock::CANCEL, Gtk::RESPONSE_CANCEL);
2922 d.add_button(Gtk::Stock::OK, Gtk::RESPONSE_OK);
2924 d.set_size_request (300, -1);
2926 entry.set_text (rs.front()->region()->name());
2927 entry.select_region (0, -1);
2929 entry.signal_activate().connect (sigc::bind (sigc::mem_fun (d, &Dialog::response), RESPONSE_OK));
2935 int const ret = d.run();
2939 if (ret != RESPONSE_OK) {
2943 std::string str = entry.get_text();
2944 strip_whitespace_edges (str);
2946 rs.front()->region()->set_name (str);
2947 _regions->redisplay ();
2951 /** Start an audition of the first selected region */
2953 Editor::play_edit_range ()
2955 framepos_t start, end;
2957 if (get_edit_op_range (start, end)) {
2958 _session->request_bounded_roll (start, end);
2963 Editor::play_selected_region ()
2965 framepos_t start = max_framepos;
2968 RegionSelection rs = get_regions_from_selection_and_entered ();
2974 for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
2975 if ((*i)->region()->position() < start) {
2976 start = (*i)->region()->position();
2978 if ((*i)->region()->last_frame() + 1 > end) {
2979 end = (*i)->region()->last_frame() + 1;
2983 _session->request_bounded_roll (start, end);
2987 Editor::audition_playlist_region_standalone (boost::shared_ptr<Region> region)
2989 _session->audition_region (region);
2993 Editor::region_from_selection ()
2995 if (clicked_axisview == 0) {
2999 if (selection->time.empty()) {
3003 framepos_t start = selection->time[clicked_selection].start;
3004 framepos_t end = selection->time[clicked_selection].end;
3006 TrackViewList tracks = get_tracks_for_range_action ();
3008 framepos_t selection_cnt = end - start + 1;
3010 for (TrackSelection::iterator i = tracks.begin(); i != tracks.end(); ++i) {
3011 boost::shared_ptr<Region> current;
3012 boost::shared_ptr<Playlist> pl;
3013 framepos_t internal_start;
3016 if ((pl = (*i)->playlist()) == 0) {
3020 if ((current = pl->top_region_at (start)) == 0) {
3024 internal_start = start - current->position();
3025 RegionFactory::region_name (new_name, current->name(), true);
3029 plist.add (ARDOUR::Properties::start, current->start() + internal_start);
3030 plist.add (ARDOUR::Properties::length, selection_cnt);
3031 plist.add (ARDOUR::Properties::name, new_name);
3032 plist.add (ARDOUR::Properties::layer, 0);
3034 boost::shared_ptr<Region> region (RegionFactory::create (current, plist));
3039 Editor::create_region_from_selection (vector<boost::shared_ptr<Region> >& new_regions)
3041 if (selection->time.empty() || selection->tracks.empty()) {
3045 framepos_t start, end;
3046 if (clicked_selection) {
3047 start = selection->time[clicked_selection].start;
3048 end = selection->time[clicked_selection].end;
3050 start = selection->time.start();
3051 end = selection->time.end_frame();
3054 TrackViewList ts = selection->tracks.filter_to_unique_playlists ();
3055 sort_track_selection (ts);
3057 for (TrackSelection::iterator i = ts.begin(); i != ts.end(); ++i) {
3058 boost::shared_ptr<Region> current;
3059 boost::shared_ptr<Playlist> playlist;
3060 framepos_t internal_start;
3063 if ((playlist = (*i)->playlist()) == 0) {
3067 if ((current = playlist->top_region_at(start)) == 0) {
3071 internal_start = start - current->position();
3072 RegionFactory::region_name (new_name, current->name(), true);
3076 plist.add (ARDOUR::Properties::start, current->start() + internal_start);
3077 plist.add (ARDOUR::Properties::length, end - start + 1);
3078 plist.add (ARDOUR::Properties::name, new_name);
3080 new_regions.push_back (RegionFactory::create (current, plist));
3085 Editor::split_multichannel_region ()
3087 RegionSelection rs = get_regions_from_selection_and_entered ();
3093 vector< boost::shared_ptr<Region> > v;
3095 for (list<RegionView*>::iterator x = rs.begin(); x != rs.end(); ++x) {
3096 (*x)->region()->separate_by_channel (v);
3101 Editor::new_region_from_selection ()
3103 region_from_selection ();
3104 cancel_selection ();
3108 add_if_covered (RegionView* rv, const AudioRange* ar, RegionSelection* rs)
3110 switch (rv->region()->coverage (ar->start, ar->end - 1)) {
3111 // n.b. -1 because AudioRange::end is one past the end, but coverage expects inclusive ranges
3112 case Evoral::OverlapNone:
3120 * - selected tracks, or if there are none...
3121 * - tracks containing selected regions, or if there are none...
3126 Editor::get_tracks_for_range_action () const
3130 if (selection->tracks.empty()) {
3132 /* use tracks with selected regions */
3134 RegionSelection rs = selection->regions;
3136 for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
3137 TimeAxisView* tv = &(*i)->get_time_axis_view();
3139 if (!t.contains (tv)) {
3145 /* no regions and no tracks: use all tracks */
3151 t = selection->tracks;
3154 return t.filter_to_unique_playlists();
3158 Editor::separate_regions_between (const TimeSelection& ts)
3160 bool in_command = false;
3161 boost::shared_ptr<Playlist> playlist;
3162 RegionSelection new_selection;
3164 TrackViewList tmptracks = get_tracks_for_range_action ();
3165 sort_track_selection (tmptracks);
3167 for (TrackSelection::iterator i = tmptracks.begin(); i != tmptracks.end(); ++i) {
3169 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> ((*i));
3175 if (!rtv->is_track()) {
3179 /* no edits to destructive tracks */
3181 if (rtv->track()->destructive()) {
3185 if ((playlist = rtv->playlist()) != 0) {
3187 playlist->clear_changes ();
3189 /* XXX need to consider musical time selections here at some point */
3191 double speed = rtv->track()->speed();
3193 for (list<AudioRange>::const_iterator t = ts.begin(); t != ts.end(); ++t) {
3195 sigc::connection c = rtv->view()->RegionViewAdded.connect (
3196 sigc::mem_fun(*this, &Editor::collect_new_region_view));
3198 latest_regionviews.clear ();
3200 playlist->partition ((framepos_t)((*t).start * speed),
3201 (framepos_t)((*t).end * speed), false);
3205 if (!latest_regionviews.empty()) {
3207 rtv->view()->foreach_regionview (sigc::bind (
3208 sigc::ptr_fun (add_if_covered),
3209 &(*t), &new_selection));
3212 begin_reversible_command (_("separate"));
3216 /* pick up changes to existing regions */
3218 vector<Command*> cmds;
3219 playlist->rdiff (cmds);
3220 _session->add_commands (cmds);
3222 /* pick up changes to the playlist itself (adds/removes)
3225 _session->add_command(new StatefulDiffCommand (playlist));
3232 // selection->set (new_selection);
3234 commit_reversible_command ();
3238 struct PlaylistState {
3239 boost::shared_ptr<Playlist> playlist;
3243 /** Take tracks from get_tracks_for_range_action and cut any regions
3244 * on those tracks so that the tracks are empty over the time
3248 Editor::separate_region_from_selection ()
3250 /* preferentially use *all* ranges in the time selection if we're in range mode
3251 to allow discontiguous operation, since get_edit_op_range() currently
3252 returns a single range.
3255 if (!selection->time.empty()) {
3257 separate_regions_between (selection->time);
3264 if (get_edit_op_range (start, end)) {
3266 AudioRange ar (start, end, 1);
3270 separate_regions_between (ts);
3276 Editor::separate_region_from_punch ()
3278 Location* loc = _session->locations()->auto_punch_location();
3280 separate_regions_using_location (*loc);
3285 Editor::separate_region_from_loop ()
3287 Location* loc = _session->locations()->auto_loop_location();
3289 separate_regions_using_location (*loc);
3294 Editor::separate_regions_using_location (Location& loc)
3296 if (loc.is_mark()) {
3300 AudioRange ar (loc.start(), loc.end(), 1);
3305 separate_regions_between (ts);
3308 /** Separate regions under the selected region */
3310 Editor::separate_under_selected_regions ()
3312 vector<PlaylistState> playlists;
3316 rs = get_regions_from_selection_and_entered();
3318 if (!_session || rs.empty()) {
3322 begin_reversible_command (_("separate region under"));
3324 list<boost::shared_ptr<Region> > regions_to_remove;
3326 for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
3327 // we can't just remove the region(s) in this loop because
3328 // this removes them from the RegionSelection, and they thus
3329 // disappear from underneath the iterator, and the ++i above
3330 // SEGVs in a puzzling fashion.
3332 // so, first iterate over the regions to be removed from rs and
3333 // add them to the regions_to_remove list, and then
3334 // iterate over the list to actually remove them.
3336 regions_to_remove.push_back ((*i)->region());
3339 for (list<boost::shared_ptr<Region> >::iterator rl = regions_to_remove.begin(); rl != regions_to_remove.end(); ++rl) {
3341 boost::shared_ptr<Playlist> playlist = (*rl)->playlist();
3344 // is this check necessary?
3348 vector<PlaylistState>::iterator i;
3350 //only take state if this is a new playlist.
3351 for (i = playlists.begin(); i != playlists.end(); ++i) {
3352 if ((*i).playlist == playlist) {
3357 if (i == playlists.end()) {
3359 PlaylistState before;
3360 before.playlist = playlist;
3361 before.before = &playlist->get_state();
3362 playlist->clear_changes ();
3363 playlist->freeze ();
3364 playlists.push_back(before);
3367 //Partition on the region bounds
3368 playlist->partition ((*rl)->first_frame() - 1, (*rl)->last_frame() + 1, true);
3370 //Re-add region that was just removed due to the partition operation
3371 playlist->add_region( (*rl), (*rl)->first_frame() );
3374 vector<PlaylistState>::iterator pl;
3376 for (pl = playlists.begin(); pl != playlists.end(); ++pl) {
3377 (*pl).playlist->thaw ();
3378 _session->add_command(new MementoCommand<Playlist>(*(*pl).playlist, (*pl).before, &(*pl).playlist->get_state()));
3381 commit_reversible_command ();
3385 Editor::crop_region_to_selection ()
3387 if (!selection->time.empty()) {
3389 begin_reversible_command (_("Crop Regions to Time Selection"));
3390 for (std::list<AudioRange>::iterator i = selection->time.begin(); i != selection->time.end(); ++i) {
3391 crop_region_to ((*i).start, (*i).end);
3393 commit_reversible_command();
3399 if (get_edit_op_range (start, end)) {
3400 begin_reversible_command (_("Crop Regions to Edit Range"));
3402 crop_region_to (start, end);
3404 commit_reversible_command();
3411 Editor::crop_region_to (framepos_t start, framepos_t end)
3413 vector<boost::shared_ptr<Playlist> > playlists;
3414 boost::shared_ptr<Playlist> playlist;
3417 if (selection->tracks.empty()) {
3418 ts = track_views.filter_to_unique_playlists();
3420 ts = selection->tracks.filter_to_unique_playlists ();
3423 sort_track_selection (ts);
3425 for (TrackSelection::iterator i = ts.begin(); i != ts.end(); ++i) {
3427 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> ((*i));
3433 boost::shared_ptr<Track> t = rtv->track();
3435 if (t != 0 && ! t->destructive()) {
3437 if ((playlist = rtv->playlist()) != 0) {
3438 playlists.push_back (playlist);
3443 if (playlists.empty()) {
3448 framepos_t new_start;
3450 framecnt_t new_length;
3452 for (vector<boost::shared_ptr<Playlist> >::iterator i = playlists.begin(); i != playlists.end(); ++i) {
3454 /* Only the top regions at start and end have to be cropped */
3455 boost::shared_ptr<Region> region_at_start = (*i)->top_region_at(start);
3456 boost::shared_ptr<Region> region_at_end = (*i)->top_region_at(end);
3458 vector<boost::shared_ptr<Region> > regions;
3460 if (region_at_start != 0) {
3461 regions.push_back (region_at_start);
3463 if (region_at_end != 0) {
3464 regions.push_back (region_at_end);
3467 /* now adjust lengths */
3468 for (vector<boost::shared_ptr<Region> >::iterator i = regions.begin(); i != regions.end(); ++i) {
3470 pos = (*i)->position();
3471 new_start = max (start, pos);
3472 if (max_framepos - pos > (*i)->length()) {
3473 new_end = pos + (*i)->length() - 1;
3475 new_end = max_framepos;
3477 new_end = min (end, new_end);
3478 new_length = new_end - new_start + 1;
3480 (*i)->clear_changes ();
3481 (*i)->trim_to (new_start, new_length);
3482 _session->add_command (new StatefulDiffCommand (*i));
3488 Editor::region_fill_track ()
3490 boost::shared_ptr<Playlist> playlist;
3491 RegionSelection regions = get_regions_from_selection_and_entered ();
3492 RegionSelection foo;
3494 framepos_t const end = _session->current_end_frame ();
3496 if (regions.empty () || regions.end_frame () + 1 >= end) {
3500 framepos_t const start_frame = regions.start ();
3501 framepos_t const end_frame = regions.end_frame ();
3502 framecnt_t const gap = end_frame - start_frame + 1;
3504 begin_reversible_command (Operations::region_fill);
3506 selection->clear_regions ();
3508 for (RegionSelection::iterator i = regions.begin(); i != regions.end(); ++i) {
3510 boost::shared_ptr<Region> r ((*i)->region());
3512 TimeAxisView& tv = (*i)->get_time_axis_view();
3513 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (&tv);
3514 latest_regionviews.clear ();
3515 sigc::connection c = rtv->view()->RegionViewAdded.connect (sigc::mem_fun(*this, &Editor::collect_new_region_view));
3517 framepos_t const position = end_frame + (r->first_frame() - start_frame + 1);
3518 playlist = (*i)->region()->playlist();
3519 playlist->clear_changes ();
3520 playlist->duplicate_until (r, position, gap, end);
3521 _session->add_command(new StatefulDiffCommand (playlist));
3525 foo.insert (foo.end(), latest_regionviews.begin(), latest_regionviews.end());
3529 selection->set (foo);
3532 commit_reversible_command ();
3536 Editor::set_region_sync_position ()
3538 set_sync_point (get_preferred_edit_position (), get_regions_from_selection_and_edit_point ());
3542 Editor::set_sync_point (framepos_t where, const RegionSelection& rs)
3544 bool in_command = false;
3546 for (RegionSelection::const_iterator r = rs.begin(); r != rs.end(); ++r) {
3548 if (!(*r)->region()->covers (where)) {
3552 boost::shared_ptr<Region> region ((*r)->region());
3555 begin_reversible_command (_("set sync point"));
3559 region->clear_changes ();
3560 region->set_sync_position (where);
3561 _session->add_command(new StatefulDiffCommand (region));
3565 commit_reversible_command ();
3569 /** Remove the sync positions of the selection */
3571 Editor::remove_region_sync ()
3573 RegionSelection rs = get_regions_from_selection_and_entered ();
3579 begin_reversible_command (_("remove region sync"));
3581 for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
3583 (*i)->region()->clear_changes ();
3584 (*i)->region()->clear_sync_position ();
3585 _session->add_command(new StatefulDiffCommand ((*i)->region()));
3588 commit_reversible_command ();
3592 Editor::naturalize_region ()
3594 RegionSelection rs = get_regions_from_selection_and_entered ();
3600 if (rs.size() > 1) {
3601 begin_reversible_command (_("move regions to original position"));
3603 begin_reversible_command (_("move region to original position"));
3606 for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
3607 (*i)->region()->clear_changes ();
3608 (*i)->region()->move_to_natural_position ();
3609 _session->add_command (new StatefulDiffCommand ((*i)->region()));
3612 commit_reversible_command ();
3616 Editor::align_regions (RegionPoint what)
3618 RegionSelection const rs = get_regions_from_selection_and_edit_point ();
3624 begin_reversible_command (_("align selection"));
3626 framepos_t const position = get_preferred_edit_position ();
3628 for (RegionSelection::const_iterator i = rs.begin(); i != rs.end(); ++i) {
3629 align_region_internal ((*i)->region(), what, position);
3632 commit_reversible_command ();
3635 struct RegionSortByTime {
3636 bool operator() (const RegionView* a, const RegionView* b) {
3637 return a->region()->position() < b->region()->position();
3642 Editor::align_regions_relative (RegionPoint point)
3644 RegionSelection const rs = get_regions_from_selection_and_edit_point ();
3650 framepos_t const position = get_preferred_edit_position ();
3652 framepos_t distance = 0;
3656 list<RegionView*> sorted;
3657 rs.by_position (sorted);
3659 boost::shared_ptr<Region> r ((*sorted.begin())->region());
3664 if (position > r->position()) {
3665 distance = position - r->position();
3667 distance = r->position() - position;
3673 if (position > r->last_frame()) {
3674 distance = position - r->last_frame();
3675 pos = r->position() + distance;
3677 distance = r->last_frame() - position;
3678 pos = r->position() - distance;
3684 pos = r->adjust_to_sync (position);
3685 if (pos > r->position()) {
3686 distance = pos - r->position();
3688 distance = r->position() - pos;
3694 if (pos == r->position()) {
3698 begin_reversible_command (_("align selection (relative)"));
3700 /* move first one specially */
3702 r->clear_changes ();
3703 r->set_position (pos);
3704 _session->add_command(new StatefulDiffCommand (r));
3706 /* move rest by the same amount */
3710 for (list<RegionView*>::iterator i = sorted.begin(); i != sorted.end(); ++i) {
3712 boost::shared_ptr<Region> region ((*i)->region());
3714 region->clear_changes ();
3717 region->set_position (region->position() + distance);
3719 region->set_position (region->position() - distance);
3722 _session->add_command(new StatefulDiffCommand (region));
3726 commit_reversible_command ();
3730 Editor::align_region (boost::shared_ptr<Region> region, RegionPoint point, framepos_t position)
3732 begin_reversible_command (_("align region"));
3733 align_region_internal (region, point, position);
3734 commit_reversible_command ();
3738 Editor::align_region_internal (boost::shared_ptr<Region> region, RegionPoint point, framepos_t position)
3740 region->clear_changes ();
3744 region->set_position (region->adjust_to_sync (position));
3748 if (position > region->length()) {
3749 region->set_position (position - region->length());
3754 region->set_position (position);
3758 _session->add_command(new StatefulDiffCommand (region));
3762 Editor::trim_region_front ()
3768 Editor::trim_region_back ()
3770 trim_region (false);
3774 Editor::trim_region (bool front)
3776 framepos_t where = get_preferred_edit_position();
3777 RegionSelection rs = get_regions_from_selection_and_edit_point ();
3783 begin_reversible_command (front ? _("trim front") : _("trim back"));
3785 for (list<RegionView*>::const_iterator i = rs.by_layer().begin(); i != rs.by_layer().end(); ++i) {
3786 if (!(*i)->region()->locked()) {
3788 (*i)->region()->clear_changes ();
3791 (*i)->region()->trim_front (where);
3793 (*i)->region()->trim_end (where);
3796 _session->add_command (new StatefulDiffCommand ((*i)->region()));
3800 commit_reversible_command ();
3803 /** Trim the end of the selected regions to the position of the edit cursor */
3805 Editor::trim_region_to_loop ()
3807 Location* loc = _session->locations()->auto_loop_location();
3811 trim_region_to_location (*loc, _("trim to loop"));
3815 Editor::trim_region_to_punch ()
3817 Location* loc = _session->locations()->auto_punch_location();
3821 trim_region_to_location (*loc, _("trim to punch"));
3825 Editor::trim_region_to_location (const Location& loc, const char* str)
3827 RegionSelection rs = get_regions_from_selection_and_entered ();
3828 bool in_command = false;
3830 for (RegionSelection::iterator x = rs.begin(); x != rs.end(); ++x) {
3831 RegionView* rv = (*x);
3833 /* require region to span proposed trim */
3834 switch (rv->region()->coverage (loc.start(), loc.end())) {
3835 case Evoral::OverlapInternal:
3841 RouteTimeAxisView* tav = dynamic_cast<RouteTimeAxisView*> (&rv->get_time_axis_view());
3850 if (tav->track() != 0) {
3851 speed = tav->track()->speed();
3854 start = session_frame_to_track_frame (loc.start(), speed);
3855 end = session_frame_to_track_frame (loc.end(), speed);
3857 rv->region()->clear_changes ();
3858 rv->region()->trim_to (start, (end - start));
3861 begin_reversible_command (str);
3864 _session->add_command(new StatefulDiffCommand (rv->region()));
3868 commit_reversible_command ();
3873 Editor::trim_region_to_previous_region_end ()
3875 return trim_to_region(false);
3879 Editor::trim_region_to_next_region_start ()
3881 return trim_to_region(true);
3885 Editor::trim_to_region(bool forward)
3887 RegionSelection rs = get_regions_from_selection_and_entered ();
3888 bool in_command = false;
3890 boost::shared_ptr<Region> next_region;
3892 for (RegionSelection::iterator x = rs.begin(); x != rs.end(); ++x) {
3894 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (*x);
3900 AudioTimeAxisView* atav = dynamic_cast<AudioTimeAxisView*> (&arv->get_time_axis_view());
3908 if (atav->track() != 0) {
3909 speed = atav->track()->speed();
3913 boost::shared_ptr<Region> region = arv->region();
3914 boost::shared_ptr<Playlist> playlist (region->playlist());
3916 region->clear_changes ();
3920 next_region = playlist->find_next_region (region->first_frame(), Start, 1);
3926 region->trim_end((framepos_t) ( (next_region->first_frame() - 1) * speed));
3927 arv->region_changed (PropertyChange (ARDOUR::Properties::length));
3931 next_region = playlist->find_next_region (region->first_frame(), Start, 0);
3937 region->trim_front((framepos_t) ((next_region->last_frame() + 1) * speed));
3939 arv->region_changed (ARDOUR::bounds_change);
3943 begin_reversible_command (_("trim to region"));
3946 _session->add_command(new StatefulDiffCommand (region));
3950 commit_reversible_command ();
3955 Editor::unfreeze_route ()
3957 if (clicked_routeview == 0 || !clicked_routeview->is_track()) {
3961 clicked_routeview->track()->unfreeze ();
3965 Editor::_freeze_thread (void* arg)
3967 return static_cast<Editor*>(arg)->freeze_thread ();
3971 Editor::freeze_thread ()
3973 /* create event pool because we may need to talk to the session */
3974 SessionEvent::create_per_thread_pool ("freeze events", 64);
3975 /* create per-thread buffers for process() tree to use */
3976 clicked_routeview->audio_track()->freeze_me (*current_interthread_info);
3977 current_interthread_info->done = true;
3982 Editor::freeze_route ()
3988 /* stop transport before we start. this is important */
3990 _session->request_transport_speed (0.0);
3992 /* wait for just a little while, because the above call is asynchronous */
3994 Glib::usleep (250000);
3996 if (clicked_routeview == 0 || !clicked_routeview->is_audio_track()) {
4000 if (!clicked_routeview->track()->bounceable (clicked_routeview->track()->main_outs(), true)) {
4002 _("This track/bus cannot be frozen because the signal adds or loses channels before reaching the outputs.\n"
4003 "This is typically caused by plugins that generate stereo output from mono input or vice versa.")
4005 d.set_title (_("Cannot freeze"));
4010 if (clicked_routeview->track()->has_external_redirects()) {
4011 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"
4012 "Freezing will only process the signal as far as the first send/insert/return."),
4013 clicked_routeview->track()->name()), true, MESSAGE_INFO, BUTTONS_NONE, true);
4015 d.add_button (_("Freeze anyway"), Gtk::RESPONSE_OK);
4016 d.add_button (_("Don't freeze"), Gtk::RESPONSE_CANCEL);
4017 d.set_title (_("Freeze Limits"));
4019 int response = d.run ();
4022 case Gtk::RESPONSE_CANCEL:
4029 InterThreadInfo itt;
4030 current_interthread_info = &itt;
4032 InterthreadProgressWindow ipw (current_interthread_info, _("Freeze"), _("Cancel Freeze"));
4034 pthread_create_and_store (X_("freezer"), &itt.thread, _freeze_thread, this);
4036 CursorContext::Handle cursor_ctx = CursorContext::create(*this, _cursors->wait);
4038 while (!itt.done && !itt.cancel) {
4039 gtk_main_iteration ();
4042 pthread_join (itt.thread, 0);
4043 current_interthread_info = 0;
4047 Editor::bounce_range_selection (bool replace, bool enable_processing)
4049 if (selection->time.empty()) {
4053 TrackSelection views = selection->tracks;
4055 for (TrackViewList::iterator i = views.begin(); i != views.end(); ++i) {
4057 if (enable_processing) {
4059 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (*i);
4061 if (rtv && rtv->track() && replace && enable_processing && !rtv->track()->bounceable (rtv->track()->main_outs(), false)) {
4063 _("You can't perform this operation because the processing of the signal "
4064 "will cause one or more of the tracks to end up with a region with more channels than this track has inputs.\n\n"
4065 "You can do this without processing, which is a different operation.")
4067 d.set_title (_("Cannot bounce"));
4074 framepos_t start = selection->time[clicked_selection].start;
4075 framepos_t end = selection->time[clicked_selection].end;
4076 framepos_t cnt = end - start + 1;
4077 bool in_command = false;
4079 for (TrackViewList::iterator i = views.begin(); i != views.end(); ++i) {
4081 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (*i);
4087 boost::shared_ptr<Playlist> playlist;
4089 if ((playlist = rtv->playlist()) == 0) {
4093 InterThreadInfo itt;
4095 playlist->clear_changes ();
4096 playlist->clear_owned_changes ();
4098 boost::shared_ptr<Region> r;
4100 if (enable_processing) {
4101 r = rtv->track()->bounce_range (start, start+cnt, itt, rtv->track()->main_outs(), false);
4103 r = rtv->track()->bounce_range (start, start+cnt, itt, boost::shared_ptr<Processor>(), false);
4111 list<AudioRange> ranges;
4112 ranges.push_back (AudioRange (start, start+cnt, 0));
4113 playlist->cut (ranges); // discard result
4114 playlist->add_region (r, start);
4118 begin_reversible_command (_("bounce range"));
4121 vector<Command*> cmds;
4122 playlist->rdiff (cmds);
4123 _session->add_commands (cmds);
4125 _session->add_command (new StatefulDiffCommand (playlist));
4129 commit_reversible_command ();
4133 /** Delete selected regions, automation points or a time range */
4137 //special case: if the user is pointing in the editor/mixer strip, they may be trying to delete a plugin.
4138 //we need this because the editor-mixer strip is in the editor window, so it doesn't get the bindings from the mix window
4139 bool deleted = false;
4140 if ( current_mixer_strip && current_mixer_strip == MixerStrip::entered_mixer_strip() )
4141 deleted = current_mixer_strip->delete_processors ();
4147 /** Cut selected regions, automation points or a time range */
4154 /** Copy selected regions, automation points or a time range */
4162 /** @return true if a Cut, Copy or Clear is possible */
4164 Editor::can_cut_copy () const
4166 if (!selection->time.empty() || !selection->regions.empty() || !selection->points.empty())
4173 /** Cut, copy or clear selected regions, automation points or a time range.
4174 * @param op Operation (Delete, Cut, Copy or Clear)
4177 Editor::cut_copy (CutCopyOp op)
4179 /* only cancel selection if cut/copy is successful.*/
4185 opname = _("delete");
4194 opname = _("clear");
4198 /* if we're deleting something, and the mouse is still pressed,
4199 the thing we started a drag for will be gone when we release
4200 the mouse button(s). avoid this. see part 2 at the end of
4204 if (op == Delete || op == Cut || op == Clear) {
4205 if (_drags->active ()) {
4210 if ( op != Delete ) { //"Delete" doesn't change copy/paste buf
4211 cut_buffer->clear ();
4214 if (entered_marker) {
4216 /* cut/delete op while pointing at a marker */
4219 Location* loc = find_location_from_marker (entered_marker, ignored);
4221 if (_session && loc) {
4222 entered_marker = NULL;
4223 Glib::signal_idle().connect (sigc::bind (sigc::mem_fun(*this, &Editor::really_remove_marker), loc));
4230 switch (mouse_mode) {
4233 begin_reversible_command (opname + ' ' + X_("MIDI"));
4235 commit_reversible_command ();
4241 bool did_edit = false;
4243 if (!selection->regions.empty() || !selection->points.empty()) {
4244 begin_reversible_command (opname + ' ' + _("objects"));
4247 if (!selection->regions.empty()) {
4248 cut_copy_regions (op, selection->regions);
4250 if (op == Cut || op == Delete) {
4251 selection->clear_regions ();
4255 if (!selection->points.empty()) {
4256 cut_copy_points (op);
4258 if (op == Cut || op == Delete) {
4259 selection->clear_points ();
4262 } else if (selection->time.empty()) {
4263 framepos_t start, end;
4264 /* no time selection, see if we can get an edit range
4267 if (get_edit_op_range (start, end)) {
4268 selection->set (start, end);
4270 } else if (!selection->time.empty()) {
4271 begin_reversible_command (opname + ' ' + _("range"));
4274 cut_copy_ranges (op);
4276 if (op == Cut || op == Delete) {
4277 selection->clear_time ();
4282 /* reset repeated paste state */
4284 last_paste_pos = -1;
4285 commit_reversible_command ();
4288 if (op == Delete || op == Cut || op == Clear) {
4294 struct AutomationRecord {
4295 AutomationRecord () : state (0) , line(NULL) {}
4296 AutomationRecord (XMLNode* s, const AutomationLine* l) : state (s) , line (l) {}
4298 XMLNode* state; ///< state before any operation
4299 const AutomationLine* line; ///< line this came from
4300 boost::shared_ptr<Evoral::ControlList> copy; ///< copied events for the cut buffer
4303 struct PointsSelectionPositionSorter {
4304 bool operator() (ControlPoint* a, ControlPoint* b) {
4305 return (*(a->model()))->when < (*(b->model()))->when;
4309 /** Cut, copy or clear selected automation points.
4310 * @param op Operation (Cut, Copy or Clear)
4313 Editor::cut_copy_points (Editing::CutCopyOp op, Evoral::Beats earliest, bool midi)
4315 if (selection->points.empty ()) {
4319 /* XXX: not ideal, as there may be more than one track involved in the point selection */
4320 _last_cut_copy_source_track = &selection->points.front()->line().trackview;
4322 /* Keep a record of the AutomationLists that we end up using in this operation */
4323 typedef std::map<boost::shared_ptr<AutomationList>, AutomationRecord> Lists;
4326 /* user could select points in any order */
4327 selection->points.sort(PointsSelectionPositionSorter ());
4329 /* Go through all selected points, making an AutomationRecord for each distinct AutomationList */
4330 for (PointSelection::iterator sel_point = selection->points.begin(); sel_point != selection->points.end(); ++sel_point) {
4331 const AutomationLine& line = (*sel_point)->line();
4332 const boost::shared_ptr<AutomationList> al = line.the_list();
4333 if (lists.find (al) == lists.end ()) {
4334 /* We haven't seen this list yet, so make a record for it. This includes
4335 taking a copy of its current state, in case this is needed for undo later.
4337 lists[al] = AutomationRecord (&al->get_state (), &line);
4341 if (op == Cut || op == Copy) {
4342 /* This operation will involve putting things in the cut buffer, so create an empty
4343 ControlList for each of our source lists to put the cut buffer data in.
4345 for (Lists::iterator i = lists.begin(); i != lists.end(); ++i) {
4346 i->second.copy = i->first->create (i->first->parameter (), i->first->descriptor());
4349 /* Add all selected points to the relevant copy ControlLists */
4350 MusicFrame start (std::numeric_limits<framepos_t>::max(), 0);
4351 for (PointSelection::iterator sel_point = selection->points.begin(); sel_point != selection->points.end(); ++sel_point) {
4352 boost::shared_ptr<AutomationList> al = (*sel_point)->line().the_list();
4353 AutomationList::const_iterator ctrl_evt = (*sel_point)->model ();
4355 lists[al].copy->fast_simple_add ((*ctrl_evt)->when, (*ctrl_evt)->value);
4357 /* Update earliest MIDI start time in beats */
4358 earliest = std::min(earliest, Evoral::Beats((*ctrl_evt)->when));
4360 /* Update earliest session start time in frames */
4361 start.frame = std::min(start.frame, (*sel_point)->line().session_position(ctrl_evt));
4365 /* Snap start time backwards, so copy/paste is snap aligned. */
4367 if (earliest == Evoral::Beats::max()) {
4368 earliest = Evoral::Beats(); // Weird... don't offset
4370 earliest.round_down_to_beat();
4372 if (start.frame == std::numeric_limits<double>::max()) {
4373 start.frame = 0; // Weird... don't offset
4375 snap_to(start, RoundDownMaybe);
4378 const double line_offset = midi ? earliest.to_double() : start.frame;
4379 for (Lists::iterator i = lists.begin(); i != lists.end(); ++i) {
4380 /* Correct this copy list so that it is relative to the earliest
4381 start time, so relative ordering between points is preserved
4382 when copying from several lists and the paste starts at the
4383 earliest copied piece of data. */
4384 boost::shared_ptr<Evoral::ControlList> &al_cpy = i->second.copy;
4385 for (AutomationList::iterator ctrl_evt = al_cpy->begin(); ctrl_evt != al_cpy->end(); ++ctrl_evt) {
4386 (*ctrl_evt)->when -= line_offset;
4389 /* And add it to the cut buffer */
4390 cut_buffer->add (al_cpy);
4394 if (op == Delete || op == Cut) {
4395 /* This operation needs to remove things from the main AutomationList, so do that now */
4397 for (Lists::iterator i = lists.begin(); i != lists.end(); ++i) {
4398 i->first->freeze ();
4401 /* Remove each selected point from its AutomationList */
4402 for (PointSelection::iterator sel_point = selection->points.begin(); sel_point != selection->points.end(); ++sel_point) {
4403 AutomationLine& line = (*sel_point)->line ();
4404 boost::shared_ptr<AutomationList> al = line.the_list();
4408 if (dynamic_cast<AudioRegionGainLine*> (&line)) {
4409 /* removing of first and last gain point in region gain lines is prohibited*/
4410 if (line.is_last_point (*(*sel_point)) || line.is_first_point (*(*sel_point))) {
4416 al->erase ((*sel_point)->model ());
4420 /* Thaw the lists and add undo records for them */
4421 for (Lists::iterator i = lists.begin(); i != lists.end(); ++i) {
4422 boost::shared_ptr<AutomationList> al = i->first;
4424 _session->add_command (new MementoCommand<AutomationList> (*al.get(), i->second.state, &(al->get_state ())));
4429 /** Cut, copy or clear selected automation points.
4430 * @param op Operation (Cut, Copy or Clear)
4433 Editor::cut_copy_midi (CutCopyOp op)
4435 Evoral::Beats earliest = Evoral::Beats::max();
4436 for (MidiRegionSelection::iterator i = selection->midi_regions.begin(); i != selection->midi_regions.end(); ++i) {
4437 MidiRegionView* mrv = dynamic_cast<MidiRegionView*>(*i);
4439 if (!mrv->selection().empty()) {
4440 earliest = std::min(earliest, (*mrv->selection().begin())->note()->time());
4442 mrv->cut_copy_clear (op);
4444 /* XXX: not ideal, as there may be more than one track involved in the selection */
4445 _last_cut_copy_source_track = &mrv->get_time_axis_view();
4449 if (!selection->points.empty()) {
4450 cut_copy_points (op, earliest, true);
4451 if (op == Cut || op == Delete) {
4452 selection->clear_points ();
4457 struct lt_playlist {
4458 bool operator () (const PlaylistState& a, const PlaylistState& b) {
4459 return a.playlist < b.playlist;
4463 struct PlaylistMapping {
4465 boost::shared_ptr<Playlist> pl;
4467 PlaylistMapping (TimeAxisView* tvp) : tv (tvp) {}
4470 /** Remove `clicked_regionview' */
4472 Editor::remove_clicked_region ()
4474 if (clicked_routeview == 0 || clicked_regionview == 0) {
4478 begin_reversible_command (_("remove region"));
4480 boost::shared_ptr<Playlist> playlist = clicked_routeview->playlist();
4482 playlist->clear_changes ();
4483 playlist->clear_owned_changes ();
4484 playlist->remove_region (clicked_regionview->region());
4485 if (Config->get_edit_mode() == Ripple)
4486 playlist->ripple (clicked_regionview->region()->position(), -clicked_regionview->region()->length(), boost::shared_ptr<Region>());
4488 /* We might have removed regions, which alters other regions' layering_index,
4489 so we need to do a recursive diff here.
4491 vector<Command*> cmds;
4492 playlist->rdiff (cmds);
4493 _session->add_commands (cmds);
4495 _session->add_command(new StatefulDiffCommand (playlist));
4496 commit_reversible_command ();
4500 /** Remove the selected regions */
4502 Editor::remove_selected_regions ()
4504 RegionSelection rs = get_regions_from_selection_and_entered ();
4506 if (!_session || rs.empty()) {
4510 list<boost::shared_ptr<Region> > regions_to_remove;
4512 for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
4513 // we can't just remove the region(s) in this loop because
4514 // this removes them from the RegionSelection, and they thus
4515 // disappear from underneath the iterator, and the ++i above
4516 // SEGVs in a puzzling fashion.
4518 // so, first iterate over the regions to be removed from rs and
4519 // add them to the regions_to_remove list, and then
4520 // iterate over the list to actually remove them.
4522 regions_to_remove.push_back ((*i)->region());
4525 vector<boost::shared_ptr<Playlist> > playlists;
4527 for (list<boost::shared_ptr<Region> >::iterator rl = regions_to_remove.begin(); rl != regions_to_remove.end(); ++rl) {
4529 boost::shared_ptr<Playlist> playlist = (*rl)->playlist();
4532 // is this check necessary?
4536 /* get_regions_from_selection_and_entered() guarantees that
4537 the playlists involved are unique, so there is no need
4541 playlists.push_back (playlist);
4543 playlist->clear_changes ();
4544 playlist->clear_owned_changes ();
4545 playlist->freeze ();
4546 playlist->remove_region (*rl);
4547 if (Config->get_edit_mode() == Ripple)
4548 playlist->ripple ((*rl)->position(), -(*rl)->length(), boost::shared_ptr<Region>());
4552 vector<boost::shared_ptr<Playlist> >::iterator pl;
4553 bool in_command = false;
4555 for (pl = playlists.begin(); pl != playlists.end(); ++pl) {
4558 /* We might have removed regions, which alters other regions' layering_index,
4559 so we need to do a recursive diff here.
4563 begin_reversible_command (_("remove region"));
4566 vector<Command*> cmds;
4567 (*pl)->rdiff (cmds);
4568 _session->add_commands (cmds);
4570 _session->add_command(new StatefulDiffCommand (*pl));
4574 commit_reversible_command ();
4578 /** Cut, copy or clear selected regions.
4579 * @param op Operation (Cut, Copy or Clear)
4582 Editor::cut_copy_regions (CutCopyOp op, RegionSelection& rs)
4584 /* we can't use a std::map here because the ordering is important, and we can't trivially sort
4585 a map when we want ordered access to both elements. i think.
4588 vector<PlaylistMapping> pmap;
4590 framepos_t first_position = max_framepos;
4592 typedef set<boost::shared_ptr<Playlist> > FreezeList;
4593 FreezeList freezelist;
4595 /* get ordering correct before we cut/copy */
4597 rs.sort_by_position_and_track ();
4599 for (RegionSelection::iterator x = rs.begin(); x != rs.end(); ++x) {
4601 first_position = min ((framepos_t) (*x)->region()->position(), first_position);
4603 if (op == Cut || op == Clear || op == Delete) {
4604 boost::shared_ptr<Playlist> pl = (*x)->region()->playlist();
4607 FreezeList::iterator fl;
4609 // only take state if this is a new playlist.
4610 for (fl = freezelist.begin(); fl != freezelist.end(); ++fl) {
4616 if (fl == freezelist.end()) {
4617 pl->clear_changes();
4618 pl->clear_owned_changes ();
4620 freezelist.insert (pl);
4625 TimeAxisView* tv = &(*x)->get_time_axis_view();
4626 vector<PlaylistMapping>::iterator z;
4628 for (z = pmap.begin(); z != pmap.end(); ++z) {
4629 if ((*z).tv == tv) {
4634 if (z == pmap.end()) {
4635 pmap.push_back (PlaylistMapping (tv));
4639 for (RegionSelection::iterator x = rs.begin(); x != rs.end(); ) {
4641 boost::shared_ptr<Playlist> pl = (*x)->region()->playlist();
4644 /* region not yet associated with a playlist (e.g. unfinished
4651 TimeAxisView& tv = (*x)->get_time_axis_view();
4652 boost::shared_ptr<Playlist> npl;
4653 RegionSelection::iterator tmp;
4660 vector<PlaylistMapping>::iterator z;
4662 for (z = pmap.begin(); z != pmap.end(); ++z) {
4663 if ((*z).tv == &tv) {
4668 assert (z != pmap.end());
4671 npl = PlaylistFactory::create (pl->data_type(), *_session, "cutlist", true);
4679 boost::shared_ptr<Region> r = (*x)->region();
4680 boost::shared_ptr<Region> _xx;
4686 pl->remove_region (r);
4687 if (Config->get_edit_mode() == Ripple)
4688 pl->ripple (r->position(), -r->length(), boost::shared_ptr<Region>());
4692 _xx = RegionFactory::create (r);
4693 npl->add_region (_xx, r->position() - first_position);
4694 pl->remove_region (r);
4695 if (Config->get_edit_mode() == Ripple)
4696 pl->ripple (r->position(), -r->length(), boost::shared_ptr<Region>());
4700 /* copy region before adding, so we're not putting same object into two different playlists */
4701 npl->add_region (RegionFactory::create (r), r->position() - first_position);
4705 pl->remove_region (r);
4706 if (Config->get_edit_mode() == Ripple)
4707 pl->ripple (r->position(), -r->length(), boost::shared_ptr<Region>());
4716 list<boost::shared_ptr<Playlist> > foo;
4718 /* the pmap is in the same order as the tracks in which selected regions occurred */
4720 for (vector<PlaylistMapping>::iterator i = pmap.begin(); i != pmap.end(); ++i) {
4723 foo.push_back ((*i).pl);
4728 cut_buffer->set (foo);
4732 _last_cut_copy_source_track = 0;
4734 _last_cut_copy_source_track = pmap.front().tv;
4738 for (FreezeList::iterator pl = freezelist.begin(); pl != freezelist.end(); ++pl) {
4741 /* We might have removed regions, which alters other regions' layering_index,
4742 so we need to do a recursive diff here.
4744 vector<Command*> cmds;
4745 (*pl)->rdiff (cmds);
4746 _session->add_commands (cmds);
4748 _session->add_command (new StatefulDiffCommand (*pl));
4753 Editor::cut_copy_ranges (CutCopyOp op)
4755 TrackViewList ts = selection->tracks.filter_to_unique_playlists ();
4757 /* Sort the track selection now, so that it if is used, the playlists
4758 selected by the calls below to cut_copy_clear are in the order that
4759 their tracks appear in the editor. This makes things like paste
4760 of ranges work properly.
4763 sort_track_selection (ts);
4766 if (!entered_track) {
4769 ts.push_back (entered_track);
4772 for (TrackViewList::iterator i = ts.begin(); i != ts.end(); ++i) {
4773 (*i)->cut_copy_clear (*selection, op);
4778 Editor::paste (float times, bool from_context)
4780 DEBUG_TRACE (DEBUG::CutNPaste, "paste to preferred edit pos\n");
4781 MusicFrame where (get_preferred_edit_position (EDIT_IGNORE_NONE, from_context), 0);
4782 paste_internal (where.frame, times, 0);
4786 Editor::mouse_paste ()
4788 MusicFrame where (0, 0);
4790 if (!mouse_frame (where.frame, ignored)) {
4795 paste_internal (where.frame, 1, where.division);
4799 Editor::paste_internal (framepos_t position, float times, const int32_t sub_num)
4801 DEBUG_TRACE (DEBUG::CutNPaste, string_compose ("apparent paste position is %1\n", position));
4803 if (cut_buffer->empty(internal_editing())) {
4807 if (position == max_framepos) {
4808 position = get_preferred_edit_position();
4809 DEBUG_TRACE (DEBUG::CutNPaste, string_compose ("preferred edit position is %1\n", position));
4812 if (position == last_paste_pos) {
4813 /* repeated paste in the same position */
4816 /* paste in new location, reset repeated paste state */
4818 last_paste_pos = position;
4821 /* get everything in the correct order */
4824 if (!selection->tracks.empty()) {
4825 /* If there is a track selection, paste into exactly those tracks and
4826 * only those tracks. This allows the user to be explicit and override
4827 * the below "do the reasonable thing" logic. */
4828 ts = selection->tracks.filter_to_unique_playlists ();
4829 sort_track_selection (ts);
4831 /* Figure out which track to base the paste at. */
4832 TimeAxisView* base_track = NULL;
4833 if (_edit_point == Editing::EditAtMouse && entered_track) {
4834 /* With the mouse edit point, paste onto the track under the mouse. */
4835 base_track = entered_track;
4836 } else if (_edit_point == Editing::EditAtMouse && entered_regionview) {
4837 /* With the mouse edit point, paste onto the track of the region under the mouse. */
4838 base_track = &entered_regionview->get_time_axis_view();
4839 } else if (_last_cut_copy_source_track) {
4840 /* Paste to the track that the cut/copy came from (see mantis #333). */
4841 base_track = _last_cut_copy_source_track;
4843 /* This is "impossible" since we've copied... well, do nothing. */
4847 /* Walk up to parent if necessary, so base track is a route. */
4848 while (base_track->get_parent()) {
4849 base_track = base_track->get_parent();
4852 /* Add base track and all tracks below it. The paste logic will select
4853 the appropriate object types from the cut buffer in relative order. */
4854 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
4855 if ((*i)->order() >= base_track->order()) {
4860 /* Sort tracks so the nth track of type T will pick the nth object of type T. */
4861 sort_track_selection (ts);
4863 /* Add automation children of each track in order, for pasting several lines. */
4864 for (TrackViewList::iterator i = ts.begin(); i != ts.end();) {
4865 /* Add any automation children for pasting several lines */
4866 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*>(*i++);
4871 typedef RouteTimeAxisView::AutomationTracks ATracks;
4872 const ATracks& atracks = rtv->automation_tracks();
4873 for (ATracks::const_iterator a = atracks.begin(); a != atracks.end(); ++a) {
4874 i = ts.insert(i, a->second.get());
4879 /* We now have a list of trackviews starting at base_track, including
4880 automation children, in the order shown in the editor, e.g. R1,
4881 R1.A1, R1.A2, R2, R2.A1, ... */
4884 begin_reversible_command (Operations::paste);
4886 if (ts.size() == 1 && cut_buffer->lines.size() == 1 &&
4887 dynamic_cast<AutomationTimeAxisView*>(ts.front())) {
4888 /* Only one line copied, and one automation track selected. Do a
4889 "greedy" paste from one automation type to another. */
4891 PasteContext ctx(paste_count, times, ItemCounts(), true);
4892 ts.front()->paste (position, *cut_buffer, ctx, sub_num);
4896 /* Paste into tracks */
4898 PasteContext ctx(paste_count, times, ItemCounts(), false);
4899 for (TrackViewList::iterator i = ts.begin(); i != ts.end(); ++i) {
4900 (*i)->paste (position, *cut_buffer, ctx, sub_num);
4904 commit_reversible_command ();
4908 Editor::duplicate_regions (float times)
4910 RegionSelection rs (get_regions_from_selection_and_entered());
4911 duplicate_some_regions (rs, times);
4915 Editor::duplicate_some_regions (RegionSelection& regions, float times)
4917 if (regions.empty ()) {
4921 boost::shared_ptr<Playlist> playlist;
4922 RegionSelection sel = regions; // clear (below) may clear the argument list if its the current region selection
4923 RegionSelection foo;
4925 framepos_t const start_frame = regions.start ();
4926 framepos_t const end_frame = regions.end_frame ();
4927 framecnt_t const gap = end_frame - start_frame + 1;
4929 begin_reversible_command (Operations::duplicate_region);
4931 selection->clear_regions ();
4933 for (RegionSelection::iterator i = sel.begin(); i != sel.end(); ++i) {
4935 boost::shared_ptr<Region> r ((*i)->region());
4937 TimeAxisView& tv = (*i)->get_time_axis_view();
4938 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (&tv);
4939 latest_regionviews.clear ();
4940 sigc::connection c = rtv->view()->RegionViewAdded.connect (sigc::mem_fun(*this, &Editor::collect_new_region_view));
4942 framepos_t const position = end_frame + (r->first_frame() - start_frame + 1);
4943 playlist = (*i)->region()->playlist();
4944 playlist->clear_changes ();
4945 playlist->duplicate (r, position, gap, times);
4946 _session->add_command(new StatefulDiffCommand (playlist));
4950 foo.insert (foo.end(), latest_regionviews.begin(), latest_regionviews.end());
4954 selection->set (foo);
4957 commit_reversible_command ();
4961 Editor::duplicate_selection (float times)
4963 if (selection->time.empty() || selection->tracks.empty()) {
4967 boost::shared_ptr<Playlist> playlist;
4969 TrackViewList ts = selection->tracks.filter_to_unique_playlists ();
4971 bool in_command = false;
4973 for (TrackViewList::iterator i = ts.begin(); i != ts.end(); ++i) {
4974 if ((playlist = (*i)->playlist()) == 0) {
4977 playlist->clear_changes ();
4979 if (clicked_selection) {
4980 playlist->duplicate_range (selection->time[clicked_selection], times);
4982 playlist->duplicate_ranges (selection->time, times);
4986 begin_reversible_command (_("duplicate range selection"));
4989 _session->add_command (new StatefulDiffCommand (playlist));
4994 if (times == 1.0f) {
4995 // now "move" range selection to after the current range selection
4996 framecnt_t distance = 0;
4998 if (clicked_selection) {
5000 selection->time[clicked_selection].end - selection->time[clicked_selection].start;
5002 distance = selection->time.end_frame () - selection->time.start ();
5005 selection->move_time (distance);
5007 commit_reversible_command ();
5011 /** Reset all selected points to the relevant default value */
5013 Editor::reset_point_selection ()
5015 for (PointSelection::iterator i = selection->points.begin(); i != selection->points.end(); ++i) {
5016 ARDOUR::AutomationList::iterator j = (*i)->model ();
5017 (*j)->value = (*i)->line().the_list()->descriptor ().normal;
5022 Editor::center_playhead ()
5024 float const page = _visible_canvas_width * samples_per_pixel;
5025 center_screen_internal (playhead_cursor->current_frame (), page);
5029 Editor::center_edit_point ()
5031 float const page = _visible_canvas_width * samples_per_pixel;
5032 center_screen_internal (get_preferred_edit_position(), page);
5035 /** Caller must begin and commit a reversible command */
5037 Editor::clear_playlist (boost::shared_ptr<Playlist> playlist)
5039 playlist->clear_changes ();
5041 _session->add_command (new StatefulDiffCommand (playlist));
5045 Editor::nudge_track (bool use_edit, bool forwards)
5047 boost::shared_ptr<Playlist> playlist;
5048 framepos_t distance;
5049 framepos_t next_distance;
5053 start = get_preferred_edit_position();
5058 if ((distance = get_nudge_distance (start, next_distance)) == 0) {
5062 if (selection->tracks.empty()) {
5066 TrackViewList ts = selection->tracks.filter_to_unique_playlists ();
5067 bool in_command = false;
5069 for (TrackViewList::iterator i = ts.begin(); i != ts.end(); ++i) {
5071 if ((playlist = (*i)->playlist()) == 0) {
5075 playlist->clear_changes ();
5076 playlist->clear_owned_changes ();
5078 playlist->nudge_after (start, distance, forwards);
5081 begin_reversible_command (_("nudge track"));
5084 vector<Command*> cmds;
5086 playlist->rdiff (cmds);
5087 _session->add_commands (cmds);
5089 _session->add_command (new StatefulDiffCommand (playlist));
5093 commit_reversible_command ();
5098 Editor::remove_last_capture ()
5100 vector<string> choices;
5107 if (Config->get_verify_remove_last_capture()) {
5108 prompt = _("Do you really want to destroy the last capture?"
5109 "\n(This is destructive and cannot be undone)");
5111 choices.push_back (_("No, do nothing."));
5112 choices.push_back (_("Yes, destroy it."));
5114 Choice prompter (_("Destroy last capture"), prompt, choices);
5116 if (prompter.run () == 1) {
5117 _session->remove_last_capture ();
5118 _regions->redisplay ();
5122 _session->remove_last_capture();
5123 _regions->redisplay ();
5128 Editor::normalize_region ()
5134 RegionSelection rs = get_regions_from_selection_and_entered ();
5140 NormalizeDialog dialog (rs.size() > 1);
5142 if (dialog.run () != RESPONSE_ACCEPT) {
5146 CursorContext::Handle cursor_ctx = CursorContext::create(*this, _cursors->wait);
5149 /* XXX: should really only count audio regions here */
5150 int const regions = rs.size ();
5152 /* Make a list of the selected audio regions' maximum amplitudes, and also
5153 obtain the maximum amplitude of them all.
5155 list<double> max_amps;
5156 list<double> rms_vals;
5159 bool use_rms = dialog.constrain_rms ();
5161 for (RegionSelection::const_iterator i = rs.begin(); i != rs.end(); ++i) {
5162 AudioRegionView const * arv = dynamic_cast<AudioRegionView const *> (*i);
5166 dialog.descend (1.0 / regions);
5167 double const a = arv->audio_region()->maximum_amplitude (&dialog);
5169 double r = arv->audio_region()->rms (&dialog);
5170 max_rms = max (max_rms, r);
5171 rms_vals.push_back (r);
5175 /* the user cancelled the operation */
5179 max_amps.push_back (a);
5180 max_amp = max (max_amp, a);
5184 list<double>::const_iterator a = max_amps.begin ();
5185 list<double>::const_iterator l = rms_vals.begin ();
5186 bool in_command = false;
5188 for (RegionSelection::iterator r = rs.begin(); r != rs.end(); ++r) {
5189 AudioRegionView* const arv = dynamic_cast<AudioRegionView*> (*r);
5194 arv->region()->clear_changes ();
5196 double amp = dialog.normalize_individually() ? *a : max_amp;
5197 double target = dialog.target_peak (); // dB
5200 double const amp_rms = dialog.normalize_individually() ? *l : max_rms;
5201 const double t_rms = dialog.target_rms ();
5202 const gain_t c_peak = dB_to_coefficient (target);
5203 const gain_t c_rms = dB_to_coefficient (t_rms);
5204 if ((amp_rms / c_rms) > (amp / c_peak)) {
5210 arv->audio_region()->normalize (amp, target);
5213 begin_reversible_command (_("normalize"));
5216 _session->add_command (new StatefulDiffCommand (arv->region()));
5223 commit_reversible_command ();
5229 Editor::reset_region_scale_amplitude ()
5235 RegionSelection rs = get_regions_from_selection_and_entered ();
5241 bool in_command = false;
5243 for (RegionSelection::iterator r = rs.begin(); r != rs.end(); ++r) {
5244 AudioRegionView* const arv = dynamic_cast<AudioRegionView*>(*r);
5247 arv->region()->clear_changes ();
5248 arv->audio_region()->set_scale_amplitude (1.0f);
5251 begin_reversible_command ("reset gain");
5254 _session->add_command (new StatefulDiffCommand (arv->region()));
5258 commit_reversible_command ();
5263 Editor::adjust_region_gain (bool up)
5265 RegionSelection rs = get_regions_from_selection_and_entered ();
5267 if (!_session || rs.empty()) {
5271 bool in_command = false;
5273 for (RegionSelection::iterator r = rs.begin(); r != rs.end(); ++r) {
5274 AudioRegionView* const arv = dynamic_cast<AudioRegionView*>(*r);
5279 arv->region()->clear_changes ();
5281 double dB = accurate_coefficient_to_dB (arv->audio_region()->scale_amplitude ());
5289 arv->audio_region()->set_scale_amplitude (dB_to_coefficient (dB));
5292 begin_reversible_command ("adjust region gain");
5295 _session->add_command (new StatefulDiffCommand (arv->region()));
5299 commit_reversible_command ();
5304 Editor::reset_region_gain ()
5306 RegionSelection rs = get_regions_from_selection_and_entered ();
5308 if (!_session || rs.empty()) {
5312 bool in_command = false;
5314 for (RegionSelection::iterator r = rs.begin(); r != rs.end(); ++r) {
5315 AudioRegionView* const arv = dynamic_cast<AudioRegionView*>(*r);
5320 arv->region()->clear_changes ();
5322 arv->audio_region()->set_scale_amplitude (1.0f);
5325 begin_reversible_command ("reset region gain");
5328 _session->add_command (new StatefulDiffCommand (arv->region()));
5332 commit_reversible_command ();
5337 Editor::reverse_region ()
5343 Reverse rev (*_session);
5344 apply_filter (rev, _("reverse regions"));
5348 Editor::strip_region_silence ()
5354 RegionSelection rs = get_regions_from_selection_and_entered ();
5360 std::list<RegionView*> audio_only;
5362 for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
5363 AudioRegionView* const arv = dynamic_cast<AudioRegionView*> (*i);
5365 audio_only.push_back (arv);
5369 assert (!audio_only.empty());
5371 StripSilenceDialog d (_session, audio_only);
5372 int const r = d.run ();
5376 if (r == Gtk::RESPONSE_OK) {
5377 ARDOUR::AudioIntervalMap silences;
5378 d.silences (silences);
5379 StripSilence s (*_session, silences, d.fade_length());
5381 apply_filter (s, _("strip silence"), &d);
5386 Editor::apply_midi_note_edit_op_to_region (MidiOperator& op, MidiRegionView& mrv)
5388 Evoral::Sequence<Evoral::Beats>::Notes selected;
5389 mrv.selection_as_notelist (selected, true);
5391 vector<Evoral::Sequence<Evoral::Beats>::Notes> v;
5392 v.push_back (selected);
5394 Evoral::Beats pos_beats = Evoral::Beats (mrv.midi_region()->beat()) - mrv.midi_region()->start_beats();
5396 return op (mrv.midi_region()->model(), pos_beats, v);
5400 Editor::apply_midi_note_edit_op (MidiOperator& op, const RegionSelection& rs)
5406 bool in_command = false;
5408 for (RegionSelection::const_iterator r = rs.begin(); r != rs.end(); ) {
5409 RegionSelection::const_iterator tmp = r;
5412 MidiRegionView* const mrv = dynamic_cast<MidiRegionView*> (*r);
5415 Command* cmd = apply_midi_note_edit_op_to_region (op, *mrv);
5418 begin_reversible_command (op.name ());
5422 _session->add_command (cmd);
5430 commit_reversible_command ();
5431 _session->set_dirty ();
5436 Editor::fork_region ()
5438 RegionSelection rs = get_regions_from_selection_and_entered ();
5444 CursorContext::Handle cursor_ctx = CursorContext::create(*this, _cursors->wait);
5445 bool in_command = false;
5449 for (RegionSelection::iterator r = rs.begin(); r != rs.end(); ) {
5450 RegionSelection::iterator tmp = r;
5453 MidiRegionView* const mrv = dynamic_cast<MidiRegionView*>(*r);
5457 boost::shared_ptr<Playlist> playlist = mrv->region()->playlist();
5458 boost::shared_ptr<MidiSource> new_source = _session->create_midi_source_by_stealing_name (mrv->midi_view()->track());
5459 boost::shared_ptr<MidiRegion> newregion = mrv->midi_region()->clone (new_source);
5462 begin_reversible_command (_("Fork Region(s)"));
5465 playlist->clear_changes ();
5466 playlist->replace_region (mrv->region(), newregion, mrv->region()->position());
5467 _session->add_command(new StatefulDiffCommand (playlist));
5469 error << string_compose (_("Could not unlink %1"), mrv->region()->name()) << endmsg;
5477 commit_reversible_command ();
5482 Editor::quantize_region ()
5485 quantize_regions(get_regions_from_selection_and_entered ());
5490 Editor::quantize_regions (const RegionSelection& rs)
5492 if (rs.n_midi_regions() == 0) {
5496 if (!quantize_dialog) {
5497 quantize_dialog = new QuantizeDialog (*this);
5500 if (quantize_dialog->is_mapped()) {
5501 /* in progress already */
5505 quantize_dialog->present ();
5506 const int r = quantize_dialog->run ();
5507 quantize_dialog->hide ();
5509 if (r == Gtk::RESPONSE_OK) {
5510 Quantize quant (quantize_dialog->snap_start(),
5511 quantize_dialog->snap_end(),
5512 quantize_dialog->start_grid_size(),
5513 quantize_dialog->end_grid_size(),
5514 quantize_dialog->strength(),
5515 quantize_dialog->swing(),
5516 quantize_dialog->threshold());
5518 apply_midi_note_edit_op (quant, rs);
5523 Editor::legatize_region (bool shrink_only)
5526 legatize_regions(get_regions_from_selection_and_entered (), shrink_only);
5531 Editor::legatize_regions (const RegionSelection& rs, bool shrink_only)
5533 if (rs.n_midi_regions() == 0) {
5537 Legatize legatize(shrink_only);
5538 apply_midi_note_edit_op (legatize, rs);
5542 Editor::transform_region ()
5545 transform_regions(get_regions_from_selection_and_entered ());
5550 Editor::transform_regions (const RegionSelection& rs)
5552 if (rs.n_midi_regions() == 0) {
5559 const int r = td.run();
5562 if (r == Gtk::RESPONSE_OK) {
5563 Transform transform(td.get());
5564 apply_midi_note_edit_op(transform, rs);
5569 Editor::transpose_region ()
5572 transpose_regions(get_regions_from_selection_and_entered ());
5577 Editor::transpose_regions (const RegionSelection& rs)
5579 if (rs.n_midi_regions() == 0) {
5584 int const r = d.run ();
5586 if (r == RESPONSE_ACCEPT) {
5587 Transpose transpose(d.semitones ());
5588 apply_midi_note_edit_op (transpose, rs);
5593 Editor::insert_patch_change (bool from_context)
5595 RegionSelection rs = get_regions_from_selection_and_entered ();
5601 const framepos_t p = get_preferred_edit_position (EDIT_IGNORE_NONE, from_context);
5603 /* XXX: bit of a hack; use the MIDNAM from the first selected region;
5604 there may be more than one, but the PatchChangeDialog can only offer
5605 one set of patch menus.
5607 MidiRegionView* first = dynamic_cast<MidiRegionView*> (rs.front ());
5609 Evoral::PatchChange<Evoral::Beats> empty (Evoral::Beats(), 0, 0, 0);
5610 PatchChangeDialog d (0, _session, empty, first->instrument_info(), Gtk::Stock::ADD);
5612 if (d.run() == RESPONSE_CANCEL) {
5616 for (RegionSelection::iterator i = rs.begin (); i != rs.end(); ++i) {
5617 MidiRegionView* const mrv = dynamic_cast<MidiRegionView*> (*i);
5619 if (p >= mrv->region()->first_frame() && p <= mrv->region()->last_frame()) {
5620 mrv->add_patch_change (p - mrv->region()->position(), d.patch ());
5627 Editor::apply_filter (Filter& filter, string command, ProgressReporter* progress)
5629 RegionSelection rs = get_regions_from_selection_and_entered ();
5635 CursorContext::Handle cursor_ctx = CursorContext::create(*this, _cursors->wait);
5636 bool in_command = false;
5641 int const N = rs.size ();
5643 for (RegionSelection::iterator r = rs.begin(); r != rs.end(); ) {
5644 RegionSelection::iterator tmp = r;
5647 AudioRegionView* const arv = dynamic_cast<AudioRegionView*>(*r);
5649 boost::shared_ptr<Playlist> playlist = arv->region()->playlist();
5652 progress->descend (1.0 / N);
5655 if (arv->audio_region()->apply (filter, progress) == 0) {
5657 playlist->clear_changes ();
5658 playlist->clear_owned_changes ();
5661 begin_reversible_command (command);
5665 if (filter.results.empty ()) {
5667 /* no regions returned; remove the old one */
5668 playlist->remove_region (arv->region ());
5672 std::vector<boost::shared_ptr<Region> >::iterator res = filter.results.begin ();
5674 /* first region replaces the old one */
5675 playlist->replace_region (arv->region(), *res, (*res)->position());
5679 while (res != filter.results.end()) {
5680 playlist->add_region (*res, (*res)->position());
5686 /* We might have removed regions, which alters other regions' layering_index,
5687 so we need to do a recursive diff here.
5689 vector<Command*> cmds;
5690 playlist->rdiff (cmds);
5691 _session->add_commands (cmds);
5693 _session->add_command(new StatefulDiffCommand (playlist));
5697 progress->ascend ();
5706 commit_reversible_command ();
5711 Editor::external_edit_region ()
5717 Editor::reset_region_gain_envelopes ()
5719 RegionSelection rs = get_regions_from_selection_and_entered ();
5721 if (!_session || rs.empty()) {
5725 bool in_command = false;
5727 for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
5728 AudioRegionView* const arv = dynamic_cast<AudioRegionView*>(*i);
5730 boost::shared_ptr<AutomationList> alist (arv->audio_region()->envelope());
5731 XMLNode& before (alist->get_state());
5733 arv->audio_region()->set_default_envelope ();
5736 begin_reversible_command (_("reset region gain"));
5739 _session->add_command (new MementoCommand<AutomationList>(*arv->audio_region()->envelope().get(), &before, &alist->get_state()));
5744 commit_reversible_command ();
5749 Editor::set_region_gain_visibility (RegionView* rv)
5751 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (rv);
5753 arv->update_envelope_visibility();
5758 Editor::set_gain_envelope_visibility ()
5764 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
5765 AudioTimeAxisView* v = dynamic_cast<AudioTimeAxisView*>(*i);
5767 v->audio_view()->foreach_regionview (sigc::mem_fun (this, &Editor::set_region_gain_visibility));
5773 Editor::toggle_gain_envelope_active ()
5775 if (_ignore_region_action) {
5779 RegionSelection rs = get_regions_from_selection_and_entered ();
5781 if (!_session || rs.empty()) {
5785 bool in_command = false;
5787 for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
5788 AudioRegionView* const arv = dynamic_cast<AudioRegionView*>(*i);
5790 arv->region()->clear_changes ();
5791 arv->audio_region()->set_envelope_active (!arv->audio_region()->envelope_active());
5794 begin_reversible_command (_("region gain envelope active"));
5797 _session->add_command (new StatefulDiffCommand (arv->region()));
5802 commit_reversible_command ();
5807 Editor::toggle_region_lock ()
5809 if (_ignore_region_action) {
5813 RegionSelection rs = get_regions_from_selection_and_entered ();
5815 if (!_session || rs.empty()) {
5819 begin_reversible_command (_("toggle region lock"));
5821 for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
5822 (*i)->region()->clear_changes ();
5823 (*i)->region()->set_locked (!(*i)->region()->locked());
5824 _session->add_command (new StatefulDiffCommand ((*i)->region()));
5827 commit_reversible_command ();
5831 Editor::toggle_region_video_lock ()
5833 if (_ignore_region_action) {
5837 RegionSelection rs = get_regions_from_selection_and_entered ();
5839 if (!_session || rs.empty()) {
5843 begin_reversible_command (_("Toggle Video Lock"));
5845 for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
5846 (*i)->region()->clear_changes ();
5847 (*i)->region()->set_video_locked (!(*i)->region()->video_locked());
5848 _session->add_command (new StatefulDiffCommand ((*i)->region()));
5851 commit_reversible_command ();
5855 Editor::toggle_region_lock_style ()
5857 if (_ignore_region_action) {
5861 RegionSelection rs = get_regions_from_selection_and_entered ();
5863 if (!_session || rs.empty()) {
5867 Glib::RefPtr<ToggleAction> a = Glib::RefPtr<ToggleAction>::cast_dynamic (_region_actions->get_action("toggle-region-lock-style"));
5868 vector<Widget*> proxies = a->get_proxies();
5869 Gtk::CheckMenuItem* cmi = dynamic_cast<Gtk::CheckMenuItem*> (proxies.front());
5873 begin_reversible_command (_("toggle region lock style"));
5875 for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
5876 (*i)->region()->clear_changes ();
5877 PositionLockStyle const ns = ((*i)->region()->position_lock_style() == AudioTime && !cmi->get_inconsistent()) ? MusicTime : AudioTime;
5878 (*i)->region()->set_position_lock_style (ns);
5879 _session->add_command (new StatefulDiffCommand ((*i)->region()));
5882 commit_reversible_command ();
5886 Editor::toggle_opaque_region ()
5888 if (_ignore_region_action) {
5892 RegionSelection rs = get_regions_from_selection_and_entered ();
5894 if (!_session || rs.empty()) {
5898 begin_reversible_command (_("change region opacity"));
5900 for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
5901 (*i)->region()->clear_changes ();
5902 (*i)->region()->set_opaque (!(*i)->region()->opaque());
5903 _session->add_command (new StatefulDiffCommand ((*i)->region()));
5906 commit_reversible_command ();
5910 Editor::toggle_record_enable ()
5912 bool new_state = false;
5914 for (TrackSelection::iterator i = selection->tracks.begin(); i != selection->tracks.end(); ++i) {
5915 RouteTimeAxisView *rtav = dynamic_cast<RouteTimeAxisView *>(*i);
5918 if (!rtav->is_track())
5922 new_state = !rtav->track()->rec_enable_control()->get_value();
5926 rtav->track()->rec_enable_control()->set_value (new_state, Controllable::UseGroup);
5931 Editor::toggle_solo ()
5933 bool new_state = false;
5935 boost::shared_ptr<ControlList> cl (new ControlList);
5937 for (TrackSelection::iterator i = selection->tracks.begin(); i != selection->tracks.end(); ++i) {
5938 StripableTimeAxisView *stav = dynamic_cast<StripableTimeAxisView *>(*i);
5940 if (!stav || !stav->stripable()->solo_control()) {
5945 new_state = !stav->stripable()->solo_control()->soloed ();
5949 cl->push_back (stav->stripable()->solo_control());
5952 _session->set_controls (cl, new_state ? 1.0 : 0.0, Controllable::UseGroup);
5956 Editor::toggle_mute ()
5958 bool new_state = false;
5960 boost::shared_ptr<ControlList> cl (new ControlList);
5962 for (TrackSelection::iterator i = selection->tracks.begin(); i != selection->tracks.end(); ++i) {
5963 StripableTimeAxisView *stav = dynamic_cast<StripableTimeAxisView *>(*i);
5965 if (!stav || !stav->stripable()->mute_control()) {
5970 new_state = !stav->stripable()->mute_control()->muted();
5974 cl->push_back (stav->stripable()->mute_control());
5977 _session->set_controls (cl, new_state, Controllable::UseGroup);
5981 Editor::toggle_solo_isolate ()
5987 Editor::fade_range ()
5989 TrackViewList ts = selection->tracks.filter_to_unique_playlists ();
5991 begin_reversible_command (_("fade range"));
5993 for (TrackViewList::iterator i = ts.begin(); i != ts.end(); ++i) {
5994 (*i)->fade_range (selection->time);
5997 commit_reversible_command ();
6002 Editor::set_fade_length (bool in)
6004 RegionSelection rs = get_regions_from_selection_and_entered ();
6010 /* we need a region to measure the offset from the start */
6012 RegionView* rv = rs.front ();
6014 framepos_t pos = get_preferred_edit_position();
6018 if (pos > rv->region()->last_frame() || pos < rv->region()->first_frame()) {
6019 /* edit point is outside the relevant region */
6024 if (pos <= rv->region()->position()) {
6028 len = pos - rv->region()->position();
6029 cmd = _("set fade in length");
6031 if (pos >= rv->region()->last_frame()) {
6035 len = rv->region()->last_frame() - pos;
6036 cmd = _("set fade out length");
6039 bool in_command = false;
6041 for (RegionSelection::iterator x = rs.begin(); x != rs.end(); ++x) {
6042 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (*x);
6048 boost::shared_ptr<AutomationList> alist;
6050 alist = tmp->audio_region()->fade_in();
6052 alist = tmp->audio_region()->fade_out();
6055 XMLNode &before = alist->get_state();
6058 tmp->audio_region()->set_fade_in_length (len);
6059 tmp->audio_region()->set_fade_in_active (true);
6061 tmp->audio_region()->set_fade_out_length (len);
6062 tmp->audio_region()->set_fade_out_active (true);
6066 begin_reversible_command (cmd);
6069 XMLNode &after = alist->get_state();
6070 _session->add_command(new MementoCommand<AutomationList>(*alist, &before, &after));
6074 commit_reversible_command ();
6079 Editor::set_fade_in_shape (FadeShape shape)
6081 RegionSelection rs = get_regions_from_selection_and_entered ();
6086 bool in_command = false;
6088 for (RegionSelection::iterator x = rs.begin(); x != rs.end(); ++x) {
6089 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (*x);
6095 boost::shared_ptr<AutomationList> alist = tmp->audio_region()->fade_in();
6096 XMLNode &before = alist->get_state();
6098 tmp->audio_region()->set_fade_in_shape (shape);
6101 begin_reversible_command (_("set fade in shape"));
6104 XMLNode &after = alist->get_state();
6105 _session->add_command(new MementoCommand<AutomationList>(*alist.get(), &before, &after));
6109 commit_reversible_command ();
6114 Editor::set_fade_out_shape (FadeShape shape)
6116 RegionSelection rs = get_regions_from_selection_and_entered ();
6121 bool in_command = false;
6123 for (RegionSelection::iterator x = rs.begin(); x != rs.end(); ++x) {
6124 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (*x);
6130 boost::shared_ptr<AutomationList> alist = tmp->audio_region()->fade_out();
6131 XMLNode &before = alist->get_state();
6133 tmp->audio_region()->set_fade_out_shape (shape);
6136 begin_reversible_command (_("set fade out shape"));
6139 XMLNode &after = alist->get_state();
6140 _session->add_command(new MementoCommand<AutomationList>(*alist.get(), &before, &after));
6144 commit_reversible_command ();
6149 Editor::set_fade_in_active (bool yn)
6151 RegionSelection rs = get_regions_from_selection_and_entered ();
6156 bool in_command = false;
6158 for (RegionSelection::iterator x = rs.begin(); x != rs.end(); ++x) {
6159 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (*x);
6166 boost::shared_ptr<AudioRegion> ar (tmp->audio_region());
6168 ar->clear_changes ();
6169 ar->set_fade_in_active (yn);
6172 begin_reversible_command (_("set fade in active"));
6175 _session->add_command (new StatefulDiffCommand (ar));
6179 commit_reversible_command ();
6184 Editor::set_fade_out_active (bool yn)
6186 RegionSelection rs = get_regions_from_selection_and_entered ();
6191 bool in_command = false;
6193 for (RegionSelection::iterator x = rs.begin(); x != rs.end(); ++x) {
6194 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (*x);
6200 boost::shared_ptr<AudioRegion> ar (tmp->audio_region());
6202 ar->clear_changes ();
6203 ar->set_fade_out_active (yn);
6206 begin_reversible_command (_("set fade out active"));
6209 _session->add_command(new StatefulDiffCommand (ar));
6213 commit_reversible_command ();
6218 Editor::toggle_region_fades (int dir)
6220 if (_ignore_region_action) {
6224 boost::shared_ptr<AudioRegion> ar;
6227 RegionSelection rs = get_regions_from_selection_and_entered ();
6233 RegionSelection::iterator i;
6234 for (i = rs.begin(); i != rs.end(); ++i) {
6235 if ((ar = boost::dynamic_pointer_cast<AudioRegion>((*i)->region())) != 0) {
6237 yn = ar->fade_out_active ();
6239 yn = ar->fade_in_active ();
6245 if (i == rs.end()) {
6249 /* XXX should this undo-able? */
6250 bool in_command = false;
6252 for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
6253 if ((ar = boost::dynamic_pointer_cast<AudioRegion>((*i)->region())) == 0) {
6256 ar->clear_changes ();
6258 if (dir == 1 || dir == 0) {
6259 ar->set_fade_in_active (!yn);
6262 if (dir == -1 || dir == 0) {
6263 ar->set_fade_out_active (!yn);
6266 begin_reversible_command (_("toggle fade active"));
6269 _session->add_command(new StatefulDiffCommand (ar));
6273 commit_reversible_command ();
6278 /** Update region fade visibility after its configuration has been changed */
6280 Editor::update_region_fade_visibility ()
6282 bool _fade_visibility = _session->config.get_show_region_fades ();
6284 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
6285 AudioTimeAxisView* v = dynamic_cast<AudioTimeAxisView*>(*i);
6287 if (_fade_visibility) {
6288 v->audio_view()->show_all_fades ();
6290 v->audio_view()->hide_all_fades ();
6297 Editor::set_edit_point ()
6300 MusicFrame where (0, 0);
6302 if (!mouse_frame (where.frame, ignored)) {
6308 if (selection->markers.empty()) {
6310 mouse_add_new_marker (where.frame);
6315 Location* loc = find_location_from_marker (selection->markers.front(), ignored);
6318 loc->move_to (where.frame, where.division);
6324 Editor::set_playhead_cursor ()
6326 if (entered_marker) {
6327 _session->request_locate (entered_marker->position(), _session->transport_rolling());
6329 MusicFrame where (0, 0);
6332 if (!mouse_frame (where.frame, ignored)) {
6339 _session->request_locate (where.frame, _session->transport_rolling());
6343 //not sure what this was for; remove it for now.
6344 // if (UIConfiguration::instance().get_follow_edits() && (!_session || !_session->config.get_external_sync())) {
6345 // cancel_time_selection();
6351 Editor::split_region ()
6353 if (_drags->active ()) {
6357 //if a range is selected, separate it
6358 if ( !selection->time.empty()) {
6359 separate_regions_between (selection->time);
6363 //if no range was selected, try to find some regions to split
6364 if (current_mouse_mode() == MouseObject) { //don't try this for Internal Edit, Stretch, Draw, etc.
6366 RegionSelection rs = get_regions_from_selection_and_edit_point ();
6367 const framepos_t pos = get_preferred_edit_position();
6368 const int32_t division = get_grid_music_divisions (0);
6369 MusicFrame where (pos, division);
6375 split_regions_at (where, rs);
6381 Editor::select_next_stripable (bool routes_only)
6383 if (selection->tracks.empty()) {
6384 selection->set (track_views.front());
6388 TimeAxisView* current = selection->tracks.front();
6392 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
6394 if (*i == current) {
6396 if (i != track_views.end()) {
6399 current = (*(track_views.begin()));
6400 //selection->set (*(track_views.begin()));
6407 RouteUI* rui = dynamic_cast<RouteUI *>(current);
6408 valid = rui && rui->route()->active();
6410 valid = 0 != current->stripable ().get();
6413 } while (current->hidden() || !valid);
6415 selection->set (current);
6417 ensure_time_axis_view_is_visible (*current, false);
6421 Editor::select_prev_stripable (bool routes_only)
6423 if (selection->tracks.empty()) {
6424 selection->set (track_views.front());
6428 TimeAxisView* current = selection->tracks.front();
6432 for (TrackViewList::reverse_iterator i = track_views.rbegin(); i != track_views.rend(); ++i) {
6434 if (*i == current) {
6436 if (i != track_views.rend()) {
6439 current = *(track_views.rbegin());
6445 RouteUI* rui = dynamic_cast<RouteUI *>(current);
6446 valid = rui && rui->route()->active();
6448 valid = 0 != current->stripable ().get();
6451 } while (current->hidden() || !valid);
6453 selection->set (current);
6455 ensure_time_axis_view_is_visible (*current, false);
6459 Editor::set_loop_from_selection (bool play)
6461 if (_session == 0) {
6465 framepos_t start, end;
6466 if (!get_selection_extents ( start, end))
6469 set_loop_range (start, end, _("set loop range from selection"));
6472 _session->request_play_loop (true, true);
6477 Editor::set_loop_from_region (bool play)
6479 framepos_t start, end;
6480 if (!get_selection_extents ( start, end))
6483 set_loop_range (start, end, _("set loop range from region"));
6486 _session->request_locate (start, true);
6487 _session->request_play_loop (true);
6492 Editor::set_punch_from_selection ()
6494 if (_session == 0) {
6498 framepos_t start, end;
6499 if (!get_selection_extents ( start, end))
6502 set_punch_range (start, end, _("set punch range from selection"));
6506 Editor::set_auto_punch_range ()
6508 // auto punch in/out button from a single button
6509 // If Punch In is unset, set punch range from playhead to end, enable punch in
6510 // If Punch In is set, the next punch sets Punch Out, unless the playhead has been
6511 // rewound beyond the Punch In marker, in which case that marker will be moved back
6512 // to the current playhead position.
6513 // If punch out is set, it clears the punch range and Punch In/Out buttons
6515 if (_session == 0) {
6519 Location* tpl = transport_punch_location();
6520 framepos_t now = playhead_cursor->current_frame();
6521 framepos_t begin = now;
6522 framepos_t end = _session->current_end_frame();
6524 if (!_session->config.get_punch_in()) {
6525 // First Press - set punch in and create range from here to eternity
6526 set_punch_range (begin, end, _("Auto Punch In"));
6527 _session->config.set_punch_in(true);
6528 } else if (tpl && !_session->config.get_punch_out()) {
6529 // Second press - update end range marker and set punch_out
6530 if (now < tpl->start()) {
6531 // playhead has been rewound - move start back and pretend nothing happened
6533 set_punch_range (begin, end, _("Auto Punch In/Out"));
6535 // normal case for 2nd press - set the punch out
6536 end = playhead_cursor->current_frame ();
6537 set_punch_range (tpl->start(), now, _("Auto Punch In/Out"));
6538 _session->config.set_punch_out(true);
6541 if (_session->config.get_punch_out()) {
6542 _session->config.set_punch_out(false);
6545 if (_session->config.get_punch_in()) {
6546 _session->config.set_punch_in(false);
6551 // third press - unset punch in/out and remove range
6552 _session->locations()->remove(tpl);
6559 Editor::set_session_extents_from_selection ()
6561 if (_session == 0) {
6565 framepos_t start, end;
6566 if (!get_selection_extents ( start, end))
6570 if ((loc = _session->locations()->session_range_location()) == 0) {
6571 _session->set_session_extents (start, end); // this will create a new session range; no need for UNDO
6573 XMLNode &before = loc->get_state();
6575 _session->set_session_extents (start, end);
6577 XMLNode &after = loc->get_state();
6579 begin_reversible_command (_("set session start/end from selection"));
6581 _session->add_command (new MementoCommand<Location>(*loc, &before, &after));
6583 commit_reversible_command ();
6586 _session->set_end_is_free (false);
6590 Editor::set_punch_start_from_edit_point ()
6594 MusicFrame start (0, 0);
6595 framepos_t end = max_framepos;
6597 //use the existing punch end, if any
6598 Location* tpl = transport_punch_location();
6603 if ((_edit_point == EditAtPlayhead) && _session->transport_rolling()) {
6604 start.frame = _session->audible_frame();
6606 start.frame = get_preferred_edit_position();
6609 //snap the selection start/end
6612 //if there's not already a sensible selection endpoint, go "forever"
6613 if (start.frame > end ) {
6617 set_punch_range (start.frame, end, _("set punch start from EP"));
6623 Editor::set_punch_end_from_edit_point ()
6627 framepos_t start = 0;
6628 MusicFrame end (max_framepos, 0);
6630 //use the existing punch start, if any
6631 Location* tpl = transport_punch_location();
6633 start = tpl->start();
6636 if ((_edit_point == EditAtPlayhead) && _session->transport_rolling()) {
6637 end.frame = _session->audible_frame();
6639 end.frame = get_preferred_edit_position();
6642 //snap the selection start/end
6645 set_punch_range (start, end.frame, _("set punch end from EP"));
6651 Editor::set_loop_start_from_edit_point ()
6655 MusicFrame start (0, 0);
6656 framepos_t end = max_framepos;
6658 //use the existing loop end, if any
6659 Location* tpl = transport_loop_location();
6664 if ((_edit_point == EditAtPlayhead) && _session->transport_rolling()) {
6665 start.frame = _session->audible_frame();
6667 start.frame = get_preferred_edit_position();
6670 //snap the selection start/end
6673 //if there's not already a sensible selection endpoint, go "forever"
6674 if (start.frame > end ) {
6678 set_loop_range (start.frame, end, _("set loop start from EP"));
6684 Editor::set_loop_end_from_edit_point ()
6688 framepos_t start = 0;
6689 MusicFrame end (max_framepos, 0);
6691 //use the existing loop start, if any
6692 Location* tpl = transport_loop_location();
6694 start = tpl->start();
6697 if ((_edit_point == EditAtPlayhead) && _session->transport_rolling()) {
6698 end.frame = _session->audible_frame();
6700 end.frame = get_preferred_edit_position();
6703 //snap the selection start/end
6706 set_loop_range (start, end.frame, _("set loop end from EP"));
6711 Editor::set_punch_from_region ()
6713 framepos_t start, end;
6714 if (!get_selection_extents ( start, end))
6717 set_punch_range (start, end, _("set punch range from region"));
6721 Editor::pitch_shift_region ()
6723 RegionSelection rs = get_regions_from_selection_and_entered ();
6725 RegionSelection audio_rs;
6726 for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
6727 if (dynamic_cast<AudioRegionView*> (*i)) {
6728 audio_rs.push_back (*i);
6732 if (audio_rs.empty()) {
6736 pitch_shift (audio_rs, 1.2);
6740 Editor::set_tempo_from_region ()
6742 RegionSelection rs = get_regions_from_selection_and_entered ();
6744 if (!_session || rs.empty()) {
6748 RegionView* rv = rs.front();
6750 define_one_bar (rv->region()->position(), rv->region()->last_frame() + 1);
6754 Editor::use_range_as_bar ()
6756 framepos_t start, end;
6757 if (get_edit_op_range (start, end)) {
6758 define_one_bar (start, end);
6763 Editor::define_one_bar (framepos_t start, framepos_t end)
6765 framepos_t length = end - start;
6767 const Meter& m (_session->tempo_map().meter_at_frame (start));
6769 /* length = 1 bar */
6771 /* We're going to deliver a constant tempo here,
6772 so we can use frames per beat to determine length.
6773 now we want frames per beat.
6774 we have frames per bar, and beats per bar, so ...
6777 /* XXXX METER MATH */
6779 double frames_per_beat = length / m.divisions_per_bar();
6781 /* beats per minute = */
6783 double beats_per_minute = (_session->frame_rate() * 60.0) / frames_per_beat;
6785 /* now decide whether to:
6787 (a) set global tempo
6788 (b) add a new tempo marker
6792 const TempoSection& t (_session->tempo_map().tempo_section_at_frame (start));
6794 bool do_global = false;
6796 if ((_session->tempo_map().n_tempos() == 1) && (_session->tempo_map().n_meters() == 1)) {
6798 /* only 1 tempo & 1 meter: ask if the user wants to set the tempo
6799 at the start, or create a new marker
6802 vector<string> options;
6803 options.push_back (_("Cancel"));
6804 options.push_back (_("Add new marker"));
6805 options.push_back (_("Set global tempo"));
6808 _("Define one bar"),
6809 _("Do you want to set the global tempo or add a new tempo marker?"),
6813 c.set_default_response (2);
6829 /* more than 1 tempo and/or meter section already, go ahead do the "usual":
6830 if the marker is at the region starter, change it, otherwise add
6835 begin_reversible_command (_("set tempo from region"));
6836 XMLNode& before (_session->tempo_map().get_state());
6839 _session->tempo_map().change_initial_tempo (beats_per_minute, t.note_type(), t.end_note_types_per_minute());
6840 } else if (t.frame() == start) {
6841 _session->tempo_map().change_existing_tempo_at (start, beats_per_minute, t.note_type(), t.end_note_types_per_minute());
6843 /* constant tempo */
6844 const Tempo tempo (beats_per_minute, t.note_type());
6845 _session->tempo_map().add_tempo (tempo, 0.0, start, AudioTime);
6848 XMLNode& after (_session->tempo_map().get_state());
6850 _session->add_command (new MementoCommand<TempoMap>(_session->tempo_map(), &before, &after));
6851 commit_reversible_command ();
6855 Editor::split_region_at_transients ()
6857 AnalysisFeatureList positions;
6859 RegionSelection rs = get_regions_from_selection_and_entered ();
6861 if (!_session || rs.empty()) {
6865 begin_reversible_command (_("split regions"));
6867 for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ) {
6869 RegionSelection::iterator tmp;
6874 boost::shared_ptr<AudioRegion> ar = boost::dynamic_pointer_cast<AudioRegion> ((*i)->region());
6877 ar->transients (positions);
6878 split_region_at_points ((*i)->region(), positions, true);
6885 commit_reversible_command ();
6890 Editor::split_region_at_points (boost::shared_ptr<Region> r, AnalysisFeatureList& positions, bool can_ferret, bool select_new)
6892 bool use_rhythmic_rodent = false;
6894 boost::shared_ptr<Playlist> pl = r->playlist();
6896 list<boost::shared_ptr<Region> > new_regions;
6902 if (positions.empty()) {
6906 if (positions.size() > 20 && can_ferret) {
6907 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);
6908 MessageDialog msg (msgstr,
6911 Gtk::BUTTONS_OK_CANCEL);
6914 msg.add_button (_("Call for the Ferret!"), RESPONSE_APPLY);
6915 msg.set_secondary_text (_("Press OK to continue with this split operation\nor ask the Ferret dialog to tune the analysis"));
6917 msg.set_secondary_text (_("Press OK to continue with this split operation"));
6920 msg.set_title (_("Excessive split?"));
6923 int response = msg.run();
6929 case RESPONSE_APPLY:
6930 use_rhythmic_rodent = true;
6937 if (use_rhythmic_rodent) {
6938 show_rhythm_ferret ();
6942 AnalysisFeatureList::const_iterator x;
6944 pl->clear_changes ();
6945 pl->clear_owned_changes ();
6947 x = positions.begin();
6949 if (x == positions.end()) {
6954 pl->remove_region (r);
6958 framepos_t rstart = r->first_frame ();
6959 framepos_t rend = r->last_frame ();
6961 while (x != positions.end()) {
6963 /* deal with positons that are out of scope of present region bounds */
6964 if (*x <= rstart || *x > rend) {
6969 /* file start = original start + how far we from the initial position ? */
6971 framepos_t file_start = r->start() + pos;
6973 /* length = next position - current position */
6975 framepos_t len = (*x) - pos - rstart;
6977 /* XXX we do we really want to allow even single-sample regions?
6978 * shouldn't we have some kind of lower limit on region size?
6987 if (RegionFactory::region_name (new_name, r->name())) {
6991 /* do NOT announce new regions 1 by one, just wait till they are all done */
6995 plist.add (ARDOUR::Properties::start, file_start);
6996 plist.add (ARDOUR::Properties::length, len);
6997 plist.add (ARDOUR::Properties::name, new_name);
6998 plist.add (ARDOUR::Properties::layer, 0);
6999 // TODO set transients_offset
7001 boost::shared_ptr<Region> nr = RegionFactory::create (r->sources(), plist, false);
7002 /* because we set annouce to false, manually add the new region to the
7005 RegionFactory::map_add (nr);
7007 pl->add_region (nr, rstart + pos);
7010 new_regions.push_front(nr);
7019 RegionFactory::region_name (new_name, r->name());
7021 /* Add the final region */
7024 plist.add (ARDOUR::Properties::start, r->start() + pos);
7025 plist.add (ARDOUR::Properties::length, r->last_frame() - (r->position() + pos) + 1);
7026 plist.add (ARDOUR::Properties::name, new_name);
7027 plist.add (ARDOUR::Properties::layer, 0);
7029 boost::shared_ptr<Region> nr = RegionFactory::create (r->sources(), plist, false);
7030 /* because we set annouce to false, manually add the new region to the
7033 RegionFactory::map_add (nr);
7034 pl->add_region (nr, r->position() + pos);
7037 new_regions.push_front(nr);
7042 /* We might have removed regions, which alters other regions' layering_index,
7043 so we need to do a recursive diff here.
7045 vector<Command*> cmds;
7047 _session->add_commands (cmds);
7049 _session->add_command (new StatefulDiffCommand (pl));
7053 for (list<boost::shared_ptr<Region> >::iterator i = new_regions.begin(); i != new_regions.end(); ++i){
7054 set_selected_regionview_from_region_list ((*i), Selection::Add);
7060 Editor::place_transient()
7066 RegionSelection rs = get_regions_from_selection_and_edit_point ();
7072 framepos_t where = get_preferred_edit_position();
7074 begin_reversible_command (_("place transient"));
7076 for (RegionSelection::iterator r = rs.begin(); r != rs.end(); ++r) {
7077 (*r)->region()->add_transient(where);
7080 commit_reversible_command ();
7084 Editor::remove_transient(ArdourCanvas::Item* item)
7090 ArdourCanvas::Line* _line = reinterpret_cast<ArdourCanvas::Line*> (item);
7093 AudioRegionView* _arv = reinterpret_cast<AudioRegionView*> (item->get_data ("regionview"));
7094 _arv->remove_transient (*(float*) _line->get_data ("position"));
7098 Editor::snap_regions_to_grid ()
7100 list <boost::shared_ptr<Playlist > > used_playlists;
7102 RegionSelection rs = get_regions_from_selection_and_entered ();
7104 if (!_session || rs.empty()) {
7108 begin_reversible_command (_("snap regions to grid"));
7110 for (RegionSelection::iterator r = rs.begin(); r != rs.end(); ++r) {
7112 boost::shared_ptr<Playlist> pl = (*r)->region()->playlist();
7114 if (!pl->frozen()) {
7115 /* we haven't seen this playlist before */
7117 /* remember used playlists so we can thaw them later */
7118 used_playlists.push_back(pl);
7121 (*r)->region()->clear_changes ();
7123 MusicFrame start ((*r)->region()->first_frame (), 0);
7125 (*r)->region()->set_position (start.frame, start.division);
7126 _session->add_command(new StatefulDiffCommand ((*r)->region()));
7129 while (used_playlists.size() > 0) {
7130 list <boost::shared_ptr<Playlist > >::iterator i = used_playlists.begin();
7132 used_playlists.pop_front();
7135 commit_reversible_command ();
7139 Editor::close_region_gaps ()
7141 list <boost::shared_ptr<Playlist > > used_playlists;
7143 RegionSelection rs = get_regions_from_selection_and_entered ();
7145 if (!_session || rs.empty()) {
7149 Dialog dialog (_("Close Region Gaps"));
7152 table.set_spacings (12);
7153 table.set_border_width (12);
7154 Label* l = manage (left_aligned_label (_("Crossfade length")));
7155 table.attach (*l, 0, 1, 0, 1);
7157 SpinButton spin_crossfade (1, 0);
7158 spin_crossfade.set_range (0, 15);
7159 spin_crossfade.set_increments (1, 1);
7160 spin_crossfade.set_value (5);
7161 table.attach (spin_crossfade, 1, 2, 0, 1);
7163 table.attach (*manage (new Label (_("ms"))), 2, 3, 0, 1);
7165 l = manage (left_aligned_label (_("Pull-back length")));
7166 table.attach (*l, 0, 1, 1, 2);
7168 SpinButton spin_pullback (1, 0);
7169 spin_pullback.set_range (0, 100);
7170 spin_pullback.set_increments (1, 1);
7171 spin_pullback.set_value(30);
7172 table.attach (spin_pullback, 1, 2, 1, 2);
7174 table.attach (*manage (new Label (_("ms"))), 2, 3, 1, 2);
7176 dialog.get_vbox()->pack_start (table);
7177 dialog.add_button (Stock::CANCEL, RESPONSE_CANCEL);
7178 dialog.add_button (_("Ok"), RESPONSE_ACCEPT);
7181 if (dialog.run () == RESPONSE_CANCEL) {
7185 framepos_t crossfade_len = spin_crossfade.get_value();
7186 framepos_t pull_back_frames = spin_pullback.get_value();
7188 crossfade_len = lrintf (crossfade_len * _session->frame_rate()/1000);
7189 pull_back_frames = lrintf (pull_back_frames * _session->frame_rate()/1000);
7191 /* Iterate over the region list and make adjacent regions overlap by crossfade_len_ms */
7193 begin_reversible_command (_("close region gaps"));
7196 boost::shared_ptr<Region> last_region;
7198 rs.sort_by_position_and_track();
7200 for (RegionSelection::iterator r = rs.begin(); r != rs.end(); ++r) {
7202 boost::shared_ptr<Playlist> pl = (*r)->region()->playlist();
7204 if (!pl->frozen()) {
7205 /* we haven't seen this playlist before */
7207 /* remember used playlists so we can thaw them later */
7208 used_playlists.push_back(pl);
7212 framepos_t position = (*r)->region()->position();
7214 if (idx == 0 || position < last_region->position()){
7215 last_region = (*r)->region();
7220 (*r)->region()->clear_changes ();
7221 (*r)->region()->trim_front( (position - pull_back_frames));
7223 last_region->clear_changes ();
7224 last_region->trim_end( (position - pull_back_frames + crossfade_len));
7226 _session->add_command (new StatefulDiffCommand ((*r)->region()));
7227 _session->add_command (new StatefulDiffCommand (last_region));
7229 last_region = (*r)->region();
7233 while (used_playlists.size() > 0) {
7234 list <boost::shared_ptr<Playlist > >::iterator i = used_playlists.begin();
7236 used_playlists.pop_front();
7239 commit_reversible_command ();
7243 Editor::tab_to_transient (bool forward)
7245 AnalysisFeatureList positions;
7247 RegionSelection rs = get_regions_from_selection_and_entered ();
7253 framepos_t pos = _session->audible_frame ();
7255 if (!selection->tracks.empty()) {
7257 /* don't waste time searching for transients in duplicate playlists.
7260 TrackViewList ts = selection->tracks.filter_to_unique_playlists ();
7262 for (TrackViewList::iterator t = ts.begin(); t != ts.end(); ++t) {
7264 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (*t);
7267 boost::shared_ptr<Track> tr = rtv->track();
7269 boost::shared_ptr<Playlist> pl = tr->playlist ();
7271 framepos_t result = pl->find_next_transient (pos, forward ? 1 : -1);
7274 positions.push_back (result);
7287 for (RegionSelection::iterator r = rs.begin(); r != rs.end(); ++r) {
7288 (*r)->region()->get_transients (positions);
7292 TransientDetector::cleanup_transients (positions, _session->frame_rate(), 3.0);
7295 AnalysisFeatureList::iterator x;
7297 for (x = positions.begin(); x != positions.end(); ++x) {
7303 if (x != positions.end ()) {
7304 _session->request_locate (*x);
7308 AnalysisFeatureList::reverse_iterator x;
7310 for (x = positions.rbegin(); x != positions.rend(); ++x) {
7316 if (x != positions.rend ()) {
7317 _session->request_locate (*x);
7323 Editor::playhead_forward_to_grid ()
7329 MusicFrame pos (playhead_cursor->current_frame (), 0);
7331 if (pos.frame < max_framepos - 1) {
7333 snap_to_internal (pos, RoundUpAlways, false, true);
7334 _session->request_locate (pos.frame);
7340 Editor::playhead_backward_to_grid ()
7346 MusicFrame pos (playhead_cursor->current_frame (), 0);
7348 if (pos.frame > 2) {
7350 snap_to_internal (pos, RoundDownAlways, false, true);
7351 _session->request_locate (pos.frame);
7356 Editor::set_track_height (Height h)
7358 TrackSelection& ts (selection->tracks);
7360 for (TrackSelection::iterator x = ts.begin(); x != ts.end(); ++x) {
7361 (*x)->set_height_enum (h);
7366 Editor::toggle_tracks_active ()
7368 TrackSelection& ts (selection->tracks);
7370 bool target = false;
7376 for (TrackSelection::iterator x = ts.begin(); x != ts.end(); ++x) {
7377 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*>(*x);
7381 target = !rtv->_route->active();
7384 rtv->_route->set_active (target, this);
7390 Editor::remove_tracks ()
7392 /* this will delete GUI objects that may be the subject of an event
7393 handler in which this method is called. Defer actual deletion to the
7394 next idle callback, when all event handling is finished.
7396 Glib::signal_idle().connect (sigc::mem_fun (*this, &Editor::idle_remove_tracks));
7400 Editor::idle_remove_tracks ()
7402 Session::StateProtector sp (_session);
7404 return false; /* do not call again */
7408 Editor::_remove_tracks ()
7410 TrackSelection& ts (selection->tracks);
7416 vector<string> choices;
7421 const char* trackstr;
7424 vector<boost::shared_ptr<Route> > routes;
7425 vector<boost::shared_ptr<VCA> > vcas;
7426 bool special_bus = false;
7428 for (TrackSelection::iterator x = ts.begin(); x != ts.end(); ++x) {
7429 VCATimeAxisView* vtv = dynamic_cast<VCATimeAxisView*> (*x);
7431 vcas.push_back (vtv->vca());
7435 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (*x);
7439 if (rtv->is_track()) {
7444 routes.push_back (rtv->_route);
7446 if (rtv->route()->is_master() || rtv->route()->is_monitor()) {
7451 if (special_bus && !Config->get_allow_special_bus_removal()) {
7452 MessageDialog msg (_("That would be bad news ...."),
7456 msg.set_secondary_text (string_compose (_(
7457 "Removing the master or monitor bus is such a bad idea\n\
7458 that %1 is not going to allow it.\n\
7460 If you really want to do this sort of thing\n\
7461 edit your ardour.rc file to set the\n\
7462 \"allow-special-bus-removal\" option to be \"yes\""), PROGRAM_NAME));
7469 if (ntracks + nbusses + nvcas == 0) {
7475 trackstr = P_("track", "tracks", ntracks);
7476 busstr = P_("bus", "busses", nbusses);
7477 vcastr = P_("VCA", "VCAs", nvcas);
7479 if (ntracks > 0 && nbusses > 0 && nvcas > 0) {
7480 title = _("Remove various strips");
7481 prompt = string_compose (_("Do you really want to remove %1 %2, %3 %4 and %5 %6?"),
7482 ntracks, trackstr, nbusses, busstr, nvcas, vcastr);
7484 else if (ntracks > 0 && nbusses > 0) {
7485 title = string_compose (_("Remove %1 and %2"), trackstr, busstr);
7486 prompt = string_compose (_("Do you really want to remove %1 %2 and %3 %4?"),
7487 ntracks, trackstr, nbusses, busstr);
7489 else if (ntracks > 0 && nvcas > 0) {
7490 title = string_compose (_("Remove %1 and %2"), trackstr, vcastr);
7491 prompt = string_compose (_("Do you really want to remove %1 %2 and %3 %4?"),
7492 ntracks, trackstr, nvcas, vcastr);
7494 else if (nbusses > 0 && nvcas > 0) {
7495 title = string_compose (_("Remove %1 and %2"), busstr, vcastr);
7496 prompt = string_compose (_("Do you really want to remove %1 %2 and %3 %4?"),
7497 nbusses, busstr, nvcas, vcastr);
7499 else if (ntracks > 0) {
7500 title = string_compose (_("Remove %1"), trackstr);
7501 prompt = string_compose (_("Do you really want to remove %1 %2?"),
7504 else if (nbusses > 0) {
7505 title = string_compose (_("Remove %1"), busstr);
7506 prompt = string_compose (_("Do you really want to remove %1 %2?"),
7509 else if (nvcas > 0) {
7510 title = string_compose (_("Remove %1"), vcastr);
7511 prompt = string_compose (_("Do you really want to remove %1 %2?"),
7519 prompt += "\n" + string_compose ("(You may also lose the playlists associated with the %1)", trackstr) + "\n";
7522 prompt += "\n" + string(_("This action cannot be undone, and the session file will be overwritten!"));
7524 choices.push_back (_("No, do nothing."));
7525 if (ntracks + nbusses + nvcas > 1) {
7526 choices.push_back (_("Yes, remove them."));
7528 choices.push_back (_("Yes, remove it."));
7531 Choice prompter (title, prompt, choices);
7533 if (prompter.run () != 1) {
7537 if (current_mixer_strip && routes.size () > 1 && std::find (routes.begin(), routes.end(), current_mixer_strip->route()) != routes.end ()) {
7538 /* Route deletion calls Editor::timeaxisview_deleted() iteratively (for each deleted
7539 * route). If the deleted route is currently displayed in the Editor-Mixer (highly
7540 * likely because deletion requires selection) this will call
7541 * Editor::set_selected_mixer_strip () which is expensive ( MixerStrip::set_route() ).
7542 * It's likewise likely that the route that has just been displayed in the
7543 * Editor-Mixer will be next in line for deletion.
7545 * So simply switch to the master-bus (if present)
7547 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
7548 if ((*i)->stripable ()->is_master ()) {
7549 set_selected_mixer_strip (*(*i));
7556 PresentationInfo::ChangeSuspender cs;
7557 DisplaySuspender ds;
7559 boost::shared_ptr<RouteList> rl (new RouteList);
7560 for (vector<boost::shared_ptr<Route> >::iterator x = routes.begin(); x != routes.end(); ++x) {
7563 _session->remove_routes (rl);
7565 for (vector<boost::shared_ptr<VCA> >::iterator x = vcas.begin(); x != vcas.end(); ++x) {
7566 _session->vca_manager().remove_vca (*x);
7570 /* TrackSelection and RouteList leave scope,
7571 * destructors are called,
7572 * diskstream drops references, save_state is called (again for every track)
7577 Editor::do_insert_time ()
7579 if (selection->tracks.empty()) {
7583 InsertRemoveTimeDialog d (*this);
7584 int response = d.run ();
7586 if (response != RESPONSE_OK) {
7590 if (d.distance() == 0) {
7597 d.intersected_region_action (),
7601 d.move_glued_markers(),
7602 d.move_locked_markers(),
7608 Editor::insert_time (
7609 framepos_t pos, framecnt_t frames, InsertTimeOption opt,
7610 bool all_playlists, bool ignore_music_glue, bool markers_too, bool glued_markers_too, bool locked_markers_too, bool tempo_too
7614 if (Config->get_edit_mode() == Lock) {
7617 bool in_command = false;
7619 TrackViewList ts = selection->tracks.filter_to_unique_playlists ();
7621 for (TrackViewList::iterator x = ts.begin(); x != ts.end(); ++x) {
7625 /* don't operate on any playlist more than once, which could
7626 * happen if "all playlists" is enabled, but there is more
7627 * than 1 track using playlists "from" a given track.
7630 set<boost::shared_ptr<Playlist> > pl;
7632 if (all_playlists) {
7633 RouteTimeAxisView* rtav = dynamic_cast<RouteTimeAxisView*> (*x);
7634 if (rtav && rtav->track ()) {
7635 vector<boost::shared_ptr<Playlist> > all = _session->playlists->playlists_for_track (rtav->track ());
7636 for (vector<boost::shared_ptr<Playlist> >::iterator p = all.begin(); p != all.end(); ++p) {
7641 if ((*x)->playlist ()) {
7642 pl.insert ((*x)->playlist ());
7646 for (set<boost::shared_ptr<Playlist> >::iterator i = pl.begin(); i != pl.end(); ++i) {
7648 (*i)->clear_changes ();
7649 (*i)->clear_owned_changes ();
7652 begin_reversible_command (_("insert time"));
7656 if (opt == SplitIntersected) {
7657 /* non musical split */
7658 (*i)->split (MusicFrame (pos, 0));
7661 (*i)->shift (pos, frames, (opt == MoveIntersected), ignore_music_glue);
7663 vector<Command*> cmds;
7665 _session->add_commands (cmds);
7667 _session->add_command (new StatefulDiffCommand (*i));
7671 RouteTimeAxisView* rtav = dynamic_cast<RouteTimeAxisView*> (*x);
7674 begin_reversible_command (_("insert time"));
7677 rtav->route ()->shift (pos, frames);
7684 const int32_t divisions = get_grid_music_divisions (0);
7685 XMLNode& before (_session->locations()->get_state());
7686 Locations::LocationList copy (_session->locations()->list());
7688 for (Locations::LocationList::iterator i = copy.begin(); i != copy.end(); ++i) {
7690 Locations::LocationList::const_iterator tmp;
7692 if ((*i)->position_lock_style() == AudioTime || glued_markers_too) {
7693 bool const was_locked = (*i)->locked ();
7694 if (locked_markers_too) {
7698 if ((*i)->start() >= pos) {
7699 // move end first, in case we're moving by more than the length of the range
7700 if (!(*i)->is_mark()) {
7701 (*i)->set_end ((*i)->end() + frames, false, true, divisions);
7703 (*i)->set_start ((*i)->start() + frames, false, true, divisions);
7715 begin_reversible_command (_("insert time"));
7718 XMLNode& after (_session->locations()->get_state());
7719 _session->add_command (new MementoCommand<Locations>(*_session->locations(), &before, &after));
7725 begin_reversible_command (_("insert time"));
7728 XMLNode& before (_session->tempo_map().get_state());
7729 _session->tempo_map().insert_time (pos, frames);
7730 XMLNode& after (_session->tempo_map().get_state());
7731 _session->add_command (new MementoCommand<TempoMap>(_session->tempo_map(), &before, &after));
7735 commit_reversible_command ();
7740 Editor::do_remove_time ()
7742 if (selection->tracks.empty()) {
7746 InsertRemoveTimeDialog d (*this, true);
7748 int response = d.run ();
7750 if (response != RESPONSE_OK) {
7754 framecnt_t distance = d.distance();
7756 if (distance == 0) {
7766 d.move_glued_markers(),
7767 d.move_locked_markers(),
7773 Editor::remove_time (framepos_t pos, framecnt_t frames, InsertTimeOption opt,
7774 bool ignore_music_glue, bool markers_too, bool glued_markers_too, bool locked_markers_too, bool tempo_too)
7776 if (Config->get_edit_mode() == Lock) {
7777 error << (_("Cannot insert or delete time when in Lock edit.")) << endmsg;
7780 bool in_command = false;
7782 for (TrackSelection::iterator x = selection->tracks.begin(); x != selection->tracks.end(); ++x) {
7784 boost::shared_ptr<Playlist> pl = (*x)->playlist();
7788 XMLNode &before = pl->get_state();
7791 begin_reversible_command (_("remove time"));
7795 std::list<AudioRange> rl;
7796 AudioRange ar(pos, pos+frames, 0);
7799 pl->shift (pos, -frames, true, ignore_music_glue);
7801 XMLNode &after = pl->get_state();
7803 _session->add_command (new MementoCommand<Playlist> (*pl, &before, &after));
7807 RouteTimeAxisView* rtav = dynamic_cast<RouteTimeAxisView*> (*x);
7810 begin_reversible_command (_("remove time"));
7813 rtav->route ()->shift (pos, -frames);
7817 const int32_t divisions = get_grid_music_divisions (0);
7818 std::list<Location*> loc_kill_list;
7823 XMLNode& before (_session->locations()->get_state());
7824 Locations::LocationList copy (_session->locations()->list());
7826 for (Locations::LocationList::iterator i = copy.begin(); i != copy.end(); ++i) {
7827 if ((*i)->position_lock_style() == AudioTime || glued_markers_too) {
7829 bool const was_locked = (*i)->locked ();
7830 if (locked_markers_too) {
7834 if (!(*i)->is_mark()) { // it's a range; have to handle both start and end
7835 if ((*i)->end() >= pos
7836 && (*i)->end() < pos+frames
7837 && (*i)->start() >= pos
7838 && (*i)->end() < pos+frames) { // range is completely enclosed; kill it
7840 loc_kill_list.push_back(*i);
7841 } else { // only start or end is included, try to do the right thing
7842 // move start before moving end, to avoid trying to move the end to before the start
7843 // if we're removing more time than the length of the range
7844 if ((*i)->start() >= pos && (*i)->start() < pos+frames) {
7845 // start is within cut
7846 (*i)->set_start (pos, false, true,divisions); // bring the start marker to the beginning of the cut
7848 } else if ((*i)->start() >= pos+frames) {
7849 // start (and thus entire range) lies beyond end of cut
7850 (*i)->set_start ((*i)->start() - frames, false, true, divisions); // slip the start marker back
7853 if ((*i)->end() >= pos && (*i)->end() < pos+frames) {
7854 // end is inside cut
7855 (*i)->set_end (pos, false, true, divisions); // bring the end to the cut
7857 } else if ((*i)->end() >= pos+frames) {
7858 // end is beyond end of cut
7859 (*i)->set_end ((*i)->end() - frames, false, true, divisions); // slip the end marker back
7864 } else if ((*i)->start() >= pos && (*i)->start() < pos+frames ) {
7865 loc_kill_list.push_back(*i);
7867 } else if ((*i)->start() >= pos) {
7868 (*i)->set_start ((*i)->start() -frames, false, true, divisions);
7878 for (list<Location*>::iterator i = loc_kill_list.begin(); i != loc_kill_list.end(); ++i) {
7879 _session->locations()->remove( *i );
7884 begin_reversible_command (_("remove time"));
7887 XMLNode& after (_session->locations()->get_state());
7888 _session->add_command (new MementoCommand<Locations>(*_session->locations(), &before, &after));
7893 XMLNode& before (_session->tempo_map().get_state());
7895 if (_session->tempo_map().remove_time (pos, frames) ) {
7897 begin_reversible_command (_("remove time"));
7900 XMLNode& after (_session->tempo_map().get_state());
7901 _session->add_command (new MementoCommand<TempoMap>(_session->tempo_map(), &before, &after));
7906 commit_reversible_command ();
7911 Editor::fit_selection ()
7913 if (!selection->tracks.empty()) {
7914 fit_tracks (selection->tracks);
7918 /* no selected tracks - use tracks with selected regions */
7920 if (!selection->regions.empty()) {
7921 for (RegionSelection::iterator r = selection->regions.begin(); r != selection->regions.end(); ++r) {
7922 tvl.push_back (&(*r)->get_time_axis_view ());
7928 } else if (internal_editing()) {
7929 /* no selected tracks, or regions, but in internal edit mode, so follow the mouse and use
7932 if (entered_track) {
7933 tvl.push_back (entered_track);
7941 Editor::fit_tracks (TrackViewList & tracks)
7943 if (tracks.empty()) {
7947 uint32_t child_heights = 0;
7948 int visible_tracks = 0;
7950 for (TrackSelection::iterator t = tracks.begin(); t != tracks.end(); ++t) {
7952 if (!(*t)->marked_for_display()) {
7956 child_heights += (*t)->effective_height() - (*t)->current_height();
7960 /* compute the per-track height from:
7962 * total canvas visible height
7963 * - height that will be taken by visible children of selected tracks
7964 * - height of the ruler/hscroll area
7966 uint32_t h = (uint32_t) floor ((trackviews_height() - child_heights) / visible_tracks);
7967 double first_y_pos = DBL_MAX;
7969 if (h < TimeAxisView::preset_height (HeightSmall)) {
7970 MessageDialog msg (_("There are too many tracks to fit in the current window"));
7971 /* too small to be displayed */
7975 undo_visual_stack.push_back (current_visual_state (true));
7976 PBD::Unwinder<bool> nsv (no_save_visual, true);
7978 /* build a list of all tracks, including children */
7981 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
7983 TimeAxisView::Children c = (*i)->get_child_list ();
7984 for (TimeAxisView::Children::iterator j = c.begin(); j != c.end(); ++j) {
7985 all.push_back (j->get());
7990 // find selection range.
7991 // if someone knows how to user TrackViewList::iterator for this
7993 int selected_top = -1;
7994 int selected_bottom = -1;
7996 for (TrackViewList::iterator t = all.begin(); t != all.end(); ++t, ++i) {
7997 if ((*t)->marked_for_display ()) {
7998 if (tracks.contains(*t)) {
7999 if (selected_top == -1) {
8002 selected_bottom = i;
8008 for (TrackViewList::iterator t = all.begin(); t != all.end(); ++t, ++i) {
8009 if ((*t)->marked_for_display ()) {
8010 if (tracks.contains(*t)) {
8011 (*t)->set_height (h);
8012 first_y_pos = std::min ((*t)->y_position (), first_y_pos);
8014 if (i > selected_top && i < selected_bottom) {
8015 hide_track_in_display (*t);
8022 set the controls_layout height now, because waiting for its size
8023 request signal handler will cause the vertical adjustment setting to fail
8026 controls_layout.property_height () = _full_canvas_height;
8027 vertical_adjustment.set_value (first_y_pos);
8029 redo_visual_stack.push_back (current_visual_state (true));
8031 visible_tracks_selector.set_text (_("Sel"));
8035 Editor::save_visual_state (uint32_t n)
8037 while (visual_states.size() <= n) {
8038 visual_states.push_back (0);
8041 if (visual_states[n] != 0) {
8042 delete visual_states[n];
8045 visual_states[n] = current_visual_state (true);
8050 Editor::goto_visual_state (uint32_t n)
8052 if (visual_states.size() <= n) {
8056 if (visual_states[n] == 0) {
8060 use_visual_state (*visual_states[n]);
8064 Editor::start_visual_state_op (uint32_t n)
8066 save_visual_state (n);
8068 PopUp* pup = new PopUp (WIN_POS_MOUSE, 1000, true);
8070 snprintf (buf, sizeof (buf), _("Saved view %u"), n+1);
8071 pup->set_text (buf);
8076 Editor::cancel_visual_state_op (uint32_t n)
8078 goto_visual_state (n);
8082 Editor::toggle_region_mute ()
8084 if (_ignore_region_action) {
8088 RegionSelection rs = get_regions_from_selection_and_entered ();
8094 if (rs.size() > 1) {
8095 begin_reversible_command (_("mute regions"));
8097 begin_reversible_command (_("mute region"));
8100 for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
8102 (*i)->region()->playlist()->clear_changes ();
8103 (*i)->region()->set_muted (!(*i)->region()->muted ());
8104 _session->add_command (new StatefulDiffCommand ((*i)->region()));
8108 commit_reversible_command ();
8112 Editor::combine_regions ()
8114 /* foreach track with selected regions, take all selected regions
8115 and join them into a new region containing the subregions (as a
8119 typedef set<RouteTimeAxisView*> RTVS;
8122 if (selection->regions.empty()) {
8126 for (RegionSelection::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
8127 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*>(&(*i)->get_time_axis_view());
8130 tracks.insert (rtv);
8134 begin_reversible_command (_("combine regions"));
8136 vector<RegionView*> new_selection;
8138 for (RTVS::iterator i = tracks.begin(); i != tracks.end(); ++i) {
8141 if ((rv = (*i)->combine_regions ()) != 0) {
8142 new_selection.push_back (rv);
8146 selection->clear_regions ();
8147 for (vector<RegionView*>::iterator i = new_selection.begin(); i != new_selection.end(); ++i) {
8148 selection->add (*i);
8151 commit_reversible_command ();
8155 Editor::uncombine_regions ()
8157 typedef set<RouteTimeAxisView*> RTVS;
8160 if (selection->regions.empty()) {
8164 for (RegionSelection::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
8165 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*>(&(*i)->get_time_axis_view());
8168 tracks.insert (rtv);
8172 begin_reversible_command (_("uncombine regions"));
8174 for (RTVS::iterator i = tracks.begin(); i != tracks.end(); ++i) {
8175 (*i)->uncombine_regions ();
8178 commit_reversible_command ();
8182 Editor::toggle_midi_input_active (bool flip_others)
8185 boost::shared_ptr<RouteList> rl (new RouteList);
8187 for (TrackSelection::iterator i = selection->tracks.begin(); i != selection->tracks.end(); ++i) {
8188 RouteTimeAxisView *rtav = dynamic_cast<RouteTimeAxisView *>(*i);
8194 boost::shared_ptr<MidiTrack> mt = rtav->midi_track();
8197 rl->push_back (rtav->route());
8198 onoff = !mt->input_active();
8202 _session->set_exclusive_input_active (rl, onoff, flip_others);
8205 static bool ok_fine (GdkEventAny*) { return true; }
8211 lock_dialog = new Gtk::Dialog (string_compose (_("%1: Locked"), PROGRAM_NAME), true);
8213 Gtk::Image* padlock = manage (new Gtk::Image (ARDOUR_UI_UTILS::get_icon ("padlock_closed")));
8214 lock_dialog->get_vbox()->pack_start (*padlock);
8215 lock_dialog->signal_delete_event ().connect (sigc::ptr_fun (ok_fine));
8217 ArdourButton* b = manage (new ArdourButton);
8218 b->set_name ("lock button");
8219 b->set_text (_("Click to unlock"));
8220 b->signal_clicked.connect (sigc::mem_fun (*this, &Editor::unlock));
8221 lock_dialog->get_vbox()->pack_start (*b);
8223 lock_dialog->get_vbox()->show_all ();
8224 lock_dialog->set_size_request (200, 200);
8227 delete _main_menu_disabler;
8228 _main_menu_disabler = new MainMenuDisabler;
8230 lock_dialog->present ();
8232 lock_dialog->get_window()->set_decorations (Gdk::WMDecoration (0));
8238 lock_dialog->hide ();
8240 delete _main_menu_disabler;
8241 _main_menu_disabler = 0;
8243 if (UIConfiguration::instance().get_lock_gui_after_seconds()) {
8244 start_lock_event_timing ();
8249 Editor::bring_in_callback (Gtk::Label* label, uint32_t n, uint32_t total, string name)
8251 Gtkmm2ext::UI::instance()->call_slot (invalidator (*this), boost::bind (&Editor::update_bring_in_message, this, label, n, total, name));
8255 Editor::update_bring_in_message (Gtk::Label* label, uint32_t n, uint32_t total, string name)
8257 Timers::TimerSuspender t;
8258 label->set_text (string_compose ("Copying %1, %2 of %3", name, n, total));
8259 Gtkmm2ext::UI::instance()->flush_pending (1);
8263 Editor::bring_all_sources_into_session ()
8270 ArdourDialog w (_("Moving embedded files into session folder"));
8271 w.get_vbox()->pack_start (msg);
8274 /* flush all pending GUI events because we're about to start copying
8278 Timers::TimerSuspender t;
8279 Gtkmm2ext::UI::instance()->flush_pending (3);
8283 _session->bring_all_sources_into_session (boost::bind (&Editor::bring_in_callback, this, &msg, _1, _2, _3));