2 Copyright (C) 2000-2004 Paul Davis
4 This program is free software; you can redistribute it and/or modify
5 it under the terms of the GNU General Public License as published by
6 the Free Software Foundation; either version 2 of the License, or
7 (at your option) any later version.
9 This program is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 GNU General Public License for more details.
14 You should have received a copy of the GNU General Public License
15 along with this program; if not, write to the Free Software
16 Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
20 /* Note: public Editor methods are documented in public_editor.h */
31 #include "pbd/error.h"
32 #include "pbd/basename.h"
33 #include "pbd/pthread_utils.h"
34 #include "pbd/memento_command.h"
35 #include "pbd/unwind.h"
36 #include "pbd/whitespace.h"
37 #include "pbd/stateful_diff_command.h"
39 #include <gtkmm2ext/utils.h>
40 #include <gtkmm2ext/choice.h>
41 #include <gtkmm2ext/popup.h>
43 #include "ardour/audio_track.h"
44 #include "ardour/audioregion.h"
45 #include "ardour/boost_debug.h"
46 #include "ardour/dB.h"
47 #include "ardour/location.h"
48 #include "ardour/midi_region.h"
49 #include "ardour/midi_track.h"
50 #include "ardour/operations.h"
51 #include "ardour/playlist_factory.h"
52 #include "ardour/profile.h"
53 #include "ardour/quantize.h"
54 #include "ardour/legatize.h"
55 #include "ardour/region_factory.h"
56 #include "ardour/reverse.h"
57 #include "ardour/session.h"
58 #include "ardour/session_playlists.h"
59 #include "ardour/strip_silence.h"
60 #include "ardour/transient_detector.h"
61 #include "ardour/transpose.h"
63 #include "canvas/canvas.h"
66 #include "audio_region_view.h"
67 #include "audio_streamview.h"
68 #include "audio_time_axis.h"
69 #include "automation_region_view.h"
70 #include "automation_time_axis.h"
71 #include "control_point.h"
75 #include "editor_cursors.h"
76 #include "editor_drag.h"
77 #include "editor_regions.h"
78 #include "editor_routes.h"
79 #include "gui_thread.h"
80 #include "insert_remove_time_dialog.h"
81 #include "interthread_progress_window.h"
82 #include "item_counts.h"
84 #include "midi_region_view.h"
86 #include "mixer_strip.h"
87 #include "mouse_cursors.h"
88 #include "normalize_dialog.h"
90 #include "paste_context.h"
91 #include "patch_change_dialog.h"
92 #include "quantize_dialog.h"
93 #include "region_gain_line.h"
94 #include "rgb_macros.h"
95 #include "route_time_axis.h"
96 #include "selection.h"
97 #include "selection_templates.h"
98 #include "streamview.h"
99 #include "strip_silence_dialog.h"
100 #include "time_axis_view.h"
102 #include "transpose_dialog.h"
103 #include "transform_dialog.h"
104 #include "ui_config.h"
106 #include "pbd/i18n.h"
109 using namespace ARDOUR;
112 using namespace Gtkmm2ext;
113 using namespace Editing;
114 using Gtkmm2ext::Keyboard;
116 /***********************************************************************
118 ***********************************************************************/
121 Editor::undo (uint32_t n)
123 if (_session && _session->actively_recording()) {
124 /* no undo allowed while recording. Session will check also,
125 but we don't even want to get to that.
130 if (_drags->active ()) {
136 if (_session->undo_depth() == 0) {
137 undo_action->set_sensitive(false);
139 redo_action->set_sensitive(true);
140 begin_selection_op_history ();
145 Editor::redo (uint32_t n)
147 if (_session && _session->actively_recording()) {
148 /* no redo allowed while recording. Session will check also,
149 but we don't even want to get to that.
154 if (_drags->active ()) {
160 if (_session->redo_depth() == 0) {
161 redo_action->set_sensitive(false);
163 undo_action->set_sensitive(true);
164 begin_selection_op_history ();
169 Editor::split_regions_at (MusicFrame where, RegionSelection& regions, bool snap_frame)
173 RegionSelection pre_selected_regions = selection->regions;
174 bool working_on_selection = !pre_selected_regions.empty();
176 list<boost::shared_ptr<Playlist> > used_playlists;
177 list<RouteTimeAxisView*> used_trackviews;
179 if (regions.empty()) {
183 begin_reversible_command (_("split"));
185 // if splitting a single region, and snap-to is using
186 // region boundaries, don't pay attention to them
188 if (regions.size() == 1) {
189 switch (_snap_type) {
190 case SnapToRegionStart:
191 case SnapToRegionSync:
192 case SnapToRegionEnd:
205 EditorFreeze(); /* Emit Signal */
208 for (RegionSelection::iterator a = regions.begin(); a != regions.end(); ) {
210 RegionSelection::iterator tmp;
212 /* XXX this test needs to be more complicated, to make sure we really
213 have something to split.
216 if (!(*a)->region()->covers (where.frame)) {
224 boost::shared_ptr<Playlist> pl = (*a)->region()->playlist();
232 /* we haven't seen this playlist before */
234 /* remember used playlists so we can thaw them later */
235 used_playlists.push_back(pl);
237 TimeAxisView& tv = (*a)->get_time_axis_view();
238 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (&tv);
240 used_trackviews.push_back (rtv);
247 pl->clear_changes ();
248 pl->split_region ((*a)->region(), where);
249 _session->add_command (new StatefulDiffCommand (pl));
255 latest_regionviews.clear ();
257 vector<sigc::connection> region_added_connections;
259 for (list<RouteTimeAxisView*>::iterator i = used_trackviews.begin(); i != used_trackviews.end(); ++i) {
260 region_added_connections.push_back ((*i)->view()->RegionViewAdded.connect (sigc::mem_fun(*this, &Editor::collect_new_region_view)));
263 while (used_playlists.size() > 0) {
264 list <boost::shared_ptr<Playlist > >::iterator i = used_playlists.begin();
266 used_playlists.pop_front();
269 for (vector<sigc::connection>::iterator c = region_added_connections.begin(); c != region_added_connections.end(); ++c) {
274 EditorThaw(); /* Emit Signal */
277 if (working_on_selection) {
278 // IFF we were working on selected regions, try to reinstate the other region selections that existed before the freeze/thaw.
280 RegionSelectionAfterSplit rsas = Config->get_region_selection_after_split();
281 /* There are three classes of regions that we might want selected after
282 splitting selected regions:
283 - regions selected before the split operation, and unaffected by it
284 - newly-created regions before the split
285 - newly-created regions after the split
288 if (rsas & Existing) {
289 // region selections that existed before the split.
290 selection->add ( pre_selected_regions );
293 for (RegionSelection::iterator ri = latest_regionviews.begin(); ri != latest_regionviews.end(); ri++) {
294 if ((*ri)->region()->position() < where.frame) {
295 // new regions created before the split
296 if (rsas & NewlyCreatedLeft) {
297 selection->add (*ri);
300 // new regions created after the split
301 if (rsas & NewlyCreatedRight) {
302 selection->add (*ri);
307 if( working_on_selection ) {
308 selection->add (latest_regionviews); //these are the new regions created after the split
312 commit_reversible_command ();
315 /** Move one extreme of the current range selection. If more than one range is selected,
316 * the start of the earliest range or the end of the latest range is moved.
318 * @param move_end true to move the end of the current range selection, false to move
320 * @param next true to move the extreme to the next region boundary, false to move to
324 Editor::move_range_selection_start_or_end_to_region_boundary (bool move_end, bool next)
326 if (selection->time.start() == selection->time.end_frame()) {
330 framepos_t start = selection->time.start ();
331 framepos_t end = selection->time.end_frame ();
333 /* the position of the thing we may move */
334 framepos_t pos = move_end ? end : start;
335 int dir = next ? 1 : -1;
337 /* so we don't find the current region again */
338 if (dir > 0 || pos > 0) {
342 framepos_t const target = get_region_boundary (pos, dir, true, false);
357 begin_reversible_selection_op (_("alter selection"));
358 selection->set_preserving_all_ranges (start, end);
359 commit_reversible_selection_op ();
363 Editor::nudge_forward_release (GdkEventButton* ev)
365 if (ev->state & Keyboard::PrimaryModifier) {
366 nudge_forward (false, true);
368 nudge_forward (false, false);
374 Editor::nudge_backward_release (GdkEventButton* ev)
376 if (ev->state & Keyboard::PrimaryModifier) {
377 nudge_backward (false, true);
379 nudge_backward (false, false);
386 Editor::nudge_forward (bool next, bool force_playhead)
389 framepos_t next_distance;
395 RegionSelection rs = get_regions_from_selection_and_entered ();
397 if (!force_playhead && !rs.empty()) {
399 begin_reversible_command (_("nudge regions forward"));
401 for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
402 boost::shared_ptr<Region> r ((*i)->region());
404 distance = get_nudge_distance (r->position(), next_distance);
407 distance = next_distance;
411 r->set_position (r->position() + distance);
412 _session->add_command (new StatefulDiffCommand (r));
415 commit_reversible_command ();
418 } else if (!force_playhead && !selection->markers.empty()) {
421 bool in_command = false;
422 const int32_t divisions = get_grid_music_divisions (0);
424 for (MarkerSelection::iterator i = selection->markers.begin(); i != selection->markers.end(); ++i) {
426 Location* loc = find_location_from_marker ((*i), is_start);
430 XMLNode& before (loc->get_state());
433 distance = get_nudge_distance (loc->start(), next_distance);
435 distance = next_distance;
437 if (max_framepos - distance > loc->start() + loc->length()) {
438 loc->set_start (loc->start() + distance, false, true, divisions);
440 loc->set_start (max_framepos - loc->length(), false, true, divisions);
443 distance = get_nudge_distance (loc->end(), next_distance);
445 distance = next_distance;
447 if (max_framepos - distance > loc->end()) {
448 loc->set_end (loc->end() + distance, false, true, divisions);
450 loc->set_end (max_framepos, false, true, divisions);
452 if (loc->is_session_range()) {
453 _session->set_end_is_free (false);
457 begin_reversible_command (_("nudge location forward"));
460 XMLNode& after (loc->get_state());
461 _session->add_command (new MementoCommand<Location>(*loc, &before, &after));
466 commit_reversible_command ();
469 distance = get_nudge_distance (playhead_cursor->current_frame (), next_distance);
470 _session->request_locate (playhead_cursor->current_frame () + distance);
475 Editor::nudge_backward (bool next, bool force_playhead)
478 framepos_t next_distance;
484 RegionSelection rs = get_regions_from_selection_and_entered ();
486 if (!force_playhead && !rs.empty()) {
488 begin_reversible_command (_("nudge regions backward"));
490 for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
491 boost::shared_ptr<Region> r ((*i)->region());
493 distance = get_nudge_distance (r->position(), next_distance);
496 distance = next_distance;
501 if (r->position() > distance) {
502 r->set_position (r->position() - distance);
506 _session->add_command (new StatefulDiffCommand (r));
509 commit_reversible_command ();
511 } else if (!force_playhead && !selection->markers.empty()) {
514 bool in_command = false;
516 for (MarkerSelection::iterator i = selection->markers.begin(); i != selection->markers.end(); ++i) {
518 Location* loc = find_location_from_marker ((*i), is_start);
522 XMLNode& before (loc->get_state());
525 distance = get_nudge_distance (loc->start(), next_distance);
527 distance = next_distance;
529 if (distance < loc->start()) {
530 loc->set_start (loc->start() - distance, false, true, get_grid_music_divisions(0));
532 loc->set_start (0, false, true, get_grid_music_divisions(0));
535 distance = get_nudge_distance (loc->end(), next_distance);
538 distance = next_distance;
541 if (distance < loc->end() - loc->length()) {
542 loc->set_end (loc->end() - distance, false, true, get_grid_music_divisions(0));
544 loc->set_end (loc->length(), false, true, get_grid_music_divisions(0));
546 if (loc->is_session_range()) {
547 _session->set_end_is_free (false);
551 begin_reversible_command (_("nudge location forward"));
554 XMLNode& after (loc->get_state());
555 _session->add_command (new MementoCommand<Location>(*loc, &before, &after));
559 commit_reversible_command ();
564 distance = get_nudge_distance (playhead_cursor->current_frame (), next_distance);
566 if (playhead_cursor->current_frame () > distance) {
567 _session->request_locate (playhead_cursor->current_frame () - distance);
569 _session->goto_start();
575 Editor::nudge_forward_capture_offset ()
577 RegionSelection rs = get_regions_from_selection_and_entered ();
579 if (!_session || rs.empty()) {
583 begin_reversible_command (_("nudge forward"));
585 framepos_t const distance = _session->worst_output_latency();
587 for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
588 boost::shared_ptr<Region> r ((*i)->region());
591 r->set_position (r->position() + distance);
592 _session->add_command(new StatefulDiffCommand (r));
595 commit_reversible_command ();
599 Editor::nudge_backward_capture_offset ()
601 RegionSelection rs = get_regions_from_selection_and_entered ();
603 if (!_session || rs.empty()) {
607 begin_reversible_command (_("nudge backward"));
609 framepos_t const distance = _session->worst_output_latency();
611 for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
612 boost::shared_ptr<Region> r ((*i)->region());
616 if (r->position() > distance) {
617 r->set_position (r->position() - distance);
621 _session->add_command(new StatefulDiffCommand (r));
624 commit_reversible_command ();
627 struct RegionSelectionPositionSorter {
628 bool operator() (RegionView* a, RegionView* b) {
629 return a->region()->position() < b->region()->position();
634 Editor::sequence_regions ()
637 framepos_t r_end_prev;
645 RegionSelection rs = get_regions_from_selection_and_entered ();
646 rs.sort(RegionSelectionPositionSorter());
650 bool in_command = false;
652 for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
653 boost::shared_ptr<Region> r ((*i)->region());
661 if(r->position_locked())
668 r->set_position(r_end_prev);
672 begin_reversible_command (_("sequence regions"));
675 _session->add_command (new StatefulDiffCommand (r));
677 r_end=r->position() + r->length();
683 commit_reversible_command ();
692 Editor::move_to_start ()
694 _session->goto_start ();
698 Editor::move_to_end ()
701 _session->request_locate (_session->current_end_frame());
705 Editor::build_region_boundary_cache ()
708 vector<RegionPoint> interesting_points;
709 boost::shared_ptr<Region> r;
710 TrackViewList tracks;
713 region_boundary_cache.clear ();
719 switch (_snap_type) {
720 case SnapToRegionStart:
721 interesting_points.push_back (Start);
723 case SnapToRegionEnd:
724 interesting_points.push_back (End);
726 case SnapToRegionSync:
727 interesting_points.push_back (SyncPoint);
729 case SnapToRegionBoundary:
730 interesting_points.push_back (Start);
731 interesting_points.push_back (End);
734 fatal << string_compose (_("build_region_boundary_cache called with snap_type = %1"), _snap_type) << endmsg;
735 abort(); /*NOTREACHED*/
739 TimeAxisView *ontrack = 0;
742 if (!selection->tracks.empty()) {
743 tlist = selection->tracks.filter_to_unique_playlists ();
745 tlist = track_views.filter_to_unique_playlists ();
748 while (pos < _session->current_end_frame() && !at_end) {
751 framepos_t lpos = max_framepos;
753 for (vector<RegionPoint>::iterator p = interesting_points.begin(); p != interesting_points.end(); ++p) {
755 if ((r = find_next_region (pos, *p, 1, tlist, &ontrack)) == 0) {
756 if (*p == interesting_points.back()) {
759 /* move to next point type */
765 rpos = r->first_frame();
769 rpos = r->last_frame();
773 rpos = r->sync_position ();
781 RouteTimeAxisView *rtav;
783 if (ontrack != 0 && (rtav = dynamic_cast<RouteTimeAxisView*>(ontrack)) != 0 ) {
784 if (rtav->track() != 0) {
785 speed = rtav->track()->speed();
789 rpos = track_frame_to_session_frame (rpos, speed);
795 /* prevent duplicates, but we don't use set<> because we want to be able
799 vector<framepos_t>::iterator ri;
801 for (ri = region_boundary_cache.begin(); ri != region_boundary_cache.end(); ++ri) {
807 if (ri == region_boundary_cache.end()) {
808 region_boundary_cache.push_back (rpos);
815 /* finally sort to be sure that the order is correct */
817 sort (region_boundary_cache.begin(), region_boundary_cache.end());
820 boost::shared_ptr<Region>
821 Editor::find_next_region (framepos_t frame, RegionPoint point, int32_t dir, TrackViewList& tracks, TimeAxisView **ontrack)
823 TrackViewList::iterator i;
824 framepos_t closest = max_framepos;
825 boost::shared_ptr<Region> ret;
829 framepos_t track_frame;
830 RouteTimeAxisView *rtav;
832 for (i = tracks.begin(); i != tracks.end(); ++i) {
835 boost::shared_ptr<Region> r;
838 if ( (rtav = dynamic_cast<RouteTimeAxisView*>(*i)) != 0 ) {
839 if (rtav->track()!=0)
840 track_speed = rtav->track()->speed();
843 track_frame = session_frame_to_track_frame(frame, track_speed);
845 if ((r = (*i)->find_next_region (track_frame, point, dir)) == 0) {
851 rpos = r->first_frame ();
855 rpos = r->last_frame ();
859 rpos = r->sync_position ();
863 // rpos is a "track frame", converting it to "_session frame"
864 rpos = track_frame_to_session_frame(rpos, track_speed);
867 distance = rpos - frame;
869 distance = frame - rpos;
872 if (distance < closest) {
884 Editor::find_next_region_boundary (framepos_t pos, int32_t dir, const TrackViewList& tracks)
886 framecnt_t distance = max_framepos;
887 framepos_t current_nearest = -1;
889 for (TrackViewList::const_iterator i = tracks.begin(); i != tracks.end(); ++i) {
890 framepos_t contender;
893 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (*i);
899 if ((contender = rtv->find_next_region_boundary (pos, dir)) < 0) {
903 d = ::llabs (pos - contender);
906 current_nearest = contender;
911 return current_nearest;
915 Editor::get_region_boundary (framepos_t pos, int32_t dir, bool with_selection, bool only_onscreen)
920 if (with_selection && Config->get_region_boundaries_from_selected_tracks()) {
922 if (!selection->tracks.empty()) {
924 target = find_next_region_boundary (pos, dir, selection->tracks);
928 if (only_onscreen || Config->get_region_boundaries_from_onscreen_tracks()) {
929 get_onscreen_tracks (tvl);
930 target = find_next_region_boundary (pos, dir, tvl);
932 target = find_next_region_boundary (pos, dir, track_views);
938 if (only_onscreen || Config->get_region_boundaries_from_onscreen_tracks()) {
939 get_onscreen_tracks (tvl);
940 target = find_next_region_boundary (pos, dir, tvl);
942 target = find_next_region_boundary (pos, dir, track_views);
950 Editor::cursor_to_region_boundary (bool with_selection, int32_t dir)
952 framepos_t pos = playhead_cursor->current_frame ();
959 // so we don't find the current region again..
960 if (dir > 0 || pos > 0) {
964 if ((target = get_region_boundary (pos, dir, with_selection, false)) < 0) {
968 _session->request_locate (target);
972 Editor::cursor_to_next_region_boundary (bool with_selection)
974 cursor_to_region_boundary (with_selection, 1);
978 Editor::cursor_to_previous_region_boundary (bool with_selection)
980 cursor_to_region_boundary (with_selection, -1);
984 Editor::cursor_to_region_point (EditorCursor* cursor, RegionPoint point, int32_t dir)
986 boost::shared_ptr<Region> r;
987 framepos_t pos = cursor->current_frame ();
993 TimeAxisView *ontrack = 0;
995 // so we don't find the current region again..
999 if (!selection->tracks.empty()) {
1001 r = find_next_region (pos, point, dir, selection->tracks, &ontrack);
1003 } else if (clicked_axisview) {
1006 t.push_back (clicked_axisview);
1008 r = find_next_region (pos, point, dir, t, &ontrack);
1012 r = find_next_region (pos, point, dir, track_views, &ontrack);
1021 pos = r->first_frame ();
1025 pos = r->last_frame ();
1029 pos = r->sync_position ();
1034 RouteTimeAxisView *rtav;
1036 if ( ontrack != 0 && (rtav = dynamic_cast<RouteTimeAxisView*>(ontrack)) != 0 ) {
1037 if (rtav->track() != 0) {
1038 speed = rtav->track()->speed();
1042 pos = track_frame_to_session_frame(pos, speed);
1044 if (cursor == playhead_cursor) {
1045 _session->request_locate (pos);
1047 cursor->set_position (pos);
1052 Editor::cursor_to_next_region_point (EditorCursor* cursor, RegionPoint point)
1054 cursor_to_region_point (cursor, point, 1);
1058 Editor::cursor_to_previous_region_point (EditorCursor* cursor, RegionPoint point)
1060 cursor_to_region_point (cursor, point, -1);
1064 Editor::cursor_to_selection_start (EditorCursor *cursor)
1068 switch (mouse_mode) {
1070 if (!selection->regions.empty()) {
1071 pos = selection->regions.start();
1076 if (!selection->time.empty()) {
1077 pos = selection->time.start ();
1085 if (cursor == playhead_cursor) {
1086 _session->request_locate (pos);
1088 cursor->set_position (pos);
1093 Editor::cursor_to_selection_end (EditorCursor *cursor)
1097 switch (mouse_mode) {
1099 if (!selection->regions.empty()) {
1100 pos = selection->regions.end_frame();
1105 if (!selection->time.empty()) {
1106 pos = selection->time.end_frame ();
1114 if (cursor == playhead_cursor) {
1115 _session->request_locate (pos);
1117 cursor->set_position (pos);
1122 Editor::selected_marker_to_region_boundary (bool with_selection, int32_t dir)
1132 if (selection->markers.empty()) {
1136 if (!mouse_frame (mouse, ignored)) {
1140 add_location_mark (mouse);
1143 if ((loc = find_location_from_marker (selection->markers.front(), ignored)) == 0) {
1147 framepos_t pos = loc->start();
1149 // so we don't find the current region again..
1150 if (dir > 0 || pos > 0) {
1154 if ((target = get_region_boundary (pos, dir, with_selection, false)) < 0) {
1158 loc->move_to (target, 0);
1162 Editor::selected_marker_to_next_region_boundary (bool with_selection)
1164 selected_marker_to_region_boundary (with_selection, 1);
1168 Editor::selected_marker_to_previous_region_boundary (bool with_selection)
1170 selected_marker_to_region_boundary (with_selection, -1);
1174 Editor::selected_marker_to_region_point (RegionPoint point, int32_t dir)
1176 boost::shared_ptr<Region> r;
1181 if (!_session || selection->markers.empty()) {
1185 if ((loc = find_location_from_marker (selection->markers.front(), ignored)) == 0) {
1189 TimeAxisView *ontrack = 0;
1193 // so we don't find the current region again..
1197 if (!selection->tracks.empty()) {
1199 r = find_next_region (pos, point, dir, selection->tracks, &ontrack);
1203 r = find_next_region (pos, point, dir, track_views, &ontrack);
1212 pos = r->first_frame ();
1216 pos = r->last_frame ();
1220 pos = r->adjust_to_sync (r->first_frame());
1225 RouteTimeAxisView *rtav;
1227 if (ontrack != 0 && (rtav = dynamic_cast<RouteTimeAxisView*>(ontrack)) != 0) {
1228 if (rtav->track() != 0) {
1229 speed = rtav->track()->speed();
1233 pos = track_frame_to_session_frame(pos, speed);
1235 loc->move_to (pos, 0);
1239 Editor::selected_marker_to_next_region_point (RegionPoint point)
1241 selected_marker_to_region_point (point, 1);
1245 Editor::selected_marker_to_previous_region_point (RegionPoint point)
1247 selected_marker_to_region_point (point, -1);
1251 Editor::selected_marker_to_selection_start ()
1257 if (!_session || selection->markers.empty()) {
1261 if ((loc = find_location_from_marker (selection->markers.front(), ignored)) == 0) {
1265 switch (mouse_mode) {
1267 if (!selection->regions.empty()) {
1268 pos = selection->regions.start();
1273 if (!selection->time.empty()) {
1274 pos = selection->time.start ();
1282 loc->move_to (pos, 0);
1286 Editor::selected_marker_to_selection_end ()
1292 if (!_session || selection->markers.empty()) {
1296 if ((loc = find_location_from_marker (selection->markers.front(), ignored)) == 0) {
1300 switch (mouse_mode) {
1302 if (!selection->regions.empty()) {
1303 pos = selection->regions.end_frame();
1308 if (!selection->time.empty()) {
1309 pos = selection->time.end_frame ();
1317 loc->move_to (pos, 0);
1321 Editor::scroll_playhead (bool forward)
1323 framepos_t pos = playhead_cursor->current_frame ();
1324 framecnt_t delta = (framecnt_t) floor (current_page_samples() / 0.8);
1327 if (pos == max_framepos) {
1331 if (pos < max_framepos - delta) {
1350 _session->request_locate (pos);
1354 Editor::cursor_align (bool playhead_to_edit)
1360 if (playhead_to_edit) {
1362 if (selection->markers.empty()) {
1366 _session->request_locate (selection->markers.front()->position(), _session->transport_rolling());
1369 const int32_t divisions = get_grid_music_divisions (0);
1370 /* move selected markers to playhead */
1372 for (MarkerSelection::iterator i = selection->markers.begin(); i != selection->markers.end(); ++i) {
1375 Location* loc = find_location_from_marker (*i, ignored);
1377 if (loc->is_mark()) {
1378 loc->set_start (playhead_cursor->current_frame (), false, true, divisions);
1380 loc->set (playhead_cursor->current_frame (),
1381 playhead_cursor->current_frame () + loc->length(), true, divisions);
1388 Editor::scroll_backward (float pages)
1390 framepos_t const one_page = (framepos_t) rint (_visible_canvas_width * samples_per_pixel);
1391 framepos_t const cnt = (framepos_t) floor (pages * one_page);
1394 if (leftmost_frame < cnt) {
1397 frame = leftmost_frame - cnt;
1400 reset_x_origin (frame);
1404 Editor::scroll_forward (float pages)
1406 framepos_t const one_page = (framepos_t) rint (_visible_canvas_width * samples_per_pixel);
1407 framepos_t const cnt = (framepos_t) floor (pages * one_page);
1410 if (max_framepos - cnt < leftmost_frame) {
1411 frame = max_framepos - cnt;
1413 frame = leftmost_frame + cnt;
1416 reset_x_origin (frame);
1420 Editor::scroll_tracks_down ()
1422 double vert_value = vertical_adjustment.get_value() + vertical_adjustment.get_page_size();
1423 if (vert_value > vertical_adjustment.get_upper() - _visible_canvas_height) {
1424 vert_value = vertical_adjustment.get_upper() - _visible_canvas_height;
1427 vertical_adjustment.set_value (vert_value);
1431 Editor::scroll_tracks_up ()
1433 vertical_adjustment.set_value (vertical_adjustment.get_value() - vertical_adjustment.get_page_size());
1437 Editor::scroll_tracks_down_line ()
1439 double vert_value = vertical_adjustment.get_value() + 60;
1441 if (vert_value > vertical_adjustment.get_upper() - _visible_canvas_height) {
1442 vert_value = vertical_adjustment.get_upper() - _visible_canvas_height;
1445 vertical_adjustment.set_value (vert_value);
1449 Editor::scroll_tracks_up_line ()
1451 reset_y_origin (vertical_adjustment.get_value() - 60);
1455 Editor::select_topmost_track ()
1457 const double top_of_trackviews = vertical_adjustment.get_value();
1458 for (TrackViewList::iterator t = track_views.begin(); t != track_views.end(); ++t) {
1459 if ((*t)->hidden()) {
1462 std::pair<TimeAxisView*,double> res = (*t)->covers_y_position (top_of_trackviews);
1464 selection->set (*t);
1471 Editor::scroll_down_one_track (bool skip_child_views)
1473 TrackViewList::reverse_iterator next = track_views.rend();
1474 const double top_of_trackviews = vertical_adjustment.get_value();
1476 for (TrackViewList::reverse_iterator t = track_views.rbegin(); t != track_views.rend(); ++t) {
1477 if ((*t)->hidden()) {
1481 /* If this is the upper-most visible trackview, we want to display
1482 * the one above it (next)
1484 * Note that covers_y_position() is recursive and includes child views
1486 std::pair<TimeAxisView*,double> res = (*t)->covers_y_position (top_of_trackviews);
1489 if (skip_child_views) {
1492 /* automation lane (one level, non-recursive)
1494 * - if no automation lane exists -> move to next tack
1495 * - if the first (here: bottom-most) matches -> move to next tack
1496 * - if no y-axis match is found -> the current track is at the top
1497 * -> move to last (here: top-most) automation lane
1499 TimeAxisView::Children kids = (*t)->get_child_list();
1500 TimeAxisView::Children::reverse_iterator nkid = kids.rend();
1502 for (TimeAxisView::Children::reverse_iterator ci = kids.rbegin(); ci != kids.rend(); ++ci) {
1503 if ((*ci)->hidden()) {
1507 std::pair<TimeAxisView*,double> dev;
1508 dev = (*ci)->covers_y_position (top_of_trackviews);
1510 /* some automation lane is currently at the top */
1511 if (ci == kids.rbegin()) {
1512 /* first (bottom-most) autmation lane is at the top.
1513 * -> move to next track
1522 if (nkid != kids.rend()) {
1523 ensure_time_axis_view_is_visible (**nkid, true);
1531 /* move to the track below the first one that covers the */
1533 if (next != track_views.rend()) {
1534 ensure_time_axis_view_is_visible (**next, true);
1542 Editor::scroll_up_one_track (bool skip_child_views)
1544 TrackViewList::iterator prev = track_views.end();
1545 double top_of_trackviews = vertical_adjustment.get_value ();
1547 for (TrackViewList::iterator t = track_views.begin(); t != track_views.end(); ++t) {
1549 if ((*t)->hidden()) {
1553 /* find the trackview at the top of the trackview group
1555 * Note that covers_y_position() is recursive and includes child views
1557 std::pair<TimeAxisView*,double> res = (*t)->covers_y_position (top_of_trackviews);
1560 if (skip_child_views) {
1563 /* automation lane (one level, non-recursive)
1565 * - if no automation lane exists -> move to prev tack
1566 * - if no y-axis match is found -> the current track is at the top -> move to prev track
1567 * (actually last automation lane of previous track, see below)
1568 * - if first (top-most) lane is at the top -> move to this track
1569 * - else move up one lane
1571 TimeAxisView::Children kids = (*t)->get_child_list();
1572 TimeAxisView::Children::iterator pkid = kids.end();
1574 for (TimeAxisView::Children::iterator ci = kids.begin(); ci != kids.end(); ++ci) {
1575 if ((*ci)->hidden()) {
1579 std::pair<TimeAxisView*,double> dev;
1580 dev = (*ci)->covers_y_position (top_of_trackviews);
1582 /* some automation lane is currently at the top */
1583 if (ci == kids.begin()) {
1584 /* first (top-most) autmation lane is at the top.
1585 * jump directly to this track's top
1587 ensure_time_axis_view_is_visible (**t, true);
1590 else if (pkid != kids.end()) {
1591 /* some other automation lane is at the top.
1592 * move up to prev automation lane.
1594 ensure_time_axis_view_is_visible (**pkid, true);
1597 assert(0); // not reached
1608 if (prev != track_views.end()) {
1609 // move to bottom-most automation-lane of the previous track
1610 TimeAxisView::Children kids = (*prev)->get_child_list();
1611 TimeAxisView::Children::reverse_iterator pkid = kids.rend();
1612 if (!skip_child_views) {
1613 // find the last visible lane
1614 for (TimeAxisView::Children::reverse_iterator ci = kids.rbegin(); ci != kids.rend(); ++ci) {
1615 if (!(*ci)->hidden()) {
1621 if (pkid != kids.rend()) {
1622 ensure_time_axis_view_is_visible (**pkid, true);
1624 ensure_time_axis_view_is_visible (**prev, true);
1633 Editor::scroll_left_step ()
1635 framepos_t xdelta = (current_page_samples() / 8);
1637 if (leftmost_frame > xdelta) {
1638 reset_x_origin (leftmost_frame - xdelta);
1646 Editor::scroll_right_step ()
1648 framepos_t xdelta = (current_page_samples() / 8);
1650 if (max_framepos - xdelta > leftmost_frame) {
1651 reset_x_origin (leftmost_frame + xdelta);
1653 reset_x_origin (max_framepos - current_page_samples());
1658 Editor::scroll_left_half_page ()
1660 framepos_t xdelta = (current_page_samples() / 2);
1661 if (leftmost_frame > xdelta) {
1662 reset_x_origin (leftmost_frame - xdelta);
1669 Editor::scroll_right_half_page ()
1671 framepos_t xdelta = (current_page_samples() / 2);
1672 if (max_framepos - xdelta > leftmost_frame) {
1673 reset_x_origin (leftmost_frame + xdelta);
1675 reset_x_origin (max_framepos - current_page_samples());
1682 Editor::tav_zoom_step (bool coarser)
1684 DisplaySuspender ds;
1688 if (selection->tracks.empty()) {
1691 ts = &selection->tracks;
1694 for (TrackViewList::iterator i = ts->begin(); i != ts->end(); ++i) {
1695 TimeAxisView *tv = (static_cast<TimeAxisView*>(*i));
1696 tv->step_height (coarser);
1701 Editor::tav_zoom_smooth (bool coarser, bool force_all)
1703 DisplaySuspender ds;
1707 if (selection->tracks.empty() || force_all) {
1710 ts = &selection->tracks;
1713 for (TrackViewList::iterator i = ts->begin(); i != ts->end(); ++i) {
1714 TimeAxisView *tv = (static_cast<TimeAxisView*>(*i));
1715 uint32_t h = tv->current_height ();
1720 if (h >= TimeAxisView::preset_height (HeightSmall)) {
1725 tv->set_height (h + 5);
1731 Editor::temporal_zoom_step_mouse_focus_scale (bool zoom_out, double scale)
1733 Editing::ZoomFocus temp_focus = zoom_focus;
1734 zoom_focus = Editing::ZoomFocusMouse;
1735 temporal_zoom_step_scale (zoom_out, scale);
1736 zoom_focus = temp_focus;
1740 Editor::temporal_zoom_step_mouse_focus (bool zoom_out)
1742 temporal_zoom_step_mouse_focus_scale (zoom_out, 2.0);
1746 Editor::temporal_zoom_step (bool zoom_out)
1748 temporal_zoom_step_scale (zoom_out, 2.0);
1752 Editor::temporal_zoom_step_scale (bool zoom_out, double scale)
1754 ENSURE_GUI_THREAD (*this, &Editor::temporal_zoom_step, zoom_out, scale)
1756 framecnt_t nspp = samples_per_pixel;
1760 if (nspp == samples_per_pixel) {
1765 if (nspp == samples_per_pixel) {
1770 temporal_zoom (nspp);
1774 Editor::temporal_zoom (framecnt_t fpp)
1780 framepos_t current_page = current_page_samples();
1781 framepos_t current_leftmost = leftmost_frame;
1782 framepos_t current_rightmost;
1783 framepos_t current_center;
1784 framepos_t new_page_size;
1785 framepos_t half_page_size;
1786 framepos_t leftmost_after_zoom = 0;
1788 bool in_track_canvas;
1789 bool use_mouse_frame = true;
1793 if (fpp == samples_per_pixel) {
1797 // Imposing an arbitrary limit to zoom out as too much zoom out produces
1798 // segfaults for lack of memory. If somebody decides this is not high enough I
1799 // believe it can be raisen to higher values but some limit must be in place.
1801 // This constant represents 1 day @ 48kHz on a 1600 pixel wide display
1802 // all of which is used for the editor track displays. The whole day
1803 // would be 4147200000 samples, so 2592000 samples per pixel.
1805 nfpp = min (fpp, (framecnt_t) 2592000);
1806 nfpp = max ((framecnt_t) 1, nfpp);
1808 new_page_size = (framepos_t) floor (_visible_canvas_width * nfpp);
1809 half_page_size = new_page_size / 2;
1811 switch (zoom_focus) {
1813 leftmost_after_zoom = current_leftmost;
1816 case ZoomFocusRight:
1817 current_rightmost = leftmost_frame + current_page;
1818 if (current_rightmost < new_page_size) {
1819 leftmost_after_zoom = 0;
1821 leftmost_after_zoom = current_rightmost - new_page_size;
1825 case ZoomFocusCenter:
1826 current_center = current_leftmost + (current_page/2);
1827 if (current_center < half_page_size) {
1828 leftmost_after_zoom = 0;
1830 leftmost_after_zoom = current_center - half_page_size;
1834 case ZoomFocusPlayhead:
1835 /* centre playhead */
1836 l = playhead_cursor->current_frame () - (new_page_size * 0.5);
1839 leftmost_after_zoom = 0;
1840 } else if (l > max_framepos) {
1841 leftmost_after_zoom = max_framepos - new_page_size;
1843 leftmost_after_zoom = (framepos_t) l;
1847 case ZoomFocusMouse:
1848 /* try to keep the mouse over the same point in the display */
1850 if (_drags->active()) {
1851 where = _drags->current_pointer_frame ();
1852 } else if (!mouse_frame (where, in_track_canvas)) {
1853 use_mouse_frame = false;
1856 if (use_mouse_frame) {
1857 l = - ((new_page_size * ((where - current_leftmost)/(double)current_page)) - where);
1860 leftmost_after_zoom = 0;
1861 } else if (l > max_framepos) {
1862 leftmost_after_zoom = max_framepos - new_page_size;
1864 leftmost_after_zoom = (framepos_t) l;
1867 /* use playhead instead */
1868 where = playhead_cursor->current_frame ();
1870 if (where < half_page_size) {
1871 leftmost_after_zoom = 0;
1873 leftmost_after_zoom = where - half_page_size;
1879 /* try to keep the edit point in the same place */
1880 where = get_preferred_edit_position ();
1884 double l = - ((new_page_size * ((where - current_leftmost)/(double)current_page)) - where);
1887 leftmost_after_zoom = 0;
1888 } else if (l > max_framepos) {
1889 leftmost_after_zoom = max_framepos - new_page_size;
1891 leftmost_after_zoom = (framepos_t) l;
1895 /* edit point not defined */
1902 // leftmost_after_zoom = min (leftmost_after_zoom, _session->current_end_frame());
1904 reposition_and_zoom (leftmost_after_zoom, nfpp);
1908 Editor::calc_extra_zoom_edges(framepos_t &start, framepos_t &end)
1910 /* this func helps make sure we leave a little space
1911 at each end of the editor so that the zoom doesn't fit the region
1912 precisely to the screen.
1915 GdkScreen* screen = gdk_screen_get_default ();
1916 const gint pixwidth = gdk_screen_get_width (screen);
1917 const gint mmwidth = gdk_screen_get_width_mm (screen);
1918 const double pix_per_mm = (double) pixwidth/ (double) mmwidth;
1919 const double one_centimeter_in_pixels = pix_per_mm * 10.0;
1921 const framepos_t range = end - start;
1922 const framecnt_t new_fpp = (framecnt_t) ceil ((double) range / (double) _visible_canvas_width);
1923 const framepos_t extra_samples = (framepos_t) floor (one_centimeter_in_pixels * new_fpp);
1925 if (start > extra_samples) {
1926 start -= extra_samples;
1931 if (max_framepos - extra_samples > end) {
1932 end += extra_samples;
1939 Editor::get_selection_extents (framepos_t &start, framepos_t &end) const
1941 start = max_framepos;
1945 //ToDo: if notes are selected, set extents to that selection
1947 //ToDo: if control points are selected, set extents to that selection
1949 if ( !selection->regions.empty() ) {
1950 RegionSelection rs = get_regions_from_selection_and_entered ();
1952 for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
1954 if ((*i)->region()->position() < start) {
1955 start = (*i)->region()->position();
1958 if ((*i)->region()->last_frame() + 1 > end) {
1959 end = (*i)->region()->last_frame() + 1;
1963 } else if (!selection->time.empty()) {
1964 start = selection->time.start();
1965 end = selection->time.end_frame();
1967 ret = false; //no selection found
1970 if ((start == 0 && end == 0) || end < start) {
1979 Editor::temporal_zoom_selection (Editing::ZoomAxis axes)
1981 if (!selection) return;
1983 //ToDo: if notes are selected, zoom to that
1985 //ToDo: if control points are selected, zoom to that
1987 if (axes == Horizontal || axes == Both) {
1989 framepos_t start, end;
1990 if (get_selection_extents (start, end)) {
1991 calc_extra_zoom_edges (start, end);
1992 temporal_zoom_by_frame (start, end);
1996 if (axes == Vertical || axes == Both) {
2002 Editor::temporal_zoom_session ()
2004 ENSURE_GUI_THREAD (*this, &Editor::temporal_zoom_session)
2007 framecnt_t start = _session->current_start_frame();
2008 framecnt_t end = _session->current_end_frame();
2010 if (_session->actively_recording () ) {
2011 framepos_t cur = playhead_cursor->current_frame ();
2013 /* recording beyond the end marker; zoom out
2014 * by 5 seconds more so that if 'follow
2015 * playhead' is active we don't immediately
2018 end = cur + _session->frame_rate() * 5;
2022 if ((start == 0 && end == 0) || end < start) {
2026 calc_extra_zoom_edges(start, end);
2028 temporal_zoom_by_frame (start, end);
2033 Editor::temporal_zoom_by_frame (framepos_t start, framepos_t end)
2035 if (!_session) return;
2037 if ((start == 0 && end == 0) || end < start) {
2041 framepos_t range = end - start;
2043 const framecnt_t new_fpp = (framecnt_t) ceil ((double) range / (double) _visible_canvas_width);
2045 framepos_t new_page = range;
2046 framepos_t middle = (framepos_t) floor ((double) start + ((double) range / 2.0f));
2047 framepos_t new_leftmost = (framepos_t) floor ((double) middle - ((double) new_page / 2.0f));
2049 if (new_leftmost > middle) {
2053 if (new_leftmost < 0) {
2057 reposition_and_zoom (new_leftmost, new_fpp);
2061 Editor::temporal_zoom_to_frame (bool coarser, framepos_t frame)
2067 framecnt_t range_before = frame - leftmost_frame;
2071 if (samples_per_pixel <= 1) {
2074 new_spp = samples_per_pixel + (samples_per_pixel/2);
2076 range_before += range_before/2;
2078 if (samples_per_pixel >= 1) {
2079 new_spp = samples_per_pixel - (samples_per_pixel/2);
2081 /* could bail out here since we cannot zoom any finer,
2082 but leave that to the equality test below
2084 new_spp = samples_per_pixel;
2087 range_before -= range_before/2;
2090 if (new_spp == samples_per_pixel) {
2094 /* zoom focus is automatically taken as @param frame when this
2098 framepos_t new_leftmost = frame - (framepos_t)range_before;
2100 if (new_leftmost > frame) {
2104 if (new_leftmost < 0) {
2108 reposition_and_zoom (new_leftmost, new_spp);
2113 Editor::choose_new_marker_name(string &name) {
2115 if (!UIConfiguration::instance().get_name_new_markers()) {
2116 /* don't prompt user for a new name */
2120 ArdourPrompter dialog (true);
2122 dialog.set_prompt (_("New Name:"));
2124 dialog.set_title (_("New Location Marker"));
2126 dialog.set_name ("MarkNameWindow");
2127 dialog.set_size_request (250, -1);
2128 dialog.set_position (Gtk::WIN_POS_MOUSE);
2130 dialog.add_button (Stock::OK, RESPONSE_ACCEPT);
2131 dialog.set_initial_text (name);
2135 switch (dialog.run ()) {
2136 case RESPONSE_ACCEPT:
2142 dialog.get_result(name);
2149 Editor::add_location_from_selection ()
2153 if (selection->time.empty()) {
2157 if (_session == 0 || clicked_axisview == 0) {
2161 framepos_t start = selection->time[clicked_selection].start;
2162 framepos_t end = selection->time[clicked_selection].end;
2164 _session->locations()->next_available_name(rangename,"selection");
2165 Location *location = new Location (*_session, start, end, rangename, Location::IsRangeMarker, get_grid_music_divisions(0));
2167 begin_reversible_command (_("add marker"));
2169 XMLNode &before = _session->locations()->get_state();
2170 _session->locations()->add (location, true);
2171 XMLNode &after = _session->locations()->get_state();
2172 _session->add_command(new MementoCommand<Locations>(*(_session->locations()), &before, &after));
2174 commit_reversible_command ();
2178 Editor::add_location_mark (framepos_t where)
2182 select_new_marker = true;
2184 _session->locations()->next_available_name(markername,"mark");
2185 if (!choose_new_marker_name(markername)) {
2188 Location *location = new Location (*_session, where, where, markername, Location::IsMark, get_grid_music_divisions (0));
2189 begin_reversible_command (_("add marker"));
2191 XMLNode &before = _session->locations()->get_state();
2192 _session->locations()->add (location, true);
2193 XMLNode &after = _session->locations()->get_state();
2194 _session->add_command(new MementoCommand<Locations>(*(_session->locations()), &before, &after));
2196 commit_reversible_command ();
2200 Editor::set_session_start_from_playhead ()
2206 if ((loc = _session->locations()->session_range_location()) == 0) { //should never happen
2207 _session->set_session_extents ( _session->audible_frame(), _session->audible_frame() );
2209 XMLNode &before = loc->get_state();
2211 _session->set_session_extents ( _session->audible_frame(), loc->end() );
2213 XMLNode &after = loc->get_state();
2215 begin_reversible_command (_("Set session start"));
2217 _session->add_command (new MementoCommand<Location>(*loc, &before, &after));
2219 commit_reversible_command ();
2224 Editor::set_session_end_from_playhead ()
2230 if ((loc = _session->locations()->session_range_location()) == 0) { //should never happen
2231 _session->set_session_extents ( _session->audible_frame(), _session->audible_frame() );
2233 XMLNode &before = loc->get_state();
2235 _session->set_session_extents ( loc->start(), _session->audible_frame() );
2237 XMLNode &after = loc->get_state();
2239 begin_reversible_command (_("Set session start"));
2241 _session->add_command (new MementoCommand<Location>(*loc, &before, &after));
2243 commit_reversible_command ();
2246 _session->set_end_is_free (false);
2251 Editor::toggle_location_at_playhead_cursor ()
2253 if (!do_remove_location_at_playhead_cursor())
2255 add_location_from_playhead_cursor();
2260 Editor::add_location_from_playhead_cursor ()
2262 add_location_mark (_session->audible_frame());
2266 Editor::do_remove_location_at_playhead_cursor ()
2268 bool removed = false;
2271 XMLNode &before = _session->locations()->get_state();
2273 //find location(s) at this time
2274 Locations::LocationList locs;
2275 _session->locations()->find_all_between (_session->audible_frame(), _session->audible_frame()+1, locs, Location::Flags(0));
2276 for (Locations::LocationList::iterator i = locs.begin(); i != locs.end(); ++i) {
2277 if ((*i)->is_mark()) {
2278 _session->locations()->remove (*i);
2285 begin_reversible_command (_("remove marker"));
2286 XMLNode &after = _session->locations()->get_state();
2287 _session->add_command(new MementoCommand<Locations>(*(_session->locations()), &before, &after));
2288 commit_reversible_command ();
2295 Editor::remove_location_at_playhead_cursor ()
2297 do_remove_location_at_playhead_cursor ();
2300 /** Add a range marker around each selected region */
2302 Editor::add_locations_from_region ()
2304 RegionSelection rs = get_regions_from_selection_and_entered ();
2309 bool commit = false;
2311 XMLNode &before = _session->locations()->get_state();
2313 for (RegionSelection::iterator i = rs.begin (); i != rs.end (); ++i) {
2315 boost::shared_ptr<Region> region = (*i)->region ();
2317 Location *location = new Location (*_session, region->position(), region->last_frame(), region->name(), Location::IsRangeMarker, 0);
2319 _session->locations()->add (location, true);
2324 begin_reversible_command (selection->regions.size () > 1 ? _("add markers") : _("add marker"));
2325 XMLNode &after = _session->locations()->get_state();
2326 _session->add_command (new MementoCommand<Locations>(*(_session->locations()), &before, &after));
2327 commit_reversible_command ();
2331 /** Add a single range marker around all selected regions */
2333 Editor::add_location_from_region ()
2335 RegionSelection rs = get_regions_from_selection_and_entered ();
2341 XMLNode &before = _session->locations()->get_state();
2345 if (rs.size() > 1) {
2346 _session->locations()->next_available_name(markername, "regions");
2348 RegionView* rv = *(rs.begin());
2349 boost::shared_ptr<Region> region = rv->region();
2350 markername = region->name();
2353 if (!choose_new_marker_name(markername)) {
2357 // single range spanning all selected
2358 Location *location = new Location (*_session, selection->regions.start(), selection->regions.end_frame(), markername, Location::IsRangeMarker, 0);
2359 _session->locations()->add (location, true);
2361 begin_reversible_command (_("add marker"));
2362 XMLNode &after = _session->locations()->get_state();
2363 _session->add_command (new MementoCommand<Locations>(*(_session->locations()), &before, &after));
2364 commit_reversible_command ();
2370 Editor::jump_forward_to_mark ()
2376 framepos_t pos = _session->locations()->first_mark_after (playhead_cursor->current_frame());
2382 _session->request_locate (pos, _session->transport_rolling());
2386 Editor::jump_backward_to_mark ()
2392 framepos_t pos = _session->locations()->first_mark_before (playhead_cursor->current_frame());
2394 //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...
2395 if ( _session->transport_rolling() ) {
2396 if ( (playhead_cursor->current_frame() - pos) < _session->frame_rate()/2 ) {
2397 framepos_t prior = _session->locations()->first_mark_before ( pos );
2406 _session->request_locate (pos, _session->transport_rolling());
2412 framepos_t const pos = _session->audible_frame ();
2415 _session->locations()->next_available_name (markername, "mark");
2417 if (!choose_new_marker_name (markername)) {
2421 _session->locations()->add (new Location (*_session, pos, 0, markername, Location::IsMark, 0), true);
2425 Editor::clear_markers ()
2428 begin_reversible_command (_("clear markers"));
2430 XMLNode &before = _session->locations()->get_state();
2431 _session->locations()->clear_markers ();
2432 XMLNode &after = _session->locations()->get_state();
2433 _session->add_command(new MementoCommand<Locations>(*(_session->locations()), &before, &after));
2435 commit_reversible_command ();
2440 Editor::clear_ranges ()
2443 begin_reversible_command (_("clear ranges"));
2445 XMLNode &before = _session->locations()->get_state();
2447 _session->locations()->clear_ranges ();
2449 XMLNode &after = _session->locations()->get_state();
2450 _session->add_command(new MementoCommand<Locations>(*(_session->locations()), &before, &after));
2452 commit_reversible_command ();
2457 Editor::clear_locations ()
2459 begin_reversible_command (_("clear locations"));
2461 XMLNode &before = _session->locations()->get_state();
2462 _session->locations()->clear ();
2463 XMLNode &after = _session->locations()->get_state();
2464 _session->add_command(new MementoCommand<Locations>(*(_session->locations()), &before, &after));
2466 commit_reversible_command ();
2470 Editor::unhide_markers ()
2472 for (LocationMarkerMap::iterator i = location_markers.begin(); i != location_markers.end(); ++i) {
2473 Location *l = (*i).first;
2474 if (l->is_hidden() && l->is_mark()) {
2475 l->set_hidden(false, this);
2481 Editor::unhide_ranges ()
2483 for (LocationMarkerMap::iterator i = location_markers.begin(); i != location_markers.end(); ++i) {
2484 Location *l = (*i).first;
2485 if (l->is_hidden() && l->is_range_marker()) {
2486 l->set_hidden(false, this);
2491 /* INSERT/REPLACE */
2494 Editor::insert_region_list_selection (float times)
2496 RouteTimeAxisView *tv = 0;
2497 boost::shared_ptr<Playlist> playlist;
2499 if (clicked_routeview != 0) {
2500 tv = clicked_routeview;
2501 } else if (!selection->tracks.empty()) {
2502 if ((tv = dynamic_cast<RouteTimeAxisView*>(selection->tracks.front())) == 0) {
2505 } else if (entered_track != 0) {
2506 if ((tv = dynamic_cast<RouteTimeAxisView*>(entered_track)) == 0) {
2513 if ((playlist = tv->playlist()) == 0) {
2517 boost::shared_ptr<Region> region = _regions->get_single_selection ();
2522 begin_reversible_command (_("insert region"));
2523 playlist->clear_changes ();
2524 playlist->add_region ((RegionFactory::create (region, true)), get_preferred_edit_position(), times);
2525 if (Config->get_edit_mode() == Ripple)
2526 playlist->ripple (get_preferred_edit_position(), region->length() * times, boost::shared_ptr<Region>());
2528 _session->add_command(new StatefulDiffCommand (playlist));
2529 commit_reversible_command ();
2532 /* BUILT-IN EFFECTS */
2535 Editor::reverse_selection ()
2540 /* GAIN ENVELOPE EDITING */
2543 Editor::edit_envelope ()
2550 Editor::transition_to_rolling (bool fwd)
2556 if (_session->config.get_external_sync()) {
2557 switch (Config->get_sync_source()) {
2561 /* transport controlled by the master */
2566 if (_session->is_auditioning()) {
2567 _session->cancel_audition ();
2571 _session->request_transport_speed (fwd ? 1.0f : -1.0f);
2575 Editor::play_from_start ()
2577 _session->request_locate (_session->current_start_frame(), true);
2581 Editor::play_from_edit_point ()
2583 _session->request_locate (get_preferred_edit_position(), true);
2587 Editor::play_from_edit_point_and_return ()
2589 framepos_t start_frame;
2590 framepos_t return_frame;
2592 start_frame = get_preferred_edit_position ( EDIT_IGNORE_PHEAD );
2594 if (_session->transport_rolling()) {
2595 _session->request_locate (start_frame, false);
2599 /* don't reset the return frame if its already set */
2601 if ((return_frame = _session->requested_return_frame()) < 0) {
2602 return_frame = _session->audible_frame();
2605 if (start_frame >= 0) {
2606 _session->request_roll_at_and_return (start_frame, return_frame);
2611 Editor::play_selection ()
2613 framepos_t start, end;
2614 if (!get_selection_extents ( start, end))
2617 AudioRange ar (start, end, 0);
2618 list<AudioRange> lar;
2621 _session->request_play_range (&lar, true);
2626 Editor::maybe_locate_with_edit_preroll (framepos_t location)
2628 if ( _session->transport_rolling() || !UIConfiguration::instance().get_follow_edits() || _session->config.get_external_sync() )
2631 location -= _session->preroll_samples (location);
2633 //don't try to locate before the beginning of time
2638 //if follow_playhead is on, keep the playhead on the screen
2639 if ( _follow_playhead )
2640 if ( location < leftmost_frame )
2641 location = leftmost_frame;
2643 _session->request_locate( location );
2647 Editor::play_with_preroll ()
2649 framepos_t start, end;
2650 if ( UIConfiguration::instance().get_follow_edits() && get_selection_extents ( start, end) ) {
2651 const framepos_t preroll = _session->preroll_samples (start);
2653 framepos_t ret = start;
2655 if (start > preroll) {
2656 start = start - preroll;
2659 end = end + preroll; //"post-roll"
2661 AudioRange ar (start, end, 0);
2662 list<AudioRange> lar;
2665 _session->request_play_range (&lar, true);
2666 _session->set_requested_return_frame (ret); //force auto-return to return to range start, without the preroll
2668 framepos_t ph = playhead_cursor->current_frame ();
2669 const framepos_t preroll = _session->preroll_samples (ph);
2672 start = ph - preroll;
2676 _session->request_locate (start, true);
2677 _session->set_requested_return_frame (ph); //force auto-return to return to playhead location, without the preroll
2682 Editor::rec_with_preroll ()
2684 framepos_t ph = playhead_cursor->current_frame ();
2685 framepos_t preroll = _session->preroll_samples (ph);
2686 _session->request_preroll_record_trim (ph, preroll);
2690 Editor::rec_with_count_in ()
2692 _session->request_count_in_record ();
2696 Editor::play_location (Location& location)
2698 if (location.start() <= location.end()) {
2702 _session->request_bounded_roll (location.start(), location.end());
2706 Editor::loop_location (Location& location)
2708 if (location.start() <= location.end()) {
2714 if ((tll = transport_loop_location()) != 0) {
2715 tll->set (location.start(), location.end());
2717 // enable looping, reposition and start rolling
2718 _session->request_locate (tll->start(), true);
2719 _session->request_play_loop (true);
2724 Editor::do_layer_operation (LayerOperation op)
2726 if (selection->regions.empty ()) {
2730 bool const multiple = selection->regions.size() > 1;
2734 begin_reversible_command (_("raise regions"));
2736 begin_reversible_command (_("raise region"));
2742 begin_reversible_command (_("raise regions to top"));
2744 begin_reversible_command (_("raise region to top"));
2750 begin_reversible_command (_("lower regions"));
2752 begin_reversible_command (_("lower region"));
2758 begin_reversible_command (_("lower regions to bottom"));
2760 begin_reversible_command (_("lower region"));
2765 set<boost::shared_ptr<Playlist> > playlists = selection->regions.playlists ();
2766 for (set<boost::shared_ptr<Playlist> >::iterator i = playlists.begin(); i != playlists.end(); ++i) {
2767 (*i)->clear_owned_changes ();
2770 for (RegionSelection::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
2771 boost::shared_ptr<Region> r = (*i)->region ();
2783 r->lower_to_bottom ();
2787 for (set<boost::shared_ptr<Playlist> >::iterator i = playlists.begin(); i != playlists.end(); ++i) {
2788 vector<Command*> cmds;
2790 _session->add_commands (cmds);
2793 commit_reversible_command ();
2797 Editor::raise_region ()
2799 do_layer_operation (Raise);
2803 Editor::raise_region_to_top ()
2805 do_layer_operation (RaiseToTop);
2809 Editor::lower_region ()
2811 do_layer_operation (Lower);
2815 Editor::lower_region_to_bottom ()
2817 do_layer_operation (LowerToBottom);
2820 /** Show the region editor for the selected regions */
2822 Editor::show_region_properties ()
2824 selection->foreach_regionview (&RegionView::show_region_editor);
2827 /** Show the midi list editor for the selected MIDI regions */
2829 Editor::show_midi_list_editor ()
2831 selection->foreach_midi_regionview (&MidiRegionView::show_list_editor);
2835 Editor::rename_region ()
2837 RegionSelection rs = get_regions_from_selection_and_entered ();
2843 ArdourDialog d (_("Rename Region"), true, false);
2845 Label label (_("New name:"));
2848 hbox.set_spacing (6);
2849 hbox.pack_start (label, false, false);
2850 hbox.pack_start (entry, true, true);
2852 d.get_vbox()->set_border_width (12);
2853 d.get_vbox()->pack_start (hbox, false, false);
2855 d.add_button(Gtk::Stock::CANCEL, Gtk::RESPONSE_CANCEL);
2856 d.add_button(Gtk::Stock::OK, Gtk::RESPONSE_OK);
2858 d.set_size_request (300, -1);
2860 entry.set_text (rs.front()->region()->name());
2861 entry.select_region (0, -1);
2863 entry.signal_activate().connect (sigc::bind (sigc::mem_fun (d, &Dialog::response), RESPONSE_OK));
2869 int const ret = d.run();
2873 if (ret != RESPONSE_OK) {
2877 std::string str = entry.get_text();
2878 strip_whitespace_edges (str);
2880 rs.front()->region()->set_name (str);
2881 _regions->redisplay ();
2885 /** Start an audition of the first selected region */
2887 Editor::play_edit_range ()
2889 framepos_t start, end;
2891 if (get_edit_op_range (start, end)) {
2892 _session->request_bounded_roll (start, end);
2897 Editor::play_selected_region ()
2899 framepos_t start = max_framepos;
2902 RegionSelection rs = get_regions_from_selection_and_entered ();
2908 for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
2909 if ((*i)->region()->position() < start) {
2910 start = (*i)->region()->position();
2912 if ((*i)->region()->last_frame() + 1 > end) {
2913 end = (*i)->region()->last_frame() + 1;
2917 _session->request_bounded_roll (start, end);
2921 Editor::audition_playlist_region_standalone (boost::shared_ptr<Region> region)
2923 _session->audition_region (region);
2927 Editor::region_from_selection ()
2929 if (clicked_axisview == 0) {
2933 if (selection->time.empty()) {
2937 framepos_t start = selection->time[clicked_selection].start;
2938 framepos_t end = selection->time[clicked_selection].end;
2940 TrackViewList tracks = get_tracks_for_range_action ();
2942 framepos_t selection_cnt = end - start + 1;
2944 for (TrackSelection::iterator i = tracks.begin(); i != tracks.end(); ++i) {
2945 boost::shared_ptr<Region> current;
2946 boost::shared_ptr<Playlist> pl;
2947 framepos_t internal_start;
2950 if ((pl = (*i)->playlist()) == 0) {
2954 if ((current = pl->top_region_at (start)) == 0) {
2958 internal_start = start - current->position();
2959 RegionFactory::region_name (new_name, current->name(), true);
2963 plist.add (ARDOUR::Properties::start, current->start() + internal_start);
2964 plist.add (ARDOUR::Properties::length, selection_cnt);
2965 plist.add (ARDOUR::Properties::name, new_name);
2966 plist.add (ARDOUR::Properties::layer, 0);
2968 boost::shared_ptr<Region> region (RegionFactory::create (current, plist));
2973 Editor::create_region_from_selection (vector<boost::shared_ptr<Region> >& new_regions)
2975 if (selection->time.empty() || selection->tracks.empty()) {
2979 framepos_t start, end;
2980 if (clicked_selection) {
2981 start = selection->time[clicked_selection].start;
2982 end = selection->time[clicked_selection].end;
2984 start = selection->time.start();
2985 end = selection->time.end_frame();
2988 TrackViewList ts = selection->tracks.filter_to_unique_playlists ();
2989 sort_track_selection (ts);
2991 for (TrackSelection::iterator i = ts.begin(); i != ts.end(); ++i) {
2992 boost::shared_ptr<Region> current;
2993 boost::shared_ptr<Playlist> playlist;
2994 framepos_t internal_start;
2997 if ((playlist = (*i)->playlist()) == 0) {
3001 if ((current = playlist->top_region_at(start)) == 0) {
3005 internal_start = start - current->position();
3006 RegionFactory::region_name (new_name, current->name(), true);
3010 plist.add (ARDOUR::Properties::start, current->start() + internal_start);
3011 plist.add (ARDOUR::Properties::length, end - start + 1);
3012 plist.add (ARDOUR::Properties::name, new_name);
3014 new_regions.push_back (RegionFactory::create (current, plist));
3019 Editor::split_multichannel_region ()
3021 RegionSelection rs = get_regions_from_selection_and_entered ();
3027 vector< boost::shared_ptr<Region> > v;
3029 for (list<RegionView*>::iterator x = rs.begin(); x != rs.end(); ++x) {
3030 (*x)->region()->separate_by_channel (*_session, v);
3035 Editor::new_region_from_selection ()
3037 region_from_selection ();
3038 cancel_selection ();
3042 add_if_covered (RegionView* rv, const AudioRange* ar, RegionSelection* rs)
3044 switch (rv->region()->coverage (ar->start, ar->end - 1)) {
3045 // n.b. -1 because AudioRange::end is one past the end, but coverage expects inclusive ranges
3046 case Evoral::OverlapNone:
3054 * - selected tracks, or if there are none...
3055 * - tracks containing selected regions, or if there are none...
3060 Editor::get_tracks_for_range_action () const
3064 if (selection->tracks.empty()) {
3066 /* use tracks with selected regions */
3068 RegionSelection rs = selection->regions;
3070 for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
3071 TimeAxisView* tv = &(*i)->get_time_axis_view();
3073 if (!t.contains (tv)) {
3079 /* no regions and no tracks: use all tracks */
3085 t = selection->tracks;
3088 return t.filter_to_unique_playlists();
3092 Editor::separate_regions_between (const TimeSelection& ts)
3094 bool in_command = false;
3095 boost::shared_ptr<Playlist> playlist;
3096 RegionSelection new_selection;
3098 TrackViewList tmptracks = get_tracks_for_range_action ();
3099 sort_track_selection (tmptracks);
3101 for (TrackSelection::iterator i = tmptracks.begin(); i != tmptracks.end(); ++i) {
3103 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> ((*i));
3109 if (!rtv->is_track()) {
3113 /* no edits to destructive tracks */
3115 if (rtv->track()->destructive()) {
3119 if ((playlist = rtv->playlist()) != 0) {
3121 playlist->clear_changes ();
3123 /* XXX need to consider musical time selections here at some point */
3125 double speed = rtv->track()->speed();
3127 for (list<AudioRange>::const_iterator t = ts.begin(); t != ts.end(); ++t) {
3129 sigc::connection c = rtv->view()->RegionViewAdded.connect (
3130 sigc::mem_fun(*this, &Editor::collect_new_region_view));
3132 latest_regionviews.clear ();
3134 playlist->partition ((framepos_t)((*t).start * speed),
3135 (framepos_t)((*t).end * speed), false);
3139 if (!latest_regionviews.empty()) {
3141 rtv->view()->foreach_regionview (sigc::bind (
3142 sigc::ptr_fun (add_if_covered),
3143 &(*t), &new_selection));
3146 begin_reversible_command (_("separate"));
3150 /* pick up changes to existing regions */
3152 vector<Command*> cmds;
3153 playlist->rdiff (cmds);
3154 _session->add_commands (cmds);
3156 /* pick up changes to the playlist itself (adds/removes)
3159 _session->add_command(new StatefulDiffCommand (playlist));
3166 // selection->set (new_selection);
3168 commit_reversible_command ();
3172 struct PlaylistState {
3173 boost::shared_ptr<Playlist> playlist;
3177 /** Take tracks from get_tracks_for_range_action and cut any regions
3178 * on those tracks so that the tracks are empty over the time
3182 Editor::separate_region_from_selection ()
3184 /* preferentially use *all* ranges in the time selection if we're in range mode
3185 to allow discontiguous operation, since get_edit_op_range() currently
3186 returns a single range.
3189 if (!selection->time.empty()) {
3191 separate_regions_between (selection->time);
3198 if (get_edit_op_range (start, end)) {
3200 AudioRange ar (start, end, 1);
3204 separate_regions_between (ts);
3210 Editor::separate_region_from_punch ()
3212 Location* loc = _session->locations()->auto_punch_location();
3214 separate_regions_using_location (*loc);
3219 Editor::separate_region_from_loop ()
3221 Location* loc = _session->locations()->auto_loop_location();
3223 separate_regions_using_location (*loc);
3228 Editor::separate_regions_using_location (Location& loc)
3230 if (loc.is_mark()) {
3234 AudioRange ar (loc.start(), loc.end(), 1);
3239 separate_regions_between (ts);
3242 /** Separate regions under the selected region */
3244 Editor::separate_under_selected_regions ()
3246 vector<PlaylistState> playlists;
3250 rs = get_regions_from_selection_and_entered();
3252 if (!_session || rs.empty()) {
3256 begin_reversible_command (_("separate region under"));
3258 list<boost::shared_ptr<Region> > regions_to_remove;
3260 for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
3261 // we can't just remove the region(s) in this loop because
3262 // this removes them from the RegionSelection, and they thus
3263 // disappear from underneath the iterator, and the ++i above
3264 // SEGVs in a puzzling fashion.
3266 // so, first iterate over the regions to be removed from rs and
3267 // add them to the regions_to_remove list, and then
3268 // iterate over the list to actually remove them.
3270 regions_to_remove.push_back ((*i)->region());
3273 for (list<boost::shared_ptr<Region> >::iterator rl = regions_to_remove.begin(); rl != regions_to_remove.end(); ++rl) {
3275 boost::shared_ptr<Playlist> playlist = (*rl)->playlist();
3278 // is this check necessary?
3282 vector<PlaylistState>::iterator i;
3284 //only take state if this is a new playlist.
3285 for (i = playlists.begin(); i != playlists.end(); ++i) {
3286 if ((*i).playlist == playlist) {
3291 if (i == playlists.end()) {
3293 PlaylistState before;
3294 before.playlist = playlist;
3295 before.before = &playlist->get_state();
3297 playlist->freeze ();
3298 playlists.push_back(before);
3301 //Partition on the region bounds
3302 playlist->partition ((*rl)->first_frame() - 1, (*rl)->last_frame() + 1, true);
3304 //Re-add region that was just removed due to the partition operation
3305 playlist->add_region( (*rl), (*rl)->first_frame() );
3308 vector<PlaylistState>::iterator pl;
3310 for (pl = playlists.begin(); pl != playlists.end(); ++pl) {
3311 (*pl).playlist->thaw ();
3312 _session->add_command(new MementoCommand<Playlist>(*(*pl).playlist, (*pl).before, &(*pl).playlist->get_state()));
3315 commit_reversible_command ();
3319 Editor::crop_region_to_selection ()
3321 if (!selection->time.empty()) {
3323 crop_region_to (selection->time.start(), selection->time.end_frame());
3330 if (get_edit_op_range (start, end)) {
3331 crop_region_to (start, end);
3338 Editor::crop_region_to (framepos_t start, framepos_t end)
3340 vector<boost::shared_ptr<Playlist> > playlists;
3341 boost::shared_ptr<Playlist> playlist;
3344 if (selection->tracks.empty()) {
3345 ts = track_views.filter_to_unique_playlists();
3347 ts = selection->tracks.filter_to_unique_playlists ();
3350 sort_track_selection (ts);
3352 for (TrackSelection::iterator i = ts.begin(); i != ts.end(); ++i) {
3354 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> ((*i));
3360 boost::shared_ptr<Track> t = rtv->track();
3362 if (t != 0 && ! t->destructive()) {
3364 if ((playlist = rtv->playlist()) != 0) {
3365 playlists.push_back (playlist);
3370 if (playlists.empty()) {
3375 framepos_t new_start;
3377 framecnt_t new_length;
3378 bool in_command = false;
3380 for (vector<boost::shared_ptr<Playlist> >::iterator i = playlists.begin(); i != playlists.end(); ++i) {
3382 /* Only the top regions at start and end have to be cropped */
3383 boost::shared_ptr<Region> region_at_start = (*i)->top_region_at(start);
3384 boost::shared_ptr<Region> region_at_end = (*i)->top_region_at(end);
3386 vector<boost::shared_ptr<Region> > regions;
3388 if (region_at_start != 0) {
3389 regions.push_back (region_at_start);
3391 if (region_at_end != 0) {
3392 regions.push_back (region_at_end);
3395 /* now adjust lengths */
3396 for (vector<boost::shared_ptr<Region> >::iterator i = regions.begin(); i != regions.end(); ++i) {
3398 pos = (*i)->position();
3399 new_start = max (start, pos);
3400 if (max_framepos - pos > (*i)->length()) {
3401 new_end = pos + (*i)->length() - 1;
3403 new_end = max_framepos;
3405 new_end = min (end, new_end);
3406 new_length = new_end - new_start + 1;
3409 begin_reversible_command (_("trim to selection"));
3412 (*i)->clear_changes ();
3413 (*i)->trim_to (new_start, new_length);
3414 _session->add_command (new StatefulDiffCommand (*i));
3419 commit_reversible_command ();
3424 Editor::region_fill_track ()
3426 boost::shared_ptr<Playlist> playlist;
3427 RegionSelection regions = get_regions_from_selection_and_entered ();
3428 RegionSelection foo;
3430 framepos_t const end = _session->current_end_frame ();
3432 if (regions.empty () || regions.end_frame () + 1 >= end) {
3436 framepos_t const start_frame = regions.start ();
3437 framepos_t const end_frame = regions.end_frame ();
3438 framecnt_t const gap = end_frame - start_frame + 1;
3440 begin_reversible_command (Operations::region_fill);
3442 selection->clear_regions ();
3444 for (RegionSelection::iterator i = regions.begin(); i != regions.end(); ++i) {
3446 boost::shared_ptr<Region> r ((*i)->region());
3448 TimeAxisView& tv = (*i)->get_time_axis_view();
3449 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (&tv);
3450 latest_regionviews.clear ();
3451 sigc::connection c = rtv->view()->RegionViewAdded.connect (sigc::mem_fun(*this, &Editor::collect_new_region_view));
3453 framepos_t const position = end_frame + (r->first_frame() - start_frame + 1);
3454 playlist = (*i)->region()->playlist();
3455 playlist->clear_changes ();
3456 playlist->duplicate_until (r, position, gap, end);
3457 _session->add_command(new StatefulDiffCommand (playlist));
3461 foo.insert (foo.end(), latest_regionviews.begin(), latest_regionviews.end());
3465 selection->set (foo);
3468 commit_reversible_command ();
3472 Editor::set_region_sync_position ()
3474 set_sync_point (get_preferred_edit_position (), get_regions_from_selection_and_edit_point ());
3478 Editor::set_sync_point (framepos_t where, const RegionSelection& rs)
3480 bool in_command = false;
3482 for (RegionSelection::const_iterator r = rs.begin(); r != rs.end(); ++r) {
3484 if (!(*r)->region()->covers (where)) {
3488 boost::shared_ptr<Region> region ((*r)->region());
3491 begin_reversible_command (_("set sync point"));
3495 region->clear_changes ();
3496 region->set_sync_position (where);
3497 _session->add_command(new StatefulDiffCommand (region));
3501 commit_reversible_command ();
3505 /** Remove the sync positions of the selection */
3507 Editor::remove_region_sync ()
3509 RegionSelection rs = get_regions_from_selection_and_entered ();
3515 begin_reversible_command (_("remove region sync"));
3517 for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
3519 (*i)->region()->clear_changes ();
3520 (*i)->region()->clear_sync_position ();
3521 _session->add_command(new StatefulDiffCommand ((*i)->region()));
3524 commit_reversible_command ();
3528 Editor::naturalize_region ()
3530 RegionSelection rs = get_regions_from_selection_and_entered ();
3536 if (rs.size() > 1) {
3537 begin_reversible_command (_("move regions to original position"));
3539 begin_reversible_command (_("move region to original position"));
3542 for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
3543 (*i)->region()->clear_changes ();
3544 (*i)->region()->move_to_natural_position ();
3545 _session->add_command (new StatefulDiffCommand ((*i)->region()));
3548 commit_reversible_command ();
3552 Editor::align_regions (RegionPoint what)
3554 RegionSelection const rs = get_regions_from_selection_and_edit_point ();
3560 begin_reversible_command (_("align selection"));
3562 framepos_t const position = get_preferred_edit_position ();
3564 for (RegionSelection::const_iterator i = rs.begin(); i != rs.end(); ++i) {
3565 align_region_internal ((*i)->region(), what, position);
3568 commit_reversible_command ();
3571 struct RegionSortByTime {
3572 bool operator() (const RegionView* a, const RegionView* b) {
3573 return a->region()->position() < b->region()->position();
3578 Editor::align_regions_relative (RegionPoint point)
3580 RegionSelection const rs = get_regions_from_selection_and_edit_point ();
3586 framepos_t const position = get_preferred_edit_position ();
3588 framepos_t distance = 0;
3592 list<RegionView*> sorted;
3593 rs.by_position (sorted);
3595 boost::shared_ptr<Region> r ((*sorted.begin())->region());
3600 if (position > r->position()) {
3601 distance = position - r->position();
3603 distance = r->position() - position;
3609 if (position > r->last_frame()) {
3610 distance = position - r->last_frame();
3611 pos = r->position() + distance;
3613 distance = r->last_frame() - position;
3614 pos = r->position() - distance;
3620 pos = r->adjust_to_sync (position);
3621 if (pos > r->position()) {
3622 distance = pos - r->position();
3624 distance = r->position() - pos;
3630 if (pos == r->position()) {
3634 begin_reversible_command (_("align selection (relative)"));
3636 /* move first one specially */
3638 r->clear_changes ();
3639 r->set_position (pos);
3640 _session->add_command(new StatefulDiffCommand (r));
3642 /* move rest by the same amount */
3646 for (list<RegionView*>::iterator i = sorted.begin(); i != sorted.end(); ++i) {
3648 boost::shared_ptr<Region> region ((*i)->region());
3650 region->clear_changes ();
3653 region->set_position (region->position() + distance);
3655 region->set_position (region->position() - distance);
3658 _session->add_command(new StatefulDiffCommand (region));
3662 commit_reversible_command ();
3666 Editor::align_region (boost::shared_ptr<Region> region, RegionPoint point, framepos_t position)
3668 begin_reversible_command (_("align region"));
3669 align_region_internal (region, point, position);
3670 commit_reversible_command ();
3674 Editor::align_region_internal (boost::shared_ptr<Region> region, RegionPoint point, framepos_t position)
3676 region->clear_changes ();
3680 region->set_position (region->adjust_to_sync (position));
3684 if (position > region->length()) {
3685 region->set_position (position - region->length());
3690 region->set_position (position);
3694 _session->add_command(new StatefulDiffCommand (region));
3698 Editor::trim_region_front ()
3704 Editor::trim_region_back ()
3706 trim_region (false);
3710 Editor::trim_region (bool front)
3712 framepos_t where = get_preferred_edit_position();
3713 RegionSelection rs = get_regions_from_selection_and_edit_point ();
3719 begin_reversible_command (front ? _("trim front") : _("trim back"));
3721 for (list<RegionView*>::const_iterator i = rs.by_layer().begin(); i != rs.by_layer().end(); ++i) {
3722 if (!(*i)->region()->locked()) {
3724 (*i)->region()->clear_changes ();
3727 (*i)->region()->trim_front (where);
3729 (*i)->region()->trim_end (where);
3732 _session->add_command (new StatefulDiffCommand ((*i)->region()));
3736 commit_reversible_command ();
3739 /** Trim the end of the selected regions to the position of the edit cursor */
3741 Editor::trim_region_to_loop ()
3743 Location* loc = _session->locations()->auto_loop_location();
3747 trim_region_to_location (*loc, _("trim to loop"));
3751 Editor::trim_region_to_punch ()
3753 Location* loc = _session->locations()->auto_punch_location();
3757 trim_region_to_location (*loc, _("trim to punch"));
3761 Editor::trim_region_to_location (const Location& loc, const char* str)
3763 RegionSelection rs = get_regions_from_selection_and_entered ();
3764 bool in_command = false;
3766 for (RegionSelection::iterator x = rs.begin(); x != rs.end(); ++x) {
3767 RegionView* rv = (*x);
3769 /* require region to span proposed trim */
3770 switch (rv->region()->coverage (loc.start(), loc.end())) {
3771 case Evoral::OverlapInternal:
3777 RouteTimeAxisView* tav = dynamic_cast<RouteTimeAxisView*> (&rv->get_time_axis_view());
3786 if (tav->track() != 0) {
3787 speed = tav->track()->speed();
3790 start = session_frame_to_track_frame (loc.start(), speed);
3791 end = session_frame_to_track_frame (loc.end(), speed);
3793 rv->region()->clear_changes ();
3794 rv->region()->trim_to (start, (end - start));
3797 begin_reversible_command (str);
3800 _session->add_command(new StatefulDiffCommand (rv->region()));
3804 commit_reversible_command ();
3809 Editor::trim_region_to_previous_region_end ()
3811 return trim_to_region(false);
3815 Editor::trim_region_to_next_region_start ()
3817 return trim_to_region(true);
3821 Editor::trim_to_region(bool forward)
3823 RegionSelection rs = get_regions_from_selection_and_entered ();
3824 bool in_command = false;
3826 boost::shared_ptr<Region> next_region;
3828 for (RegionSelection::iterator x = rs.begin(); x != rs.end(); ++x) {
3830 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (*x);
3836 AudioTimeAxisView* atav = dynamic_cast<AudioTimeAxisView*> (&arv->get_time_axis_view());
3844 if (atav->track() != 0) {
3845 speed = atav->track()->speed();
3849 boost::shared_ptr<Region> region = arv->region();
3850 boost::shared_ptr<Playlist> playlist (region->playlist());
3852 region->clear_changes ();
3856 next_region = playlist->find_next_region (region->first_frame(), Start, 1);
3862 region->trim_end((framepos_t) ( (next_region->first_frame() - 1) * speed));
3863 arv->region_changed (PropertyChange (ARDOUR::Properties::length));
3867 next_region = playlist->find_next_region (region->first_frame(), Start, 0);
3873 region->trim_front((framepos_t) ((next_region->last_frame() + 1) * speed));
3875 arv->region_changed (ARDOUR::bounds_change);
3879 begin_reversible_command (_("trim to region"));
3882 _session->add_command(new StatefulDiffCommand (region));
3886 commit_reversible_command ();
3891 Editor::unfreeze_route ()
3893 if (clicked_routeview == 0 || !clicked_routeview->is_track()) {
3897 clicked_routeview->track()->unfreeze ();
3901 Editor::_freeze_thread (void* arg)
3903 return static_cast<Editor*>(arg)->freeze_thread ();
3907 Editor::freeze_thread ()
3909 /* create event pool because we may need to talk to the session */
3910 SessionEvent::create_per_thread_pool ("freeze events", 64);
3911 /* create per-thread buffers for process() tree to use */
3912 clicked_routeview->audio_track()->freeze_me (*current_interthread_info);
3913 current_interthread_info->done = true;
3918 Editor::freeze_route ()
3924 /* stop transport before we start. this is important */
3926 _session->request_transport_speed (0.0);
3928 /* wait for just a little while, because the above call is asynchronous */
3930 Glib::usleep (250000);
3932 if (clicked_routeview == 0 || !clicked_routeview->is_audio_track()) {
3936 if (!clicked_routeview->track()->bounceable (clicked_routeview->track()->main_outs(), true)) {
3938 _("This track/bus cannot be frozen because the signal adds or loses channels before reaching the outputs.\n"
3939 "This is typically caused by plugins that generate stereo output from mono input or vice versa.")
3941 d.set_title (_("Cannot freeze"));
3946 if (clicked_routeview->track()->has_external_redirects()) {
3947 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"
3948 "Freezing will only process the signal as far as the first send/insert/return."),
3949 clicked_routeview->track()->name()), true, MESSAGE_INFO, BUTTONS_NONE, true);
3951 d.add_button (_("Freeze anyway"), Gtk::RESPONSE_OK);
3952 d.add_button (_("Don't freeze"), Gtk::RESPONSE_CANCEL);
3953 d.set_title (_("Freeze Limits"));
3955 int response = d.run ();
3958 case Gtk::RESPONSE_CANCEL:
3965 InterThreadInfo itt;
3966 current_interthread_info = &itt;
3968 InterthreadProgressWindow ipw (current_interthread_info, _("Freeze"), _("Cancel Freeze"));
3970 pthread_create_and_store (X_("freezer"), &itt.thread, _freeze_thread, this);
3972 CursorContext::Handle cursor_ctx = CursorContext::create(*this, _cursors->wait);
3974 while (!itt.done && !itt.cancel) {
3975 gtk_main_iteration ();
3978 pthread_join (itt.thread, 0);
3979 current_interthread_info = 0;
3983 Editor::bounce_range_selection (bool replace, bool enable_processing)
3985 if (selection->time.empty()) {
3989 TrackSelection views = selection->tracks;
3991 for (TrackViewList::iterator i = views.begin(); i != views.end(); ++i) {
3993 if (enable_processing) {
3995 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (*i);
3997 if (rtv && rtv->track() && replace && enable_processing && !rtv->track()->bounceable (rtv->track()->main_outs(), false)) {
3999 _("You can't perform this operation because the processing of the signal "
4000 "will cause one or more of the tracks to end up with a region with more channels than this track has inputs.\n\n"
4001 "You can do this without processing, which is a different operation.")
4003 d.set_title (_("Cannot bounce"));
4010 framepos_t start = selection->time[clicked_selection].start;
4011 framepos_t end = selection->time[clicked_selection].end;
4012 framepos_t cnt = end - start + 1;
4013 bool in_command = false;
4015 for (TrackViewList::iterator i = views.begin(); i != views.end(); ++i) {
4017 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (*i);
4023 boost::shared_ptr<Playlist> playlist;
4025 if ((playlist = rtv->playlist()) == 0) {
4029 InterThreadInfo itt;
4031 playlist->clear_changes ();
4032 playlist->clear_owned_changes ();
4034 boost::shared_ptr<Region> r;
4036 if (enable_processing) {
4037 r = rtv->track()->bounce_range (start, start+cnt, itt, rtv->track()->main_outs(), false);
4039 r = rtv->track()->bounce_range (start, start+cnt, itt, boost::shared_ptr<Processor>(), false);
4047 list<AudioRange> ranges;
4048 ranges.push_back (AudioRange (start, start+cnt, 0));
4049 playlist->cut (ranges); // discard result
4050 playlist->add_region (r, start);
4054 begin_reversible_command (_("bounce range"));
4057 vector<Command*> cmds;
4058 playlist->rdiff (cmds);
4059 _session->add_commands (cmds);
4061 _session->add_command (new StatefulDiffCommand (playlist));
4065 commit_reversible_command ();
4069 /** Delete selected regions, automation points or a time range */
4073 //special case: if the user is pointing in the editor/mixer strip, they may be trying to delete a plugin.
4074 //we need this because the editor-mixer strip is in the editor window, so it doesn't get the bindings from the mix window
4075 bool deleted = false;
4076 if ( current_mixer_strip && current_mixer_strip == MixerStrip::entered_mixer_strip() )
4077 deleted = current_mixer_strip->delete_processors ();
4083 /** Cut selected regions, automation points or a time range */
4090 /** Copy selected regions, automation points or a time range */
4098 /** @return true if a Cut, Copy or Clear is possible */
4100 Editor::can_cut_copy () const
4102 if (!selection->time.empty() || !selection->regions.empty() || !selection->points.empty())
4109 /** Cut, copy or clear selected regions, automation points or a time range.
4110 * @param op Operation (Delete, Cut, Copy or Clear)
4113 Editor::cut_copy (CutCopyOp op)
4115 /* only cancel selection if cut/copy is successful.*/
4121 opname = _("delete");
4130 opname = _("clear");
4134 /* if we're deleting something, and the mouse is still pressed,
4135 the thing we started a drag for will be gone when we release
4136 the mouse button(s). avoid this. see part 2 at the end of
4140 if (op == Delete || op == Cut || op == Clear) {
4141 if (_drags->active ()) {
4146 if ( op != Delete ) //"Delete" doesn't change copy/paste buf
4147 cut_buffer->clear ();
4149 if (entered_marker) {
4151 /* cut/delete op while pointing at a marker */
4154 Location* loc = find_location_from_marker (entered_marker, ignored);
4156 if (_session && loc) {
4157 entered_marker = NULL;
4158 Glib::signal_idle().connect (sigc::bind (sigc::mem_fun(*this, &Editor::really_remove_marker), loc));
4165 switch (mouse_mode) {
4168 begin_reversible_command (opname + ' ' + X_("MIDI"));
4170 commit_reversible_command ();
4176 bool did_edit = false;
4178 if (!selection->regions.empty() || !selection->points.empty()) {
4179 begin_reversible_command (opname + ' ' + _("objects"));
4182 if (!selection->regions.empty()) {
4183 cut_copy_regions (op, selection->regions);
4185 if (op == Cut || op == Delete) {
4186 selection->clear_regions ();
4190 if (!selection->points.empty()) {
4191 cut_copy_points (op);
4193 if (op == Cut || op == Delete) {
4194 selection->clear_points ();
4197 } else if (selection->time.empty()) {
4198 framepos_t start, end;
4199 /* no time selection, see if we can get an edit range
4202 if (get_edit_op_range (start, end)) {
4203 selection->set (start, end);
4205 } else if (!selection->time.empty()) {
4206 begin_reversible_command (opname + ' ' + _("range"));
4209 cut_copy_ranges (op);
4211 if (op == Cut || op == Delete) {
4212 selection->clear_time ();
4217 /* reset repeated paste state */
4220 commit_reversible_command ();
4223 if (op == Delete || op == Cut || op == Clear) {
4229 struct AutomationRecord {
4230 AutomationRecord () : state (0) , line(NULL) {}
4231 AutomationRecord (XMLNode* s, const AutomationLine* l) : state (s) , line (l) {}
4233 XMLNode* state; ///< state before any operation
4234 const AutomationLine* line; ///< line this came from
4235 boost::shared_ptr<Evoral::ControlList> copy; ///< copied events for the cut buffer
4238 struct PointsSelectionPositionSorter {
4239 bool operator() (ControlPoint* a, ControlPoint* b) {
4240 return (*(a->model()))->when < (*(b->model()))->when;
4244 /** Cut, copy or clear selected automation points.
4245 * @param op Operation (Cut, Copy or Clear)
4248 Editor::cut_copy_points (Editing::CutCopyOp op, Evoral::Beats earliest, bool midi)
4250 if (selection->points.empty ()) {
4254 /* XXX: not ideal, as there may be more than one track involved in the point selection */
4255 _last_cut_copy_source_track = &selection->points.front()->line().trackview;
4257 /* Keep a record of the AutomationLists that we end up using in this operation */
4258 typedef std::map<boost::shared_ptr<AutomationList>, AutomationRecord> Lists;
4261 /* user could select points in any order */
4262 selection->points.sort(PointsSelectionPositionSorter ());
4264 /* Go through all selected points, making an AutomationRecord for each distinct AutomationList */
4265 for (PointSelection::iterator sel_point = selection->points.begin(); sel_point != selection->points.end(); ++sel_point) {
4266 const AutomationLine& line = (*sel_point)->line();
4267 const boost::shared_ptr<AutomationList> al = line.the_list();
4268 if (lists.find (al) == lists.end ()) {
4269 /* We haven't seen this list yet, so make a record for it. This includes
4270 taking a copy of its current state, in case this is needed for undo later.
4272 lists[al] = AutomationRecord (&al->get_state (), &line);
4276 if (op == Cut || op == Copy) {
4277 /* This operation will involve putting things in the cut buffer, so create an empty
4278 ControlList for each of our source lists to put the cut buffer data in.
4280 for (Lists::iterator i = lists.begin(); i != lists.end(); ++i) {
4281 i->second.copy = i->first->create (i->first->parameter (), i->first->descriptor());
4284 /* Add all selected points to the relevant copy ControlLists */
4285 MusicFrame start (std::numeric_limits<framepos_t>::max(), 0);
4286 for (PointSelection::iterator sel_point = selection->points.begin(); sel_point != selection->points.end(); ++sel_point) {
4287 boost::shared_ptr<AutomationList> al = (*sel_point)->line().the_list();
4288 AutomationList::const_iterator ctrl_evt = (*sel_point)->model ();
4290 lists[al].copy->fast_simple_add ((*ctrl_evt)->when, (*ctrl_evt)->value);
4292 /* Update earliest MIDI start time in beats */
4293 earliest = std::min(earliest, Evoral::Beats((*ctrl_evt)->when));
4295 /* Update earliest session start time in frames */
4296 start.frame = std::min(start.frame, (*sel_point)->line().session_position(ctrl_evt));
4300 /* Snap start time backwards, so copy/paste is snap aligned. */
4302 if (earliest == Evoral::Beats::max()) {
4303 earliest = Evoral::Beats(); // Weird... don't offset
4305 earliest.round_down_to_beat();
4307 if (start.frame == std::numeric_limits<double>::max()) {
4308 start.frame = 0; // Weird... don't offset
4310 snap_to(start, RoundDownMaybe);
4313 const double line_offset = midi ? earliest.to_double() : start.frame;
4314 for (Lists::iterator i = lists.begin(); i != lists.end(); ++i) {
4315 /* Correct this copy list so that it is relative to the earliest
4316 start time, so relative ordering between points is preserved
4317 when copying from several lists and the paste starts at the
4318 earliest copied piece of data. */
4319 boost::shared_ptr<Evoral::ControlList> &al_cpy = i->second.copy;
4320 for (AutomationList::iterator ctrl_evt = al_cpy->begin(); ctrl_evt != al_cpy->end(); ++ctrl_evt) {
4321 (*ctrl_evt)->when -= line_offset;
4324 /* And add it to the cut buffer */
4325 cut_buffer->add (al_cpy);
4329 if (op == Delete || op == Cut) {
4330 /* This operation needs to remove things from the main AutomationList, so do that now */
4332 for (Lists::iterator i = lists.begin(); i != lists.end(); ++i) {
4333 i->first->freeze ();
4336 /* Remove each selected point from its AutomationList */
4337 for (PointSelection::iterator sel_point = selection->points.begin(); sel_point != selection->points.end(); ++sel_point) {
4338 AutomationLine& line = (*sel_point)->line ();
4339 boost::shared_ptr<AutomationList> al = line.the_list();
4343 if (dynamic_cast<AudioRegionGainLine*> (&line)) {
4344 /* removing of first and last gain point in region gain lines is prohibited*/
4345 if (line.is_last_point (*(*sel_point)) || line.is_first_point (*(*sel_point))) {
4351 al->erase ((*sel_point)->model ());
4355 /* Thaw the lists and add undo records for them */
4356 for (Lists::iterator i = lists.begin(); i != lists.end(); ++i) {
4357 boost::shared_ptr<AutomationList> al = i->first;
4359 _session->add_command (new MementoCommand<AutomationList> (*al.get(), i->second.state, &(al->get_state ())));
4364 /** Cut, copy or clear selected automation points.
4365 * @param op Operation (Cut, Copy or Clear)
4368 Editor::cut_copy_midi (CutCopyOp op)
4370 Evoral::Beats earliest = Evoral::Beats::max();
4371 for (MidiRegionSelection::iterator i = selection->midi_regions.begin(); i != selection->midi_regions.end(); ++i) {
4372 MidiRegionView* mrv = dynamic_cast<MidiRegionView*>(*i);
4374 if (!mrv->selection().empty()) {
4375 earliest = std::min(earliest, (*mrv->selection().begin())->note()->time());
4377 mrv->cut_copy_clear (op);
4379 /* XXX: not ideal, as there may be more than one track involved in the selection */
4380 _last_cut_copy_source_track = &mrv->get_time_axis_view();
4384 if (!selection->points.empty()) {
4385 cut_copy_points (op, earliest, true);
4386 if (op == Cut || op == Delete) {
4387 selection->clear_points ();
4392 struct lt_playlist {
4393 bool operator () (const PlaylistState& a, const PlaylistState& b) {
4394 return a.playlist < b.playlist;
4398 struct PlaylistMapping {
4400 boost::shared_ptr<Playlist> pl;
4402 PlaylistMapping (TimeAxisView* tvp) : tv (tvp) {}
4405 /** Remove `clicked_regionview' */
4407 Editor::remove_clicked_region ()
4409 if (clicked_routeview == 0 || clicked_regionview == 0) {
4413 begin_reversible_command (_("remove region"));
4415 boost::shared_ptr<Playlist> playlist = clicked_routeview->playlist();
4417 playlist->clear_changes ();
4418 playlist->clear_owned_changes ();
4419 playlist->remove_region (clicked_regionview->region());
4420 if (Config->get_edit_mode() == Ripple)
4421 playlist->ripple (clicked_regionview->region()->position(), -clicked_regionview->region()->length(), boost::shared_ptr<Region>());
4423 /* We might have removed regions, which alters other regions' layering_index,
4424 so we need to do a recursive diff here.
4426 vector<Command*> cmds;
4427 playlist->rdiff (cmds);
4428 _session->add_commands (cmds);
4430 _session->add_command(new StatefulDiffCommand (playlist));
4431 commit_reversible_command ();
4435 /** Remove the selected regions */
4437 Editor::remove_selected_regions ()
4439 RegionSelection rs = get_regions_from_selection_and_entered ();
4441 if (!_session || rs.empty()) {
4445 list<boost::shared_ptr<Region> > regions_to_remove;
4447 for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
4448 // we can't just remove the region(s) in this loop because
4449 // this removes them from the RegionSelection, and they thus
4450 // disappear from underneath the iterator, and the ++i above
4451 // SEGVs in a puzzling fashion.
4453 // so, first iterate over the regions to be removed from rs and
4454 // add them to the regions_to_remove list, and then
4455 // iterate over the list to actually remove them.
4457 regions_to_remove.push_back ((*i)->region());
4460 vector<boost::shared_ptr<Playlist> > playlists;
4462 for (list<boost::shared_ptr<Region> >::iterator rl = regions_to_remove.begin(); rl != regions_to_remove.end(); ++rl) {
4464 boost::shared_ptr<Playlist> playlist = (*rl)->playlist();
4467 // is this check necessary?
4471 /* get_regions_from_selection_and_entered() guarantees that
4472 the playlists involved are unique, so there is no need
4476 playlists.push_back (playlist);
4478 playlist->clear_changes ();
4479 playlist->clear_owned_changes ();
4480 playlist->freeze ();
4481 playlist->remove_region (*rl);
4482 if (Config->get_edit_mode() == Ripple)
4483 playlist->ripple ((*rl)->position(), -(*rl)->length(), boost::shared_ptr<Region>());
4487 vector<boost::shared_ptr<Playlist> >::iterator pl;
4488 bool in_command = false;
4490 for (pl = playlists.begin(); pl != playlists.end(); ++pl) {
4493 /* We might have removed regions, which alters other regions' layering_index,
4494 so we need to do a recursive diff here.
4498 begin_reversible_command (_("remove region"));
4501 vector<Command*> cmds;
4502 (*pl)->rdiff (cmds);
4503 _session->add_commands (cmds);
4505 _session->add_command(new StatefulDiffCommand (*pl));
4509 commit_reversible_command ();
4513 /** Cut, copy or clear selected regions.
4514 * @param op Operation (Cut, Copy or Clear)
4517 Editor::cut_copy_regions (CutCopyOp op, RegionSelection& rs)
4519 /* we can't use a std::map here because the ordering is important, and we can't trivially sort
4520 a map when we want ordered access to both elements. i think.
4523 vector<PlaylistMapping> pmap;
4525 framepos_t first_position = max_framepos;
4527 typedef set<boost::shared_ptr<Playlist> > FreezeList;
4528 FreezeList freezelist;
4530 /* get ordering correct before we cut/copy */
4532 rs.sort_by_position_and_track ();
4534 for (RegionSelection::iterator x = rs.begin(); x != rs.end(); ++x) {
4536 first_position = min ((framepos_t) (*x)->region()->position(), first_position);
4538 if (op == Cut || op == Clear || op == Delete) {
4539 boost::shared_ptr<Playlist> pl = (*x)->region()->playlist();
4542 FreezeList::iterator fl;
4544 // only take state if this is a new playlist.
4545 for (fl = freezelist.begin(); fl != freezelist.end(); ++fl) {
4551 if (fl == freezelist.end()) {
4552 pl->clear_changes();
4553 pl->clear_owned_changes ();
4555 freezelist.insert (pl);
4560 TimeAxisView* tv = &(*x)->get_time_axis_view();
4561 vector<PlaylistMapping>::iterator z;
4563 for (z = pmap.begin(); z != pmap.end(); ++z) {
4564 if ((*z).tv == tv) {
4569 if (z == pmap.end()) {
4570 pmap.push_back (PlaylistMapping (tv));
4574 for (RegionSelection::iterator x = rs.begin(); x != rs.end(); ) {
4576 boost::shared_ptr<Playlist> pl = (*x)->region()->playlist();
4579 /* region not yet associated with a playlist (e.g. unfinished
4586 TimeAxisView& tv = (*x)->get_time_axis_view();
4587 boost::shared_ptr<Playlist> npl;
4588 RegionSelection::iterator tmp;
4595 vector<PlaylistMapping>::iterator z;
4597 for (z = pmap.begin(); z != pmap.end(); ++z) {
4598 if ((*z).tv == &tv) {
4603 assert (z != pmap.end());
4606 npl = PlaylistFactory::create (pl->data_type(), *_session, "cutlist", true);
4614 boost::shared_ptr<Region> r = (*x)->region();
4615 boost::shared_ptr<Region> _xx;
4621 pl->remove_region (r);
4622 if (Config->get_edit_mode() == Ripple)
4623 pl->ripple (r->position(), -r->length(), boost::shared_ptr<Region>());
4627 _xx = RegionFactory::create (r);
4628 npl->add_region (_xx, r->position() - first_position);
4629 pl->remove_region (r);
4630 if (Config->get_edit_mode() == Ripple)
4631 pl->ripple (r->position(), -r->length(), boost::shared_ptr<Region>());
4635 /* copy region before adding, so we're not putting same object into two different playlists */
4636 npl->add_region (RegionFactory::create (r), r->position() - first_position);
4640 pl->remove_region (r);
4641 if (Config->get_edit_mode() == Ripple)
4642 pl->ripple (r->position(), -r->length(), boost::shared_ptr<Region>());
4651 list<boost::shared_ptr<Playlist> > foo;
4653 /* the pmap is in the same order as the tracks in which selected regions occurred */
4655 for (vector<PlaylistMapping>::iterator i = pmap.begin(); i != pmap.end(); ++i) {
4658 foo.push_back ((*i).pl);
4663 cut_buffer->set (foo);
4667 _last_cut_copy_source_track = 0;
4669 _last_cut_copy_source_track = pmap.front().tv;
4673 for (FreezeList::iterator pl = freezelist.begin(); pl != freezelist.end(); ++pl) {
4676 /* We might have removed regions, which alters other regions' layering_index,
4677 so we need to do a recursive diff here.
4679 vector<Command*> cmds;
4680 (*pl)->rdiff (cmds);
4681 _session->add_commands (cmds);
4683 _session->add_command (new StatefulDiffCommand (*pl));
4688 Editor::cut_copy_ranges (CutCopyOp op)
4690 TrackViewList ts = selection->tracks.filter_to_unique_playlists ();
4692 /* Sort the track selection now, so that it if is used, the playlists
4693 selected by the calls below to cut_copy_clear are in the order that
4694 their tracks appear in the editor. This makes things like paste
4695 of ranges work properly.
4698 sort_track_selection (ts);
4701 if (!entered_track) {
4704 ts.push_back (entered_track);
4707 for (TrackViewList::iterator i = ts.begin(); i != ts.end(); ++i) {
4708 (*i)->cut_copy_clear (*selection, op);
4713 Editor::paste (float times, bool from_context)
4715 DEBUG_TRACE (DEBUG::CutNPaste, "paste to preferred edit pos\n");
4716 MusicFrame where (get_preferred_edit_position (EDIT_IGNORE_NONE, from_context), 0);
4717 paste_internal (where.frame, times, 0);
4721 Editor::mouse_paste ()
4723 MusicFrame where (0, 0);
4725 if (!mouse_frame (where.frame, ignored)) {
4730 paste_internal (where.frame, 1, where.division);
4734 Editor::paste_internal (framepos_t position, float times, const int32_t sub_num)
4736 DEBUG_TRACE (DEBUG::CutNPaste, string_compose ("apparent paste position is %1\n", position));
4738 if (cut_buffer->empty(internal_editing())) {
4742 if (position == max_framepos) {
4743 position = get_preferred_edit_position();
4744 DEBUG_TRACE (DEBUG::CutNPaste, string_compose ("preferred edit position is %1\n", position));
4747 if (position == last_paste_pos) {
4748 /* repeated paste in the same position */
4751 /* paste in new location, reset repeated paste state */
4753 last_paste_pos = position;
4756 /* get everything in the correct order */
4759 if (!selection->tracks.empty()) {
4760 /* If there is a track selection, paste into exactly those tracks and
4761 * only those tracks. This allows the user to be explicit and override
4762 * the below "do the reasonable thing" logic. */
4763 ts = selection->tracks.filter_to_unique_playlists ();
4764 sort_track_selection (ts);
4766 /* Figure out which track to base the paste at. */
4767 TimeAxisView* base_track = NULL;
4768 if (_edit_point == Editing::EditAtMouse && entered_track) {
4769 /* With the mouse edit point, paste onto the track under the mouse. */
4770 base_track = entered_track;
4771 } else if (_edit_point == Editing::EditAtMouse && entered_regionview) {
4772 /* With the mouse edit point, paste onto the track of the region under the mouse. */
4773 base_track = &entered_regionview->get_time_axis_view();
4774 } else if (_last_cut_copy_source_track) {
4775 /* Paste to the track that the cut/copy came from (see mantis #333). */
4776 base_track = _last_cut_copy_source_track;
4778 /* This is "impossible" since we've copied... well, do nothing. */
4782 /* Walk up to parent if necessary, so base track is a route. */
4783 while (base_track->get_parent()) {
4784 base_track = base_track->get_parent();
4787 /* Add base track and all tracks below it. The paste logic will select
4788 the appropriate object types from the cut buffer in relative order. */
4789 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
4790 if ((*i)->order() >= base_track->order()) {
4795 /* Sort tracks so the nth track of type T will pick the nth object of type T. */
4796 sort_track_selection (ts);
4798 /* Add automation children of each track in order, for pasting several lines. */
4799 for (TrackViewList::iterator i = ts.begin(); i != ts.end();) {
4800 /* Add any automation children for pasting several lines */
4801 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*>(*i++);
4806 typedef RouteTimeAxisView::AutomationTracks ATracks;
4807 const ATracks& atracks = rtv->automation_tracks();
4808 for (ATracks::const_iterator a = atracks.begin(); a != atracks.end(); ++a) {
4809 i = ts.insert(i, a->second.get());
4814 /* We now have a list of trackviews starting at base_track, including
4815 automation children, in the order shown in the editor, e.g. R1,
4816 R1.A1, R1.A2, R2, R2.A1, ... */
4819 begin_reversible_command (Operations::paste);
4821 if (ts.size() == 1 && cut_buffer->lines.size() == 1 &&
4822 dynamic_cast<AutomationTimeAxisView*>(ts.front())) {
4823 /* Only one line copied, and one automation track selected. Do a
4824 "greedy" paste from one automation type to another. */
4826 PasteContext ctx(paste_count, times, ItemCounts(), true);
4827 ts.front()->paste (position, *cut_buffer, ctx, sub_num);
4831 /* Paste into tracks */
4833 PasteContext ctx(paste_count, times, ItemCounts(), false);
4834 for (TrackViewList::iterator i = ts.begin(); i != ts.end(); ++i) {
4835 (*i)->paste (position, *cut_buffer, ctx, sub_num);
4839 commit_reversible_command ();
4843 Editor::duplicate_regions (float times)
4845 RegionSelection rs (get_regions_from_selection_and_entered());
4846 duplicate_some_regions (rs, times);
4850 Editor::duplicate_some_regions (RegionSelection& regions, float times)
4852 if (regions.empty ()) {
4856 boost::shared_ptr<Playlist> playlist;
4857 RegionSelection sel = regions; // clear (below) may clear the argument list if its the current region selection
4858 RegionSelection foo;
4860 framepos_t const start_frame = regions.start ();
4861 framepos_t const end_frame = regions.end_frame ();
4862 framecnt_t const gap = end_frame - start_frame + 1;
4864 begin_reversible_command (Operations::duplicate_region);
4866 selection->clear_regions ();
4868 for (RegionSelection::iterator i = sel.begin(); i != sel.end(); ++i) {
4870 boost::shared_ptr<Region> r ((*i)->region());
4872 TimeAxisView& tv = (*i)->get_time_axis_view();
4873 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (&tv);
4874 latest_regionviews.clear ();
4875 sigc::connection c = rtv->view()->RegionViewAdded.connect (sigc::mem_fun(*this, &Editor::collect_new_region_view));
4877 framepos_t const position = end_frame + (r->first_frame() - start_frame + 1);
4878 playlist = (*i)->region()->playlist();
4879 playlist->clear_changes ();
4880 playlist->duplicate (r, position, gap, times);
4881 _session->add_command(new StatefulDiffCommand (playlist));
4885 foo.insert (foo.end(), latest_regionviews.begin(), latest_regionviews.end());
4889 selection->set (foo);
4892 commit_reversible_command ();
4896 Editor::duplicate_selection (float times)
4898 if (selection->time.empty() || selection->tracks.empty()) {
4902 boost::shared_ptr<Playlist> playlist;
4904 TrackViewList ts = selection->tracks.filter_to_unique_playlists ();
4906 bool in_command = false;
4908 for (TrackViewList::iterator i = ts.begin(); i != ts.end(); ++i) {
4909 if ((playlist = (*i)->playlist()) == 0) {
4912 playlist->clear_changes ();
4914 if (clicked_selection) {
4915 playlist->duplicate_range (selection->time[clicked_selection], times);
4917 playlist->duplicate_ranges (selection->time, times);
4921 begin_reversible_command (_("duplicate range selection"));
4924 _session->add_command (new StatefulDiffCommand (playlist));
4929 if (times == 1.0f) {
4930 // now "move" range selection to after the current range selection
4931 framecnt_t distance = 0;
4933 if (clicked_selection) {
4935 selection->time[clicked_selection].end - selection->time[clicked_selection].start;
4937 distance = selection->time.end_frame () - selection->time.start ();
4940 selection->move_time (distance);
4942 commit_reversible_command ();
4946 /** Reset all selected points to the relevant default value */
4948 Editor::reset_point_selection ()
4950 for (PointSelection::iterator i = selection->points.begin(); i != selection->points.end(); ++i) {
4951 ARDOUR::AutomationList::iterator j = (*i)->model ();
4952 (*j)->value = (*i)->line().the_list()->descriptor ().normal;
4957 Editor::center_playhead ()
4959 float const page = _visible_canvas_width * samples_per_pixel;
4960 center_screen_internal (playhead_cursor->current_frame (), page);
4964 Editor::center_edit_point ()
4966 float const page = _visible_canvas_width * samples_per_pixel;
4967 center_screen_internal (get_preferred_edit_position(), page);
4970 /** Caller must begin and commit a reversible command */
4972 Editor::clear_playlist (boost::shared_ptr<Playlist> playlist)
4974 playlist->clear_changes ();
4976 _session->add_command (new StatefulDiffCommand (playlist));
4980 Editor::nudge_track (bool use_edit, bool forwards)
4982 boost::shared_ptr<Playlist> playlist;
4983 framepos_t distance;
4984 framepos_t next_distance;
4988 start = get_preferred_edit_position();
4993 if ((distance = get_nudge_distance (start, next_distance)) == 0) {
4997 if (selection->tracks.empty()) {
5001 TrackViewList ts = selection->tracks.filter_to_unique_playlists ();
5002 bool in_command = false;
5004 for (TrackViewList::iterator i = ts.begin(); i != ts.end(); ++i) {
5006 if ((playlist = (*i)->playlist()) == 0) {
5010 playlist->clear_changes ();
5011 playlist->clear_owned_changes ();
5013 playlist->nudge_after (start, distance, forwards);
5016 begin_reversible_command (_("nudge track"));
5019 vector<Command*> cmds;
5021 playlist->rdiff (cmds);
5022 _session->add_commands (cmds);
5024 _session->add_command (new StatefulDiffCommand (playlist));
5028 commit_reversible_command ();
5033 Editor::remove_last_capture ()
5035 vector<string> choices;
5042 if (Config->get_verify_remove_last_capture()) {
5043 prompt = _("Do you really want to destroy the last capture?"
5044 "\n(This is destructive and cannot be undone)");
5046 choices.push_back (_("No, do nothing."));
5047 choices.push_back (_("Yes, destroy it."));
5049 Gtkmm2ext::Choice prompter (_("Destroy last capture"), prompt, choices);
5051 if (prompter.run () == 1) {
5052 _session->remove_last_capture ();
5053 _regions->redisplay ();
5057 _session->remove_last_capture();
5058 _regions->redisplay ();
5063 Editor::normalize_region ()
5069 RegionSelection rs = get_regions_from_selection_and_entered ();
5075 NormalizeDialog dialog (rs.size() > 1);
5077 if (dialog.run () != RESPONSE_ACCEPT) {
5081 CursorContext::Handle cursor_ctx = CursorContext::create(*this, _cursors->wait);
5084 /* XXX: should really only count audio regions here */
5085 int const regions = rs.size ();
5087 /* Make a list of the selected audio regions' maximum amplitudes, and also
5088 obtain the maximum amplitude of them all.
5090 list<double> max_amps;
5091 list<double> rms_vals;
5094 bool use_rms = dialog.constrain_rms ();
5096 for (RegionSelection::const_iterator i = rs.begin(); i != rs.end(); ++i) {
5097 AudioRegionView const * arv = dynamic_cast<AudioRegionView const *> (*i);
5101 dialog.descend (1.0 / regions);
5102 double const a = arv->audio_region()->maximum_amplitude (&dialog);
5104 double r = arv->audio_region()->rms (&dialog);
5105 max_rms = max (max_rms, r);
5106 rms_vals.push_back (r);
5110 /* the user cancelled the operation */
5114 max_amps.push_back (a);
5115 max_amp = max (max_amp, a);
5119 list<double>::const_iterator a = max_amps.begin ();
5120 list<double>::const_iterator l = rms_vals.begin ();
5121 bool in_command = false;
5123 for (RegionSelection::iterator r = rs.begin(); r != rs.end(); ++r) {
5124 AudioRegionView* const arv = dynamic_cast<AudioRegionView*> (*r);
5129 arv->region()->clear_changes ();
5131 double amp = dialog.normalize_individually() ? *a : max_amp;
5132 double target = dialog.target_peak (); // dB
5135 double const amp_rms = dialog.normalize_individually() ? *l : max_rms;
5136 const double t_rms = dialog.target_rms ();
5137 const gain_t c_peak = dB_to_coefficient (target);
5138 const gain_t c_rms = dB_to_coefficient (t_rms);
5139 if ((amp_rms / c_rms) > (amp / c_peak)) {
5145 arv->audio_region()->normalize (amp, target);
5148 begin_reversible_command (_("normalize"));
5151 _session->add_command (new StatefulDiffCommand (arv->region()));
5158 commit_reversible_command ();
5164 Editor::reset_region_scale_amplitude ()
5170 RegionSelection rs = get_regions_from_selection_and_entered ();
5176 bool in_command = false;
5178 for (RegionSelection::iterator r = rs.begin(); r != rs.end(); ++r) {
5179 AudioRegionView* const arv = dynamic_cast<AudioRegionView*>(*r);
5182 arv->region()->clear_changes ();
5183 arv->audio_region()->set_scale_amplitude (1.0f);
5186 begin_reversible_command ("reset gain");
5189 _session->add_command (new StatefulDiffCommand (arv->region()));
5193 commit_reversible_command ();
5198 Editor::adjust_region_gain (bool up)
5200 RegionSelection rs = get_regions_from_selection_and_entered ();
5202 if (!_session || rs.empty()) {
5206 bool in_command = false;
5208 for (RegionSelection::iterator r = rs.begin(); r != rs.end(); ++r) {
5209 AudioRegionView* const arv = dynamic_cast<AudioRegionView*>(*r);
5214 arv->region()->clear_changes ();
5216 double dB = accurate_coefficient_to_dB (arv->audio_region()->scale_amplitude ());
5224 arv->audio_region()->set_scale_amplitude (dB_to_coefficient (dB));
5227 begin_reversible_command ("adjust region gain");
5230 _session->add_command (new StatefulDiffCommand (arv->region()));
5234 commit_reversible_command ();
5239 Editor::reset_region_gain ()
5241 RegionSelection rs = get_regions_from_selection_and_entered ();
5243 if (!_session || rs.empty()) {
5247 bool in_command = false;
5249 for (RegionSelection::iterator r = rs.begin(); r != rs.end(); ++r) {
5250 AudioRegionView* const arv = dynamic_cast<AudioRegionView*>(*r);
5255 arv->region()->clear_changes ();
5257 arv->audio_region()->set_scale_amplitude (1.0f);
5260 begin_reversible_command ("reset region gain");
5263 _session->add_command (new StatefulDiffCommand (arv->region()));
5267 commit_reversible_command ();
5272 Editor::reverse_region ()
5278 Reverse rev (*_session);
5279 apply_filter (rev, _("reverse regions"));
5283 Editor::strip_region_silence ()
5289 RegionSelection rs = get_regions_from_selection_and_entered ();
5295 std::list<RegionView*> audio_only;
5297 for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
5298 AudioRegionView* const arv = dynamic_cast<AudioRegionView*> (*i);
5300 audio_only.push_back (arv);
5304 assert (!audio_only.empty());
5306 StripSilenceDialog d (_session, audio_only);
5307 int const r = d.run ();
5311 if (r == Gtk::RESPONSE_OK) {
5312 ARDOUR::AudioIntervalMap silences;
5313 d.silences (silences);
5314 StripSilence s (*_session, silences, d.fade_length());
5316 apply_filter (s, _("strip silence"), &d);
5321 Editor::apply_midi_note_edit_op_to_region (MidiOperator& op, MidiRegionView& mrv)
5323 Evoral::Sequence<Evoral::Beats>::Notes selected;
5324 mrv.selection_as_notelist (selected, true);
5326 vector<Evoral::Sequence<Evoral::Beats>::Notes> v;
5327 v.push_back (selected);
5329 Evoral::Beats pos_beats = Evoral::Beats (mrv.midi_region()->beat()) - mrv.midi_region()->start_beats();
5331 return op (mrv.midi_region()->model(), pos_beats, v);
5335 Editor::apply_midi_note_edit_op (MidiOperator& op, const RegionSelection& rs)
5341 bool in_command = false;
5343 for (RegionSelection::const_iterator r = rs.begin(); r != rs.end(); ) {
5344 RegionSelection::const_iterator tmp = r;
5347 MidiRegionView* const mrv = dynamic_cast<MidiRegionView*> (*r);
5350 Command* cmd = apply_midi_note_edit_op_to_region (op, *mrv);
5353 begin_reversible_command (op.name ());
5357 _session->add_command (cmd);
5365 commit_reversible_command ();
5370 Editor::fork_region ()
5372 RegionSelection rs = get_regions_from_selection_and_entered ();
5378 CursorContext::Handle cursor_ctx = CursorContext::create(*this, _cursors->wait);
5379 bool in_command = false;
5383 for (RegionSelection::iterator r = rs.begin(); r != rs.end(); ) {
5384 RegionSelection::iterator tmp = r;
5387 MidiRegionView* const mrv = dynamic_cast<MidiRegionView*>(*r);
5391 boost::shared_ptr<Playlist> playlist = mrv->region()->playlist();
5392 boost::shared_ptr<MidiSource> new_source = _session->create_midi_source_by_stealing_name (mrv->midi_view()->track());
5393 boost::shared_ptr<MidiRegion> newregion = mrv->midi_region()->clone (new_source);
5396 begin_reversible_command (_("Fork Region(s)"));
5399 playlist->clear_changes ();
5400 playlist->replace_region (mrv->region(), newregion, mrv->region()->position());
5401 _session->add_command(new StatefulDiffCommand (playlist));
5403 error << string_compose (_("Could not unlink %1"), mrv->region()->name()) << endmsg;
5411 commit_reversible_command ();
5416 Editor::quantize_region ()
5419 quantize_regions(get_regions_from_selection_and_entered ());
5424 Editor::quantize_regions (const RegionSelection& rs)
5426 if (rs.n_midi_regions() == 0) {
5430 if (!quantize_dialog) {
5431 quantize_dialog = new QuantizeDialog (*this);
5434 if (quantize_dialog->is_mapped()) {
5435 /* in progress already */
5439 quantize_dialog->present ();
5440 const int r = quantize_dialog->run ();
5441 quantize_dialog->hide ();
5443 if (r == Gtk::RESPONSE_OK) {
5444 Quantize quant (quantize_dialog->snap_start(),
5445 quantize_dialog->snap_end(),
5446 quantize_dialog->start_grid_size(),
5447 quantize_dialog->end_grid_size(),
5448 quantize_dialog->strength(),
5449 quantize_dialog->swing(),
5450 quantize_dialog->threshold());
5452 apply_midi_note_edit_op (quant, rs);
5457 Editor::legatize_region (bool shrink_only)
5460 legatize_regions(get_regions_from_selection_and_entered (), shrink_only);
5465 Editor::legatize_regions (const RegionSelection& rs, bool shrink_only)
5467 if (rs.n_midi_regions() == 0) {
5471 Legatize legatize(shrink_only);
5472 apply_midi_note_edit_op (legatize, rs);
5476 Editor::transform_region ()
5479 transform_regions(get_regions_from_selection_and_entered ());
5484 Editor::transform_regions (const RegionSelection& rs)
5486 if (rs.n_midi_regions() == 0) {
5493 const int r = td.run();
5496 if (r == Gtk::RESPONSE_OK) {
5497 Transform transform(td.get());
5498 apply_midi_note_edit_op(transform, rs);
5503 Editor::transpose_region ()
5506 transpose_regions(get_regions_from_selection_and_entered ());
5511 Editor::transpose_regions (const RegionSelection& rs)
5513 if (rs.n_midi_regions() == 0) {
5518 int const r = d.run ();
5520 if (r == RESPONSE_ACCEPT) {
5521 Transpose transpose(d.semitones ());
5522 apply_midi_note_edit_op (transpose, rs);
5527 Editor::insert_patch_change (bool from_context)
5529 RegionSelection rs = get_regions_from_selection_and_entered ();
5535 const framepos_t p = get_preferred_edit_position (EDIT_IGNORE_NONE, from_context);
5537 /* XXX: bit of a hack; use the MIDNAM from the first selected region;
5538 there may be more than one, but the PatchChangeDialog can only offer
5539 one set of patch menus.
5541 MidiRegionView* first = dynamic_cast<MidiRegionView*> (rs.front ());
5543 Evoral::PatchChange<Evoral::Beats> empty (Evoral::Beats(), 0, 0, 0);
5544 PatchChangeDialog d (0, _session, empty, first->instrument_info(), Gtk::Stock::ADD);
5546 if (d.run() == RESPONSE_CANCEL) {
5550 for (RegionSelection::iterator i = rs.begin (); i != rs.end(); ++i) {
5551 MidiRegionView* const mrv = dynamic_cast<MidiRegionView*> (*i);
5553 if (p >= mrv->region()->first_frame() && p <= mrv->region()->last_frame()) {
5554 mrv->add_patch_change (p - mrv->region()->position(), d.patch ());
5561 Editor::apply_filter (Filter& filter, string command, ProgressReporter* progress)
5563 RegionSelection rs = get_regions_from_selection_and_entered ();
5569 CursorContext::Handle cursor_ctx = CursorContext::create(*this, _cursors->wait);
5570 bool in_command = false;
5575 int const N = rs.size ();
5577 for (RegionSelection::iterator r = rs.begin(); r != rs.end(); ) {
5578 RegionSelection::iterator tmp = r;
5581 AudioRegionView* const arv = dynamic_cast<AudioRegionView*>(*r);
5583 boost::shared_ptr<Playlist> playlist = arv->region()->playlist();
5586 progress->descend (1.0 / N);
5589 if (arv->audio_region()->apply (filter, progress) == 0) {
5591 playlist->clear_changes ();
5592 playlist->clear_owned_changes ();
5595 begin_reversible_command (command);
5599 if (filter.results.empty ()) {
5601 /* no regions returned; remove the old one */
5602 playlist->remove_region (arv->region ());
5606 std::vector<boost::shared_ptr<Region> >::iterator res = filter.results.begin ();
5608 /* first region replaces the old one */
5609 playlist->replace_region (arv->region(), *res, (*res)->position());
5613 while (res != filter.results.end()) {
5614 playlist->add_region (*res, (*res)->position());
5620 /* We might have removed regions, which alters other regions' layering_index,
5621 so we need to do a recursive diff here.
5623 vector<Command*> cmds;
5624 playlist->rdiff (cmds);
5625 _session->add_commands (cmds);
5627 _session->add_command(new StatefulDiffCommand (playlist));
5631 progress->ascend ();
5640 commit_reversible_command ();
5645 Editor::external_edit_region ()
5651 Editor::reset_region_gain_envelopes ()
5653 RegionSelection rs = get_regions_from_selection_and_entered ();
5655 if (!_session || rs.empty()) {
5659 bool in_command = false;
5661 for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
5662 AudioRegionView* const arv = dynamic_cast<AudioRegionView*>(*i);
5664 boost::shared_ptr<AutomationList> alist (arv->audio_region()->envelope());
5665 XMLNode& before (alist->get_state());
5667 arv->audio_region()->set_default_envelope ();
5670 begin_reversible_command (_("reset region gain"));
5673 _session->add_command (new MementoCommand<AutomationList>(*arv->audio_region()->envelope().get(), &before, &alist->get_state()));
5678 commit_reversible_command ();
5683 Editor::set_region_gain_visibility (RegionView* rv)
5685 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (rv);
5687 arv->update_envelope_visibility();
5692 Editor::set_gain_envelope_visibility ()
5698 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
5699 AudioTimeAxisView* v = dynamic_cast<AudioTimeAxisView*>(*i);
5701 v->audio_view()->foreach_regionview (sigc::mem_fun (this, &Editor::set_region_gain_visibility));
5707 Editor::toggle_gain_envelope_active ()
5709 if (_ignore_region_action) {
5713 RegionSelection rs = get_regions_from_selection_and_entered ();
5715 if (!_session || rs.empty()) {
5719 bool in_command = false;
5721 for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
5722 AudioRegionView* const arv = dynamic_cast<AudioRegionView*>(*i);
5724 arv->region()->clear_changes ();
5725 arv->audio_region()->set_envelope_active (!arv->audio_region()->envelope_active());
5728 begin_reversible_command (_("region gain envelope active"));
5731 _session->add_command (new StatefulDiffCommand (arv->region()));
5736 commit_reversible_command ();
5741 Editor::toggle_region_lock ()
5743 if (_ignore_region_action) {
5747 RegionSelection rs = get_regions_from_selection_and_entered ();
5749 if (!_session || rs.empty()) {
5753 begin_reversible_command (_("toggle region lock"));
5755 for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
5756 (*i)->region()->clear_changes ();
5757 (*i)->region()->set_locked (!(*i)->region()->locked());
5758 _session->add_command (new StatefulDiffCommand ((*i)->region()));
5761 commit_reversible_command ();
5765 Editor::toggle_region_video_lock ()
5767 if (_ignore_region_action) {
5771 RegionSelection rs = get_regions_from_selection_and_entered ();
5773 if (!_session || rs.empty()) {
5777 begin_reversible_command (_("Toggle Video Lock"));
5779 for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
5780 (*i)->region()->clear_changes ();
5781 (*i)->region()->set_video_locked (!(*i)->region()->video_locked());
5782 _session->add_command (new StatefulDiffCommand ((*i)->region()));
5785 commit_reversible_command ();
5789 Editor::toggle_region_lock_style ()
5791 if (_ignore_region_action) {
5795 RegionSelection rs = get_regions_from_selection_and_entered ();
5797 if (!_session || rs.empty()) {
5801 Glib::RefPtr<ToggleAction> a = Glib::RefPtr<ToggleAction>::cast_dynamic (_region_actions->get_action("toggle-region-lock-style"));
5802 vector<Widget*> proxies = a->get_proxies();
5803 Gtk::CheckMenuItem* cmi = dynamic_cast<Gtk::CheckMenuItem*> (proxies.front());
5807 begin_reversible_command (_("toggle region lock style"));
5809 for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
5810 (*i)->region()->clear_changes ();
5811 PositionLockStyle const ns = ((*i)->region()->position_lock_style() == AudioTime && !cmi->get_inconsistent()) ? MusicTime : AudioTime;
5812 (*i)->region()->set_position_lock_style (ns);
5813 _session->add_command (new StatefulDiffCommand ((*i)->region()));
5816 commit_reversible_command ();
5820 Editor::toggle_opaque_region ()
5822 if (_ignore_region_action) {
5826 RegionSelection rs = get_regions_from_selection_and_entered ();
5828 if (!_session || rs.empty()) {
5832 begin_reversible_command (_("change region opacity"));
5834 for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
5835 (*i)->region()->clear_changes ();
5836 (*i)->region()->set_opaque (!(*i)->region()->opaque());
5837 _session->add_command (new StatefulDiffCommand ((*i)->region()));
5840 commit_reversible_command ();
5844 Editor::toggle_record_enable ()
5846 bool new_state = false;
5848 for (TrackSelection::iterator i = selection->tracks.begin(); i != selection->tracks.end(); ++i) {
5849 RouteTimeAxisView *rtav = dynamic_cast<RouteTimeAxisView *>(*i);
5852 if (!rtav->is_track())
5856 new_state = !rtav->track()->rec_enable_control()->get_value();
5860 rtav->track()->rec_enable_control()->set_value (new_state, Controllable::UseGroup);
5865 Editor::toggle_solo ()
5867 bool new_state = false;
5869 boost::shared_ptr<ControlList> cl (new ControlList);
5871 for (TrackSelection::iterator i = selection->tracks.begin(); i != selection->tracks.end(); ++i) {
5872 RouteTimeAxisView *rtav = dynamic_cast<RouteTimeAxisView *>(*i);
5879 new_state = !rtav->route()->soloed ();
5883 cl->push_back (rtav->route()->solo_control());
5886 _session->set_controls (cl, new_state ? 1.0 : 0.0, Controllable::UseGroup);
5890 Editor::toggle_mute ()
5892 bool new_state = false;
5894 boost::shared_ptr<RouteList> rl (new RouteList);
5896 for (TrackSelection::iterator i = selection->tracks.begin(); i != selection->tracks.end(); ++i) {
5897 RouteTimeAxisView *rtav = dynamic_cast<RouteTimeAxisView *>(*i);
5904 new_state = !rtav->route()->muted();
5908 rl->push_back (rtav->route());
5911 _session->set_controls (route_list_to_control_list (rl, &Stripable::mute_control), new_state, Controllable::UseGroup);
5915 Editor::toggle_solo_isolate ()
5921 Editor::fade_range ()
5923 TrackViewList ts = selection->tracks.filter_to_unique_playlists ();
5925 begin_reversible_command (_("fade range"));
5927 for (TrackViewList::iterator i = ts.begin(); i != ts.end(); ++i) {
5928 (*i)->fade_range (selection->time);
5931 commit_reversible_command ();
5936 Editor::set_fade_length (bool in)
5938 RegionSelection rs = get_regions_from_selection_and_entered ();
5944 /* we need a region to measure the offset from the start */
5946 RegionView* rv = rs.front ();
5948 framepos_t pos = get_preferred_edit_position();
5952 if (pos > rv->region()->last_frame() || pos < rv->region()->first_frame()) {
5953 /* edit point is outside the relevant region */
5958 if (pos <= rv->region()->position()) {
5962 len = pos - rv->region()->position();
5963 cmd = _("set fade in length");
5965 if (pos >= rv->region()->last_frame()) {
5969 len = rv->region()->last_frame() - pos;
5970 cmd = _("set fade out length");
5973 bool in_command = false;
5975 for (RegionSelection::iterator x = rs.begin(); x != rs.end(); ++x) {
5976 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (*x);
5982 boost::shared_ptr<AutomationList> alist;
5984 alist = tmp->audio_region()->fade_in();
5986 alist = tmp->audio_region()->fade_out();
5989 XMLNode &before = alist->get_state();
5992 tmp->audio_region()->set_fade_in_length (len);
5993 tmp->audio_region()->set_fade_in_active (true);
5995 tmp->audio_region()->set_fade_out_length (len);
5996 tmp->audio_region()->set_fade_out_active (true);
6000 begin_reversible_command (cmd);
6003 XMLNode &after = alist->get_state();
6004 _session->add_command(new MementoCommand<AutomationList>(*alist, &before, &after));
6008 commit_reversible_command ();
6013 Editor::set_fade_in_shape (FadeShape shape)
6015 RegionSelection rs = get_regions_from_selection_and_entered ();
6020 bool in_command = false;
6022 for (RegionSelection::iterator x = rs.begin(); x != rs.end(); ++x) {
6023 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (*x);
6029 boost::shared_ptr<AutomationList> alist = tmp->audio_region()->fade_in();
6030 XMLNode &before = alist->get_state();
6032 tmp->audio_region()->set_fade_in_shape (shape);
6035 begin_reversible_command (_("set fade in shape"));
6038 XMLNode &after = alist->get_state();
6039 _session->add_command(new MementoCommand<AutomationList>(*alist.get(), &before, &after));
6043 commit_reversible_command ();
6048 Editor::set_fade_out_shape (FadeShape shape)
6050 RegionSelection rs = get_regions_from_selection_and_entered ();
6055 bool in_command = false;
6057 for (RegionSelection::iterator x = rs.begin(); x != rs.end(); ++x) {
6058 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (*x);
6064 boost::shared_ptr<AutomationList> alist = tmp->audio_region()->fade_out();
6065 XMLNode &before = alist->get_state();
6067 tmp->audio_region()->set_fade_out_shape (shape);
6070 begin_reversible_command (_("set fade out shape"));
6073 XMLNode &after = alist->get_state();
6074 _session->add_command(new MementoCommand<AutomationList>(*alist.get(), &before, &after));
6078 commit_reversible_command ();
6083 Editor::set_fade_in_active (bool yn)
6085 RegionSelection rs = get_regions_from_selection_and_entered ();
6090 bool in_command = false;
6092 for (RegionSelection::iterator x = rs.begin(); x != rs.end(); ++x) {
6093 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (*x);
6100 boost::shared_ptr<AudioRegion> ar (tmp->audio_region());
6102 ar->clear_changes ();
6103 ar->set_fade_in_active (yn);
6106 begin_reversible_command (_("set fade in active"));
6109 _session->add_command (new StatefulDiffCommand (ar));
6113 commit_reversible_command ();
6118 Editor::set_fade_out_active (bool yn)
6120 RegionSelection rs = get_regions_from_selection_and_entered ();
6125 bool in_command = false;
6127 for (RegionSelection::iterator x = rs.begin(); x != rs.end(); ++x) {
6128 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (*x);
6134 boost::shared_ptr<AudioRegion> ar (tmp->audio_region());
6136 ar->clear_changes ();
6137 ar->set_fade_out_active (yn);
6140 begin_reversible_command (_("set fade out active"));
6143 _session->add_command(new StatefulDiffCommand (ar));
6147 commit_reversible_command ();
6152 Editor::toggle_region_fades (int dir)
6154 if (_ignore_region_action) {
6158 boost::shared_ptr<AudioRegion> ar;
6161 RegionSelection rs = get_regions_from_selection_and_entered ();
6167 RegionSelection::iterator i;
6168 for (i = rs.begin(); i != rs.end(); ++i) {
6169 if ((ar = boost::dynamic_pointer_cast<AudioRegion>((*i)->region())) != 0) {
6171 yn = ar->fade_out_active ();
6173 yn = ar->fade_in_active ();
6179 if (i == rs.end()) {
6183 /* XXX should this undo-able? */
6184 bool in_command = false;
6186 for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
6187 if ((ar = boost::dynamic_pointer_cast<AudioRegion>((*i)->region())) == 0) {
6190 ar->clear_changes ();
6192 if (dir == 1 || dir == 0) {
6193 ar->set_fade_in_active (!yn);
6196 if (dir == -1 || dir == 0) {
6197 ar->set_fade_out_active (!yn);
6200 begin_reversible_command (_("toggle fade active"));
6203 _session->add_command(new StatefulDiffCommand (ar));
6207 commit_reversible_command ();
6212 /** Update region fade visibility after its configuration has been changed */
6214 Editor::update_region_fade_visibility ()
6216 bool _fade_visibility = _session->config.get_show_region_fades ();
6218 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
6219 AudioTimeAxisView* v = dynamic_cast<AudioTimeAxisView*>(*i);
6221 if (_fade_visibility) {
6222 v->audio_view()->show_all_fades ();
6224 v->audio_view()->hide_all_fades ();
6231 Editor::set_edit_point ()
6234 MusicFrame where (0, 0);
6236 if (!mouse_frame (where.frame, ignored)) {
6242 if (selection->markers.empty()) {
6244 mouse_add_new_marker (where.frame);
6249 Location* loc = find_location_from_marker (selection->markers.front(), ignored);
6252 loc->move_to (where.frame, where.division);
6258 Editor::set_playhead_cursor ()
6260 if (entered_marker) {
6261 _session->request_locate (entered_marker->position(), _session->transport_rolling());
6263 MusicFrame where (0, 0);
6266 if (!mouse_frame (where.frame, ignored)) {
6273 _session->request_locate (where.frame, _session->transport_rolling());
6277 //not sure what this was for; remove it for now.
6278 // if (UIConfiguration::instance().get_follow_edits() && (!_session || !_session->config.get_external_sync())) {
6279 // cancel_time_selection();
6285 Editor::split_region ()
6287 if (_drags->active ()) {
6291 //if a range is selected, separate it
6292 if ( !selection->time.empty()) {
6293 separate_regions_between (selection->time);
6297 //if no range was selected, try to find some regions to split
6298 if (current_mouse_mode() == MouseObject) { //don't try this for Internal Edit, Stretch, Draw, etc.
6300 RegionSelection rs = get_regions_from_selection_and_edit_point ();
6301 const framepos_t pos = get_preferred_edit_position();
6302 const int32_t division = get_grid_music_divisions (0);
6303 MusicFrame where (pos, division);
6309 split_regions_at (where, rs);
6315 Editor::select_next_route()
6317 if (selection->tracks.empty()) {
6318 selection->set (track_views.front());
6322 TimeAxisView* current = selection->tracks.front();
6326 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
6328 if (*i == current) {
6330 if (i != track_views.end()) {
6333 current = (*(track_views.begin()));
6334 //selection->set (*(track_views.begin()));
6340 rui = dynamic_cast<RouteUI *>(current);
6342 } while (current->hidden() || (rui == NULL) || !rui->route()->active());
6344 selection->set (current);
6346 ensure_time_axis_view_is_visible (*current, false);
6350 Editor::select_prev_route()
6352 if (selection->tracks.empty()) {
6353 selection->set (track_views.front());
6357 TimeAxisView* current = selection->tracks.front();
6361 for (TrackViewList::reverse_iterator i = track_views.rbegin(); i != track_views.rend(); ++i) {
6363 if (*i == current) {
6365 if (i != track_views.rend()) {
6368 current = *(track_views.rbegin());
6373 rui = dynamic_cast<RouteUI *>(current);
6375 } while (current->hidden() || (rui == NULL) || !rui->route()->active());
6377 selection->set (current);
6379 ensure_time_axis_view_is_visible (*current, false);
6383 Editor::set_loop_from_selection (bool play)
6385 if (_session == 0) {
6389 framepos_t start, end;
6390 if (!get_selection_extents ( start, end))
6393 set_loop_range (start, end, _("set loop range from selection"));
6396 _session->request_play_loop (true, true);
6401 Editor::set_loop_from_region (bool play)
6403 framepos_t start, end;
6404 if (!get_selection_extents ( start, end))
6407 set_loop_range (start, end, _("set loop range from region"));
6410 _session->request_locate (start, true);
6411 _session->request_play_loop (true);
6416 Editor::set_punch_from_selection ()
6418 if (_session == 0) {
6422 framepos_t start, end;
6423 if (!get_selection_extents ( start, end))
6426 set_punch_range (start, end, _("set punch range from selection"));
6430 Editor::set_auto_punch_range ()
6432 // auto punch in/out button from a single button
6433 // If Punch In is unset, set punch range from playhead to end, enable punch in
6434 // If Punch In is set, the next punch sets Punch Out, unless the playhead has been
6435 // rewound beyond the Punch In marker, in which case that marker will be moved back
6436 // to the current playhead position.
6437 // If punch out is set, it clears the punch range and Punch In/Out buttons
6439 if (_session == 0) {
6443 Location* tpl = transport_punch_location();
6444 framepos_t now = playhead_cursor->current_frame();
6445 framepos_t begin = now;
6446 framepos_t end = _session->current_end_frame();
6448 if (!_session->config.get_punch_in()) {
6449 // First Press - set punch in and create range from here to eternity
6450 set_punch_range (begin, end, _("Auto Punch In"));
6451 _session->config.set_punch_in(true);
6452 } else if (tpl && !_session->config.get_punch_out()) {
6453 // Second press - update end range marker and set punch_out
6454 if (now < tpl->start()) {
6455 // playhead has been rewound - move start back and pretend nothing happened
6457 set_punch_range (begin, end, _("Auto Punch In/Out"));
6459 // normal case for 2nd press - set the punch out
6460 end = playhead_cursor->current_frame ();
6461 set_punch_range (tpl->start(), now, _("Auto Punch In/Out"));
6462 _session->config.set_punch_out(true);
6465 if (_session->config.get_punch_out()) {
6466 _session->config.set_punch_out(false);
6469 if (_session->config.get_punch_in()) {
6470 _session->config.set_punch_in(false);
6475 // third press - unset punch in/out and remove range
6476 _session->locations()->remove(tpl);
6483 Editor::set_session_extents_from_selection ()
6485 if (_session == 0) {
6489 framepos_t start, end;
6490 if (!get_selection_extents ( start, end))
6494 if ((loc = _session->locations()->session_range_location()) == 0) {
6495 _session->set_session_extents (start, end); // this will create a new session range; no need for UNDO
6497 XMLNode &before = loc->get_state();
6499 _session->set_session_extents (start, end);
6501 XMLNode &after = loc->get_state();
6503 begin_reversible_command (_("set session start/end from selection"));
6505 _session->add_command (new MementoCommand<Location>(*loc, &before, &after));
6507 commit_reversible_command ();
6510 _session->set_end_is_free (false);
6514 Editor::set_punch_start_from_edit_point ()
6518 MusicFrame start (0, 0);
6519 framepos_t end = max_framepos;
6521 //use the existing punch end, if any
6522 Location* tpl = transport_punch_location();
6527 if ((_edit_point == EditAtPlayhead) && _session->transport_rolling()) {
6528 start.frame = _session->audible_frame();
6530 start.frame = get_preferred_edit_position();
6533 //snap the selection start/end
6536 //if there's not already a sensible selection endpoint, go "forever"
6537 if (start.frame > end ) {
6541 set_punch_range (start.frame, end, _("set punch start from EP"));
6547 Editor::set_punch_end_from_edit_point ()
6551 framepos_t start = 0;
6552 MusicFrame end (max_framepos, 0);
6554 //use the existing punch start, if any
6555 Location* tpl = transport_punch_location();
6557 start = tpl->start();
6560 if ((_edit_point == EditAtPlayhead) && _session->transport_rolling()) {
6561 end.frame = _session->audible_frame();
6563 end.frame = get_preferred_edit_position();
6566 //snap the selection start/end
6569 set_punch_range (start, end.frame, _("set punch end from EP"));
6575 Editor::set_loop_start_from_edit_point ()
6579 MusicFrame start (0, 0);
6580 framepos_t end = max_framepos;
6582 //use the existing loop end, if any
6583 Location* tpl = transport_loop_location();
6588 if ((_edit_point == EditAtPlayhead) && _session->transport_rolling()) {
6589 start.frame = _session->audible_frame();
6591 start.frame = get_preferred_edit_position();
6594 //snap the selection start/end
6597 //if there's not already a sensible selection endpoint, go "forever"
6598 if (start.frame > end ) {
6602 set_loop_range (start.frame, end, _("set loop start from EP"));
6608 Editor::set_loop_end_from_edit_point ()
6612 framepos_t start = 0;
6613 MusicFrame end (max_framepos, 0);
6615 //use the existing loop start, if any
6616 Location* tpl = transport_loop_location();
6618 start = tpl->start();
6621 if ((_edit_point == EditAtPlayhead) && _session->transport_rolling()) {
6622 end.frame = _session->audible_frame();
6624 end.frame = get_preferred_edit_position();
6627 //snap the selection start/end
6630 set_loop_range (start, end.frame, _("set loop end from EP"));
6635 Editor::set_punch_from_region ()
6637 framepos_t start, end;
6638 if (!get_selection_extents ( start, end))
6641 set_punch_range (start, end, _("set punch range from region"));
6645 Editor::pitch_shift_region ()
6647 RegionSelection rs = get_regions_from_selection_and_entered ();
6649 RegionSelection audio_rs;
6650 for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
6651 if (dynamic_cast<AudioRegionView*> (*i)) {
6652 audio_rs.push_back (*i);
6656 if (audio_rs.empty()) {
6660 pitch_shift (audio_rs, 1.2);
6664 Editor::set_tempo_from_region ()
6666 RegionSelection rs = get_regions_from_selection_and_entered ();
6668 if (!_session || rs.empty()) {
6672 RegionView* rv = rs.front();
6674 define_one_bar (rv->region()->position(), rv->region()->last_frame() + 1);
6678 Editor::use_range_as_bar ()
6680 framepos_t start, end;
6681 if (get_edit_op_range (start, end)) {
6682 define_one_bar (start, end);
6687 Editor::define_one_bar (framepos_t start, framepos_t end)
6689 framepos_t length = end - start;
6691 const Meter& m (_session->tempo_map().meter_at_frame (start));
6693 /* length = 1 bar */
6695 /* We're going to deliver a constant tempo here,
6696 so we can use frames per beat to determine length.
6697 now we want frames per beat.
6698 we have frames per bar, and beats per bar, so ...
6701 /* XXXX METER MATH */
6703 double frames_per_beat = length / m.divisions_per_bar();
6705 /* beats per minute = */
6707 double beats_per_minute = (_session->frame_rate() * 60.0) / frames_per_beat;
6709 /* now decide whether to:
6711 (a) set global tempo
6712 (b) add a new tempo marker
6716 const TempoSection& t (_session->tempo_map().tempo_section_at_frame (start));
6718 bool do_global = false;
6720 if ((_session->tempo_map().n_tempos() == 1) && (_session->tempo_map().n_meters() == 1)) {
6722 /* only 1 tempo & 1 meter: ask if the user wants to set the tempo
6723 at the start, or create a new marker
6726 vector<string> options;
6727 options.push_back (_("Cancel"));
6728 options.push_back (_("Add new marker"));
6729 options.push_back (_("Set global tempo"));
6732 _("Define one bar"),
6733 _("Do you want to set the global tempo or add a new tempo marker?"),
6737 c.set_default_response (2);
6753 /* more than 1 tempo and/or meter section already, go ahead do the "usual":
6754 if the marker is at the region starter, change it, otherwise add
6759 begin_reversible_command (_("set tempo from region"));
6760 XMLNode& before (_session->tempo_map().get_state());
6763 _session->tempo_map().change_initial_tempo (beats_per_minute, t.note_type(), t.end_note_types_per_minute());
6764 } else if (t.frame() == start) {
6765 _session->tempo_map().change_existing_tempo_at (start, beats_per_minute, t.note_type(), t.end_note_types_per_minute());
6767 /* constant tempo */
6768 const Tempo tempo (beats_per_minute, t.note_type());
6769 _session->tempo_map().add_tempo (tempo, 0.0, start, AudioTime);
6772 XMLNode& after (_session->tempo_map().get_state());
6774 _session->add_command (new MementoCommand<TempoMap>(_session->tempo_map(), &before, &after));
6775 commit_reversible_command ();
6779 Editor::split_region_at_transients ()
6781 AnalysisFeatureList positions;
6783 RegionSelection rs = get_regions_from_selection_and_entered ();
6785 if (!_session || rs.empty()) {
6789 begin_reversible_command (_("split regions"));
6791 for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ) {
6793 RegionSelection::iterator tmp;
6798 boost::shared_ptr<AudioRegion> ar = boost::dynamic_pointer_cast<AudioRegion> ((*i)->region());
6801 ar->transients (positions);
6802 split_region_at_points ((*i)->region(), positions, true);
6809 commit_reversible_command ();
6814 Editor::split_region_at_points (boost::shared_ptr<Region> r, AnalysisFeatureList& positions, bool can_ferret, bool select_new)
6816 bool use_rhythmic_rodent = false;
6818 boost::shared_ptr<Playlist> pl = r->playlist();
6820 list<boost::shared_ptr<Region> > new_regions;
6826 if (positions.empty()) {
6830 if (positions.size() > 20 && can_ferret) {
6831 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);
6832 MessageDialog msg (msgstr,
6835 Gtk::BUTTONS_OK_CANCEL);
6838 msg.add_button (_("Call for the Ferret!"), RESPONSE_APPLY);
6839 msg.set_secondary_text (_("Press OK to continue with this split operation\nor ask the Ferret dialog to tune the analysis"));
6841 msg.set_secondary_text (_("Press OK to continue with this split operation"));
6844 msg.set_title (_("Excessive split?"));
6847 int response = msg.run();
6853 case RESPONSE_APPLY:
6854 use_rhythmic_rodent = true;
6861 if (use_rhythmic_rodent) {
6862 show_rhythm_ferret ();
6866 AnalysisFeatureList::const_iterator x;
6868 pl->clear_changes ();
6869 pl->clear_owned_changes ();
6871 x = positions.begin();
6873 if (x == positions.end()) {
6878 pl->remove_region (r);
6882 framepos_t rstart = r->first_frame ();
6883 framepos_t rend = r->last_frame ();
6885 while (x != positions.end()) {
6887 /* deal with positons that are out of scope of present region bounds */
6888 if (*x <= rstart || *x > rend) {
6893 /* file start = original start + how far we from the initial position ? */
6895 framepos_t file_start = r->start() + pos;
6897 /* length = next position - current position */
6899 framepos_t len = (*x) - pos - rstart;
6901 /* XXX we do we really want to allow even single-sample regions?
6902 * shouldn't we have some kind of lower limit on region size?
6911 if (RegionFactory::region_name (new_name, r->name())) {
6915 /* do NOT announce new regions 1 by one, just wait till they are all done */
6919 plist.add (ARDOUR::Properties::start, file_start);
6920 plist.add (ARDOUR::Properties::length, len);
6921 plist.add (ARDOUR::Properties::name, new_name);
6922 plist.add (ARDOUR::Properties::layer, 0);
6923 // TODO set transients_offset
6925 boost::shared_ptr<Region> nr = RegionFactory::create (r->sources(), plist, false);
6926 /* because we set annouce to false, manually add the new region to the
6929 RegionFactory::map_add (nr);
6931 pl->add_region (nr, rstart + pos);
6934 new_regions.push_front(nr);
6943 RegionFactory::region_name (new_name, r->name());
6945 /* Add the final region */
6948 plist.add (ARDOUR::Properties::start, r->start() + pos);
6949 plist.add (ARDOUR::Properties::length, r->last_frame() - (r->position() + pos) + 1);
6950 plist.add (ARDOUR::Properties::name, new_name);
6951 plist.add (ARDOUR::Properties::layer, 0);
6953 boost::shared_ptr<Region> nr = RegionFactory::create (r->sources(), plist, false);
6954 /* because we set annouce to false, manually add the new region to the
6957 RegionFactory::map_add (nr);
6958 pl->add_region (nr, r->position() + pos);
6961 new_regions.push_front(nr);
6966 /* We might have removed regions, which alters other regions' layering_index,
6967 so we need to do a recursive diff here.
6969 vector<Command*> cmds;
6971 _session->add_commands (cmds);
6973 _session->add_command (new StatefulDiffCommand (pl));
6977 for (list<boost::shared_ptr<Region> >::iterator i = new_regions.begin(); i != new_regions.end(); ++i){
6978 set_selected_regionview_from_region_list ((*i), Selection::Add);
6984 Editor::place_transient()
6990 RegionSelection rs = get_regions_from_selection_and_edit_point ();
6996 framepos_t where = get_preferred_edit_position();
6998 begin_reversible_command (_("place transient"));
7000 for (RegionSelection::iterator r = rs.begin(); r != rs.end(); ++r) {
7001 (*r)->region()->add_transient(where);
7004 commit_reversible_command ();
7008 Editor::remove_transient(ArdourCanvas::Item* item)
7014 ArdourCanvas::Line* _line = reinterpret_cast<ArdourCanvas::Line*> (item);
7017 AudioRegionView* _arv = reinterpret_cast<AudioRegionView*> (item->get_data ("regionview"));
7018 _arv->remove_transient (*(float*) _line->get_data ("position"));
7022 Editor::snap_regions_to_grid ()
7024 list <boost::shared_ptr<Playlist > > used_playlists;
7026 RegionSelection rs = get_regions_from_selection_and_entered ();
7028 if (!_session || rs.empty()) {
7032 begin_reversible_command (_("snap regions to grid"));
7034 for (RegionSelection::iterator r = rs.begin(); r != rs.end(); ++r) {
7036 boost::shared_ptr<Playlist> pl = (*r)->region()->playlist();
7038 if (!pl->frozen()) {
7039 /* we haven't seen this playlist before */
7041 /* remember used playlists so we can thaw them later */
7042 used_playlists.push_back(pl);
7045 (*r)->region()->clear_changes ();
7047 MusicFrame start ((*r)->region()->first_frame (), 0);
7049 (*r)->region()->set_position (start.frame, start.division);
7050 _session->add_command(new StatefulDiffCommand ((*r)->region()));
7053 while (used_playlists.size() > 0) {
7054 list <boost::shared_ptr<Playlist > >::iterator i = used_playlists.begin();
7056 used_playlists.pop_front();
7059 commit_reversible_command ();
7063 Editor::close_region_gaps ()
7065 list <boost::shared_ptr<Playlist > > used_playlists;
7067 RegionSelection rs = get_regions_from_selection_and_entered ();
7069 if (!_session || rs.empty()) {
7073 Dialog dialog (_("Close Region Gaps"));
7076 table.set_spacings (12);
7077 table.set_border_width (12);
7078 Label* l = manage (left_aligned_label (_("Crossfade length")));
7079 table.attach (*l, 0, 1, 0, 1);
7081 SpinButton spin_crossfade (1, 0);
7082 spin_crossfade.set_range (0, 15);
7083 spin_crossfade.set_increments (1, 1);
7084 spin_crossfade.set_value (5);
7085 table.attach (spin_crossfade, 1, 2, 0, 1);
7087 table.attach (*manage (new Label (_("ms"))), 2, 3, 0, 1);
7089 l = manage (left_aligned_label (_("Pull-back length")));
7090 table.attach (*l, 0, 1, 1, 2);
7092 SpinButton spin_pullback (1, 0);
7093 spin_pullback.set_range (0, 100);
7094 spin_pullback.set_increments (1, 1);
7095 spin_pullback.set_value(30);
7096 table.attach (spin_pullback, 1, 2, 1, 2);
7098 table.attach (*manage (new Label (_("ms"))), 2, 3, 1, 2);
7100 dialog.get_vbox()->pack_start (table);
7101 dialog.add_button (Stock::CANCEL, RESPONSE_CANCEL);
7102 dialog.add_button (_("Ok"), RESPONSE_ACCEPT);
7105 if (dialog.run () == RESPONSE_CANCEL) {
7109 framepos_t crossfade_len = spin_crossfade.get_value();
7110 framepos_t pull_back_frames = spin_pullback.get_value();
7112 crossfade_len = lrintf (crossfade_len * _session->frame_rate()/1000);
7113 pull_back_frames = lrintf (pull_back_frames * _session->frame_rate()/1000);
7115 /* Iterate over the region list and make adjacent regions overlap by crossfade_len_ms */
7117 begin_reversible_command (_("close region gaps"));
7120 boost::shared_ptr<Region> last_region;
7122 rs.sort_by_position_and_track();
7124 for (RegionSelection::iterator r = rs.begin(); r != rs.end(); ++r) {
7126 boost::shared_ptr<Playlist> pl = (*r)->region()->playlist();
7128 if (!pl->frozen()) {
7129 /* we haven't seen this playlist before */
7131 /* remember used playlists so we can thaw them later */
7132 used_playlists.push_back(pl);
7136 framepos_t position = (*r)->region()->position();
7138 if (idx == 0 || position < last_region->position()){
7139 last_region = (*r)->region();
7144 (*r)->region()->clear_changes ();
7145 (*r)->region()->trim_front( (position - pull_back_frames));
7147 last_region->clear_changes ();
7148 last_region->trim_end( (position - pull_back_frames + crossfade_len));
7150 _session->add_command (new StatefulDiffCommand ((*r)->region()));
7151 _session->add_command (new StatefulDiffCommand (last_region));
7153 last_region = (*r)->region();
7157 while (used_playlists.size() > 0) {
7158 list <boost::shared_ptr<Playlist > >::iterator i = used_playlists.begin();
7160 used_playlists.pop_front();
7163 commit_reversible_command ();
7167 Editor::tab_to_transient (bool forward)
7169 AnalysisFeatureList positions;
7171 RegionSelection rs = get_regions_from_selection_and_entered ();
7177 framepos_t pos = _session->audible_frame ();
7179 if (!selection->tracks.empty()) {
7181 /* don't waste time searching for transients in duplicate playlists.
7184 TrackViewList ts = selection->tracks.filter_to_unique_playlists ();
7186 for (TrackViewList::iterator t = ts.begin(); t != ts.end(); ++t) {
7188 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (*t);
7191 boost::shared_ptr<Track> tr = rtv->track();
7193 boost::shared_ptr<Playlist> pl = tr->playlist ();
7195 framepos_t result = pl->find_next_transient (pos, forward ? 1 : -1);
7198 positions.push_back (result);
7211 for (RegionSelection::iterator r = rs.begin(); r != rs.end(); ++r) {
7212 (*r)->region()->get_transients (positions);
7216 TransientDetector::cleanup_transients (positions, _session->frame_rate(), 3.0);
7219 AnalysisFeatureList::iterator x;
7221 for (x = positions.begin(); x != positions.end(); ++x) {
7227 if (x != positions.end ()) {
7228 _session->request_locate (*x);
7232 AnalysisFeatureList::reverse_iterator x;
7234 for (x = positions.rbegin(); x != positions.rend(); ++x) {
7240 if (x != positions.rend ()) {
7241 _session->request_locate (*x);
7247 Editor::playhead_forward_to_grid ()
7253 MusicFrame pos (playhead_cursor->current_frame (), 0);
7255 if (pos.frame < max_framepos - 1) {
7257 snap_to_internal (pos, RoundUpAlways, false);
7258 _session->request_locate (pos.frame);
7264 Editor::playhead_backward_to_grid ()
7270 MusicFrame pos (playhead_cursor->current_frame (), 0);
7272 if (pos.frame > 2) {
7274 snap_to_internal (pos, RoundDownAlways, false);
7275 _session->request_locate (pos.frame);
7280 Editor::set_track_height (Height h)
7282 TrackSelection& ts (selection->tracks);
7284 for (TrackSelection::iterator x = ts.begin(); x != ts.end(); ++x) {
7285 (*x)->set_height_enum (h);
7290 Editor::toggle_tracks_active ()
7292 TrackSelection& ts (selection->tracks);
7294 bool target = false;
7300 for (TrackSelection::iterator x = ts.begin(); x != ts.end(); ++x) {
7301 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*>(*x);
7305 target = !rtv->_route->active();
7308 rtv->_route->set_active (target, this);
7314 Editor::remove_tracks ()
7316 /* this will delete GUI objects that may be the subject of an event
7317 handler in which this method is called. Defer actual deletion to the
7318 next idle callback, when all event handling is finished.
7320 Glib::signal_idle().connect (sigc::mem_fun (*this, &Editor::idle_remove_tracks));
7324 Editor::idle_remove_tracks ()
7326 Session::StateProtector sp (_session);
7328 return false; /* do not call again */
7332 Editor::_remove_tracks ()
7334 TrackSelection& ts (selection->tracks);
7340 vector<string> choices;
7344 const char* trackstr;
7346 vector<boost::shared_ptr<Route> > routes;
7347 bool special_bus = false;
7349 for (TrackSelection::iterator x = ts.begin(); x != ts.end(); ++x) {
7350 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (*x);
7354 if (rtv->is_track()) {
7359 routes.push_back (rtv->_route);
7361 if (rtv->route()->is_master() || rtv->route()->is_monitor()) {
7366 if (special_bus && !Config->get_allow_special_bus_removal()) {
7367 MessageDialog msg (_("That would be bad news ...."),
7371 msg.set_secondary_text (string_compose (_(
7372 "Removing the master or monitor bus is such a bad idea\n\
7373 that %1 is not going to allow it.\n\
7375 If you really want to do this sort of thing\n\
7376 edit your ardour.rc file to set the\n\
7377 \"allow-special-bus-removal\" option to be \"yes\""), PROGRAM_NAME));
7384 if (ntracks + nbusses == 0) {
7388 trackstr = P_("track", "tracks", ntracks);
7389 busstr = P_("bus", "busses", nbusses);
7393 prompt = string_compose (_("Do you really want to remove %1 %2 and %3 %4?\n"
7394 "(You may also lose the playlists associated with the %2)\n\n"
7395 "This action cannot be undone, and the session file will be overwritten!"),
7396 ntracks, trackstr, nbusses, busstr);
7398 prompt = string_compose (_("Do you really want to remove %1 %2?\n"
7399 "(You may also lose the playlists associated with the %2)\n\n"
7400 "This action cannot be undone, and the session file will be overwritten!"),
7403 } else if (nbusses) {
7404 prompt = string_compose (_("Do you really want to remove %1 %2?\n\n"
7405 "This action cannot be undone, and the session file will be overwritten"),
7409 choices.push_back (_("No, do nothing."));
7410 if (ntracks + nbusses > 1) {
7411 choices.push_back (_("Yes, remove them."));
7413 choices.push_back (_("Yes, remove it."));
7418 title = string_compose (_("Remove %1"), trackstr);
7420 title = string_compose (_("Remove %1"), busstr);
7423 Choice prompter (title, prompt, choices);
7425 if (prompter.run () != 1) {
7429 if (current_mixer_strip && routes.size () > 1 && std::find (routes.begin(), routes.end(), current_mixer_strip->route()) != routes.end ()) {
7430 /* Route deletion calls Editor::timeaxisview_deleted() iteratively (for each deleted
7431 * route). If the deleted route is currently displayed in the Editor-Mixer (highly
7432 * likely because deletion requires selection) this will call
7433 * Editor::set_selected_mixer_strip () which is expensive ( MixerStrip::set_route() ).
7434 * It's likewise likely that the route that has just been displayed in the
7435 * Editor-Mixer will be next in line for deletion.
7437 * So simply switch to the master-bus (if present)
7439 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
7440 if ((*i)->stripable ()->is_master ()) {
7441 set_selected_mixer_strip (*(*i));
7448 PresentationInfo::ChangeSuspender cs;
7449 DisplaySuspender ds;
7451 boost::shared_ptr<RouteList> rl (new RouteList);
7452 for (vector<boost::shared_ptr<Route> >::iterator x = routes.begin(); x != routes.end(); ++x) {
7455 _session->remove_routes (rl);
7457 /* TrackSelection and RouteList leave scope,
7458 * destructors are called,
7459 * diskstream drops references, save_state is called (again for every track)
7464 Editor::do_insert_time ()
7466 if (selection->tracks.empty()) {
7470 InsertRemoveTimeDialog d (*this);
7471 int response = d.run ();
7473 if (response != RESPONSE_OK) {
7477 if (d.distance() == 0) {
7484 d.intersected_region_action (),
7488 d.move_glued_markers(),
7489 d.move_locked_markers(),
7495 Editor::insert_time (
7496 framepos_t pos, framecnt_t frames, InsertTimeOption opt,
7497 bool all_playlists, bool ignore_music_glue, bool markers_too, bool glued_markers_too, bool locked_markers_too, bool tempo_too
7501 if (Config->get_edit_mode() == Lock) {
7504 bool in_command = false;
7506 TrackViewList ts = selection->tracks.filter_to_unique_playlists ();
7508 for (TrackViewList::iterator x = ts.begin(); x != ts.end(); ++x) {
7512 /* don't operate on any playlist more than once, which could
7513 * happen if "all playlists" is enabled, but there is more
7514 * than 1 track using playlists "from" a given track.
7517 set<boost::shared_ptr<Playlist> > pl;
7519 if (all_playlists) {
7520 RouteTimeAxisView* rtav = dynamic_cast<RouteTimeAxisView*> (*x);
7521 if (rtav && rtav->track ()) {
7522 vector<boost::shared_ptr<Playlist> > all = _session->playlists->playlists_for_track (rtav->track ());
7523 for (vector<boost::shared_ptr<Playlist> >::iterator p = all.begin(); p != all.end(); ++p) {
7528 if ((*x)->playlist ()) {
7529 pl.insert ((*x)->playlist ());
7533 for (set<boost::shared_ptr<Playlist> >::iterator i = pl.begin(); i != pl.end(); ++i) {
7535 (*i)->clear_changes ();
7536 (*i)->clear_owned_changes ();
7539 begin_reversible_command (_("insert time"));
7543 if (opt == SplitIntersected) {
7544 /* non musical split */
7545 (*i)->split (MusicFrame (pos, 0));
7548 (*i)->shift (pos, frames, (opt == MoveIntersected), ignore_music_glue);
7550 vector<Command*> cmds;
7552 _session->add_commands (cmds);
7554 _session->add_command (new StatefulDiffCommand (*i));
7558 RouteTimeAxisView* rtav = dynamic_cast<RouteTimeAxisView*> (*x);
7561 begin_reversible_command (_("insert time"));
7564 rtav->route ()->shift (pos, frames);
7571 const int32_t divisions = get_grid_music_divisions (0);
7572 XMLNode& before (_session->locations()->get_state());
7573 Locations::LocationList copy (_session->locations()->list());
7575 for (Locations::LocationList::iterator i = copy.begin(); i != copy.end(); ++i) {
7577 Locations::LocationList::const_iterator tmp;
7579 if ((*i)->position_lock_style() == AudioTime || glued_markers_too) {
7580 bool const was_locked = (*i)->locked ();
7581 if (locked_markers_too) {
7585 if ((*i)->start() >= pos) {
7586 // move end first, in case we're moving by more than the length of the range
7587 if (!(*i)->is_mark()) {
7588 (*i)->set_end ((*i)->end() + frames, false, true, divisions);
7590 (*i)->set_start ((*i)->start() + frames, false, true, divisions);
7602 begin_reversible_command (_("insert time"));
7605 XMLNode& after (_session->locations()->get_state());
7606 _session->add_command (new MementoCommand<Locations>(*_session->locations(), &before, &after));
7612 begin_reversible_command (_("insert time"));
7615 XMLNode& before (_session->tempo_map().get_state());
7616 _session->tempo_map().insert_time (pos, frames);
7617 XMLNode& after (_session->tempo_map().get_state());
7618 _session->add_command (new MementoCommand<TempoMap>(_session->tempo_map(), &before, &after));
7622 commit_reversible_command ();
7627 Editor::do_remove_time ()
7629 if (selection->tracks.empty()) {
7633 InsertRemoveTimeDialog d (*this, true);
7635 int response = d.run ();
7637 if (response != RESPONSE_OK) {
7641 framecnt_t distance = d.distance();
7643 if (distance == 0) {
7653 d.move_glued_markers(),
7654 d.move_locked_markers(),
7660 Editor::remove_time (framepos_t pos, framecnt_t frames, InsertTimeOption opt,
7661 bool ignore_music_glue, bool markers_too, bool glued_markers_too, bool locked_markers_too, bool tempo_too)
7663 if (Config->get_edit_mode() == Lock) {
7664 error << (_("Cannot insert or delete time when in Lock edit.")) << endmsg;
7667 bool in_command = false;
7669 for (TrackSelection::iterator x = selection->tracks.begin(); x != selection->tracks.end(); ++x) {
7671 boost::shared_ptr<Playlist> pl = (*x)->playlist();
7675 XMLNode &before = pl->get_state();
7678 begin_reversible_command (_("remove time"));
7682 std::list<AudioRange> rl;
7683 AudioRange ar(pos, pos+frames, 0);
7686 pl->shift (pos, -frames, true, ignore_music_glue);
7688 XMLNode &after = pl->get_state();
7690 _session->add_command (new MementoCommand<Playlist> (*pl, &before, &after));
7694 RouteTimeAxisView* rtav = dynamic_cast<RouteTimeAxisView*> (*x);
7697 begin_reversible_command (_("remove time"));
7700 rtav->route ()->shift (pos, -frames);
7704 const int32_t divisions = get_grid_music_divisions (0);
7705 std::list<Location*> loc_kill_list;
7710 XMLNode& before (_session->locations()->get_state());
7711 Locations::LocationList copy (_session->locations()->list());
7713 for (Locations::LocationList::iterator i = copy.begin(); i != copy.end(); ++i) {
7714 if ((*i)->position_lock_style() == AudioTime || glued_markers_too) {
7716 bool const was_locked = (*i)->locked ();
7717 if (locked_markers_too) {
7721 if (!(*i)->is_mark()) { // it's a range; have to handle both start and end
7722 if ((*i)->end() >= pos
7723 && (*i)->end() < pos+frames
7724 && (*i)->start() >= pos
7725 && (*i)->end() < pos+frames) { // range is completely enclosed; kill it
7727 loc_kill_list.push_back(*i);
7728 } else { // only start or end is included, try to do the right thing
7729 // move start before moving end, to avoid trying to move the end to before the start
7730 // if we're removing more time than the length of the range
7731 if ((*i)->start() >= pos && (*i)->start() < pos+frames) {
7732 // start is within cut
7733 (*i)->set_start (pos, false, true,divisions); // bring the start marker to the beginning of the cut
7735 } else if ((*i)->start() >= pos+frames) {
7736 // start (and thus entire range) lies beyond end of cut
7737 (*i)->set_start ((*i)->start() - frames, false, true, divisions); // slip the start marker back
7740 if ((*i)->end() >= pos && (*i)->end() < pos+frames) {
7741 // end is inside cut
7742 (*i)->set_end (pos, false, true, divisions); // bring the end to the cut
7744 } else if ((*i)->end() >= pos+frames) {
7745 // end is beyond end of cut
7746 (*i)->set_end ((*i)->end() - frames, false, true, divisions); // slip the end marker back
7751 } else if ((*i)->start() >= pos && (*i)->start() < pos+frames ) {
7752 loc_kill_list.push_back(*i);
7754 } else if ((*i)->start() >= pos) {
7755 (*i)->set_start ((*i)->start() -frames, false, true, divisions);
7765 for (list<Location*>::iterator i = loc_kill_list.begin(); i != loc_kill_list.end(); ++i) {
7766 _session->locations()->remove( *i );
7771 begin_reversible_command (_("remove time"));
7774 XMLNode& after (_session->locations()->get_state());
7775 _session->add_command (new MementoCommand<Locations>(*_session->locations(), &before, &after));
7780 XMLNode& before (_session->tempo_map().get_state());
7782 if (_session->tempo_map().remove_time (pos, frames) ) {
7784 begin_reversible_command (_("remove time"));
7787 XMLNode& after (_session->tempo_map().get_state());
7788 _session->add_command (new MementoCommand<TempoMap>(_session->tempo_map(), &before, &after));
7793 commit_reversible_command ();
7798 Editor::fit_selection ()
7800 if (!selection->tracks.empty()) {
7801 fit_tracks (selection->tracks);
7805 /* no selected tracks - use tracks with selected regions */
7807 if (!selection->regions.empty()) {
7808 for (RegionSelection::iterator r = selection->regions.begin(); r != selection->regions.end(); ++r) {
7809 tvl.push_back (&(*r)->get_time_axis_view ());
7815 } else if (internal_editing()) {
7816 /* no selected tracks, or regions, but in internal edit mode, so follow the mouse and use
7819 if (entered_track) {
7820 tvl.push_back (entered_track);
7828 Editor::fit_tracks (TrackViewList & tracks)
7830 if (tracks.empty()) {
7834 uint32_t child_heights = 0;
7835 int visible_tracks = 0;
7837 for (TrackSelection::iterator t = tracks.begin(); t != tracks.end(); ++t) {
7839 if (!(*t)->marked_for_display()) {
7843 child_heights += (*t)->effective_height() - (*t)->current_height();
7847 /* compute the per-track height from:
7849 * total canvas visible height
7850 * - height that will be taken by visible children of selected tracks
7851 * - height of the ruler/hscroll area
7853 uint32_t h = (uint32_t) floor ((trackviews_height() - child_heights) / visible_tracks);
7854 double first_y_pos = DBL_MAX;
7856 if (h < TimeAxisView::preset_height (HeightSmall)) {
7857 MessageDialog msg (_("There are too many tracks to fit in the current window"));
7858 /* too small to be displayed */
7862 undo_visual_stack.push_back (current_visual_state (true));
7863 PBD::Unwinder<bool> nsv (no_save_visual, true);
7865 /* build a list of all tracks, including children */
7868 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
7870 TimeAxisView::Children c = (*i)->get_child_list ();
7871 for (TimeAxisView::Children::iterator j = c.begin(); j != c.end(); ++j) {
7872 all.push_back (j->get());
7877 // find selection range.
7878 // if someone knows how to user TrackViewList::iterator for this
7880 int selected_top = -1;
7881 int selected_bottom = -1;
7883 for (TrackViewList::iterator t = all.begin(); t != all.end(); ++t, ++i) {
7884 if ((*t)->marked_for_display ()) {
7885 if (tracks.contains(*t)) {
7886 if (selected_top == -1) {
7889 selected_bottom = i;
7895 for (TrackViewList::iterator t = all.begin(); t != all.end(); ++t, ++i) {
7896 if ((*t)->marked_for_display ()) {
7897 if (tracks.contains(*t)) {
7898 (*t)->set_height (h);
7899 first_y_pos = std::min ((*t)->y_position (), first_y_pos);
7901 if (i > selected_top && i < selected_bottom) {
7902 hide_track_in_display (*t);
7909 set the controls_layout height now, because waiting for its size
7910 request signal handler will cause the vertical adjustment setting to fail
7913 controls_layout.property_height () = _full_canvas_height;
7914 vertical_adjustment.set_value (first_y_pos);
7916 redo_visual_stack.push_back (current_visual_state (true));
7918 visible_tracks_selector.set_text (_("Sel"));
7922 Editor::save_visual_state (uint32_t n)
7924 while (visual_states.size() <= n) {
7925 visual_states.push_back (0);
7928 if (visual_states[n] != 0) {
7929 delete visual_states[n];
7932 visual_states[n] = current_visual_state (true);
7937 Editor::goto_visual_state (uint32_t n)
7939 if (visual_states.size() <= n) {
7943 if (visual_states[n] == 0) {
7947 use_visual_state (*visual_states[n]);
7951 Editor::start_visual_state_op (uint32_t n)
7953 save_visual_state (n);
7955 PopUp* pup = new PopUp (WIN_POS_MOUSE, 1000, true);
7957 snprintf (buf, sizeof (buf), _("Saved view %u"), n+1);
7958 pup->set_text (buf);
7963 Editor::cancel_visual_state_op (uint32_t n)
7965 goto_visual_state (n);
7969 Editor::toggle_region_mute ()
7971 if (_ignore_region_action) {
7975 RegionSelection rs = get_regions_from_selection_and_entered ();
7981 if (rs.size() > 1) {
7982 begin_reversible_command (_("mute regions"));
7984 begin_reversible_command (_("mute region"));
7987 for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
7989 (*i)->region()->playlist()->clear_changes ();
7990 (*i)->region()->set_muted (!(*i)->region()->muted ());
7991 _session->add_command (new StatefulDiffCommand ((*i)->region()));
7995 commit_reversible_command ();
7999 Editor::combine_regions ()
8001 /* foreach track with selected regions, take all selected regions
8002 and join them into a new region containing the subregions (as a
8006 typedef set<RouteTimeAxisView*> RTVS;
8009 if (selection->regions.empty()) {
8013 for (RegionSelection::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
8014 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*>(&(*i)->get_time_axis_view());
8017 tracks.insert (rtv);
8021 begin_reversible_command (_("combine regions"));
8023 vector<RegionView*> new_selection;
8025 for (RTVS::iterator i = tracks.begin(); i != tracks.end(); ++i) {
8028 if ((rv = (*i)->combine_regions ()) != 0) {
8029 new_selection.push_back (rv);
8033 selection->clear_regions ();
8034 for (vector<RegionView*>::iterator i = new_selection.begin(); i != new_selection.end(); ++i) {
8035 selection->add (*i);
8038 commit_reversible_command ();
8042 Editor::uncombine_regions ()
8044 typedef set<RouteTimeAxisView*> RTVS;
8047 if (selection->regions.empty()) {
8051 for (RegionSelection::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
8052 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*>(&(*i)->get_time_axis_view());
8055 tracks.insert (rtv);
8059 begin_reversible_command (_("uncombine regions"));
8061 for (RTVS::iterator i = tracks.begin(); i != tracks.end(); ++i) {
8062 (*i)->uncombine_regions ();
8065 commit_reversible_command ();
8069 Editor::toggle_midi_input_active (bool flip_others)
8072 boost::shared_ptr<RouteList> rl (new RouteList);
8074 for (TrackSelection::iterator i = selection->tracks.begin(); i != selection->tracks.end(); ++i) {
8075 RouteTimeAxisView *rtav = dynamic_cast<RouteTimeAxisView *>(*i);
8081 boost::shared_ptr<MidiTrack> mt = rtav->midi_track();
8084 rl->push_back (rtav->route());
8085 onoff = !mt->input_active();
8089 _session->set_exclusive_input_active (rl, onoff, flip_others);
8092 static bool ok_fine (GdkEventAny*) { return true; }
8098 lock_dialog = new Gtk::Dialog (string_compose (_("%1: Locked"), PROGRAM_NAME), true);
8100 Gtk::Image* padlock = manage (new Gtk::Image (ARDOUR_UI_UTILS::get_icon ("padlock_closed")));
8101 lock_dialog->get_vbox()->pack_start (*padlock);
8102 lock_dialog->signal_delete_event ().connect (sigc::ptr_fun (ok_fine));
8104 ArdourButton* b = manage (new ArdourButton);
8105 b->set_name ("lock button");
8106 b->set_text (_("Click to unlock"));
8107 b->signal_clicked.connect (sigc::mem_fun (*this, &Editor::unlock));
8108 lock_dialog->get_vbox()->pack_start (*b);
8110 lock_dialog->get_vbox()->show_all ();
8111 lock_dialog->set_size_request (200, 200);
8114 delete _main_menu_disabler;
8115 _main_menu_disabler = new MainMenuDisabler;
8117 lock_dialog->present ();
8119 lock_dialog->get_window()->set_decorations (Gdk::WMDecoration (0));
8125 lock_dialog->hide ();
8127 delete _main_menu_disabler;
8128 _main_menu_disabler = 0;
8130 if (UIConfiguration::instance().get_lock_gui_after_seconds()) {
8131 start_lock_event_timing ();
8136 Editor::bring_in_callback (Gtk::Label* label, uint32_t n, uint32_t total, string name)
8138 Gtkmm2ext::UI::instance()->call_slot (invalidator (*this), boost::bind (&Editor::update_bring_in_message, this, label, n, total, name));
8142 Editor::update_bring_in_message (Gtk::Label* label, uint32_t n, uint32_t total, string name)
8144 Timers::TimerSuspender t;
8145 label->set_text (string_compose ("Copying %1, %2 of %3", name, n, total));
8146 Gtkmm2ext::UI::instance()->flush_pending (1);
8150 Editor::bring_all_sources_into_session ()
8157 ArdourDialog w (_("Moving embedded files into session folder"));
8158 w.get_vbox()->pack_start (msg);
8161 /* flush all pending GUI events because we're about to start copying
8165 Timers::TimerSuspender t;
8166 Gtkmm2ext::UI::instance()->flush_pending (3);
8170 _session->bring_all_sources_into_session (boost::bind (&Editor::bring_in_callback, this, &msg, _1, _2, _3));