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 (framepos_t where, RegionSelection& regions, const int32_t sub_num)
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:
201 EditorFreeze(); /* Emit Signal */
204 for (RegionSelection::iterator a = regions.begin(); a != regions.end(); ) {
206 RegionSelection::iterator tmp;
208 /* XXX this test needs to be more complicated, to make sure we really
209 have something to split.
212 if (!(*a)->region()->covers (where)) {
220 boost::shared_ptr<Playlist> pl = (*a)->region()->playlist();
228 /* we haven't seen this playlist before */
230 /* remember used playlists so we can thaw them later */
231 used_playlists.push_back(pl);
233 TimeAxisView& tv = (*a)->get_time_axis_view();
234 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (&tv);
236 used_trackviews.push_back (rtv);
243 pl->clear_changes ();
244 pl->split_region ((*a)->region(), where, sub_num);
245 _session->add_command (new StatefulDiffCommand (pl));
251 latest_regionviews.clear ();
253 vector<sigc::connection> region_added_connections;
255 for (list<RouteTimeAxisView*>::iterator i = used_trackviews.begin(); i != used_trackviews.end(); ++i) {
256 region_added_connections.push_back ((*i)->view()->RegionViewAdded.connect (sigc::mem_fun(*this, &Editor::collect_new_region_view)));
259 while (used_playlists.size() > 0) {
260 list <boost::shared_ptr<Playlist > >::iterator i = used_playlists.begin();
262 used_playlists.pop_front();
265 for (vector<sigc::connection>::iterator c = region_added_connections.begin(); c != region_added_connections.end(); ++c) {
270 EditorThaw(); /* Emit Signal */
273 if (working_on_selection) {
274 // IFF we were working on selected regions, try to reinstate the other region selections that existed before the freeze/thaw.
276 _ignore_follow_edits = true; // a split will change the region selection in mysterious ways; it's not practical or wanted to follow this edit
277 RegionSelectionAfterSplit rsas = Config->get_region_selection_after_split();
278 /* There are three classes of regions that we might want selected after
279 splitting selected regions:
280 - regions selected before the split operation, and unaffected by it
281 - newly-created regions before the split
282 - newly-created regions after the split
285 if (rsas & Existing) {
286 // region selections that existed before the split.
287 selection->add ( pre_selected_regions );
290 for (RegionSelection::iterator ri = latest_regionviews.begin(); ri != latest_regionviews.end(); ri++) {
291 if ((*ri)->region()->position() < where) {
292 // new regions created before the split
293 if (rsas & NewlyCreatedLeft) {
294 selection->add (*ri);
297 // new regions created after the split
298 if (rsas & NewlyCreatedRight) {
299 selection->add (*ri);
303 _ignore_follow_edits = false;
305 _ignore_follow_edits = true;
306 if( working_on_selection ) {
307 selection->add (latest_regionviews); //these are the new regions created after the split
309 _ignore_follow_edits = false;
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;
423 for (MarkerSelection::iterator i = selection->markers.begin(); i != selection->markers.end(); ++i) {
425 Location* loc = find_location_from_marker ((*i), is_start);
429 XMLNode& before (loc->get_state());
432 distance = get_nudge_distance (loc->start(), next_distance);
434 distance = next_distance;
436 if (max_framepos - distance > loc->start() + loc->length()) {
437 loc->set_start (loc->start() + distance);
439 loc->set_start (max_framepos - loc->length());
442 distance = get_nudge_distance (loc->end(), next_distance);
444 distance = next_distance;
446 if (max_framepos - distance > loc->end()) {
447 loc->set_end (loc->end() + distance);
449 loc->set_end (max_framepos);
451 if (loc->is_session_range()) {
452 _session->set_end_is_free (false);
456 begin_reversible_command (_("nudge location forward"));
459 XMLNode& after (loc->get_state());
460 _session->add_command (new MementoCommand<Location>(*loc, &before, &after));
465 commit_reversible_command ();
468 distance = get_nudge_distance (playhead_cursor->current_frame (), next_distance);
469 _session->request_locate (playhead_cursor->current_frame () + distance);
474 Editor::nudge_backward (bool next, bool force_playhead)
477 framepos_t next_distance;
483 RegionSelection rs = get_regions_from_selection_and_entered ();
485 if (!force_playhead && !rs.empty()) {
487 begin_reversible_command (_("nudge regions backward"));
489 for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
490 boost::shared_ptr<Region> r ((*i)->region());
492 distance = get_nudge_distance (r->position(), next_distance);
495 distance = next_distance;
500 if (r->position() > distance) {
501 r->set_position (r->position() - distance);
505 _session->add_command (new StatefulDiffCommand (r));
508 commit_reversible_command ();
510 } else if (!force_playhead && !selection->markers.empty()) {
513 bool in_command = false;
515 for (MarkerSelection::iterator i = selection->markers.begin(); i != selection->markers.end(); ++i) {
517 Location* loc = find_location_from_marker ((*i), is_start);
521 XMLNode& before (loc->get_state());
524 distance = get_nudge_distance (loc->start(), next_distance);
526 distance = next_distance;
528 if (distance < loc->start()) {
529 loc->set_start (loc->start() - distance);
534 distance = get_nudge_distance (loc->end(), next_distance);
537 distance = next_distance;
540 if (distance < loc->end() - loc->length()) {
541 loc->set_end (loc->end() - distance);
543 loc->set_end (loc->length());
545 if (loc->is_session_range()) {
546 _session->set_end_is_free (false);
550 begin_reversible_command (_("nudge location forward"));
553 XMLNode& after (loc->get_state());
554 _session->add_command (new MementoCommand<Location>(*loc, &before, &after));
558 commit_reversible_command ();
563 distance = get_nudge_distance (playhead_cursor->current_frame (), next_distance);
565 if (playhead_cursor->current_frame () > distance) {
566 _session->request_locate (playhead_cursor->current_frame () - distance);
568 _session->goto_start();
574 Editor::nudge_forward_capture_offset ()
576 RegionSelection rs = get_regions_from_selection_and_entered ();
578 if (!_session || rs.empty()) {
582 begin_reversible_command (_("nudge forward"));
584 framepos_t const distance = _session->worst_output_latency();
586 for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
587 boost::shared_ptr<Region> r ((*i)->region());
590 r->set_position (r->position() + distance);
591 _session->add_command(new StatefulDiffCommand (r));
594 commit_reversible_command ();
598 Editor::nudge_backward_capture_offset ()
600 RegionSelection rs = get_regions_from_selection_and_entered ();
602 if (!_session || rs.empty()) {
606 begin_reversible_command (_("nudge backward"));
608 framepos_t const distance = _session->worst_output_latency();
610 for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
611 boost::shared_ptr<Region> r ((*i)->region());
615 if (r->position() > distance) {
616 r->set_position (r->position() - distance);
620 _session->add_command(new StatefulDiffCommand (r));
623 commit_reversible_command ();
626 struct RegionSelectionPositionSorter {
627 bool operator() (RegionView* a, RegionView* b) {
628 return a->region()->position() < b->region()->position();
633 Editor::sequence_regions ()
636 framepos_t r_end_prev;
644 RegionSelection rs = get_regions_from_selection_and_entered ();
645 rs.sort(RegionSelectionPositionSorter());
649 bool in_command = false;
651 for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
652 boost::shared_ptr<Region> r ((*i)->region());
660 if(r->position_locked())
667 r->set_position(r_end_prev);
671 begin_reversible_command (_("sequence regions"));
674 _session->add_command (new StatefulDiffCommand (r));
676 r_end=r->position() + r->length();
682 commit_reversible_command ();
691 Editor::move_to_start ()
693 _session->goto_start ();
697 Editor::move_to_end ()
700 _session->request_locate (_session->current_end_frame());
704 Editor::build_region_boundary_cache ()
707 vector<RegionPoint> interesting_points;
708 boost::shared_ptr<Region> r;
709 TrackViewList tracks;
712 region_boundary_cache.clear ();
718 switch (_snap_type) {
719 case SnapToRegionStart:
720 interesting_points.push_back (Start);
722 case SnapToRegionEnd:
723 interesting_points.push_back (End);
725 case SnapToRegionSync:
726 interesting_points.push_back (SyncPoint);
728 case SnapToRegionBoundary:
729 interesting_points.push_back (Start);
730 interesting_points.push_back (End);
733 fatal << string_compose (_("build_region_boundary_cache called with snap_type = %1"), _snap_type) << endmsg;
734 abort(); /*NOTREACHED*/
738 TimeAxisView *ontrack = 0;
741 if (!selection->tracks.empty()) {
742 tlist = selection->tracks.filter_to_unique_playlists ();
744 tlist = track_views.filter_to_unique_playlists ();
747 while (pos < _session->current_end_frame() && !at_end) {
750 framepos_t lpos = max_framepos;
752 for (vector<RegionPoint>::iterator p = interesting_points.begin(); p != interesting_points.end(); ++p) {
754 if ((r = find_next_region (pos, *p, 1, tlist, &ontrack)) == 0) {
755 if (*p == interesting_points.back()) {
758 /* move to next point type */
764 rpos = r->first_frame();
768 rpos = r->last_frame();
772 rpos = r->sync_position ();
780 RouteTimeAxisView *rtav;
782 if (ontrack != 0 && (rtav = dynamic_cast<RouteTimeAxisView*>(ontrack)) != 0 ) {
783 if (rtav->track() != 0) {
784 speed = rtav->track()->speed();
788 rpos = track_frame_to_session_frame (rpos, speed);
794 /* prevent duplicates, but we don't use set<> because we want to be able
798 vector<framepos_t>::iterator ri;
800 for (ri = region_boundary_cache.begin(); ri != region_boundary_cache.end(); ++ri) {
806 if (ri == region_boundary_cache.end()) {
807 region_boundary_cache.push_back (rpos);
814 /* finally sort to be sure that the order is correct */
816 sort (region_boundary_cache.begin(), region_boundary_cache.end());
819 boost::shared_ptr<Region>
820 Editor::find_next_region (framepos_t frame, RegionPoint point, int32_t dir, TrackViewList& tracks, TimeAxisView **ontrack)
822 TrackViewList::iterator i;
823 framepos_t closest = max_framepos;
824 boost::shared_ptr<Region> ret;
828 framepos_t track_frame;
829 RouteTimeAxisView *rtav;
831 for (i = tracks.begin(); i != tracks.end(); ++i) {
834 boost::shared_ptr<Region> r;
837 if ( (rtav = dynamic_cast<RouteTimeAxisView*>(*i)) != 0 ) {
838 if (rtav->track()!=0)
839 track_speed = rtav->track()->speed();
842 track_frame = session_frame_to_track_frame(frame, track_speed);
844 if ((r = (*i)->find_next_region (track_frame, point, dir)) == 0) {
850 rpos = r->first_frame ();
854 rpos = r->last_frame ();
858 rpos = r->sync_position ();
862 // rpos is a "track frame", converting it to "_session frame"
863 rpos = track_frame_to_session_frame(rpos, track_speed);
866 distance = rpos - frame;
868 distance = frame - rpos;
871 if (distance < closest) {
883 Editor::find_next_region_boundary (framepos_t pos, int32_t dir, const TrackViewList& tracks)
885 framecnt_t distance = max_framepos;
886 framepos_t current_nearest = -1;
888 for (TrackViewList::const_iterator i = tracks.begin(); i != tracks.end(); ++i) {
889 framepos_t contender;
892 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (*i);
898 if ((contender = rtv->find_next_region_boundary (pos, dir)) < 0) {
902 d = ::llabs (pos - contender);
905 current_nearest = contender;
910 return current_nearest;
914 Editor::get_region_boundary (framepos_t pos, int32_t dir, bool with_selection, bool only_onscreen)
919 if (with_selection && Config->get_region_boundaries_from_selected_tracks()) {
921 if (!selection->tracks.empty()) {
923 target = find_next_region_boundary (pos, dir, selection->tracks);
927 if (only_onscreen || Config->get_region_boundaries_from_onscreen_tracks()) {
928 get_onscreen_tracks (tvl);
929 target = find_next_region_boundary (pos, dir, tvl);
931 target = find_next_region_boundary (pos, dir, track_views);
937 if (only_onscreen || Config->get_region_boundaries_from_onscreen_tracks()) {
938 get_onscreen_tracks (tvl);
939 target = find_next_region_boundary (pos, dir, tvl);
941 target = find_next_region_boundary (pos, dir, track_views);
949 Editor::cursor_to_region_boundary (bool with_selection, int32_t dir)
951 framepos_t pos = playhead_cursor->current_frame ();
958 // so we don't find the current region again..
959 if (dir > 0 || pos > 0) {
963 if ((target = get_region_boundary (pos, dir, with_selection, false)) < 0) {
967 _session->request_locate (target);
971 Editor::cursor_to_next_region_boundary (bool with_selection)
973 cursor_to_region_boundary (with_selection, 1);
977 Editor::cursor_to_previous_region_boundary (bool with_selection)
979 cursor_to_region_boundary (with_selection, -1);
983 Editor::cursor_to_region_point (EditorCursor* cursor, RegionPoint point, int32_t dir)
985 boost::shared_ptr<Region> r;
986 framepos_t pos = cursor->current_frame ();
992 TimeAxisView *ontrack = 0;
994 // so we don't find the current region again..
998 if (!selection->tracks.empty()) {
1000 r = find_next_region (pos, point, dir, selection->tracks, &ontrack);
1002 } else if (clicked_axisview) {
1005 t.push_back (clicked_axisview);
1007 r = find_next_region (pos, point, dir, t, &ontrack);
1011 r = find_next_region (pos, point, dir, track_views, &ontrack);
1020 pos = r->first_frame ();
1024 pos = r->last_frame ();
1028 pos = r->sync_position ();
1033 RouteTimeAxisView *rtav;
1035 if ( ontrack != 0 && (rtav = dynamic_cast<RouteTimeAxisView*>(ontrack)) != 0 ) {
1036 if (rtav->track() != 0) {
1037 speed = rtav->track()->speed();
1041 pos = track_frame_to_session_frame(pos, speed);
1043 if (cursor == playhead_cursor) {
1044 _session->request_locate (pos);
1046 cursor->set_position (pos);
1051 Editor::cursor_to_next_region_point (EditorCursor* cursor, RegionPoint point)
1053 cursor_to_region_point (cursor, point, 1);
1057 Editor::cursor_to_previous_region_point (EditorCursor* cursor, RegionPoint point)
1059 cursor_to_region_point (cursor, point, -1);
1063 Editor::cursor_to_selection_start (EditorCursor *cursor)
1067 switch (mouse_mode) {
1069 if (!selection->regions.empty()) {
1070 pos = selection->regions.start();
1075 if (!selection->time.empty()) {
1076 pos = selection->time.start ();
1084 if (cursor == playhead_cursor) {
1085 _session->request_locate (pos);
1087 cursor->set_position (pos);
1092 Editor::cursor_to_selection_end (EditorCursor *cursor)
1096 switch (mouse_mode) {
1098 if (!selection->regions.empty()) {
1099 pos = selection->regions.end_frame();
1104 if (!selection->time.empty()) {
1105 pos = selection->time.end_frame ();
1113 if (cursor == playhead_cursor) {
1114 _session->request_locate (pos);
1116 cursor->set_position (pos);
1121 Editor::selected_marker_to_region_boundary (bool with_selection, int32_t dir)
1131 if (selection->markers.empty()) {
1135 if (!mouse_frame (mouse, ignored)) {
1139 add_location_mark (mouse);
1142 if ((loc = find_location_from_marker (selection->markers.front(), ignored)) == 0) {
1146 framepos_t pos = loc->start();
1148 // so we don't find the current region again..
1149 if (dir > 0 || pos > 0) {
1153 if ((target = get_region_boundary (pos, dir, with_selection, false)) < 0) {
1157 loc->move_to (target);
1161 Editor::selected_marker_to_next_region_boundary (bool with_selection)
1163 selected_marker_to_region_boundary (with_selection, 1);
1167 Editor::selected_marker_to_previous_region_boundary (bool with_selection)
1169 selected_marker_to_region_boundary (with_selection, -1);
1173 Editor::selected_marker_to_region_point (RegionPoint point, int32_t dir)
1175 boost::shared_ptr<Region> r;
1180 if (!_session || selection->markers.empty()) {
1184 if ((loc = find_location_from_marker (selection->markers.front(), ignored)) == 0) {
1188 TimeAxisView *ontrack = 0;
1192 // so we don't find the current region again..
1196 if (!selection->tracks.empty()) {
1198 r = find_next_region (pos, point, dir, selection->tracks, &ontrack);
1202 r = find_next_region (pos, point, dir, track_views, &ontrack);
1211 pos = r->first_frame ();
1215 pos = r->last_frame ();
1219 pos = r->adjust_to_sync (r->first_frame());
1224 RouteTimeAxisView *rtav;
1226 if (ontrack != 0 && (rtav = dynamic_cast<RouteTimeAxisView*>(ontrack)) != 0) {
1227 if (rtav->track() != 0) {
1228 speed = rtav->track()->speed();
1232 pos = track_frame_to_session_frame(pos, speed);
1238 Editor::selected_marker_to_next_region_point (RegionPoint point)
1240 selected_marker_to_region_point (point, 1);
1244 Editor::selected_marker_to_previous_region_point (RegionPoint point)
1246 selected_marker_to_region_point (point, -1);
1250 Editor::selected_marker_to_selection_start ()
1256 if (!_session || selection->markers.empty()) {
1260 if ((loc = find_location_from_marker (selection->markers.front(), ignored)) == 0) {
1264 switch (mouse_mode) {
1266 if (!selection->regions.empty()) {
1267 pos = selection->regions.start();
1272 if (!selection->time.empty()) {
1273 pos = selection->time.start ();
1285 Editor::selected_marker_to_selection_end ()
1291 if (!_session || selection->markers.empty()) {
1295 if ((loc = find_location_from_marker (selection->markers.front(), ignored)) == 0) {
1299 switch (mouse_mode) {
1301 if (!selection->regions.empty()) {
1302 pos = selection->regions.end_frame();
1307 if (!selection->time.empty()) {
1308 pos = selection->time.end_frame ();
1320 Editor::scroll_playhead (bool forward)
1322 framepos_t pos = playhead_cursor->current_frame ();
1323 framecnt_t delta = (framecnt_t) floor (current_page_samples() / 0.8);
1326 if (pos == max_framepos) {
1330 if (pos < max_framepos - delta) {
1349 _session->request_locate (pos);
1353 Editor::cursor_align (bool playhead_to_edit)
1359 if (playhead_to_edit) {
1361 if (selection->markers.empty()) {
1365 _session->request_locate (selection->markers.front()->position(), _session->transport_rolling());
1368 /* move selected markers to playhead */
1370 for (MarkerSelection::iterator i = selection->markers.begin(); i != selection->markers.end(); ++i) {
1373 Location* loc = find_location_from_marker (*i, ignored);
1375 if (loc->is_mark()) {
1376 loc->set_start (playhead_cursor->current_frame ());
1378 loc->set (playhead_cursor->current_frame (),
1379 playhead_cursor->current_frame () + loc->length());
1386 Editor::scroll_backward (float pages)
1388 framepos_t const one_page = (framepos_t) rint (_visible_canvas_width * samples_per_pixel);
1389 framepos_t const cnt = (framepos_t) floor (pages * one_page);
1392 if (leftmost_frame < cnt) {
1395 frame = leftmost_frame - cnt;
1398 reset_x_origin (frame);
1402 Editor::scroll_forward (float pages)
1404 framepos_t const one_page = (framepos_t) rint (_visible_canvas_width * samples_per_pixel);
1405 framepos_t const cnt = (framepos_t) floor (pages * one_page);
1408 if (max_framepos - cnt < leftmost_frame) {
1409 frame = max_framepos - cnt;
1411 frame = leftmost_frame + cnt;
1414 reset_x_origin (frame);
1418 Editor::scroll_tracks_down ()
1420 double vert_value = vertical_adjustment.get_value() + vertical_adjustment.get_page_size();
1421 if (vert_value > vertical_adjustment.get_upper() - _visible_canvas_height) {
1422 vert_value = vertical_adjustment.get_upper() - _visible_canvas_height;
1425 vertical_adjustment.set_value (vert_value);
1429 Editor::scroll_tracks_up ()
1431 vertical_adjustment.set_value (vertical_adjustment.get_value() - vertical_adjustment.get_page_size());
1435 Editor::scroll_tracks_down_line ()
1437 double vert_value = vertical_adjustment.get_value() + 60;
1439 if (vert_value > vertical_adjustment.get_upper() - _visible_canvas_height) {
1440 vert_value = vertical_adjustment.get_upper() - _visible_canvas_height;
1443 vertical_adjustment.set_value (vert_value);
1447 Editor::scroll_tracks_up_line ()
1449 reset_y_origin (vertical_adjustment.get_value() - 60);
1453 Editor::scroll_down_one_track (bool skip_child_views)
1455 TrackViewList::reverse_iterator next = track_views.rend();
1456 const double top_of_trackviews = vertical_adjustment.get_value();
1458 for (TrackViewList::reverse_iterator t = track_views.rbegin(); t != track_views.rend(); ++t) {
1459 if ((*t)->hidden()) {
1463 /* If this is the upper-most visible trackview, we want to display
1464 * the one above it (next)
1466 * Note that covers_y_position() is recursive and includes child views
1468 std::pair<TimeAxisView*,double> res = (*t)->covers_y_position (top_of_trackviews);
1471 if (skip_child_views) {
1474 /* automation lane (one level, non-recursive)
1476 * - if no automation lane exists -> move to next tack
1477 * - if the first (here: bottom-most) matches -> move to next tack
1478 * - if no y-axis match is found -> the current track is at the top
1479 * -> move to last (here: top-most) automation lane
1481 TimeAxisView::Children kids = (*t)->get_child_list();
1482 TimeAxisView::Children::reverse_iterator nkid = kids.rend();
1484 for (TimeAxisView::Children::reverse_iterator ci = kids.rbegin(); ci != kids.rend(); ++ci) {
1485 if ((*ci)->hidden()) {
1489 std::pair<TimeAxisView*,double> dev;
1490 dev = (*ci)->covers_y_position (top_of_trackviews);
1492 /* some automation lane is currently at the top */
1493 if (ci == kids.rbegin()) {
1494 /* first (bottom-most) autmation lane is at the top.
1495 * -> move to next track
1504 if (nkid != kids.rend()) {
1505 ensure_time_axis_view_is_visible (**nkid, true);
1513 /* move to the track below the first one that covers the */
1515 if (next != track_views.rend()) {
1516 ensure_time_axis_view_is_visible (**next, true);
1524 Editor::scroll_up_one_track (bool skip_child_views)
1526 TrackViewList::iterator prev = track_views.end();
1527 double top_of_trackviews = vertical_adjustment.get_value ();
1529 for (TrackViewList::iterator t = track_views.begin(); t != track_views.end(); ++t) {
1531 if ((*t)->hidden()) {
1535 /* find the trackview at the top of the trackview group
1537 * Note that covers_y_position() is recursive and includes child views
1539 std::pair<TimeAxisView*,double> res = (*t)->covers_y_position (top_of_trackviews);
1542 if (skip_child_views) {
1545 /* automation lane (one level, non-recursive)
1547 * - if no automation lane exists -> move to prev tack
1548 * - if no y-axis match is found -> the current track is at the top -> move to prev track
1549 * (actually last automation lane of previous track, see below)
1550 * - if first (top-most) lane is at the top -> move to this track
1551 * - else move up one lane
1553 TimeAxisView::Children kids = (*t)->get_child_list();
1554 TimeAxisView::Children::iterator pkid = kids.end();
1556 for (TimeAxisView::Children::iterator ci = kids.begin(); ci != kids.end(); ++ci) {
1557 if ((*ci)->hidden()) {
1561 std::pair<TimeAxisView*,double> dev;
1562 dev = (*ci)->covers_y_position (top_of_trackviews);
1564 /* some automation lane is currently at the top */
1565 if (ci == kids.begin()) {
1566 /* first (top-most) autmation lane is at the top.
1567 * jump directly to this track's top
1569 ensure_time_axis_view_is_visible (**t, true);
1572 else if (pkid != kids.end()) {
1573 /* some other automation lane is at the top.
1574 * move up to prev automation lane.
1576 ensure_time_axis_view_is_visible (**pkid, true);
1579 assert(0); // not reached
1590 if (prev != track_views.end()) {
1591 // move to bottom-most automation-lane of the previous track
1592 TimeAxisView::Children kids = (*prev)->get_child_list();
1593 TimeAxisView::Children::reverse_iterator pkid = kids.rend();
1594 if (!skip_child_views) {
1595 // find the last visible lane
1596 for (TimeAxisView::Children::reverse_iterator ci = kids.rbegin(); ci != kids.rend(); ++ci) {
1597 if (!(*ci)->hidden()) {
1603 if (pkid != kids.rend()) {
1604 ensure_time_axis_view_is_visible (**pkid, true);
1606 ensure_time_axis_view_is_visible (**prev, true);
1615 Editor::scroll_left_step ()
1617 framepos_t xdelta = (current_page_samples() / 8);
1619 if (leftmost_frame > xdelta) {
1620 reset_x_origin (leftmost_frame - xdelta);
1628 Editor::scroll_right_step ()
1630 framepos_t xdelta = (current_page_samples() / 8);
1632 if (max_framepos - xdelta > leftmost_frame) {
1633 reset_x_origin (leftmost_frame + xdelta);
1635 reset_x_origin (max_framepos - current_page_samples());
1640 Editor::scroll_left_half_page ()
1642 framepos_t xdelta = (current_page_samples() / 2);
1643 if (leftmost_frame > xdelta) {
1644 reset_x_origin (leftmost_frame - xdelta);
1651 Editor::scroll_right_half_page ()
1653 framepos_t xdelta = (current_page_samples() / 2);
1654 if (max_framepos - xdelta > leftmost_frame) {
1655 reset_x_origin (leftmost_frame + xdelta);
1657 reset_x_origin (max_framepos - current_page_samples());
1664 Editor::tav_zoom_step (bool coarser)
1666 DisplaySuspender ds;
1670 if (selection->tracks.empty()) {
1673 ts = &selection->tracks;
1676 for (TrackViewList::iterator i = ts->begin(); i != ts->end(); ++i) {
1677 TimeAxisView *tv = (static_cast<TimeAxisView*>(*i));
1678 tv->step_height (coarser);
1683 Editor::tav_zoom_smooth (bool coarser, bool force_all)
1685 DisplaySuspender ds;
1689 if (selection->tracks.empty() || force_all) {
1692 ts = &selection->tracks;
1695 for (TrackViewList::iterator i = ts->begin(); i != ts->end(); ++i) {
1696 TimeAxisView *tv = (static_cast<TimeAxisView*>(*i));
1697 uint32_t h = tv->current_height ();
1702 if (h >= TimeAxisView::preset_height (HeightSmall)) {
1707 tv->set_height (h + 5);
1713 Editor::temporal_zoom_step_mouse_focus_scale (bool zoom_out, double scale)
1715 Editing::ZoomFocus temp_focus = zoom_focus;
1716 zoom_focus = Editing::ZoomFocusMouse;
1717 temporal_zoom_step_scale (zoom_out, scale);
1718 zoom_focus = temp_focus;
1722 Editor::temporal_zoom_step_mouse_focus (bool zoom_out)
1724 temporal_zoom_step_mouse_focus_scale (zoom_out, 2.0);
1728 Editor::temporal_zoom_step (bool zoom_out)
1730 temporal_zoom_step_scale (zoom_out, 2.0);
1734 Editor::temporal_zoom_step_scale (bool zoom_out, double scale)
1736 ENSURE_GUI_THREAD (*this, &Editor::temporal_zoom_step, zoom_out, scale)
1738 framecnt_t nspp = samples_per_pixel;
1742 if (nspp == samples_per_pixel) {
1747 if (nspp == samples_per_pixel) {
1752 temporal_zoom (nspp);
1756 Editor::temporal_zoom (framecnt_t fpp)
1762 framepos_t current_page = current_page_samples();
1763 framepos_t current_leftmost = leftmost_frame;
1764 framepos_t current_rightmost;
1765 framepos_t current_center;
1766 framepos_t new_page_size;
1767 framepos_t half_page_size;
1768 framepos_t leftmost_after_zoom = 0;
1770 bool in_track_canvas;
1774 if (fpp == samples_per_pixel) {
1778 // Imposing an arbitrary limit to zoom out as too much zoom out produces
1779 // segfaults for lack of memory. If somebody decides this is not high enough I
1780 // believe it can be raisen to higher values but some limit must be in place.
1782 // This constant represents 1 day @ 48kHz on a 1600 pixel wide display
1783 // all of which is used for the editor track displays. The whole day
1784 // would be 4147200000 samples, so 2592000 samples per pixel.
1786 nfpp = min (fpp, (framecnt_t) 2592000);
1787 nfpp = max ((framecnt_t) 1, nfpp);
1789 new_page_size = (framepos_t) floor (_visible_canvas_width * nfpp);
1790 half_page_size = new_page_size / 2;
1792 switch (zoom_focus) {
1794 leftmost_after_zoom = current_leftmost;
1797 case ZoomFocusRight:
1798 current_rightmost = leftmost_frame + current_page;
1799 if (current_rightmost < new_page_size) {
1800 leftmost_after_zoom = 0;
1802 leftmost_after_zoom = current_rightmost - new_page_size;
1806 case ZoomFocusCenter:
1807 current_center = current_leftmost + (current_page/2);
1808 if (current_center < half_page_size) {
1809 leftmost_after_zoom = 0;
1811 leftmost_after_zoom = current_center - half_page_size;
1815 case ZoomFocusPlayhead:
1816 /* centre playhead */
1817 l = playhead_cursor->current_frame () - (new_page_size * 0.5);
1820 leftmost_after_zoom = 0;
1821 } else if (l > max_framepos) {
1822 leftmost_after_zoom = max_framepos - new_page_size;
1824 leftmost_after_zoom = (framepos_t) l;
1828 case ZoomFocusMouse:
1829 /* try to keep the mouse over the same point in the display */
1831 if (!mouse_frame (where, in_track_canvas)) {
1832 /* use playhead instead */
1833 where = playhead_cursor->current_frame ();
1835 if (where < half_page_size) {
1836 leftmost_after_zoom = 0;
1838 leftmost_after_zoom = where - half_page_size;
1843 l = - ((new_page_size * ((where - current_leftmost)/(double)current_page)) - where);
1846 leftmost_after_zoom = 0;
1847 } else if (l > max_framepos) {
1848 leftmost_after_zoom = max_framepos - new_page_size;
1850 leftmost_after_zoom = (framepos_t) l;
1857 /* try to keep the edit point in the same place */
1858 where = get_preferred_edit_position ();
1862 double l = - ((new_page_size * ((where - current_leftmost)/(double)current_page)) - where);
1865 leftmost_after_zoom = 0;
1866 } else if (l > max_framepos) {
1867 leftmost_after_zoom = max_framepos - new_page_size;
1869 leftmost_after_zoom = (framepos_t) l;
1873 /* edit point not defined */
1880 // leftmost_after_zoom = min (leftmost_after_zoom, _session->current_end_frame());
1882 reposition_and_zoom (leftmost_after_zoom, nfpp);
1886 Editor::calc_extra_zoom_edges(framepos_t &start, framepos_t &end)
1888 /* this func helps make sure we leave a little space
1889 at each end of the editor so that the zoom doesn't fit the region
1890 precisely to the screen.
1893 GdkScreen* screen = gdk_screen_get_default ();
1894 const gint pixwidth = gdk_screen_get_width (screen);
1895 const gint mmwidth = gdk_screen_get_width_mm (screen);
1896 const double pix_per_mm = (double) pixwidth/ (double) mmwidth;
1897 const double one_centimeter_in_pixels = pix_per_mm * 10.0;
1899 const framepos_t range = end - start;
1900 const framecnt_t new_fpp = (framecnt_t) ceil ((double) range / (double) _visible_canvas_width);
1901 const framepos_t extra_samples = (framepos_t) floor (one_centimeter_in_pixels * new_fpp);
1903 if (start > extra_samples) {
1904 start -= extra_samples;
1909 if (max_framepos - extra_samples > end) {
1910 end += extra_samples;
1917 Editor::temporal_zoom_region (bool both_axes)
1919 framepos_t start = max_framepos;
1921 set<TimeAxisView*> tracks;
1923 if ( !get_selection_extents(start, end) )
1926 calc_extra_zoom_edges (start, end);
1928 /* if we're zooming on both axes we need to save track heights etc.
1931 undo_visual_stack.push_back (current_visual_state (both_axes));
1933 PBD::Unwinder<bool> nsv (no_save_visual, true);
1935 temporal_zoom_by_frame (start, end);
1938 uint32_t per_track_height = (uint32_t) floor ((_visible_canvas_height - 10.0) / tracks.size());
1940 /* set visible track heights appropriately */
1942 for (set<TimeAxisView*>::iterator t = tracks.begin(); t != tracks.end(); ++t) {
1943 (*t)->set_height (per_track_height);
1946 /* hide irrelevant tracks */
1948 DisplaySuspender ds;
1950 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
1951 if (find (tracks.begin(), tracks.end(), (*i)) == tracks.end()) {
1952 hide_track_in_display (*i);
1956 vertical_adjustment.set_value (0.0);
1959 redo_visual_stack.push_back (current_visual_state (both_axes));
1964 Editor::get_selection_extents (framepos_t &start, framepos_t &end) const
1966 start = max_framepos;
1970 //ToDo: if notes are selected, set extents to that selection
1972 //ToDo: if control points are selected, set extents to that selection
1974 if ( !selection->regions.empty() ) {
1975 RegionSelection rs = get_regions_from_selection_and_entered ();
1977 for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
1979 if ((*i)->region()->position() < start) {
1980 start = (*i)->region()->position();
1983 if ((*i)->region()->last_frame() + 1 > end) {
1984 end = (*i)->region()->last_frame() + 1;
1988 } else if (!selection->time.empty()) {
1989 start = selection->time.start();
1990 end = selection->time.end_frame();
1992 ret = false; //no selection found
1995 if ((start == 0 && end == 0) || end < start) {
2004 Editor::temporal_zoom_selection (bool both_axes)
2006 if (!selection) return;
2008 //ToDo: if notes are selected, zoom to that
2010 //ToDo: if control points are selected, zoom to that
2012 //if region(s) are selected, zoom to that
2013 if ( !selection->regions.empty() )
2014 temporal_zoom_region (both_axes);
2016 //if a range is selected, zoom to that
2017 if (!selection->time.empty()) {
2019 framepos_t start, end;
2020 if (get_selection_extents (start, end)) {
2021 calc_extra_zoom_edges(start, end);
2022 temporal_zoom_by_frame (start, end);
2032 Editor::temporal_zoom_session ()
2034 ENSURE_GUI_THREAD (*this, &Editor::temporal_zoom_session)
2037 framecnt_t start = _session->current_start_frame();
2038 framecnt_t end = _session->current_end_frame();
2040 if (_session->actively_recording () ) {
2041 framepos_t cur = playhead_cursor->current_frame ();
2043 /* recording beyond the end marker; zoom out
2044 * by 5 seconds more so that if 'follow
2045 * playhead' is active we don't immediately
2048 end = cur + _session->frame_rate() * 5;
2052 if ((start == 0 && end == 0) || end < start) {
2056 calc_extra_zoom_edges(start, end);
2058 temporal_zoom_by_frame (start, end);
2063 Editor::temporal_zoom_by_frame (framepos_t start, framepos_t end)
2065 if (!_session) return;
2067 if ((start == 0 && end == 0) || end < start) {
2071 framepos_t range = end - start;
2073 const framecnt_t new_fpp = (framecnt_t) ceil ((double) range / (double) _visible_canvas_width);
2075 framepos_t new_page = range;
2076 framepos_t middle = (framepos_t) floor ((double) start + ((double) range / 2.0f));
2077 framepos_t new_leftmost = (framepos_t) floor ((double) middle - ((double) new_page / 2.0f));
2079 if (new_leftmost > middle) {
2083 if (new_leftmost < 0) {
2087 reposition_and_zoom (new_leftmost, new_fpp);
2091 Editor::temporal_zoom_to_frame (bool coarser, framepos_t frame)
2097 framecnt_t range_before = frame - leftmost_frame;
2101 if (samples_per_pixel <= 1) {
2104 new_spp = samples_per_pixel + (samples_per_pixel/2);
2106 range_before += range_before/2;
2108 if (samples_per_pixel >= 1) {
2109 new_spp = samples_per_pixel - (samples_per_pixel/2);
2111 /* could bail out here since we cannot zoom any finer,
2112 but leave that to the equality test below
2114 new_spp = samples_per_pixel;
2117 range_before -= range_before/2;
2120 if (new_spp == samples_per_pixel) {
2124 /* zoom focus is automatically taken as @param frame when this
2128 framepos_t new_leftmost = frame - (framepos_t)range_before;
2130 if (new_leftmost > frame) {
2134 if (new_leftmost < 0) {
2138 reposition_and_zoom (new_leftmost, new_spp);
2143 Editor::choose_new_marker_name(string &name) {
2145 if (!UIConfiguration::instance().get_name_new_markers()) {
2146 /* don't prompt user for a new name */
2150 ArdourPrompter dialog (true);
2152 dialog.set_prompt (_("New Name:"));
2154 dialog.set_title (_("New Location Marker"));
2156 dialog.set_name ("MarkNameWindow");
2157 dialog.set_size_request (250, -1);
2158 dialog.set_position (Gtk::WIN_POS_MOUSE);
2160 dialog.add_button (Stock::OK, RESPONSE_ACCEPT);
2161 dialog.set_initial_text (name);
2165 switch (dialog.run ()) {
2166 case RESPONSE_ACCEPT:
2172 dialog.get_result(name);
2179 Editor::add_location_from_selection ()
2183 if (selection->time.empty()) {
2187 if (_session == 0 || clicked_axisview == 0) {
2191 framepos_t start = selection->time[clicked_selection].start;
2192 framepos_t end = selection->time[clicked_selection].end;
2194 _session->locations()->next_available_name(rangename,"selection");
2195 Location *location = new Location (*_session, start, end, rangename, Location::IsRangeMarker);
2197 begin_reversible_command (_("add marker"));
2199 XMLNode &before = _session->locations()->get_state();
2200 _session->locations()->add (location, true);
2201 XMLNode &after = _session->locations()->get_state();
2202 _session->add_command(new MementoCommand<Locations>(*(_session->locations()), &before, &after));
2204 commit_reversible_command ();
2208 Editor::add_location_mark (framepos_t where)
2212 select_new_marker = true;
2214 _session->locations()->next_available_name(markername,"mark");
2215 if (!choose_new_marker_name(markername)) {
2218 Location *location = new Location (*_session, where, where, markername, Location::IsMark);
2219 begin_reversible_command (_("add marker"));
2221 XMLNode &before = _session->locations()->get_state();
2222 _session->locations()->add (location, true);
2223 XMLNode &after = _session->locations()->get_state();
2224 _session->add_command(new MementoCommand<Locations>(*(_session->locations()), &before, &after));
2226 commit_reversible_command ();
2230 Editor::set_session_start_from_playhead ()
2236 if ((loc = _session->locations()->session_range_location()) == 0) { //should never happen
2237 _session->set_session_extents ( _session->audible_frame(), _session->audible_frame() );
2239 XMLNode &before = loc->get_state();
2241 _session->set_session_extents ( _session->audible_frame(), loc->end() );
2243 XMLNode &after = loc->get_state();
2245 begin_reversible_command (_("Set session start"));
2247 _session->add_command (new MementoCommand<Location>(*loc, &before, &after));
2249 commit_reversible_command ();
2254 Editor::set_session_end_from_playhead ()
2260 if ((loc = _session->locations()->session_range_location()) == 0) { //should never happen
2261 _session->set_session_extents ( _session->audible_frame(), _session->audible_frame() );
2263 XMLNode &before = loc->get_state();
2265 _session->set_session_extents ( loc->start(), _session->audible_frame() );
2267 XMLNode &after = loc->get_state();
2269 begin_reversible_command (_("Set session start"));
2271 _session->add_command (new MementoCommand<Location>(*loc, &before, &after));
2273 commit_reversible_command ();
2276 _session->set_end_is_free (false);
2281 Editor::toggle_location_at_playhead_cursor ()
2283 if (!do_remove_location_at_playhead_cursor())
2285 add_location_from_playhead_cursor();
2290 Editor::add_location_from_playhead_cursor ()
2292 add_location_mark (_session->audible_frame());
2296 Editor::do_remove_location_at_playhead_cursor ()
2298 bool removed = false;
2301 XMLNode &before = _session->locations()->get_state();
2303 //find location(s) at this time
2304 Locations::LocationList locs;
2305 _session->locations()->find_all_between (_session->audible_frame(), _session->audible_frame()+1, locs, Location::Flags(0));
2306 for (Locations::LocationList::iterator i = locs.begin(); i != locs.end(); ++i) {
2307 if ((*i)->is_mark()) {
2308 _session->locations()->remove (*i);
2315 begin_reversible_command (_("remove marker"));
2316 XMLNode &after = _session->locations()->get_state();
2317 _session->add_command(new MementoCommand<Locations>(*(_session->locations()), &before, &after));
2318 commit_reversible_command ();
2325 Editor::remove_location_at_playhead_cursor ()
2327 do_remove_location_at_playhead_cursor ();
2330 /** Add a range marker around each selected region */
2332 Editor::add_locations_from_region ()
2334 RegionSelection rs = get_regions_from_selection_and_entered ();
2339 bool commit = false;
2341 XMLNode &before = _session->locations()->get_state();
2343 for (RegionSelection::iterator i = rs.begin (); i != rs.end (); ++i) {
2345 boost::shared_ptr<Region> region = (*i)->region ();
2347 Location *location = new Location (*_session, region->position(), region->last_frame(), region->name(), Location::IsRangeMarker);
2349 _session->locations()->add (location, true);
2354 begin_reversible_command (selection->regions.size () > 1 ? _("add markers") : _("add marker"));
2355 XMLNode &after = _session->locations()->get_state();
2356 _session->add_command (new MementoCommand<Locations>(*(_session->locations()), &before, &after));
2357 commit_reversible_command ();
2361 /** Add a single range marker around all selected regions */
2363 Editor::add_location_from_region ()
2365 RegionSelection rs = get_regions_from_selection_and_entered ();
2371 XMLNode &before = _session->locations()->get_state();
2375 if (rs.size() > 1) {
2376 _session->locations()->next_available_name(markername, "regions");
2378 RegionView* rv = *(rs.begin());
2379 boost::shared_ptr<Region> region = rv->region();
2380 markername = region->name();
2383 if (!choose_new_marker_name(markername)) {
2387 // single range spanning all selected
2388 Location *location = new Location (*_session, selection->regions.start(), selection->regions.end_frame(), markername, Location::IsRangeMarker);
2389 _session->locations()->add (location, true);
2391 begin_reversible_command (_("add marker"));
2392 XMLNode &after = _session->locations()->get_state();
2393 _session->add_command (new MementoCommand<Locations>(*(_session->locations()), &before, &after));
2394 commit_reversible_command ();
2400 Editor::jump_forward_to_mark ()
2406 framepos_t pos = _session->locations()->first_mark_after (playhead_cursor->current_frame());
2412 _session->request_locate (pos, _session->transport_rolling());
2416 Editor::jump_backward_to_mark ()
2422 framepos_t pos = _session->locations()->first_mark_before (playhead_cursor->current_frame());
2428 _session->request_locate (pos, _session->transport_rolling());
2434 framepos_t const pos = _session->audible_frame ();
2437 _session->locations()->next_available_name (markername, "mark");
2439 if (!choose_new_marker_name (markername)) {
2443 _session->locations()->add (new Location (*_session, pos, 0, markername, Location::IsMark), true);
2447 Editor::clear_markers ()
2450 begin_reversible_command (_("clear markers"));
2452 XMLNode &before = _session->locations()->get_state();
2453 _session->locations()->clear_markers ();
2454 XMLNode &after = _session->locations()->get_state();
2455 _session->add_command(new MementoCommand<Locations>(*(_session->locations()), &before, &after));
2457 commit_reversible_command ();
2462 Editor::clear_ranges ()
2465 begin_reversible_command (_("clear ranges"));
2467 XMLNode &before = _session->locations()->get_state();
2469 _session->locations()->clear_ranges ();
2471 XMLNode &after = _session->locations()->get_state();
2472 _session->add_command(new MementoCommand<Locations>(*(_session->locations()), &before, &after));
2474 commit_reversible_command ();
2479 Editor::clear_locations ()
2481 begin_reversible_command (_("clear locations"));
2483 XMLNode &before = _session->locations()->get_state();
2484 _session->locations()->clear ();
2485 XMLNode &after = _session->locations()->get_state();
2486 _session->add_command(new MementoCommand<Locations>(*(_session->locations()), &before, &after));
2488 commit_reversible_command ();
2492 Editor::unhide_markers ()
2494 for (LocationMarkerMap::iterator i = location_markers.begin(); i != location_markers.end(); ++i) {
2495 Location *l = (*i).first;
2496 if (l->is_hidden() && l->is_mark()) {
2497 l->set_hidden(false, this);
2503 Editor::unhide_ranges ()
2505 for (LocationMarkerMap::iterator i = location_markers.begin(); i != location_markers.end(); ++i) {
2506 Location *l = (*i).first;
2507 if (l->is_hidden() && l->is_range_marker()) {
2508 l->set_hidden(false, this);
2513 /* INSERT/REPLACE */
2516 Editor::insert_region_list_selection (float times)
2518 RouteTimeAxisView *tv = 0;
2519 boost::shared_ptr<Playlist> playlist;
2521 if (clicked_routeview != 0) {
2522 tv = clicked_routeview;
2523 } else if (!selection->tracks.empty()) {
2524 if ((tv = dynamic_cast<RouteTimeAxisView*>(selection->tracks.front())) == 0) {
2527 } else if (entered_track != 0) {
2528 if ((tv = dynamic_cast<RouteTimeAxisView*>(entered_track)) == 0) {
2535 if ((playlist = tv->playlist()) == 0) {
2539 boost::shared_ptr<Region> region = _regions->get_single_selection ();
2544 begin_reversible_command (_("insert region"));
2545 playlist->clear_changes ();
2546 playlist->add_region ((RegionFactory::create (region, true)), get_preferred_edit_position(), times);
2547 if (Config->get_edit_mode() == Ripple)
2548 playlist->ripple (get_preferred_edit_position(), region->length() * times, boost::shared_ptr<Region>());
2550 _session->add_command(new StatefulDiffCommand (playlist));
2551 commit_reversible_command ();
2554 /* BUILT-IN EFFECTS */
2557 Editor::reverse_selection ()
2562 /* GAIN ENVELOPE EDITING */
2565 Editor::edit_envelope ()
2572 Editor::transition_to_rolling (bool fwd)
2578 if (_session->config.get_external_sync()) {
2579 switch (Config->get_sync_source()) {
2583 /* transport controlled by the master */
2588 if (_session->is_auditioning()) {
2589 _session->cancel_audition ();
2593 _session->request_transport_speed (fwd ? 1.0f : -1.0f);
2597 Editor::play_from_start ()
2599 _session->request_locate (_session->current_start_frame(), true);
2603 Editor::play_from_edit_point ()
2605 _session->request_locate (get_preferred_edit_position(), true);
2609 Editor::play_from_edit_point_and_return ()
2611 framepos_t start_frame;
2612 framepos_t return_frame;
2614 start_frame = get_preferred_edit_position ( EDIT_IGNORE_PHEAD );
2616 if (_session->transport_rolling()) {
2617 _session->request_locate (start_frame, false);
2621 /* don't reset the return frame if its already set */
2623 if ((return_frame = _session->requested_return_frame()) < 0) {
2624 return_frame = _session->audible_frame();
2627 if (start_frame >= 0) {
2628 _session->request_roll_at_and_return (start_frame, return_frame);
2633 Editor::play_selection ()
2635 framepos_t start, end;
2636 if (!get_selection_extents ( start, end))
2639 AudioRange ar (start, end, 0);
2640 list<AudioRange> lar;
2643 _session->request_play_range (&lar, true);
2647 Editor::get_preroll ()
2649 return Config->get_preroll_seconds() * _session->frame_rate();
2654 Editor::maybe_locate_with_edit_preroll ( framepos_t location )
2656 if ( _session->transport_rolling() || !UIConfiguration::instance().get_follow_edits() || _ignore_follow_edits || _session->config.get_external_sync() )
2659 location -= get_preroll();
2661 //don't try to locate before the beginning of time
2665 //if follow_playhead is on, keep the playhead on the screen
2666 if ( _follow_playhead )
2667 if ( location < leftmost_frame )
2668 location = leftmost_frame;
2670 _session->request_locate( location );
2674 Editor::play_with_preroll ()
2677 framepos_t preroll = get_preroll();
2679 framepos_t start, end;
2680 if (!get_selection_extents ( start, end))
2683 if (start > preroll)
2684 start = start - preroll;
2686 end = end + preroll; //"post-roll"
2688 AudioRange ar (start, end, 0);
2689 list<AudioRange> lar;
2692 _session->request_play_range (&lar, true);
2697 Editor::play_location (Location& location)
2699 if (location.start() <= location.end()) {
2703 _session->request_bounded_roll (location.start(), location.end());
2707 Editor::loop_location (Location& location)
2709 if (location.start() <= location.end()) {
2715 if ((tll = transport_loop_location()) != 0) {
2716 tll->set (location.start(), location.end());
2718 // enable looping, reposition and start rolling
2719 _session->request_locate (tll->start(), true);
2720 _session->request_play_loop (true);
2725 Editor::do_layer_operation (LayerOperation op)
2727 if (selection->regions.empty ()) {
2731 bool const multiple = selection->regions.size() > 1;
2735 begin_reversible_command (_("raise regions"));
2737 begin_reversible_command (_("raise region"));
2743 begin_reversible_command (_("raise regions to top"));
2745 begin_reversible_command (_("raise region to top"));
2751 begin_reversible_command (_("lower regions"));
2753 begin_reversible_command (_("lower region"));
2759 begin_reversible_command (_("lower regions to bottom"));
2761 begin_reversible_command (_("lower region"));
2766 set<boost::shared_ptr<Playlist> > playlists = selection->regions.playlists ();
2767 for (set<boost::shared_ptr<Playlist> >::iterator i = playlists.begin(); i != playlists.end(); ++i) {
2768 (*i)->clear_owned_changes ();
2771 for (RegionSelection::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
2772 boost::shared_ptr<Region> r = (*i)->region ();
2784 r->lower_to_bottom ();
2788 for (set<boost::shared_ptr<Playlist> >::iterator i = playlists.begin(); i != playlists.end(); ++i) {
2789 vector<Command*> cmds;
2791 _session->add_commands (cmds);
2794 commit_reversible_command ();
2798 Editor::raise_region ()
2800 do_layer_operation (Raise);
2804 Editor::raise_region_to_top ()
2806 do_layer_operation (RaiseToTop);
2810 Editor::lower_region ()
2812 do_layer_operation (Lower);
2816 Editor::lower_region_to_bottom ()
2818 do_layer_operation (LowerToBottom);
2821 /** Show the region editor for the selected regions */
2823 Editor::show_region_properties ()
2825 selection->foreach_regionview (&RegionView::show_region_editor);
2828 /** Show the midi list editor for the selected MIDI regions */
2830 Editor::show_midi_list_editor ()
2832 selection->foreach_midi_regionview (&MidiRegionView::show_list_editor);
2836 Editor::rename_region ()
2838 RegionSelection rs = get_regions_from_selection_and_entered ();
2844 ArdourDialog d (_("Rename Region"), true, false);
2846 Label label (_("New name:"));
2849 hbox.set_spacing (6);
2850 hbox.pack_start (label, false, false);
2851 hbox.pack_start (entry, true, true);
2853 d.get_vbox()->set_border_width (12);
2854 d.get_vbox()->pack_start (hbox, false, false);
2856 d.add_button(Gtk::Stock::CANCEL, Gtk::RESPONSE_CANCEL);
2857 d.add_button(Gtk::Stock::OK, Gtk::RESPONSE_OK);
2859 d.set_size_request (300, -1);
2861 entry.set_text (rs.front()->region()->name());
2862 entry.select_region (0, -1);
2864 entry.signal_activate().connect (sigc::bind (sigc::mem_fun (d, &Dialog::response), RESPONSE_OK));
2870 int const ret = d.run();
2874 if (ret != RESPONSE_OK) {
2878 std::string str = entry.get_text();
2879 strip_whitespace_edges (str);
2881 rs.front()->region()->set_name (str);
2882 _regions->redisplay ();
2886 /** Start an audition of the first selected region */
2888 Editor::play_edit_range ()
2890 framepos_t start, end;
2892 if (get_edit_op_range (start, end)) {
2893 _session->request_bounded_roll (start, end);
2898 Editor::play_selected_region ()
2900 framepos_t start = max_framepos;
2903 RegionSelection rs = get_regions_from_selection_and_entered ();
2909 for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
2910 if ((*i)->region()->position() < start) {
2911 start = (*i)->region()->position();
2913 if ((*i)->region()->last_frame() + 1 > end) {
2914 end = (*i)->region()->last_frame() + 1;
2918 _session->request_bounded_roll (start, end);
2922 Editor::audition_playlist_region_standalone (boost::shared_ptr<Region> region)
2924 _session->audition_region (region);
2928 Editor::region_from_selection ()
2930 if (clicked_axisview == 0) {
2934 if (selection->time.empty()) {
2938 framepos_t start = selection->time[clicked_selection].start;
2939 framepos_t end = selection->time[clicked_selection].end;
2941 TrackViewList tracks = get_tracks_for_range_action ();
2943 framepos_t selection_cnt = end - start + 1;
2945 for (TrackSelection::iterator i = tracks.begin(); i != tracks.end(); ++i) {
2946 boost::shared_ptr<Region> current;
2947 boost::shared_ptr<Playlist> pl;
2948 framepos_t internal_start;
2951 if ((pl = (*i)->playlist()) == 0) {
2955 if ((current = pl->top_region_at (start)) == 0) {
2959 internal_start = start - current->position();
2960 RegionFactory::region_name (new_name, current->name(), true);
2964 plist.add (ARDOUR::Properties::start, current->start() + internal_start);
2965 plist.add (ARDOUR::Properties::length, selection_cnt);
2966 plist.add (ARDOUR::Properties::name, new_name);
2967 plist.add (ARDOUR::Properties::layer, 0);
2969 boost::shared_ptr<Region> region (RegionFactory::create (current, plist));
2974 Editor::create_region_from_selection (vector<boost::shared_ptr<Region> >& new_regions)
2976 if (selection->time.empty() || selection->tracks.empty()) {
2980 framepos_t start, end;
2981 if (clicked_selection) {
2982 start = selection->time[clicked_selection].start;
2983 end = selection->time[clicked_selection].end;
2985 start = selection->time.start();
2986 end = selection->time.end_frame();
2989 TrackViewList ts = selection->tracks.filter_to_unique_playlists ();
2990 sort_track_selection (ts);
2992 for (TrackSelection::iterator i = ts.begin(); i != ts.end(); ++i) {
2993 boost::shared_ptr<Region> current;
2994 boost::shared_ptr<Playlist> playlist;
2995 framepos_t internal_start;
2998 if ((playlist = (*i)->playlist()) == 0) {
3002 if ((current = playlist->top_region_at(start)) == 0) {
3006 internal_start = start - current->position();
3007 RegionFactory::region_name (new_name, current->name(), true);
3011 plist.add (ARDOUR::Properties::start, current->start() + internal_start);
3012 plist.add (ARDOUR::Properties::length, end - start + 1);
3013 plist.add (ARDOUR::Properties::name, new_name);
3015 new_regions.push_back (RegionFactory::create (current, plist));
3020 Editor::split_multichannel_region ()
3022 RegionSelection rs = get_regions_from_selection_and_entered ();
3028 vector< boost::shared_ptr<Region> > v;
3030 for (list<RegionView*>::iterator x = rs.begin(); x != rs.end(); ++x) {
3031 (*x)->region()->separate_by_channel (*_session, v);
3036 Editor::new_region_from_selection ()
3038 region_from_selection ();
3039 cancel_selection ();
3043 add_if_covered (RegionView* rv, const AudioRange* ar, RegionSelection* rs)
3045 switch (rv->region()->coverage (ar->start, ar->end - 1)) {
3046 // n.b. -1 because AudioRange::end is one past the end, but coverage expects inclusive ranges
3047 case Evoral::OverlapNone:
3055 * - selected tracks, or if there are none...
3056 * - tracks containing selected regions, or if there are none...
3061 Editor::get_tracks_for_range_action () const
3065 if (selection->tracks.empty()) {
3067 /* use tracks with selected regions */
3069 RegionSelection rs = selection->regions;
3071 for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
3072 TimeAxisView* tv = &(*i)->get_time_axis_view();
3074 if (!t.contains (tv)) {
3080 /* no regions and no tracks: use all tracks */
3086 t = selection->tracks;
3089 return t.filter_to_unique_playlists();
3093 Editor::separate_regions_between (const TimeSelection& ts)
3095 bool in_command = false;
3096 boost::shared_ptr<Playlist> playlist;
3097 RegionSelection new_selection;
3099 TrackViewList tmptracks = get_tracks_for_range_action ();
3100 sort_track_selection (tmptracks);
3102 for (TrackSelection::iterator i = tmptracks.begin(); i != tmptracks.end(); ++i) {
3104 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> ((*i));
3110 if (!rtv->is_track()) {
3114 /* no edits to destructive tracks */
3116 if (rtv->track()->destructive()) {
3120 if ((playlist = rtv->playlist()) != 0) {
3122 playlist->clear_changes ();
3124 /* XXX need to consider musical time selections here at some point */
3126 double speed = rtv->track()->speed();
3128 for (list<AudioRange>::const_iterator t = ts.begin(); t != ts.end(); ++t) {
3130 sigc::connection c = rtv->view()->RegionViewAdded.connect (
3131 sigc::mem_fun(*this, &Editor::collect_new_region_view));
3133 latest_regionviews.clear ();
3135 playlist->partition ((framepos_t)((*t).start * speed),
3136 (framepos_t)((*t).end * speed), false);
3140 if (!latest_regionviews.empty()) {
3142 rtv->view()->foreach_regionview (sigc::bind (
3143 sigc::ptr_fun (add_if_covered),
3144 &(*t), &new_selection));
3147 begin_reversible_command (_("separate"));
3151 /* pick up changes to existing regions */
3153 vector<Command*> cmds;
3154 playlist->rdiff (cmds);
3155 _session->add_commands (cmds);
3157 /* pick up changes to the playlist itself (adds/removes)
3160 _session->add_command(new StatefulDiffCommand (playlist));
3167 // selection->set (new_selection);
3169 commit_reversible_command ();
3173 struct PlaylistState {
3174 boost::shared_ptr<Playlist> playlist;
3178 /** Take tracks from get_tracks_for_range_action and cut any regions
3179 * on those tracks so that the tracks are empty over the time
3183 Editor::separate_region_from_selection ()
3185 /* preferentially use *all* ranges in the time selection if we're in range mode
3186 to allow discontiguous operation, since get_edit_op_range() currently
3187 returns a single range.
3190 if (!selection->time.empty()) {
3192 separate_regions_between (selection->time);
3199 if (get_edit_op_range (start, end)) {
3201 AudioRange ar (start, end, 1);
3205 separate_regions_between (ts);
3211 Editor::separate_region_from_punch ()
3213 Location* loc = _session->locations()->auto_punch_location();
3215 separate_regions_using_location (*loc);
3220 Editor::separate_region_from_loop ()
3222 Location* loc = _session->locations()->auto_loop_location();
3224 separate_regions_using_location (*loc);
3229 Editor::separate_regions_using_location (Location& loc)
3231 if (loc.is_mark()) {
3235 AudioRange ar (loc.start(), loc.end(), 1);
3240 separate_regions_between (ts);
3243 /** Separate regions under the selected region */
3245 Editor::separate_under_selected_regions ()
3247 vector<PlaylistState> playlists;
3251 rs = get_regions_from_selection_and_entered();
3253 if (!_session || rs.empty()) {
3257 begin_reversible_command (_("separate region under"));
3259 list<boost::shared_ptr<Region> > regions_to_remove;
3261 for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
3262 // we can't just remove the region(s) in this loop because
3263 // this removes them from the RegionSelection, and they thus
3264 // disappear from underneath the iterator, and the ++i above
3265 // SEGVs in a puzzling fashion.
3267 // so, first iterate over the regions to be removed from rs and
3268 // add them to the regions_to_remove list, and then
3269 // iterate over the list to actually remove them.
3271 regions_to_remove.push_back ((*i)->region());
3274 for (list<boost::shared_ptr<Region> >::iterator rl = regions_to_remove.begin(); rl != regions_to_remove.end(); ++rl) {
3276 boost::shared_ptr<Playlist> playlist = (*rl)->playlist();
3279 // is this check necessary?
3283 vector<PlaylistState>::iterator i;
3285 //only take state if this is a new playlist.
3286 for (i = playlists.begin(); i != playlists.end(); ++i) {
3287 if ((*i).playlist == playlist) {
3292 if (i == playlists.end()) {
3294 PlaylistState before;
3295 before.playlist = playlist;
3296 before.before = &playlist->get_state();
3298 playlist->freeze ();
3299 playlists.push_back(before);
3302 //Partition on the region bounds
3303 playlist->partition ((*rl)->first_frame() - 1, (*rl)->last_frame() + 1, true);
3305 //Re-add region that was just removed due to the partition operation
3306 playlist->add_region( (*rl), (*rl)->first_frame() );
3309 vector<PlaylistState>::iterator pl;
3311 for (pl = playlists.begin(); pl != playlists.end(); ++pl) {
3312 (*pl).playlist->thaw ();
3313 _session->add_command(new MementoCommand<Playlist>(*(*pl).playlist, (*pl).before, &(*pl).playlist->get_state()));
3316 commit_reversible_command ();
3320 Editor::crop_region_to_selection ()
3322 if (!selection->time.empty()) {
3324 crop_region_to (selection->time.start(), selection->time.end_frame());
3331 if (get_edit_op_range (start, end)) {
3332 crop_region_to (start, end);
3339 Editor::crop_region_to (framepos_t start, framepos_t end)
3341 vector<boost::shared_ptr<Playlist> > playlists;
3342 boost::shared_ptr<Playlist> playlist;
3345 if (selection->tracks.empty()) {
3346 ts = track_views.filter_to_unique_playlists();
3348 ts = selection->tracks.filter_to_unique_playlists ();
3351 sort_track_selection (ts);
3353 for (TrackSelection::iterator i = ts.begin(); i != ts.end(); ++i) {
3355 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> ((*i));
3361 boost::shared_ptr<Track> t = rtv->track();
3363 if (t != 0 && ! t->destructive()) {
3365 if ((playlist = rtv->playlist()) != 0) {
3366 playlists.push_back (playlist);
3371 if (playlists.empty()) {
3376 framepos_t new_start;
3378 framecnt_t new_length;
3379 bool in_command = false;
3381 for (vector<boost::shared_ptr<Playlist> >::iterator i = playlists.begin(); i != playlists.end(); ++i) {
3383 /* Only the top regions at start and end have to be cropped */
3384 boost::shared_ptr<Region> region_at_start = (*i)->top_region_at(start);
3385 boost::shared_ptr<Region> region_at_end = (*i)->top_region_at(end);
3387 vector<boost::shared_ptr<Region> > regions;
3389 if (region_at_start != 0) {
3390 regions.push_back (region_at_start);
3392 if (region_at_end != 0) {
3393 regions.push_back (region_at_end);
3396 /* now adjust lengths */
3397 for (vector<boost::shared_ptr<Region> >::iterator i = regions.begin(); i != regions.end(); ++i) {
3399 pos = (*i)->position();
3400 new_start = max (start, pos);
3401 if (max_framepos - pos > (*i)->length()) {
3402 new_end = pos + (*i)->length() - 1;
3404 new_end = max_framepos;
3406 new_end = min (end, new_end);
3407 new_length = new_end - new_start + 1;
3410 begin_reversible_command (_("trim to selection"));
3413 (*i)->clear_changes ();
3414 (*i)->trim_to (new_start, new_length);
3415 _session->add_command (new StatefulDiffCommand (*i));
3420 commit_reversible_command ();
3425 Editor::region_fill_track ()
3427 boost::shared_ptr<Playlist> playlist;
3428 RegionSelection regions = get_regions_from_selection_and_entered ();
3429 RegionSelection foo;
3431 framepos_t const end = _session->current_end_frame ();
3433 if (regions.empty () || regions.end_frame () + 1 >= end) {
3437 framepos_t const start_frame = regions.start ();
3438 framepos_t const end_frame = regions.end_frame ();
3439 framecnt_t const gap = end_frame - start_frame + 1;
3441 begin_reversible_command (Operations::region_fill);
3443 selection->clear_regions ();
3445 for (RegionSelection::iterator i = regions.begin(); i != regions.end(); ++i) {
3447 boost::shared_ptr<Region> r ((*i)->region());
3449 TimeAxisView& tv = (*i)->get_time_axis_view();
3450 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (&tv);
3451 latest_regionviews.clear ();
3452 sigc::connection c = rtv->view()->RegionViewAdded.connect (sigc::mem_fun(*this, &Editor::collect_new_region_view));
3454 framepos_t const position = end_frame + (r->first_frame() - start_frame + 1);
3455 playlist = (*i)->region()->playlist();
3456 playlist->clear_changes ();
3457 playlist->duplicate_until (r, position, gap, end);
3458 _session->add_command(new StatefulDiffCommand (playlist));
3462 foo.insert (foo.end(), latest_regionviews.begin(), latest_regionviews.end());
3466 selection->set (foo);
3469 commit_reversible_command ();
3473 Editor::set_region_sync_position ()
3475 set_sync_point (get_preferred_edit_position (), get_regions_from_selection_and_edit_point ());
3479 Editor::set_sync_point (framepos_t where, const RegionSelection& rs)
3481 bool in_command = false;
3483 for (RegionSelection::const_iterator r = rs.begin(); r != rs.end(); ++r) {
3485 if (!(*r)->region()->covers (where)) {
3489 boost::shared_ptr<Region> region ((*r)->region());
3492 begin_reversible_command (_("set sync point"));
3496 region->clear_changes ();
3497 region->set_sync_position (where);
3498 _session->add_command(new StatefulDiffCommand (region));
3502 commit_reversible_command ();
3506 /** Remove the sync positions of the selection */
3508 Editor::remove_region_sync ()
3510 RegionSelection rs = get_regions_from_selection_and_entered ();
3516 begin_reversible_command (_("remove region sync"));
3518 for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
3520 (*i)->region()->clear_changes ();
3521 (*i)->region()->clear_sync_position ();
3522 _session->add_command(new StatefulDiffCommand ((*i)->region()));
3525 commit_reversible_command ();
3529 Editor::naturalize_region ()
3531 RegionSelection rs = get_regions_from_selection_and_entered ();
3537 if (rs.size() > 1) {
3538 begin_reversible_command (_("move regions to original position"));
3540 begin_reversible_command (_("move region to original position"));
3543 for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
3544 (*i)->region()->clear_changes ();
3545 (*i)->region()->move_to_natural_position ();
3546 _session->add_command (new StatefulDiffCommand ((*i)->region()));
3549 commit_reversible_command ();
3553 Editor::align_regions (RegionPoint what)
3555 RegionSelection const rs = get_regions_from_selection_and_edit_point ();
3561 begin_reversible_command (_("align selection"));
3563 framepos_t const position = get_preferred_edit_position ();
3565 for (RegionSelection::const_iterator i = rs.begin(); i != rs.end(); ++i) {
3566 align_region_internal ((*i)->region(), what, position);
3569 commit_reversible_command ();
3572 struct RegionSortByTime {
3573 bool operator() (const RegionView* a, const RegionView* b) {
3574 return a->region()->position() < b->region()->position();
3579 Editor::align_regions_relative (RegionPoint point)
3581 RegionSelection const rs = get_regions_from_selection_and_edit_point ();
3587 framepos_t const position = get_preferred_edit_position ();
3589 framepos_t distance = 0;
3593 list<RegionView*> sorted;
3594 rs.by_position (sorted);
3596 boost::shared_ptr<Region> r ((*sorted.begin())->region());
3601 if (position > r->position()) {
3602 distance = position - r->position();
3604 distance = r->position() - position;
3610 if (position > r->last_frame()) {
3611 distance = position - r->last_frame();
3612 pos = r->position() + distance;
3614 distance = r->last_frame() - position;
3615 pos = r->position() - distance;
3621 pos = r->adjust_to_sync (position);
3622 if (pos > r->position()) {
3623 distance = pos - r->position();
3625 distance = r->position() - pos;
3631 if (pos == r->position()) {
3635 begin_reversible_command (_("align selection (relative)"));
3637 /* move first one specially */
3639 r->clear_changes ();
3640 r->set_position (pos);
3641 _session->add_command(new StatefulDiffCommand (r));
3643 /* move rest by the same amount */
3647 for (list<RegionView*>::iterator i = sorted.begin(); i != sorted.end(); ++i) {
3649 boost::shared_ptr<Region> region ((*i)->region());
3651 region->clear_changes ();
3654 region->set_position (region->position() + distance);
3656 region->set_position (region->position() - distance);
3659 _session->add_command(new StatefulDiffCommand (region));
3663 commit_reversible_command ();
3667 Editor::align_region (boost::shared_ptr<Region> region, RegionPoint point, framepos_t position)
3669 begin_reversible_command (_("align region"));
3670 align_region_internal (region, point, position);
3671 commit_reversible_command ();
3675 Editor::align_region_internal (boost::shared_ptr<Region> region, RegionPoint point, framepos_t position)
3677 region->clear_changes ();
3681 region->set_position (region->adjust_to_sync (position));
3685 if (position > region->length()) {
3686 region->set_position (position - region->length());
3691 region->set_position (position);
3695 _session->add_command(new StatefulDiffCommand (region));
3699 Editor::trim_region_front ()
3705 Editor::trim_region_back ()
3707 trim_region (false);
3711 Editor::trim_region (bool front)
3713 framepos_t where = get_preferred_edit_position();
3714 RegionSelection rs = get_regions_from_selection_and_edit_point ();
3720 begin_reversible_command (front ? _("trim front") : _("trim back"));
3722 for (list<RegionView*>::const_iterator i = rs.by_layer().begin(); i != rs.by_layer().end(); ++i) {
3723 if (!(*i)->region()->locked()) {
3725 (*i)->region()->clear_changes ();
3728 (*i)->region()->trim_front (where);
3729 maybe_locate_with_edit_preroll ( where );
3731 (*i)->region()->trim_end (where);
3732 maybe_locate_with_edit_preroll ( where );
3735 _session->add_command (new StatefulDiffCommand ((*i)->region()));
3739 commit_reversible_command ();
3742 /** Trim the end of the selected regions to the position of the edit cursor */
3744 Editor::trim_region_to_loop ()
3746 Location* loc = _session->locations()->auto_loop_location();
3750 trim_region_to_location (*loc, _("trim to loop"));
3754 Editor::trim_region_to_punch ()
3756 Location* loc = _session->locations()->auto_punch_location();
3760 trim_region_to_location (*loc, _("trim to punch"));
3764 Editor::trim_region_to_location (const Location& loc, const char* str)
3766 RegionSelection rs = get_regions_from_selection_and_entered ();
3767 bool in_command = false;
3769 for (RegionSelection::iterator x = rs.begin(); x != rs.end(); ++x) {
3770 RegionView* rv = (*x);
3772 /* require region to span proposed trim */
3773 switch (rv->region()->coverage (loc.start(), loc.end())) {
3774 case Evoral::OverlapInternal:
3780 RouteTimeAxisView* tav = dynamic_cast<RouteTimeAxisView*> (&rv->get_time_axis_view());
3789 if (tav->track() != 0) {
3790 speed = tav->track()->speed();
3793 start = session_frame_to_track_frame (loc.start(), speed);
3794 end = session_frame_to_track_frame (loc.end(), speed);
3796 rv->region()->clear_changes ();
3797 rv->region()->trim_to (start, (end - start));
3800 begin_reversible_command (str);
3803 _session->add_command(new StatefulDiffCommand (rv->region()));
3807 commit_reversible_command ();
3812 Editor::trim_region_to_previous_region_end ()
3814 return trim_to_region(false);
3818 Editor::trim_region_to_next_region_start ()
3820 return trim_to_region(true);
3824 Editor::trim_to_region(bool forward)
3826 RegionSelection rs = get_regions_from_selection_and_entered ();
3827 bool in_command = false;
3829 boost::shared_ptr<Region> next_region;
3831 for (RegionSelection::iterator x = rs.begin(); x != rs.end(); ++x) {
3833 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (*x);
3839 AudioTimeAxisView* atav = dynamic_cast<AudioTimeAxisView*> (&arv->get_time_axis_view());
3847 if (atav->track() != 0) {
3848 speed = atav->track()->speed();
3852 boost::shared_ptr<Region> region = arv->region();
3853 boost::shared_ptr<Playlist> playlist (region->playlist());
3855 region->clear_changes ();
3859 next_region = playlist->find_next_region (region->first_frame(), Start, 1);
3865 region->trim_end((framepos_t) ( (next_region->first_frame() - 1) * speed));
3866 arv->region_changed (PropertyChange (ARDOUR::Properties::length));
3870 next_region = playlist->find_next_region (region->first_frame(), Start, 0);
3876 region->trim_front((framepos_t) ((next_region->last_frame() + 1) * speed));
3878 arv->region_changed (ARDOUR::bounds_change);
3882 begin_reversible_command (_("trim to region"));
3885 _session->add_command(new StatefulDiffCommand (region));
3889 commit_reversible_command ();
3894 Editor::unfreeze_route ()
3896 if (clicked_routeview == 0 || !clicked_routeview->is_track()) {
3900 clicked_routeview->track()->unfreeze ();
3904 Editor::_freeze_thread (void* arg)
3906 return static_cast<Editor*>(arg)->freeze_thread ();
3910 Editor::freeze_thread ()
3912 /* create event pool because we may need to talk to the session */
3913 SessionEvent::create_per_thread_pool ("freeze events", 64);
3914 /* create per-thread buffers for process() tree to use */
3915 clicked_routeview->audio_track()->freeze_me (*current_interthread_info);
3916 current_interthread_info->done = true;
3921 Editor::freeze_route ()
3927 /* stop transport before we start. this is important */
3929 _session->request_transport_speed (0.0);
3931 /* wait for just a little while, because the above call is asynchronous */
3933 Glib::usleep (250000);
3935 if (clicked_routeview == 0 || !clicked_routeview->is_audio_track()) {
3939 if (!clicked_routeview->track()->bounceable (clicked_routeview->track()->main_outs(), true)) {
3941 _("This track/bus cannot be frozen because the signal adds or loses channels before reaching the outputs.\n"
3942 "This is typically caused by plugins that generate stereo output from mono input or vice versa.")
3944 d.set_title (_("Cannot freeze"));
3949 if (clicked_routeview->track()->has_external_redirects()) {
3950 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"
3951 "Freezing will only process the signal as far as the first send/insert/return."),
3952 clicked_routeview->track()->name()), true, MESSAGE_INFO, BUTTONS_NONE, true);
3954 d.add_button (_("Freeze anyway"), Gtk::RESPONSE_OK);
3955 d.add_button (_("Don't freeze"), Gtk::RESPONSE_CANCEL);
3956 d.set_title (_("Freeze Limits"));
3958 int response = d.run ();
3961 case Gtk::RESPONSE_CANCEL:
3968 InterThreadInfo itt;
3969 current_interthread_info = &itt;
3971 InterthreadProgressWindow ipw (current_interthread_info, _("Freeze"), _("Cancel Freeze"));
3973 pthread_create_and_store (X_("freezer"), &itt.thread, _freeze_thread, this);
3975 CursorContext::Handle cursor_ctx = CursorContext::create(*this, _cursors->wait);
3977 while (!itt.done && !itt.cancel) {
3978 gtk_main_iteration ();
3981 pthread_join (itt.thread, 0);
3982 current_interthread_info = 0;
3986 Editor::bounce_range_selection (bool replace, bool enable_processing)
3988 if (selection->time.empty()) {
3992 TrackSelection views = selection->tracks;
3994 for (TrackViewList::iterator i = views.begin(); i != views.end(); ++i) {
3996 if (enable_processing) {
3998 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (*i);
4000 if (rtv && rtv->track() && replace && enable_processing && !rtv->track()->bounceable (rtv->track()->main_outs(), false)) {
4002 _("You can't perform this operation because the processing of the signal "
4003 "will cause one or more of the tracks to end up with a region with more channels than this track has inputs.\n\n"
4004 "You can do this without processing, which is a different operation.")
4006 d.set_title (_("Cannot bounce"));
4013 framepos_t start = selection->time[clicked_selection].start;
4014 framepos_t end = selection->time[clicked_selection].end;
4015 framepos_t cnt = end - start + 1;
4016 bool in_command = false;
4018 for (TrackViewList::iterator i = views.begin(); i != views.end(); ++i) {
4020 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (*i);
4026 boost::shared_ptr<Playlist> playlist;
4028 if ((playlist = rtv->playlist()) == 0) {
4032 InterThreadInfo itt;
4034 playlist->clear_changes ();
4035 playlist->clear_owned_changes ();
4037 boost::shared_ptr<Region> r;
4039 if (enable_processing) {
4040 r = rtv->track()->bounce_range (start, start+cnt, itt, rtv->track()->main_outs(), false);
4042 r = rtv->track()->bounce_range (start, start+cnt, itt, boost::shared_ptr<Processor>(), false);
4050 list<AudioRange> ranges;
4051 ranges.push_back (AudioRange (start, start+cnt, 0));
4052 playlist->cut (ranges); // discard result
4053 playlist->add_region (r, start);
4057 begin_reversible_command (_("bounce range"));
4060 vector<Command*> cmds;
4061 playlist->rdiff (cmds);
4062 _session->add_commands (cmds);
4064 _session->add_command (new StatefulDiffCommand (playlist));
4068 commit_reversible_command ();
4072 /** Delete selected regions, automation points or a time range */
4076 //special case: if the user is pointing in the editor/mixer strip, they may be trying to delete a plugin.
4077 //we need this because the editor-mixer strip is in the editor window, so it doesn't get the bindings from the mix window
4078 bool deleted = false;
4079 if ( current_mixer_strip && current_mixer_strip == MixerStrip::entered_mixer_strip() )
4080 deleted = current_mixer_strip->delete_processors ();
4086 /** Cut selected regions, automation points or a time range */
4093 /** Copy selected regions, automation points or a time range */
4101 /** @return true if a Cut, Copy or Clear is possible */
4103 Editor::can_cut_copy () const
4105 if (!selection->time.empty() || !selection->regions.empty() || !selection->points.empty())
4112 /** Cut, copy or clear selected regions, automation points or a time range.
4113 * @param op Operation (Delete, Cut, Copy or Clear)
4116 Editor::cut_copy (CutCopyOp op)
4118 /* only cancel selection if cut/copy is successful.*/
4124 opname = _("delete");
4133 opname = _("clear");
4137 /* if we're deleting something, and the mouse is still pressed,
4138 the thing we started a drag for will be gone when we release
4139 the mouse button(s). avoid this. see part 2 at the end of
4143 if (op == Delete || op == Cut || op == Clear) {
4144 if (_drags->active ()) {
4149 if ( op != Delete ) //"Delete" doesn't change copy/paste buf
4150 cut_buffer->clear ();
4152 if (entered_marker) {
4154 /* cut/delete op while pointing at a marker */
4157 Location* loc = find_location_from_marker (entered_marker, ignored);
4159 if (_session && loc) {
4160 entered_marker = NULL;
4161 Glib::signal_idle().connect (sigc::bind (sigc::mem_fun(*this, &Editor::really_remove_marker), loc));
4168 switch (mouse_mode) {
4171 begin_reversible_command (opname + ' ' + X_("MIDI"));
4173 commit_reversible_command ();
4179 bool did_edit = false;
4181 if (!selection->regions.empty() || !selection->points.empty()) {
4182 begin_reversible_command (opname + ' ' + _("objects"));
4185 if (!selection->regions.empty()) {
4186 cut_copy_regions (op, selection->regions);
4188 if (op == Cut || op == Delete) {
4189 selection->clear_regions ();
4193 if (!selection->points.empty()) {
4194 cut_copy_points (op);
4196 if (op == Cut || op == Delete) {
4197 selection->clear_points ();
4200 } else if (selection->time.empty()) {
4201 framepos_t start, end;
4202 /* no time selection, see if we can get an edit range
4205 if (get_edit_op_range (start, end)) {
4206 selection->set (start, end);
4208 } else if (!selection->time.empty()) {
4209 begin_reversible_command (opname + ' ' + _("range"));
4212 cut_copy_ranges (op);
4214 if (op == Cut || op == Delete) {
4215 selection->clear_time ();
4220 /* reset repeated paste state */
4223 commit_reversible_command ();
4226 if (op == Delete || op == Cut || op == Clear) {
4232 struct AutomationRecord {
4233 AutomationRecord () : state (0) , line(NULL) {}
4234 AutomationRecord (XMLNode* s, const AutomationLine* l) : state (s) , line (l) {}
4236 XMLNode* state; ///< state before any operation
4237 const AutomationLine* line; ///< line this came from
4238 boost::shared_ptr<Evoral::ControlList> copy; ///< copied events for the cut buffer
4240 struct PointsSelectionPositionSorter {
4241 bool operator() (ControlPoint* a, ControlPoint* b) {
4242 return (*(a->model()))->when < (*(b->model()))->when;
4245 /** Cut, copy or clear selected automation points.
4246 * @param op Operation (Cut, Copy or Clear)
4249 Editor::cut_copy_points (Editing::CutCopyOp op, Evoral::Beats earliest, bool midi)
4251 if (selection->points.empty ()) {
4255 /* XXX: not ideal, as there may be more than one track involved in the point selection */
4256 _last_cut_copy_source_track = &selection->points.front()->line().trackview;
4258 /* Keep a record of the AutomationLists that we end up using in this operation */
4259 typedef std::map<boost::shared_ptr<AutomationList>, AutomationRecord> Lists;
4262 /* user could select points in any order */
4263 selection->points.sort(PointsSelectionPositionSorter ());
4265 /* Go through all selected points, making an AutomationRecord for each distinct AutomationList */
4266 for (PointSelection::iterator sel_point = selection->points.begin(); sel_point != selection->points.end(); ++sel_point) {
4267 const AutomationLine& line = (*sel_point)->line();
4268 const boost::shared_ptr<AutomationList> al = line.the_list();
4269 if (lists.find (al) == lists.end ()) {
4270 /* We haven't seen this list yet, so make a record for it. This includes
4271 taking a copy of its current state, in case this is needed for undo later.
4273 lists[al] = AutomationRecord (&al->get_state (), &line);
4277 if (op == Cut || op == Copy) {
4278 /* This operation will involve putting things in the cut buffer, so create an empty
4279 ControlList for each of our source lists to put the cut buffer data in.
4281 for (Lists::iterator i = lists.begin(); i != lists.end(); ++i) {
4282 i->second.copy = i->first->create (i->first->parameter (), i->first->descriptor());
4285 /* Add all selected points to the relevant copy ControlLists */
4286 framepos_t start = std::numeric_limits<framepos_t>::max();
4287 for (PointSelection::iterator sel_point = selection->points.begin(); sel_point != selection->points.end(); ++sel_point) {
4288 boost::shared_ptr<AutomationList> al = (*sel_point)->line().the_list();
4289 AutomationList::const_iterator ctrl_evt = (*sel_point)->model ();
4291 lists[al].copy->fast_simple_add ((*ctrl_evt)->when, (*ctrl_evt)->value);
4293 /* Update earliest MIDI start time in beats */
4294 earliest = std::min(earliest, Evoral::Beats((*ctrl_evt)->when));
4296 /* Update earliest session start time in frames */
4297 start = std::min(start, (*sel_point)->line().session_position(ctrl_evt));
4301 /* Snap start time backwards, so copy/paste is snap aligned. */
4303 if (earliest == Evoral::Beats::max()) {
4304 earliest = Evoral::Beats(); // Weird... don't offset
4306 earliest.round_down_to_beat();
4308 if (start == std::numeric_limits<double>::max()) {
4309 start = 0; // Weird... don't offset
4311 snap_to(start, RoundDownMaybe);
4314 const double line_offset = midi ? earliest.to_double() : start;
4315 for (Lists::iterator i = lists.begin(); i != lists.end(); ++i) {
4316 /* Correct this copy list so that it is relative to the earliest
4317 start time, so relative ordering between points is preserved
4318 when copying from several lists and the paste starts at the
4319 earliest copied piece of data. */
4320 boost::shared_ptr<Evoral::ControlList> &al_cpy = i->second.copy;
4321 for (AutomationList::iterator ctrl_evt = al_cpy->begin(); ctrl_evt != al_cpy->end(); ++ctrl_evt) {
4322 (*ctrl_evt)->when -= line_offset;
4325 /* And add it to the cut buffer */
4326 cut_buffer->add (al_cpy);
4330 if (op == Delete || op == Cut) {
4331 /* This operation needs to remove things from the main AutomationList, so do that now */
4333 for (Lists::iterator i = lists.begin(); i != lists.end(); ++i) {
4334 i->first->freeze ();
4337 /* Remove each selected point from its AutomationList */
4338 for (PointSelection::iterator sel_point = selection->points.begin(); sel_point != selection->points.end(); ++sel_point) {
4339 AutomationLine& line = (*sel_point)->line ();
4340 boost::shared_ptr<AutomationList> al = line.the_list();
4344 if (dynamic_cast<AudioRegionGainLine*> (&line)) {
4345 /* removing of first and last gain point in region gain lines is prohibited*/
4346 if (line.is_last_point (*(*sel_point)) || line.is_first_point (*(*sel_point))) {
4352 al->erase ((*sel_point)->model ());
4356 /* Thaw the lists and add undo records for them */
4357 for (Lists::iterator i = lists.begin(); i != lists.end(); ++i) {
4358 boost::shared_ptr<AutomationList> al = i->first;
4360 _session->add_command (new MementoCommand<AutomationList> (*al.get(), i->second.state, &(al->get_state ())));
4365 /** Cut, copy or clear selected automation points.
4366 * @param op Operation (Cut, Copy or Clear)
4369 Editor::cut_copy_midi (CutCopyOp op)
4371 Evoral::Beats earliest = Evoral::Beats::max();
4372 for (MidiRegionSelection::iterator i = selection->midi_regions.begin(); i != selection->midi_regions.end(); ++i) {
4373 MidiRegionView* mrv = dynamic_cast<MidiRegionView*>(*i);
4375 if (!mrv->selection().empty()) {
4376 earliest = std::min(earliest, (*mrv->selection().begin())->note()->time());
4378 mrv->cut_copy_clear (op);
4380 /* XXX: not ideal, as there may be more than one track involved in the selection */
4381 _last_cut_copy_source_track = &mrv->get_time_axis_view();
4385 if (!selection->points.empty()) {
4386 cut_copy_points (op, earliest, true);
4387 if (op == Cut || op == Delete) {
4388 selection->clear_points ();
4393 struct lt_playlist {
4394 bool operator () (const PlaylistState& a, const PlaylistState& b) {
4395 return a.playlist < b.playlist;
4399 struct PlaylistMapping {
4401 boost::shared_ptr<Playlist> pl;
4403 PlaylistMapping (TimeAxisView* tvp) : tv (tvp) {}
4406 /** Remove `clicked_regionview' */
4408 Editor::remove_clicked_region ()
4410 if (clicked_routeview == 0 || clicked_regionview == 0) {
4414 begin_reversible_command (_("remove region"));
4416 boost::shared_ptr<Playlist> playlist = clicked_routeview->playlist();
4418 playlist->clear_changes ();
4419 playlist->clear_owned_changes ();
4420 playlist->remove_region (clicked_regionview->region());
4421 if (Config->get_edit_mode() == Ripple)
4422 playlist->ripple (clicked_regionview->region()->position(), -clicked_regionview->region()->length(), boost::shared_ptr<Region>());
4424 /* We might have removed regions, which alters other regions' layering_index,
4425 so we need to do a recursive diff here.
4427 vector<Command*> cmds;
4428 playlist->rdiff (cmds);
4429 _session->add_commands (cmds);
4431 _session->add_command(new StatefulDiffCommand (playlist));
4432 commit_reversible_command ();
4436 /** Remove the selected regions */
4438 Editor::remove_selected_regions ()
4440 RegionSelection rs = get_regions_from_selection_and_entered ();
4442 if (!_session || rs.empty()) {
4446 list<boost::shared_ptr<Region> > regions_to_remove;
4448 for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
4449 // we can't just remove the region(s) in this loop because
4450 // this removes them from the RegionSelection, and they thus
4451 // disappear from underneath the iterator, and the ++i above
4452 // SEGVs in a puzzling fashion.
4454 // so, first iterate over the regions to be removed from rs and
4455 // add them to the regions_to_remove list, and then
4456 // iterate over the list to actually remove them.
4458 regions_to_remove.push_back ((*i)->region());
4461 vector<boost::shared_ptr<Playlist> > playlists;
4463 for (list<boost::shared_ptr<Region> >::iterator rl = regions_to_remove.begin(); rl != regions_to_remove.end(); ++rl) {
4465 boost::shared_ptr<Playlist> playlist = (*rl)->playlist();
4468 // is this check necessary?
4472 /* get_regions_from_selection_and_entered() guarantees that
4473 the playlists involved are unique, so there is no need
4477 playlists.push_back (playlist);
4479 playlist->clear_changes ();
4480 playlist->clear_owned_changes ();
4481 playlist->freeze ();
4482 playlist->remove_region (*rl);
4483 if (Config->get_edit_mode() == Ripple)
4484 playlist->ripple ((*rl)->position(), -(*rl)->length(), boost::shared_ptr<Region>());
4488 vector<boost::shared_ptr<Playlist> >::iterator pl;
4489 bool in_command = false;
4491 for (pl = playlists.begin(); pl != playlists.end(); ++pl) {
4494 /* We might have removed regions, which alters other regions' layering_index,
4495 so we need to do a recursive diff here.
4499 begin_reversible_command (_("remove region"));
4502 vector<Command*> cmds;
4503 (*pl)->rdiff (cmds);
4504 _session->add_commands (cmds);
4506 _session->add_command(new StatefulDiffCommand (*pl));
4510 commit_reversible_command ();
4514 /** Cut, copy or clear selected regions.
4515 * @param op Operation (Cut, Copy or Clear)
4518 Editor::cut_copy_regions (CutCopyOp op, RegionSelection& rs)
4520 /* we can't use a std::map here because the ordering is important, and we can't trivially sort
4521 a map when we want ordered access to both elements. i think.
4524 vector<PlaylistMapping> pmap;
4526 framepos_t first_position = max_framepos;
4528 typedef set<boost::shared_ptr<Playlist> > FreezeList;
4529 FreezeList freezelist;
4531 /* get ordering correct before we cut/copy */
4533 rs.sort_by_position_and_track ();
4535 for (RegionSelection::iterator x = rs.begin(); x != rs.end(); ++x) {
4537 first_position = min ((framepos_t) (*x)->region()->position(), first_position);
4539 if (op == Cut || op == Clear || op == Delete) {
4540 boost::shared_ptr<Playlist> pl = (*x)->region()->playlist();
4543 FreezeList::iterator fl;
4545 // only take state if this is a new playlist.
4546 for (fl = freezelist.begin(); fl != freezelist.end(); ++fl) {
4552 if (fl == freezelist.end()) {
4553 pl->clear_changes();
4554 pl->clear_owned_changes ();
4556 freezelist.insert (pl);
4561 TimeAxisView* tv = &(*x)->get_time_axis_view();
4562 vector<PlaylistMapping>::iterator z;
4564 for (z = pmap.begin(); z != pmap.end(); ++z) {
4565 if ((*z).tv == tv) {
4570 if (z == pmap.end()) {
4571 pmap.push_back (PlaylistMapping (tv));
4575 for (RegionSelection::iterator x = rs.begin(); x != rs.end(); ) {
4577 boost::shared_ptr<Playlist> pl = (*x)->region()->playlist();
4580 /* region not yet associated with a playlist (e.g. unfinished
4587 TimeAxisView& tv = (*x)->get_time_axis_view();
4588 boost::shared_ptr<Playlist> npl;
4589 RegionSelection::iterator tmp;
4596 vector<PlaylistMapping>::iterator z;
4598 for (z = pmap.begin(); z != pmap.end(); ++z) {
4599 if ((*z).tv == &tv) {
4604 assert (z != pmap.end());
4607 npl = PlaylistFactory::create (pl->data_type(), *_session, "cutlist", true);
4615 boost::shared_ptr<Region> r = (*x)->region();
4616 boost::shared_ptr<Region> _xx;
4622 pl->remove_region (r);
4623 if (Config->get_edit_mode() == Ripple)
4624 pl->ripple (r->position(), -r->length(), boost::shared_ptr<Region>());
4628 _xx = RegionFactory::create (r);
4629 npl->add_region (_xx, r->position() - first_position);
4630 pl->remove_region (r);
4631 if (Config->get_edit_mode() == Ripple)
4632 pl->ripple (r->position(), -r->length(), boost::shared_ptr<Region>());
4636 /* copy region before adding, so we're not putting same object into two different playlists */
4637 npl->add_region (RegionFactory::create (r), r->position() - first_position);
4641 pl->remove_region (r);
4642 if (Config->get_edit_mode() == Ripple)
4643 pl->ripple (r->position(), -r->length(), boost::shared_ptr<Region>());
4652 list<boost::shared_ptr<Playlist> > foo;
4654 /* the pmap is in the same order as the tracks in which selected regions occurred */
4656 for (vector<PlaylistMapping>::iterator i = pmap.begin(); i != pmap.end(); ++i) {
4659 foo.push_back ((*i).pl);
4664 cut_buffer->set (foo);
4668 _last_cut_copy_source_track = 0;
4670 _last_cut_copy_source_track = pmap.front().tv;
4674 for (FreezeList::iterator pl = freezelist.begin(); pl != freezelist.end(); ++pl) {
4677 /* We might have removed regions, which alters other regions' layering_index,
4678 so we need to do a recursive diff here.
4680 vector<Command*> cmds;
4681 (*pl)->rdiff (cmds);
4682 _session->add_commands (cmds);
4684 _session->add_command (new StatefulDiffCommand (*pl));
4689 Editor::cut_copy_ranges (CutCopyOp op)
4691 TrackViewList ts = selection->tracks.filter_to_unique_playlists ();
4693 /* Sort the track selection now, so that it if is used, the playlists
4694 selected by the calls below to cut_copy_clear are in the order that
4695 their tracks appear in the editor. This makes things like paste
4696 of ranges work properly.
4699 sort_track_selection (ts);
4702 if (!entered_track) {
4705 ts.push_back (entered_track);
4708 for (TrackViewList::iterator i = ts.begin(); i != ts.end(); ++i) {
4709 (*i)->cut_copy_clear (*selection, op);
4714 Editor::paste (float times, bool from_context)
4716 DEBUG_TRACE (DEBUG::CutNPaste, "paste to preferred edit pos\n");
4718 paste_internal (get_preferred_edit_position (EDIT_IGNORE_NONE, from_context), times, get_grid_music_divisions (0));
4722 Editor::mouse_paste ()
4727 if (!mouse_frame (where, ignored)) {
4732 paste_internal (where, 1, get_grid_music_divisions (0));
4736 Editor::paste_internal (framepos_t position, float times, const int32_t sub_num)
4738 DEBUG_TRACE (DEBUG::CutNPaste, string_compose ("apparent paste position is %1\n", position));
4740 if (cut_buffer->empty(internal_editing())) {
4744 if (position == max_framepos) {
4745 position = get_preferred_edit_position();
4746 DEBUG_TRACE (DEBUG::CutNPaste, string_compose ("preferred edit position is %1\n", position));
4749 if (position == last_paste_pos) {
4750 /* repeated paste in the same position */
4753 /* paste in new location, reset repeated paste state */
4755 last_paste_pos = position;
4758 /* get everything in the correct order */
4761 if (!selection->tracks.empty()) {
4762 /* If there is a track selection, paste into exactly those tracks and
4763 only those tracks. This allows the user to be explicit and override
4764 the below "do the reasonable thing" logic. */
4765 ts = selection->tracks.filter_to_unique_playlists ();
4766 sort_track_selection (ts);
4768 /* Figure out which track to base the paste at. */
4769 TimeAxisView* base_track = NULL;
4770 if (_edit_point == Editing::EditAtMouse && entered_track) {
4771 /* With the mouse edit point, paste onto the track under the mouse. */
4772 base_track = entered_track;
4773 } else if (_edit_point == Editing::EditAtMouse && entered_regionview) {
4774 /* With the mouse edit point, paste onto the track of the region under the mouse. */
4775 base_track = &entered_regionview->get_time_axis_view();
4776 } else if (_last_cut_copy_source_track) {
4777 /* Paste to the track that the cut/copy came from (see mantis #333). */
4778 base_track = _last_cut_copy_source_track;
4780 /* This is "impossible" since we've copied... well, do nothing. */
4784 /* Walk up to parent if necessary, so base track is a route. */
4785 while (base_track->get_parent()) {
4786 base_track = base_track->get_parent();
4789 /* Add base track and all tracks below it. The paste logic will select
4790 the appropriate object types from the cut buffer in relative order. */
4791 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
4792 if ((*i)->order() >= base_track->order()) {
4797 /* Sort tracks so the nth track of type T will pick the nth object of type T. */
4798 sort_track_selection (ts);
4800 /* Add automation children of each track in order, for pasting several lines. */
4801 for (TrackViewList::iterator i = ts.begin(); i != ts.end();) {
4802 /* Add any automation children for pasting several lines */
4803 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*>(*i++);
4808 typedef RouteTimeAxisView::AutomationTracks ATracks;
4809 const ATracks& atracks = rtv->automation_tracks();
4810 for (ATracks::const_iterator a = atracks.begin(); a != atracks.end(); ++a) {
4811 i = ts.insert(i, a->second.get());
4816 /* We now have a list of trackviews starting at base_track, including
4817 automation children, in the order shown in the editor, e.g. R1,
4818 R1.A1, R1.A2, R2, R2.A1, ... */
4821 begin_reversible_command (Operations::paste);
4823 if (ts.size() == 1 && cut_buffer->lines.size() == 1 &&
4824 dynamic_cast<AutomationTimeAxisView*>(ts.front())) {
4825 /* Only one line copied, and one automation track selected. Do a
4826 "greedy" paste from one automation type to another. */
4828 PasteContext ctx(paste_count, times, ItemCounts(), true);
4829 ts.front()->paste (position, *cut_buffer, ctx, sub_num);
4833 /* Paste into tracks */
4835 PasteContext ctx(paste_count, times, ItemCounts(), false);
4836 for (TrackViewList::iterator i = ts.begin(); i != ts.end(); ++i) {
4837 (*i)->paste (position, *cut_buffer, ctx, sub_num);
4841 commit_reversible_command ();
4845 Editor::duplicate_regions (float times)
4847 RegionSelection rs (get_regions_from_selection_and_entered());
4848 duplicate_some_regions (rs, times);
4852 Editor::duplicate_some_regions (RegionSelection& regions, float times)
4854 if (regions.empty ()) {
4858 boost::shared_ptr<Playlist> playlist;
4859 RegionSelection sel = regions; // clear (below) may clear the argument list if its the current region selection
4860 RegionSelection foo;
4862 framepos_t const start_frame = regions.start ();
4863 framepos_t const end_frame = regions.end_frame ();
4864 framecnt_t const gap = end_frame - start_frame + 1;
4866 begin_reversible_command (Operations::duplicate_region);
4868 selection->clear_regions ();
4870 for (RegionSelection::iterator i = sel.begin(); i != sel.end(); ++i) {
4872 boost::shared_ptr<Region> r ((*i)->region());
4874 TimeAxisView& tv = (*i)->get_time_axis_view();
4875 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (&tv);
4876 latest_regionviews.clear ();
4877 sigc::connection c = rtv->view()->RegionViewAdded.connect (sigc::mem_fun(*this, &Editor::collect_new_region_view));
4879 framepos_t const position = end_frame + (r->first_frame() - start_frame + 1);
4880 playlist = (*i)->region()->playlist();
4881 playlist->clear_changes ();
4882 playlist->duplicate (r, position, gap, times);
4883 _session->add_command(new StatefulDiffCommand (playlist));
4887 foo.insert (foo.end(), latest_regionviews.begin(), latest_regionviews.end());
4891 selection->set (foo);
4894 commit_reversible_command ();
4898 Editor::duplicate_selection (float times)
4900 if (selection->time.empty() || selection->tracks.empty()) {
4904 boost::shared_ptr<Playlist> playlist;
4906 TrackViewList ts = selection->tracks.filter_to_unique_playlists ();
4908 bool in_command = false;
4910 for (TrackViewList::iterator i = ts.begin(); i != ts.end(); ++i) {
4911 if ((playlist = (*i)->playlist()) == 0) {
4914 playlist->clear_changes ();
4916 if (clicked_selection) {
4917 playlist->duplicate_range (selection->time[clicked_selection], times);
4919 playlist->duplicate_ranges (selection->time, times);
4923 begin_reversible_command (_("duplicate range selection"));
4926 _session->add_command (new StatefulDiffCommand (playlist));
4931 // now "move" range selection to after the current range selection
4932 framecnt_t distance = 0;
4934 if (clicked_selection) {
4935 distance = selection->time[clicked_selection].end -
4936 selection->time[clicked_selection].start;
4938 distance = selection->time.end_frame() - selection->time.start();
4941 selection->move_time (distance);
4943 commit_reversible_command ();
4947 /** Reset all selected points to the relevant default value */
4949 Editor::reset_point_selection ()
4951 for (PointSelection::iterator i = selection->points.begin(); i != selection->points.end(); ++i) {
4952 ARDOUR::AutomationList::iterator j = (*i)->model ();
4953 (*j)->value = (*i)->line().the_list()->default_value ();
4958 Editor::center_playhead ()
4960 float const page = _visible_canvas_width * samples_per_pixel;
4961 center_screen_internal (playhead_cursor->current_frame (), page);
4965 Editor::center_edit_point ()
4967 float const page = _visible_canvas_width * samples_per_pixel;
4968 center_screen_internal (get_preferred_edit_position(), page);
4971 /** Caller must begin and commit a reversible command */
4973 Editor::clear_playlist (boost::shared_ptr<Playlist> playlist)
4975 playlist->clear_changes ();
4977 _session->add_command (new StatefulDiffCommand (playlist));
4981 Editor::nudge_track (bool use_edit, bool forwards)
4983 boost::shared_ptr<Playlist> playlist;
4984 framepos_t distance;
4985 framepos_t next_distance;
4989 start = get_preferred_edit_position();
4994 if ((distance = get_nudge_distance (start, next_distance)) == 0) {
4998 if (selection->tracks.empty()) {
5002 TrackViewList ts = selection->tracks.filter_to_unique_playlists ();
5003 bool in_command = false;
5005 for (TrackViewList::iterator i = ts.begin(); i != ts.end(); ++i) {
5007 if ((playlist = (*i)->playlist()) == 0) {
5011 playlist->clear_changes ();
5012 playlist->clear_owned_changes ();
5014 playlist->nudge_after (start, distance, forwards);
5017 begin_reversible_command (_("nudge track"));
5020 vector<Command*> cmds;
5022 playlist->rdiff (cmds);
5023 _session->add_commands (cmds);
5025 _session->add_command (new StatefulDiffCommand (playlist));
5029 commit_reversible_command ();
5034 Editor::remove_last_capture ()
5036 vector<string> choices;
5043 if (Config->get_verify_remove_last_capture()) {
5044 prompt = _("Do you really want to destroy the last capture?"
5045 "\n(This is destructive and cannot be undone)");
5047 choices.push_back (_("No, do nothing."));
5048 choices.push_back (_("Yes, destroy it."));
5050 Gtkmm2ext::Choice prompter (_("Destroy last capture"), prompt, choices);
5052 if (prompter.run () == 1) {
5053 _session->remove_last_capture ();
5054 _regions->redisplay ();
5058 _session->remove_last_capture();
5059 _regions->redisplay ();
5064 Editor::normalize_region ()
5070 RegionSelection rs = get_regions_from_selection_and_entered ();
5076 NormalizeDialog dialog (rs.size() > 1);
5078 if (dialog.run () != RESPONSE_ACCEPT) {
5082 CursorContext::Handle cursor_ctx = CursorContext::create(*this, _cursors->wait);
5085 /* XXX: should really only count audio regions here */
5086 int const regions = rs.size ();
5088 /* Make a list of the selected audio regions' maximum amplitudes, and also
5089 obtain the maximum amplitude of them all.
5091 list<double> max_amps;
5092 list<double> rms_vals;
5095 bool use_rms = dialog.constrain_rms ();
5097 for (RegionSelection::const_iterator i = rs.begin(); i != rs.end(); ++i) {
5098 AudioRegionView const * arv = dynamic_cast<AudioRegionView const *> (*i);
5102 dialog.descend (1.0 / regions);
5103 double const a = arv->audio_region()->maximum_amplitude (&dialog);
5105 double r = arv->audio_region()->rms (&dialog);
5106 max_rms = max (max_rms, r);
5107 rms_vals.push_back (r);
5111 /* the user cancelled the operation */
5115 max_amps.push_back (a);
5116 max_amp = max (max_amp, a);
5120 list<double>::const_iterator a = max_amps.begin ();
5121 list<double>::const_iterator l = rms_vals.begin ();
5122 bool in_command = false;
5124 for (RegionSelection::iterator r = rs.begin(); r != rs.end(); ++r) {
5125 AudioRegionView* const arv = dynamic_cast<AudioRegionView*> (*r);
5130 arv->region()->clear_changes ();
5132 double amp = dialog.normalize_individually() ? *a : max_amp;
5133 double target = dialog.target_peak (); // dB
5136 double const amp_rms = dialog.normalize_individually() ? *l : max_rms;
5137 const double t_rms = dialog.target_rms ();
5138 const gain_t c_peak = dB_to_coefficient (target);
5139 const gain_t c_rms = dB_to_coefficient (t_rms);
5140 if ((amp_rms / c_rms) > (amp / c_peak)) {
5146 arv->audio_region()->normalize (amp, target);
5149 begin_reversible_command (_("normalize"));
5152 _session->add_command (new StatefulDiffCommand (arv->region()));
5159 commit_reversible_command ();
5165 Editor::reset_region_scale_amplitude ()
5171 RegionSelection rs = get_regions_from_selection_and_entered ();
5177 bool in_command = false;
5179 for (RegionSelection::iterator r = rs.begin(); r != rs.end(); ++r) {
5180 AudioRegionView* const arv = dynamic_cast<AudioRegionView*>(*r);
5183 arv->region()->clear_changes ();
5184 arv->audio_region()->set_scale_amplitude (1.0f);
5187 begin_reversible_command ("reset gain");
5190 _session->add_command (new StatefulDiffCommand (arv->region()));
5194 commit_reversible_command ();
5199 Editor::adjust_region_gain (bool up)
5201 RegionSelection rs = get_regions_from_selection_and_entered ();
5203 if (!_session || rs.empty()) {
5207 bool in_command = false;
5209 for (RegionSelection::iterator r = rs.begin(); r != rs.end(); ++r) {
5210 AudioRegionView* const arv = dynamic_cast<AudioRegionView*>(*r);
5215 arv->region()->clear_changes ();
5217 double dB = accurate_coefficient_to_dB (arv->audio_region()->scale_amplitude ());
5225 arv->audio_region()->set_scale_amplitude (dB_to_coefficient (dB));
5228 begin_reversible_command ("adjust region gain");
5231 _session->add_command (new StatefulDiffCommand (arv->region()));
5235 commit_reversible_command ();
5241 Editor::reverse_region ()
5247 Reverse rev (*_session);
5248 apply_filter (rev, _("reverse regions"));
5252 Editor::strip_region_silence ()
5258 RegionSelection rs = get_regions_from_selection_and_entered ();
5264 std::list<RegionView*> audio_only;
5266 for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
5267 AudioRegionView* const arv = dynamic_cast<AudioRegionView*> (*i);
5269 audio_only.push_back (arv);
5273 assert (!audio_only.empty());
5275 StripSilenceDialog d (_session, audio_only);
5276 int const r = d.run ();
5280 if (r == Gtk::RESPONSE_OK) {
5281 ARDOUR::AudioIntervalMap silences;
5282 d.silences (silences);
5283 StripSilence s (*_session, silences, d.fade_length());
5285 apply_filter (s, _("strip silence"), &d);
5290 Editor::apply_midi_note_edit_op_to_region (MidiOperator& op, MidiRegionView& mrv)
5292 Evoral::Sequence<Evoral::Beats>::Notes selected;
5293 mrv.selection_as_notelist (selected, true);
5295 vector<Evoral::Sequence<Evoral::Beats>::Notes> v;
5296 v.push_back (selected);
5298 Evoral::Beats pos_beats = Evoral::Beats (mrv.midi_region()->beat()) - mrv.midi_region()->start_beats();
5300 return op (mrv.midi_region()->model(), pos_beats, v);
5304 Editor::apply_midi_note_edit_op (MidiOperator& op, const RegionSelection& rs)
5310 bool in_command = false;
5312 for (RegionSelection::const_iterator r = rs.begin(); r != rs.end(); ) {
5313 RegionSelection::const_iterator tmp = r;
5316 MidiRegionView* const mrv = dynamic_cast<MidiRegionView*> (*r);
5319 Command* cmd = apply_midi_note_edit_op_to_region (op, *mrv);
5322 begin_reversible_command (op.name ());
5326 _session->add_command (cmd);
5334 commit_reversible_command ();
5339 Editor::fork_region ()
5341 RegionSelection rs = get_regions_from_selection_and_entered ();
5347 CursorContext::Handle cursor_ctx = CursorContext::create(*this, _cursors->wait);
5348 bool in_command = false;
5352 for (RegionSelection::iterator r = rs.begin(); r != rs.end(); ) {
5353 RegionSelection::iterator tmp = r;
5356 MidiRegionView* const mrv = dynamic_cast<MidiRegionView*>(*r);
5360 boost::shared_ptr<Playlist> playlist = mrv->region()->playlist();
5361 boost::shared_ptr<MidiSource> new_source = _session->create_midi_source_by_stealing_name (mrv->midi_view()->track());
5362 boost::shared_ptr<MidiRegion> newregion = mrv->midi_region()->clone (new_source);
5365 begin_reversible_command (_("Fork Region(s)"));
5368 playlist->clear_changes ();
5369 playlist->replace_region (mrv->region(), newregion, mrv->region()->position());
5370 _session->add_command(new StatefulDiffCommand (playlist));
5372 error << string_compose (_("Could not unlink %1"), mrv->region()->name()) << endmsg;
5380 commit_reversible_command ();
5385 Editor::quantize_region ()
5388 quantize_regions(get_regions_from_selection_and_entered ());
5393 Editor::quantize_regions (const RegionSelection& rs)
5395 if (rs.n_midi_regions() == 0) {
5399 if (!quantize_dialog) {
5400 quantize_dialog = new QuantizeDialog (*this);
5403 if (quantize_dialog->is_mapped()) {
5404 /* in progress already */
5408 quantize_dialog->present ();
5409 const int r = quantize_dialog->run ();
5410 quantize_dialog->hide ();
5412 if (r == Gtk::RESPONSE_OK) {
5413 Quantize quant (quantize_dialog->snap_start(),
5414 quantize_dialog->snap_end(),
5415 quantize_dialog->start_grid_size(),
5416 quantize_dialog->end_grid_size(),
5417 quantize_dialog->strength(),
5418 quantize_dialog->swing(),
5419 quantize_dialog->threshold());
5421 apply_midi_note_edit_op (quant, rs);
5426 Editor::legatize_region (bool shrink_only)
5429 legatize_regions(get_regions_from_selection_and_entered (), shrink_only);
5434 Editor::legatize_regions (const RegionSelection& rs, bool shrink_only)
5436 if (rs.n_midi_regions() == 0) {
5440 Legatize legatize(shrink_only);
5441 apply_midi_note_edit_op (legatize, rs);
5445 Editor::transform_region ()
5448 transform_regions(get_regions_from_selection_and_entered ());
5453 Editor::transform_regions (const RegionSelection& rs)
5455 if (rs.n_midi_regions() == 0) {
5462 const int r = td.run();
5465 if (r == Gtk::RESPONSE_OK) {
5466 Transform transform(td.get());
5467 apply_midi_note_edit_op(transform, rs);
5472 Editor::transpose_region ()
5475 transpose_regions(get_regions_from_selection_and_entered ());
5480 Editor::transpose_regions (const RegionSelection& rs)
5482 if (rs.n_midi_regions() == 0) {
5487 int const r = d.run ();
5489 if (r == RESPONSE_ACCEPT) {
5490 Transpose transpose(d.semitones ());
5491 apply_midi_note_edit_op (transpose, rs);
5496 Editor::insert_patch_change (bool from_context)
5498 RegionSelection rs = get_regions_from_selection_and_entered ();
5504 const framepos_t p = get_preferred_edit_position (EDIT_IGNORE_NONE, from_context);
5506 /* XXX: bit of a hack; use the MIDNAM from the first selected region;
5507 there may be more than one, but the PatchChangeDialog can only offer
5508 one set of patch menus.
5510 MidiRegionView* first = dynamic_cast<MidiRegionView*> (rs.front ());
5512 Evoral::PatchChange<Evoral::Beats> empty (Evoral::Beats(), 0, 0, 0);
5513 PatchChangeDialog d (0, _session, empty, first->instrument_info(), Gtk::Stock::ADD);
5515 if (d.run() == RESPONSE_CANCEL) {
5519 for (RegionSelection::iterator i = rs.begin (); i != rs.end(); ++i) {
5520 MidiRegionView* const mrv = dynamic_cast<MidiRegionView*> (*i);
5522 if (p >= mrv->region()->first_frame() && p <= mrv->region()->last_frame()) {
5523 mrv->add_patch_change (p - mrv->region()->position(), d.patch ());
5530 Editor::apply_filter (Filter& filter, string command, ProgressReporter* progress)
5532 RegionSelection rs = get_regions_from_selection_and_entered ();
5538 CursorContext::Handle cursor_ctx = CursorContext::create(*this, _cursors->wait);
5539 bool in_command = false;
5544 int const N = rs.size ();
5546 for (RegionSelection::iterator r = rs.begin(); r != rs.end(); ) {
5547 RegionSelection::iterator tmp = r;
5550 AudioRegionView* const arv = dynamic_cast<AudioRegionView*>(*r);
5552 boost::shared_ptr<Playlist> playlist = arv->region()->playlist();
5555 progress->descend (1.0 / N);
5558 if (arv->audio_region()->apply (filter, progress) == 0) {
5560 playlist->clear_changes ();
5561 playlist->clear_owned_changes ();
5564 begin_reversible_command (command);
5568 if (filter.results.empty ()) {
5570 /* no regions returned; remove the old one */
5571 playlist->remove_region (arv->region ());
5575 std::vector<boost::shared_ptr<Region> >::iterator res = filter.results.begin ();
5577 /* first region replaces the old one */
5578 playlist->replace_region (arv->region(), *res, (*res)->position());
5582 while (res != filter.results.end()) {
5583 playlist->add_region (*res, (*res)->position());
5589 /* We might have removed regions, which alters other regions' layering_index,
5590 so we need to do a recursive diff here.
5592 vector<Command*> cmds;
5593 playlist->rdiff (cmds);
5594 _session->add_commands (cmds);
5596 _session->add_command(new StatefulDiffCommand (playlist));
5600 progress->ascend ();
5609 commit_reversible_command ();
5614 Editor::external_edit_region ()
5620 Editor::reset_region_gain_envelopes ()
5622 RegionSelection rs = get_regions_from_selection_and_entered ();
5624 if (!_session || rs.empty()) {
5628 bool in_command = false;
5630 for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
5631 AudioRegionView* const arv = dynamic_cast<AudioRegionView*>(*i);
5633 boost::shared_ptr<AutomationList> alist (arv->audio_region()->envelope());
5634 XMLNode& before (alist->get_state());
5636 arv->audio_region()->set_default_envelope ();
5639 begin_reversible_command (_("reset region gain"));
5642 _session->add_command (new MementoCommand<AutomationList>(*arv->audio_region()->envelope().get(), &before, &alist->get_state()));
5647 commit_reversible_command ();
5652 Editor::set_region_gain_visibility (RegionView* rv)
5654 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (rv);
5656 arv->update_envelope_visibility();
5661 Editor::set_gain_envelope_visibility ()
5667 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
5668 AudioTimeAxisView* v = dynamic_cast<AudioTimeAxisView*>(*i);
5670 v->audio_view()->foreach_regionview (sigc::mem_fun (this, &Editor::set_region_gain_visibility));
5676 Editor::toggle_gain_envelope_active ()
5678 if (_ignore_region_action) {
5682 RegionSelection rs = get_regions_from_selection_and_entered ();
5684 if (!_session || rs.empty()) {
5688 bool in_command = false;
5690 for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
5691 AudioRegionView* const arv = dynamic_cast<AudioRegionView*>(*i);
5693 arv->region()->clear_changes ();
5694 arv->audio_region()->set_envelope_active (!arv->audio_region()->envelope_active());
5697 begin_reversible_command (_("region gain envelope active"));
5700 _session->add_command (new StatefulDiffCommand (arv->region()));
5705 commit_reversible_command ();
5710 Editor::toggle_region_lock ()
5712 if (_ignore_region_action) {
5716 RegionSelection rs = get_regions_from_selection_and_entered ();
5718 if (!_session || rs.empty()) {
5722 begin_reversible_command (_("toggle region lock"));
5724 for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
5725 (*i)->region()->clear_changes ();
5726 (*i)->region()->set_locked (!(*i)->region()->locked());
5727 _session->add_command (new StatefulDiffCommand ((*i)->region()));
5730 commit_reversible_command ();
5734 Editor::toggle_region_video_lock ()
5736 if (_ignore_region_action) {
5740 RegionSelection rs = get_regions_from_selection_and_entered ();
5742 if (!_session || rs.empty()) {
5746 begin_reversible_command (_("Toggle Video Lock"));
5748 for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
5749 (*i)->region()->clear_changes ();
5750 (*i)->region()->set_video_locked (!(*i)->region()->video_locked());
5751 _session->add_command (new StatefulDiffCommand ((*i)->region()));
5754 commit_reversible_command ();
5758 Editor::toggle_region_lock_style ()
5760 if (_ignore_region_action) {
5764 RegionSelection rs = get_regions_from_selection_and_entered ();
5766 if (!_session || rs.empty()) {
5770 begin_reversible_command (_("region lock style"));
5772 for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
5773 (*i)->region()->clear_changes ();
5774 PositionLockStyle const ns = (*i)->region()->position_lock_style() == AudioTime ? MusicTime : AudioTime;
5775 (*i)->region()->set_position_lock_style (ns);
5776 _session->add_command (new StatefulDiffCommand ((*i)->region()));
5779 commit_reversible_command ();
5783 Editor::toggle_opaque_region ()
5785 if (_ignore_region_action) {
5789 RegionSelection rs = get_regions_from_selection_and_entered ();
5791 if (!_session || rs.empty()) {
5795 begin_reversible_command (_("change region opacity"));
5797 for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
5798 (*i)->region()->clear_changes ();
5799 (*i)->region()->set_opaque (!(*i)->region()->opaque());
5800 _session->add_command (new StatefulDiffCommand ((*i)->region()));
5803 commit_reversible_command ();
5807 Editor::toggle_record_enable ()
5809 bool new_state = false;
5811 for (TrackSelection::iterator i = selection->tracks.begin(); i != selection->tracks.end(); ++i) {
5812 RouteTimeAxisView *rtav = dynamic_cast<RouteTimeAxisView *>(*i);
5815 if (!rtav->is_track())
5819 new_state = !rtav->track()->rec_enable_control()->get_value();
5823 rtav->track()->rec_enable_control()->set_value (new_state, Controllable::UseGroup);
5828 Editor::toggle_solo ()
5830 bool new_state = false;
5832 boost::shared_ptr<ControlList> cl (new ControlList);
5834 for (TrackSelection::iterator i = selection->tracks.begin(); i != selection->tracks.end(); ++i) {
5835 RouteTimeAxisView *rtav = dynamic_cast<RouteTimeAxisView *>(*i);
5842 new_state = !rtav->route()->soloed ();
5846 cl->push_back (rtav->route()->solo_control());
5849 _session->set_controls (cl, new_state ? 1.0 : 0.0, Controllable::UseGroup);
5853 Editor::toggle_mute ()
5855 bool new_state = false;
5857 boost::shared_ptr<RouteList> rl (new RouteList);
5859 for (TrackSelection::iterator i = selection->tracks.begin(); i != selection->tracks.end(); ++i) {
5860 RouteTimeAxisView *rtav = dynamic_cast<RouteTimeAxisView *>(*i);
5867 new_state = !rtav->route()->muted();
5871 rl->push_back (rtav->route());
5874 _session->set_controls (route_list_to_control_list (rl, &Stripable::mute_control), new_state, Controllable::UseGroup);
5878 Editor::toggle_solo_isolate ()
5884 Editor::fade_range ()
5886 TrackViewList ts = selection->tracks.filter_to_unique_playlists ();
5888 begin_reversible_command (_("fade range"));
5890 for (TrackViewList::iterator i = ts.begin(); i != ts.end(); ++i) {
5891 (*i)->fade_range (selection->time);
5894 commit_reversible_command ();
5899 Editor::set_fade_length (bool in)
5901 RegionSelection rs = get_regions_from_selection_and_entered ();
5907 /* we need a region to measure the offset from the start */
5909 RegionView* rv = rs.front ();
5911 framepos_t pos = get_preferred_edit_position();
5915 if (pos > rv->region()->last_frame() || pos < rv->region()->first_frame()) {
5916 /* edit point is outside the relevant region */
5921 if (pos <= rv->region()->position()) {
5925 len = pos - rv->region()->position();
5926 cmd = _("set fade in length");
5928 if (pos >= rv->region()->last_frame()) {
5932 len = rv->region()->last_frame() - pos;
5933 cmd = _("set fade out length");
5936 bool in_command = false;
5938 for (RegionSelection::iterator x = rs.begin(); x != rs.end(); ++x) {
5939 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (*x);
5945 boost::shared_ptr<AutomationList> alist;
5947 alist = tmp->audio_region()->fade_in();
5949 alist = tmp->audio_region()->fade_out();
5952 XMLNode &before = alist->get_state();
5955 tmp->audio_region()->set_fade_in_length (len);
5956 tmp->audio_region()->set_fade_in_active (true);
5958 tmp->audio_region()->set_fade_out_length (len);
5959 tmp->audio_region()->set_fade_out_active (true);
5963 begin_reversible_command (cmd);
5966 XMLNode &after = alist->get_state();
5967 _session->add_command(new MementoCommand<AutomationList>(*alist, &before, &after));
5971 commit_reversible_command ();
5976 Editor::set_fade_in_shape (FadeShape shape)
5978 RegionSelection rs = get_regions_from_selection_and_entered ();
5983 bool in_command = false;
5985 for (RegionSelection::iterator x = rs.begin(); x != rs.end(); ++x) {
5986 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (*x);
5992 boost::shared_ptr<AutomationList> alist = tmp->audio_region()->fade_in();
5993 XMLNode &before = alist->get_state();
5995 tmp->audio_region()->set_fade_in_shape (shape);
5998 begin_reversible_command (_("set fade in shape"));
6001 XMLNode &after = alist->get_state();
6002 _session->add_command(new MementoCommand<AutomationList>(*alist.get(), &before, &after));
6006 commit_reversible_command ();
6011 Editor::set_fade_out_shape (FadeShape shape)
6013 RegionSelection rs = get_regions_from_selection_and_entered ();
6018 bool in_command = false;
6020 for (RegionSelection::iterator x = rs.begin(); x != rs.end(); ++x) {
6021 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (*x);
6027 boost::shared_ptr<AutomationList> alist = tmp->audio_region()->fade_out();
6028 XMLNode &before = alist->get_state();
6030 tmp->audio_region()->set_fade_out_shape (shape);
6033 begin_reversible_command (_("set fade out shape"));
6036 XMLNode &after = alist->get_state();
6037 _session->add_command(new MementoCommand<AutomationList>(*alist.get(), &before, &after));
6041 commit_reversible_command ();
6046 Editor::set_fade_in_active (bool yn)
6048 RegionSelection rs = get_regions_from_selection_and_entered ();
6053 bool in_command = false;
6055 for (RegionSelection::iterator x = rs.begin(); x != rs.end(); ++x) {
6056 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (*x);
6063 boost::shared_ptr<AudioRegion> ar (tmp->audio_region());
6065 ar->clear_changes ();
6066 ar->set_fade_in_active (yn);
6069 begin_reversible_command (_("set fade in active"));
6072 _session->add_command (new StatefulDiffCommand (ar));
6076 commit_reversible_command ();
6081 Editor::set_fade_out_active (bool yn)
6083 RegionSelection rs = get_regions_from_selection_and_entered ();
6088 bool in_command = false;
6090 for (RegionSelection::iterator x = rs.begin(); x != rs.end(); ++x) {
6091 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (*x);
6097 boost::shared_ptr<AudioRegion> ar (tmp->audio_region());
6099 ar->clear_changes ();
6100 ar->set_fade_out_active (yn);
6103 begin_reversible_command (_("set fade out active"));
6106 _session->add_command(new StatefulDiffCommand (ar));
6110 commit_reversible_command ();
6115 Editor::toggle_region_fades (int dir)
6117 if (_ignore_region_action) {
6121 boost::shared_ptr<AudioRegion> ar;
6124 RegionSelection rs = get_regions_from_selection_and_entered ();
6130 RegionSelection::iterator i;
6131 for (i = rs.begin(); i != rs.end(); ++i) {
6132 if ((ar = boost::dynamic_pointer_cast<AudioRegion>((*i)->region())) != 0) {
6134 yn = ar->fade_out_active ();
6136 yn = ar->fade_in_active ();
6142 if (i == rs.end()) {
6146 /* XXX should this undo-able? */
6147 bool in_command = false;
6149 for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
6150 if ((ar = boost::dynamic_pointer_cast<AudioRegion>((*i)->region())) == 0) {
6153 ar->clear_changes ();
6155 if (dir == 1 || dir == 0) {
6156 ar->set_fade_in_active (!yn);
6159 if (dir == -1 || dir == 0) {
6160 ar->set_fade_out_active (!yn);
6163 begin_reversible_command (_("toggle fade active"));
6166 _session->add_command(new StatefulDiffCommand (ar));
6170 commit_reversible_command ();
6175 /** Update region fade visibility after its configuration has been changed */
6177 Editor::update_region_fade_visibility ()
6179 bool _fade_visibility = _session->config.get_show_region_fades ();
6181 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
6182 AudioTimeAxisView* v = dynamic_cast<AudioTimeAxisView*>(*i);
6184 if (_fade_visibility) {
6185 v->audio_view()->show_all_fades ();
6187 v->audio_view()->hide_all_fades ();
6194 Editor::set_edit_point ()
6199 if (!mouse_frame (where, ignored)) {
6205 if (selection->markers.empty()) {
6207 mouse_add_new_marker (where);
6212 Location* loc = find_location_from_marker (selection->markers.front(), ignored);
6215 loc->move_to (where);
6221 Editor::set_playhead_cursor ()
6223 if (entered_marker) {
6224 _session->request_locate (entered_marker->position(), _session->transport_rolling());
6229 if (!mouse_frame (where, ignored)) {
6236 _session->request_locate (where, _session->transport_rolling());
6240 if (UIConfiguration::instance().get_follow_edits() && (!_session || !_session->config.get_external_sync())) {
6241 cancel_time_selection();
6246 Editor::split_region ()
6248 if (_drags->active ()) {
6252 //if a range is selected, separate it
6253 if ( !selection->time.empty()) {
6254 separate_regions_between (selection->time);
6258 //if no range was selected, try to find some regions to split
6259 if (current_mouse_mode() == MouseObject) { //don't try this for Internal Edit, Stretch, Draw, etc.
6261 RegionSelection rs = get_regions_from_selection_and_edit_point ();
6263 framepos_t where = get_preferred_edit_position ();
6269 if (snap_musical()) {
6270 split_regions_at (where, rs, get_grid_music_divisions (0));
6272 split_regions_at (where, rs, 0);
6278 Editor::select_next_route()
6280 if (selection->tracks.empty()) {
6281 selection->set (track_views.front());
6285 TimeAxisView* current = selection->tracks.front();
6289 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
6291 if (*i == current) {
6293 if (i != track_views.end()) {
6296 current = (*(track_views.begin()));
6297 //selection->set (*(track_views.begin()));
6303 rui = dynamic_cast<RouteUI *>(current);
6305 } while (current->hidden() || (rui == NULL) || !rui->route()->active());
6307 selection->set (current);
6309 ensure_time_axis_view_is_visible (*current, false);
6313 Editor::select_prev_route()
6315 if (selection->tracks.empty()) {
6316 selection->set (track_views.front());
6320 TimeAxisView* current = selection->tracks.front();
6324 for (TrackViewList::reverse_iterator i = track_views.rbegin(); i != track_views.rend(); ++i) {
6326 if (*i == current) {
6328 if (i != track_views.rend()) {
6331 current = *(track_views.rbegin());
6336 rui = dynamic_cast<RouteUI *>(current);
6338 } while (current->hidden() || (rui == NULL) || !rui->route()->active());
6340 selection->set (current);
6342 ensure_time_axis_view_is_visible (*current, false);
6346 Editor::set_loop_from_selection (bool play)
6348 if (_session == 0) {
6352 framepos_t start, end;
6353 if (!get_selection_extents ( start, end))
6356 set_loop_range (start, end, _("set loop range from selection"));
6359 _session->request_play_loop (true, true);
6364 Editor::set_loop_from_region (bool play)
6366 framepos_t start, end;
6367 if (!get_selection_extents ( start, end))
6370 set_loop_range (start, end, _("set loop range from region"));
6373 _session->request_locate (start, true);
6374 _session->request_play_loop (true);
6379 Editor::set_punch_from_selection ()
6381 if (_session == 0) {
6385 framepos_t start, end;
6386 if (!get_selection_extents ( start, end))
6389 set_punch_range (start, end, _("set punch range from selection"));
6393 Editor::set_auto_punch_range ()
6395 // auto punch in/out button from a single button
6396 // If Punch In is unset, set punch range from playhead to end, enable punch in
6397 // If Punch In is set, the next punch sets Punch Out, unless the playhead has been
6398 // rewound beyond the Punch In marker, in which case that marker will be moved back
6399 // to the current playhead position.
6400 // If punch out is set, it clears the punch range and Punch In/Out buttons
6402 if (_session == 0) {
6406 Location* tpl = transport_punch_location();
6407 framepos_t now = playhead_cursor->current_frame();
6408 framepos_t begin = now;
6409 framepos_t end = _session->current_end_frame();
6411 if (!_session->config.get_punch_in()) {
6412 // First Press - set punch in and create range from here to eternity
6413 set_punch_range (begin, end, _("Auto Punch In"));
6414 _session->config.set_punch_in(true);
6415 } else if (tpl && !_session->config.get_punch_out()) {
6416 // Second press - update end range marker and set punch_out
6417 if (now < tpl->start()) {
6418 // playhead has been rewound - move start back and pretend nothing happened
6420 set_punch_range (begin, end, _("Auto Punch In/Out"));
6422 // normal case for 2nd press - set the punch out
6423 end = playhead_cursor->current_frame ();
6424 set_punch_range (tpl->start(), now, _("Auto Punch In/Out"));
6425 _session->config.set_punch_out(true);
6428 if (_session->config.get_punch_out()) {
6429 _session->config.set_punch_out(false);
6432 if (_session->config.get_punch_in()) {
6433 _session->config.set_punch_in(false);
6438 // third press - unset punch in/out and remove range
6439 _session->locations()->remove(tpl);
6446 Editor::set_session_extents_from_selection ()
6448 if (_session == 0) {
6452 framepos_t start, end;
6453 if (!get_selection_extents ( start, end))
6457 if ((loc = _session->locations()->session_range_location()) == 0) {
6458 _session->set_session_extents (start, end); // this will create a new session range; no need for UNDO
6460 XMLNode &before = loc->get_state();
6462 _session->set_session_extents (start, end);
6464 XMLNode &after = loc->get_state();
6466 begin_reversible_command (_("set session start/end from selection"));
6468 _session->add_command (new MementoCommand<Location>(*loc, &before, &after));
6470 commit_reversible_command ();
6473 _session->set_end_is_free (false);
6477 Editor::set_punch_start_from_edit_point ()
6481 framepos_t start = 0;
6482 framepos_t end = max_framepos;
6484 //use the existing punch end, if any
6485 Location* tpl = transport_punch_location();
6490 if ((_edit_point == EditAtPlayhead) && _session->transport_rolling()) {
6491 start = _session->audible_frame();
6493 start = get_preferred_edit_position();
6496 //snap the selection start/end
6499 //if there's not already a sensible selection endpoint, go "forever"
6500 if ( start > end ) {
6504 set_punch_range (start, end, _("set punch start from EP"));
6510 Editor::set_punch_end_from_edit_point ()
6514 framepos_t start = 0;
6515 framepos_t end = max_framepos;
6517 //use the existing punch start, if any
6518 Location* tpl = transport_punch_location();
6520 start = tpl->start();
6523 if ((_edit_point == EditAtPlayhead) && _session->transport_rolling()) {
6524 end = _session->audible_frame();
6526 end = get_preferred_edit_position();
6529 //snap the selection start/end
6532 set_punch_range (start, end, _("set punch end from EP"));
6538 Editor::set_loop_start_from_edit_point ()
6542 framepos_t start = 0;
6543 framepos_t end = max_framepos;
6545 //use the existing loop end, if any
6546 Location* tpl = transport_loop_location();
6551 if ((_edit_point == EditAtPlayhead) && _session->transport_rolling()) {
6552 start = _session->audible_frame();
6554 start = get_preferred_edit_position();
6557 //snap the selection start/end
6560 //if there's not already a sensible selection endpoint, go "forever"
6561 if ( start > end ) {
6565 set_loop_range (start, end, _("set loop start from EP"));
6571 Editor::set_loop_end_from_edit_point ()
6575 framepos_t start = 0;
6576 framepos_t end = max_framepos;
6578 //use the existing loop start, if any
6579 Location* tpl = transport_loop_location();
6581 start = tpl->start();
6584 if ((_edit_point == EditAtPlayhead) && _session->transport_rolling()) {
6585 end = _session->audible_frame();
6587 end = get_preferred_edit_position();
6590 //snap the selection start/end
6593 set_loop_range (start, end, _("set loop end from EP"));
6598 Editor::set_punch_from_region ()
6600 framepos_t start, end;
6601 if (!get_selection_extents ( start, end))
6604 set_punch_range (start, end, _("set punch range from region"));
6608 Editor::pitch_shift_region ()
6610 RegionSelection rs = get_regions_from_selection_and_entered ();
6612 RegionSelection audio_rs;
6613 for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
6614 if (dynamic_cast<AudioRegionView*> (*i)) {
6615 audio_rs.push_back (*i);
6619 if (audio_rs.empty()) {
6623 pitch_shift (audio_rs, 1.2);
6627 Editor::set_tempo_from_region ()
6629 RegionSelection rs = get_regions_from_selection_and_entered ();
6631 if (!_session || rs.empty()) {
6635 RegionView* rv = rs.front();
6637 define_one_bar (rv->region()->position(), rv->region()->last_frame() + 1);
6641 Editor::use_range_as_bar ()
6643 framepos_t start, end;
6644 if (get_edit_op_range (start, end)) {
6645 define_one_bar (start, end);
6650 Editor::define_one_bar (framepos_t start, framepos_t end)
6652 framepos_t length = end - start;
6654 const Meter& m (_session->tempo_map().meter_at_frame (start));
6656 /* length = 1 bar */
6658 /* We're going to deliver a constant tempo here,
6659 so we can use frames per beat to determine length.
6660 now we want frames per beat.
6661 we have frames per bar, and beats per bar, so ...
6664 /* XXXX METER MATH */
6666 double frames_per_beat = length / m.divisions_per_bar();
6668 /* beats per minute = */
6670 double beats_per_minute = (_session->frame_rate() * 60.0) / frames_per_beat;
6672 /* now decide whether to:
6674 (a) set global tempo
6675 (b) add a new tempo marker
6679 const TempoSection& t (_session->tempo_map().tempo_section_at_frame (start));
6681 bool do_global = false;
6683 if ((_session->tempo_map().n_tempos() == 1) && (_session->tempo_map().n_meters() == 1)) {
6685 /* only 1 tempo & 1 meter: ask if the user wants to set the tempo
6686 at the start, or create a new marker
6689 vector<string> options;
6690 options.push_back (_("Cancel"));
6691 options.push_back (_("Add new marker"));
6692 options.push_back (_("Set global tempo"));
6695 _("Define one bar"),
6696 _("Do you want to set the global tempo or add a new tempo marker?"),
6700 c.set_default_response (2);
6716 /* more than 1 tempo and/or meter section already, go ahead do the "usual":
6717 if the marker is at the region starter, change it, otherwise add
6722 begin_reversible_command (_("set tempo from region"));
6723 XMLNode& before (_session->tempo_map().get_state());
6726 _session->tempo_map().change_initial_tempo (beats_per_minute, t.note_type());
6727 } else if (t.frame() == start) {
6728 _session->tempo_map().change_existing_tempo_at (start, beats_per_minute, t.note_type());
6730 const Tempo tempo (beats_per_minute, t.note_type());
6731 _session->tempo_map().add_tempo (tempo, 0.0, start, TempoSection::Constant, AudioTime);
6734 XMLNode& after (_session->tempo_map().get_state());
6736 _session->add_command (new MementoCommand<TempoMap>(_session->tempo_map(), &before, &after));
6737 commit_reversible_command ();
6741 Editor::split_region_at_transients ()
6743 AnalysisFeatureList positions;
6745 RegionSelection rs = get_regions_from_selection_and_entered ();
6747 if (!_session || rs.empty()) {
6751 begin_reversible_command (_("split regions"));
6753 for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ) {
6755 RegionSelection::iterator tmp;
6760 boost::shared_ptr<AudioRegion> ar = boost::dynamic_pointer_cast<AudioRegion> ((*i)->region());
6763 ar->transients (positions);
6764 split_region_at_points ((*i)->region(), positions, true);
6771 commit_reversible_command ();
6776 Editor::split_region_at_points (boost::shared_ptr<Region> r, AnalysisFeatureList& positions, bool can_ferret, bool select_new)
6778 bool use_rhythmic_rodent = false;
6780 boost::shared_ptr<Playlist> pl = r->playlist();
6782 list<boost::shared_ptr<Region> > new_regions;
6788 if (positions.empty()) {
6792 if (positions.size() > 20 && can_ferret) {
6793 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);
6794 MessageDialog msg (msgstr,
6797 Gtk::BUTTONS_OK_CANCEL);
6800 msg.add_button (_("Call for the Ferret!"), RESPONSE_APPLY);
6801 msg.set_secondary_text (_("Press OK to continue with this split operation\nor ask the Ferret dialog to tune the analysis"));
6803 msg.set_secondary_text (_("Press OK to continue with this split operation"));
6806 msg.set_title (_("Excessive split?"));
6809 int response = msg.run();
6815 case RESPONSE_APPLY:
6816 use_rhythmic_rodent = true;
6823 if (use_rhythmic_rodent) {
6824 show_rhythm_ferret ();
6828 AnalysisFeatureList::const_iterator x;
6830 pl->clear_changes ();
6831 pl->clear_owned_changes ();
6833 x = positions.begin();
6835 if (x == positions.end()) {
6840 pl->remove_region (r);
6844 framepos_t rstart = r->first_frame ();
6845 framepos_t rend = r->last_frame ();
6847 while (x != positions.end()) {
6849 /* deal with positons that are out of scope of present region bounds */
6850 if (*x <= rstart || *x > rend) {
6855 /* file start = original start + how far we from the initial position ? */
6857 framepos_t file_start = r->start() + pos;
6859 /* length = next position - current position */
6861 framepos_t len = (*x) - pos - rstart;
6863 /* XXX we do we really want to allow even single-sample regions?
6864 * shouldn't we have some kind of lower limit on region size?
6873 if (RegionFactory::region_name (new_name, r->name())) {
6877 /* do NOT announce new regions 1 by one, just wait till they are all done */
6881 plist.add (ARDOUR::Properties::start, file_start);
6882 plist.add (ARDOUR::Properties::length, len);
6883 plist.add (ARDOUR::Properties::name, new_name);
6884 plist.add (ARDOUR::Properties::layer, 0);
6885 // TODO set transients_offset
6887 boost::shared_ptr<Region> nr = RegionFactory::create (r->sources(), plist, false);
6888 /* because we set annouce to false, manually add the new region to the
6891 RegionFactory::map_add (nr);
6893 pl->add_region (nr, rstart + pos);
6896 new_regions.push_front(nr);
6905 RegionFactory::region_name (new_name, r->name());
6907 /* Add the final region */
6910 plist.add (ARDOUR::Properties::start, r->start() + pos);
6911 plist.add (ARDOUR::Properties::length, r->last_frame() - (r->position() + pos) + 1);
6912 plist.add (ARDOUR::Properties::name, new_name);
6913 plist.add (ARDOUR::Properties::layer, 0);
6915 boost::shared_ptr<Region> nr = RegionFactory::create (r->sources(), plist, false);
6916 /* because we set annouce to false, manually add the new region to the
6919 RegionFactory::map_add (nr);
6920 pl->add_region (nr, r->position() + pos);
6923 new_regions.push_front(nr);
6928 /* We might have removed regions, which alters other regions' layering_index,
6929 so we need to do a recursive diff here.
6931 vector<Command*> cmds;
6933 _session->add_commands (cmds);
6935 _session->add_command (new StatefulDiffCommand (pl));
6939 for (list<boost::shared_ptr<Region> >::iterator i = new_regions.begin(); i != new_regions.end(); ++i){
6940 set_selected_regionview_from_region_list ((*i), Selection::Add);
6946 Editor::place_transient()
6952 RegionSelection rs = get_regions_from_selection_and_edit_point ();
6958 framepos_t where = get_preferred_edit_position();
6960 begin_reversible_command (_("place transient"));
6962 for (RegionSelection::iterator r = rs.begin(); r != rs.end(); ++r) {
6963 (*r)->region()->add_transient(where);
6966 commit_reversible_command ();
6970 Editor::remove_transient(ArdourCanvas::Item* item)
6976 ArdourCanvas::Line* _line = reinterpret_cast<ArdourCanvas::Line*> (item);
6979 AudioRegionView* _arv = reinterpret_cast<AudioRegionView*> (item->get_data ("regionview"));
6980 _arv->remove_transient (*(float*) _line->get_data ("position"));
6984 Editor::snap_regions_to_grid ()
6986 list <boost::shared_ptr<Playlist > > used_playlists;
6988 RegionSelection rs = get_regions_from_selection_and_entered ();
6990 if (!_session || rs.empty()) {
6994 begin_reversible_command (_("snap regions to grid"));
6996 for (RegionSelection::iterator r = rs.begin(); r != rs.end(); ++r) {
6998 boost::shared_ptr<Playlist> pl = (*r)->region()->playlist();
7000 if (!pl->frozen()) {
7001 /* we haven't seen this playlist before */
7003 /* remember used playlists so we can thaw them later */
7004 used_playlists.push_back(pl);
7008 framepos_t start_frame = (*r)->region()->first_frame ();
7009 snap_to (start_frame);
7010 (*r)->region()->set_position (start_frame);
7013 while (used_playlists.size() > 0) {
7014 list <boost::shared_ptr<Playlist > >::iterator i = used_playlists.begin();
7016 used_playlists.pop_front();
7019 commit_reversible_command ();
7023 Editor::close_region_gaps ()
7025 list <boost::shared_ptr<Playlist > > used_playlists;
7027 RegionSelection rs = get_regions_from_selection_and_entered ();
7029 if (!_session || rs.empty()) {
7033 Dialog dialog (_("Close Region Gaps"));
7036 table.set_spacings (12);
7037 table.set_border_width (12);
7038 Label* l = manage (left_aligned_label (_("Crossfade length")));
7039 table.attach (*l, 0, 1, 0, 1);
7041 SpinButton spin_crossfade (1, 0);
7042 spin_crossfade.set_range (0, 15);
7043 spin_crossfade.set_increments (1, 1);
7044 spin_crossfade.set_value (5);
7045 table.attach (spin_crossfade, 1, 2, 0, 1);
7047 table.attach (*manage (new Label (_("ms"))), 2, 3, 0, 1);
7049 l = manage (left_aligned_label (_("Pull-back length")));
7050 table.attach (*l, 0, 1, 1, 2);
7052 SpinButton spin_pullback (1, 0);
7053 spin_pullback.set_range (0, 100);
7054 spin_pullback.set_increments (1, 1);
7055 spin_pullback.set_value(30);
7056 table.attach (spin_pullback, 1, 2, 1, 2);
7058 table.attach (*manage (new Label (_("ms"))), 2, 3, 1, 2);
7060 dialog.get_vbox()->pack_start (table);
7061 dialog.add_button (Stock::CANCEL, RESPONSE_CANCEL);
7062 dialog.add_button (_("Ok"), RESPONSE_ACCEPT);
7065 if (dialog.run () == RESPONSE_CANCEL) {
7069 framepos_t crossfade_len = spin_crossfade.get_value();
7070 framepos_t pull_back_frames = spin_pullback.get_value();
7072 crossfade_len = lrintf (crossfade_len * _session->frame_rate()/1000);
7073 pull_back_frames = lrintf (pull_back_frames * _session->frame_rate()/1000);
7075 /* Iterate over the region list and make adjacent regions overlap by crossfade_len_ms */
7077 begin_reversible_command (_("close region gaps"));
7080 boost::shared_ptr<Region> last_region;
7082 rs.sort_by_position_and_track();
7084 for (RegionSelection::iterator r = rs.begin(); r != rs.end(); ++r) {
7086 boost::shared_ptr<Playlist> pl = (*r)->region()->playlist();
7088 if (!pl->frozen()) {
7089 /* we haven't seen this playlist before */
7091 /* remember used playlists so we can thaw them later */
7092 used_playlists.push_back(pl);
7096 framepos_t position = (*r)->region()->position();
7098 if (idx == 0 || position < last_region->position()){
7099 last_region = (*r)->region();
7104 (*r)->region()->trim_front( (position - pull_back_frames));
7105 last_region->trim_end( (position - pull_back_frames + crossfade_len));
7107 last_region = (*r)->region();
7112 while (used_playlists.size() > 0) {
7113 list <boost::shared_ptr<Playlist > >::iterator i = used_playlists.begin();
7115 used_playlists.pop_front();
7118 commit_reversible_command ();
7122 Editor::tab_to_transient (bool forward)
7124 AnalysisFeatureList positions;
7126 RegionSelection rs = get_regions_from_selection_and_entered ();
7132 framepos_t pos = _session->audible_frame ();
7134 if (!selection->tracks.empty()) {
7136 /* don't waste time searching for transients in duplicate playlists.
7139 TrackViewList ts = selection->tracks.filter_to_unique_playlists ();
7141 for (TrackViewList::iterator t = ts.begin(); t != ts.end(); ++t) {
7143 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (*t);
7146 boost::shared_ptr<Track> tr = rtv->track();
7148 boost::shared_ptr<Playlist> pl = tr->playlist ();
7150 framepos_t result = pl->find_next_transient (pos, forward ? 1 : -1);
7153 positions.push_back (result);
7166 for (RegionSelection::iterator r = rs.begin(); r != rs.end(); ++r) {
7167 (*r)->region()->get_transients (positions);
7171 TransientDetector::cleanup_transients (positions, _session->frame_rate(), 3.0);
7174 AnalysisFeatureList::iterator x;
7176 for (x = positions.begin(); x != positions.end(); ++x) {
7182 if (x != positions.end ()) {
7183 _session->request_locate (*x);
7187 AnalysisFeatureList::reverse_iterator x;
7189 for (x = positions.rbegin(); x != positions.rend(); ++x) {
7195 if (x != positions.rend ()) {
7196 _session->request_locate (*x);
7202 Editor::playhead_forward_to_grid ()
7208 framepos_t pos = playhead_cursor->current_frame ();
7209 if (pos < max_framepos - 1) {
7211 snap_to_internal (pos, RoundUpAlways, false);
7212 _session->request_locate (pos);
7218 Editor::playhead_backward_to_grid ()
7224 framepos_t pos = playhead_cursor->current_frame ();
7227 snap_to_internal (pos, RoundDownAlways, false);
7228 _session->request_locate (pos);
7233 Editor::set_track_height (Height h)
7235 TrackSelection& ts (selection->tracks);
7237 for (TrackSelection::iterator x = ts.begin(); x != ts.end(); ++x) {
7238 (*x)->set_height_enum (h);
7243 Editor::toggle_tracks_active ()
7245 TrackSelection& ts (selection->tracks);
7247 bool target = false;
7253 for (TrackSelection::iterator x = ts.begin(); x != ts.end(); ++x) {
7254 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*>(*x);
7258 target = !rtv->_route->active();
7261 rtv->_route->set_active (target, this);
7267 Editor::remove_tracks ()
7269 /* this will delete GUI objects that may be the subject of an event
7270 handler in which this method is called. Defer actual deletion to the
7271 next idle callback, when all event handling is finished.
7273 Glib::signal_idle().connect (sigc::mem_fun (*this, &Editor::idle_remove_tracks));
7277 Editor::idle_remove_tracks ()
7279 Session::StateProtector sp (_session);
7281 return false; /* do not call again */
7285 Editor::_remove_tracks ()
7287 TrackSelection& ts (selection->tracks);
7293 vector<string> choices;
7297 const char* trackstr;
7299 vector<boost::shared_ptr<Route> > routes;
7300 bool special_bus = false;
7302 for (TrackSelection::iterator x = ts.begin(); x != ts.end(); ++x) {
7303 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (*x);
7307 if (rtv->is_track()) {
7312 routes.push_back (rtv->_route);
7314 if (rtv->route()->is_master() || rtv->route()->is_monitor()) {
7319 if (special_bus && !Config->get_allow_special_bus_removal()) {
7320 MessageDialog msg (_("That would be bad news ...."),
7324 msg.set_secondary_text (string_compose (_(
7325 "Removing the master or monitor bus is such a bad idea\n\
7326 that %1 is not going to allow it.\n\
7328 If you really want to do this sort of thing\n\
7329 edit your ardour.rc file to set the\n\
7330 \"allow-special-bus-removal\" option to be \"yes\""), PROGRAM_NAME));
7337 if (ntracks + nbusses == 0) {
7341 trackstr = P_("track", "tracks", ntracks);
7342 busstr = P_("bus", "busses", nbusses);
7346 prompt = string_compose (_("Do you really want to remove %1 %2 and %3 %4?\n"
7347 "(You may also lose the playlists associated with the %2)\n\n"
7348 "This action cannot be undone, and the session file will be overwritten!"),
7349 ntracks, trackstr, nbusses, busstr);
7351 prompt = string_compose (_("Do you really want to remove %1 %2?\n"
7352 "(You may also lose the playlists associated with the %2)\n\n"
7353 "This action cannot be undone, and the session file will be overwritten!"),
7356 } else if (nbusses) {
7357 prompt = string_compose (_("Do you really want to remove %1 %2?\n\n"
7358 "This action cannot be undone, and the session file will be overwritten"),
7362 choices.push_back (_("No, do nothing."));
7363 if (ntracks + nbusses > 1) {
7364 choices.push_back (_("Yes, remove them."));
7366 choices.push_back (_("Yes, remove it."));
7371 title = string_compose (_("Remove %1"), trackstr);
7373 title = string_compose (_("Remove %1"), busstr);
7376 Choice prompter (title, prompt, choices);
7378 if (prompter.run () != 1) {
7383 Mixer_UI::instance()->selection().block_routes_changed (true);
7384 selection->block_tracks_changed (true);
7386 DisplaySuspender ds;
7387 boost::shared_ptr<RouteList> rl (new RouteList);
7388 for (vector<boost::shared_ptr<Route> >::iterator x = routes.begin(); x != routes.end(); ++x) {
7391 _session->remove_routes (rl);
7393 /* TrackSelection and RouteList leave scope,
7394 * destructors are called,
7395 * diskstream drops references, save_state is called (again for every track)
7397 selection->block_tracks_changed (false);
7398 Mixer_UI::instance()->selection().block_routes_changed (false);
7399 selection->TracksChanged (); /* EMIT SIGNAL */
7403 Editor::do_insert_time ()
7405 if (selection->tracks.empty()) {
7409 InsertRemoveTimeDialog d (*this);
7410 int response = d.run ();
7412 if (response != RESPONSE_OK) {
7416 if (d.distance() == 0) {
7423 d.intersected_region_action (),
7427 d.move_glued_markers(),
7428 d.move_locked_markers(),
7434 Editor::insert_time (
7435 framepos_t pos, framecnt_t frames, InsertTimeOption opt,
7436 bool all_playlists, bool ignore_music_glue, bool markers_too, bool glued_markers_too, bool locked_markers_too, bool tempo_too
7440 if (Config->get_edit_mode() == Lock) {
7443 bool in_command = false;
7445 TrackViewList ts = selection->tracks.filter_to_unique_playlists ();
7447 for (TrackViewList::iterator x = ts.begin(); x != ts.end(); ++x) {
7451 /* don't operate on any playlist more than once, which could
7452 * happen if "all playlists" is enabled, but there is more
7453 * than 1 track using playlists "from" a given track.
7456 set<boost::shared_ptr<Playlist> > pl;
7458 if (all_playlists) {
7459 RouteTimeAxisView* rtav = dynamic_cast<RouteTimeAxisView*> (*x);
7460 if (rtav && rtav->track ()) {
7461 vector<boost::shared_ptr<Playlist> > all = _session->playlists->playlists_for_track (rtav->track ());
7462 for (vector<boost::shared_ptr<Playlist> >::iterator p = all.begin(); p != all.end(); ++p) {
7467 if ((*x)->playlist ()) {
7468 pl.insert ((*x)->playlist ());
7472 for (set<boost::shared_ptr<Playlist> >::iterator i = pl.begin(); i != pl.end(); ++i) {
7474 (*i)->clear_changes ();
7475 (*i)->clear_owned_changes ();
7477 if (opt == SplitIntersected) {
7478 /* non musical split */
7479 (*i)->split (pos, 0);
7482 (*i)->shift (pos, frames, (opt == MoveIntersected), ignore_music_glue);
7485 begin_reversible_command (_("insert time"));
7488 vector<Command*> cmds;
7490 _session->add_commands (cmds);
7492 _session->add_command (new StatefulDiffCommand (*i));
7496 RouteTimeAxisView* rtav = dynamic_cast<RouteTimeAxisView*> (*x);
7499 begin_reversible_command (_("insert time"));
7502 rtav->route ()->shift (pos, frames);
7509 XMLNode& before (_session->locations()->get_state());
7510 Locations::LocationList copy (_session->locations()->list());
7512 for (Locations::LocationList::iterator i = copy.begin(); i != copy.end(); ++i) {
7514 Locations::LocationList::const_iterator tmp;
7516 if ((*i)->position_lock_style() == AudioTime || glued_markers_too) {
7517 bool const was_locked = (*i)->locked ();
7518 if (locked_markers_too) {
7522 if ((*i)->start() >= pos) {
7523 // move end first, in case we're moving by more than the length of the range
7524 if (!(*i)->is_mark()) {
7525 (*i)->set_end ((*i)->end() + frames);
7527 (*i)->set_start ((*i)->start() + frames);
7539 begin_reversible_command (_("insert time"));
7542 XMLNode& after (_session->locations()->get_state());
7543 _session->add_command (new MementoCommand<Locations>(*_session->locations(), &before, &after));
7549 begin_reversible_command (_("insert time"));
7552 XMLNode& before (_session->tempo_map().get_state());
7553 _session->tempo_map().insert_time (pos, frames);
7554 XMLNode& after (_session->tempo_map().get_state());
7555 _session->add_command (new MementoCommand<TempoMap>(_session->tempo_map(), &before, &after));
7559 commit_reversible_command ();
7564 Editor::do_remove_time ()
7566 if (selection->tracks.empty()) {
7570 InsertRemoveTimeDialog d (*this, true);
7572 int response = d.run ();
7574 if (response != RESPONSE_OK) {
7578 framecnt_t distance = d.distance();
7580 if (distance == 0) {
7590 d.move_glued_markers(),
7591 d.move_locked_markers(),
7597 Editor::remove_time (framepos_t pos, framecnt_t frames, InsertTimeOption opt,
7598 bool ignore_music_glue, bool markers_too, bool glued_markers_too, bool locked_markers_too, bool tempo_too)
7600 if (Config->get_edit_mode() == Lock) {
7601 error << (_("Cannot insert or delete time when in Lock edit.")) << endmsg;
7604 bool in_command = false;
7606 for (TrackSelection::iterator x = selection->tracks.begin(); x != selection->tracks.end(); ++x) {
7608 boost::shared_ptr<Playlist> pl = (*x)->playlist();
7612 XMLNode &before = pl->get_state();
7614 std::list<AudioRange> rl;
7615 AudioRange ar(pos, pos+frames, 0);
7618 pl->shift (pos, -frames, true, ignore_music_glue);
7621 begin_reversible_command (_("remove time"));
7624 XMLNode &after = pl->get_state();
7626 _session->add_command (new MementoCommand<Playlist> (*pl, &before, &after));
7630 RouteTimeAxisView* rtav = dynamic_cast<RouteTimeAxisView*> (*x);
7633 begin_reversible_command (_("remove time"));
7636 rtav->route ()->shift (pos, -frames);
7640 std::list<Location*> loc_kill_list;
7645 XMLNode& before (_session->locations()->get_state());
7646 Locations::LocationList copy (_session->locations()->list());
7648 for (Locations::LocationList::iterator i = copy.begin(); i != copy.end(); ++i) {
7649 if ((*i)->position_lock_style() == AudioTime || glued_markers_too) {
7651 bool const was_locked = (*i)->locked ();
7652 if (locked_markers_too) {
7656 if (!(*i)->is_mark()) { // it's a range; have to handle both start and end
7657 if ((*i)->end() >= pos
7658 && (*i)->end() < pos+frames
7659 && (*i)->start() >= pos
7660 && (*i)->end() < pos+frames) { // range is completely enclosed; kill it
7662 loc_kill_list.push_back(*i);
7663 } else { // only start or end is included, try to do the right thing
7664 // move start before moving end, to avoid trying to move the end to before the start
7665 // if we're removing more time than the length of the range
7666 if ((*i)->start() >= pos && (*i)->start() < pos+frames) {
7667 // start is within cut
7668 (*i)->set_start (pos); // bring the start marker to the beginning of the cut
7670 } else if ((*i)->start() >= pos+frames) {
7671 // start (and thus entire range) lies beyond end of cut
7672 (*i)->set_start ((*i)->start() - frames); // slip the start marker back
7675 if ((*i)->end() >= pos && (*i)->end() < pos+frames) {
7676 // end is inside cut
7677 (*i)->set_end (pos); // bring the end to the cut
7679 } else if ((*i)->end() >= pos+frames) {
7680 // end is beyond end of cut
7681 (*i)->set_end ((*i)->end() - frames); // slip the end marker back
7686 } else if ((*i)->start() >= pos && (*i)->start() < pos+frames ) {
7687 loc_kill_list.push_back(*i);
7689 } else if ((*i)->start() >= pos) {
7690 (*i)->set_start ((*i)->start() -frames);
7700 for (list<Location*>::iterator i = loc_kill_list.begin(); i != loc_kill_list.end(); ++i) {
7701 _session->locations()->remove( *i );
7706 begin_reversible_command (_("remove time"));
7709 XMLNode& after (_session->locations()->get_state());
7710 _session->add_command (new MementoCommand<Locations>(*_session->locations(), &before, &after));
7715 XMLNode& before (_session->tempo_map().get_state());
7717 if (_session->tempo_map().remove_time (pos, frames) ) {
7719 begin_reversible_command (_("remove time"));
7722 XMLNode& after (_session->tempo_map().get_state());
7723 _session->add_command (new MementoCommand<TempoMap>(_session->tempo_map(), &before, &after));
7728 commit_reversible_command ();
7733 Editor::fit_selection ()
7735 if (!selection->tracks.empty()) {
7736 fit_tracks (selection->tracks);
7740 /* no selected tracks - use tracks with selected regions */
7742 if (!selection->regions.empty()) {
7743 for (RegionSelection::iterator r = selection->regions.begin(); r != selection->regions.end(); ++r) {
7744 tvl.push_back (&(*r)->get_time_axis_view ());
7750 } else if (internal_editing()) {
7751 /* no selected tracks, or regions, but in internal edit mode, so follow the mouse and use
7754 if (entered_track) {
7755 tvl.push_back (entered_track);
7764 Editor::fit_tracks (TrackViewList & tracks)
7766 if (tracks.empty()) {
7770 uint32_t child_heights = 0;
7771 int visible_tracks = 0;
7773 for (TrackSelection::iterator t = tracks.begin(); t != tracks.end(); ++t) {
7775 if (!(*t)->marked_for_display()) {
7779 child_heights += (*t)->effective_height() - (*t)->current_height();
7783 /* compute the per-track height from:
7785 total canvas visible height -
7786 height that will be taken by visible children of selected
7787 tracks - height of the ruler/hscroll area
7789 uint32_t h = (uint32_t) floor ((trackviews_height() - child_heights) / visible_tracks);
7790 double first_y_pos = DBL_MAX;
7792 if (h < TimeAxisView::preset_height (HeightSmall)) {
7793 MessageDialog msg (_("There are too many tracks to fit in the current window"));
7794 /* too small to be displayed */
7798 undo_visual_stack.push_back (current_visual_state (true));
7799 PBD::Unwinder<bool> nsv (no_save_visual, true);
7801 /* build a list of all tracks, including children */
7804 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
7806 TimeAxisView::Children c = (*i)->get_child_list ();
7807 for (TimeAxisView::Children::iterator j = c.begin(); j != c.end(); ++j) {
7808 all.push_back (j->get());
7813 // find selection range.
7814 // if someone knows how to user TrackViewList::iterator for this
7816 int selected_top = -1;
7817 int selected_bottom = -1;
7819 for (TrackViewList::iterator t = all.begin(); t != all.end(); ++t, ++i) {
7820 if ((*t)->marked_for_display ()) {
7821 if (tracks.contains(*t)) {
7822 if (selected_top == -1) {
7825 selected_bottom = i;
7831 for (TrackViewList::iterator t = all.begin(); t != all.end(); ++t, ++i) {
7832 if ((*t)->marked_for_display ()) {
7833 if (tracks.contains(*t)) {
7834 (*t)->set_height (h);
7835 first_y_pos = std::min ((*t)->y_position (), first_y_pos);
7837 if (i > selected_top && i < selected_bottom) {
7838 hide_track_in_display (*t);
7845 set the controls_layout height now, because waiting for its size
7846 request signal handler will cause the vertical adjustment setting to fail
7849 controls_layout.property_height () = _full_canvas_height;
7850 vertical_adjustment.set_value (first_y_pos);
7852 redo_visual_stack.push_back (current_visual_state (true));
7854 visible_tracks_selector.set_text (_("Sel"));
7858 Editor::save_visual_state (uint32_t n)
7860 while (visual_states.size() <= n) {
7861 visual_states.push_back (0);
7864 if (visual_states[n] != 0) {
7865 delete visual_states[n];
7868 visual_states[n] = current_visual_state (true);
7873 Editor::goto_visual_state (uint32_t n)
7875 if (visual_states.size() <= n) {
7879 if (visual_states[n] == 0) {
7883 use_visual_state (*visual_states[n]);
7887 Editor::start_visual_state_op (uint32_t n)
7889 save_visual_state (n);
7891 PopUp* pup = new PopUp (WIN_POS_MOUSE, 1000, true);
7893 snprintf (buf, sizeof (buf), _("Saved view %u"), n+1);
7894 pup->set_text (buf);
7899 Editor::cancel_visual_state_op (uint32_t n)
7901 goto_visual_state (n);
7905 Editor::toggle_region_mute ()
7907 if (_ignore_region_action) {
7911 RegionSelection rs = get_regions_from_selection_and_entered ();
7917 if (rs.size() > 1) {
7918 begin_reversible_command (_("mute regions"));
7920 begin_reversible_command (_("mute region"));
7923 for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
7925 (*i)->region()->playlist()->clear_changes ();
7926 (*i)->region()->set_muted (!(*i)->region()->muted ());
7927 _session->add_command (new StatefulDiffCommand ((*i)->region()));
7931 commit_reversible_command ();
7935 Editor::combine_regions ()
7937 /* foreach track with selected regions, take all selected regions
7938 and join them into a new region containing the subregions (as a
7942 typedef set<RouteTimeAxisView*> RTVS;
7945 if (selection->regions.empty()) {
7949 for (RegionSelection::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
7950 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*>(&(*i)->get_time_axis_view());
7953 tracks.insert (rtv);
7957 begin_reversible_command (_("combine regions"));
7959 vector<RegionView*> new_selection;
7961 for (RTVS::iterator i = tracks.begin(); i != tracks.end(); ++i) {
7964 if ((rv = (*i)->combine_regions ()) != 0) {
7965 new_selection.push_back (rv);
7969 selection->clear_regions ();
7970 for (vector<RegionView*>::iterator i = new_selection.begin(); i != new_selection.end(); ++i) {
7971 selection->add (*i);
7974 commit_reversible_command ();
7978 Editor::uncombine_regions ()
7980 typedef set<RouteTimeAxisView*> RTVS;
7983 if (selection->regions.empty()) {
7987 for (RegionSelection::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
7988 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*>(&(*i)->get_time_axis_view());
7991 tracks.insert (rtv);
7995 begin_reversible_command (_("uncombine regions"));
7997 for (RTVS::iterator i = tracks.begin(); i != tracks.end(); ++i) {
7998 (*i)->uncombine_regions ();
8001 commit_reversible_command ();
8005 Editor::toggle_midi_input_active (bool flip_others)
8008 boost::shared_ptr<RouteList> rl (new RouteList);
8010 for (TrackSelection::iterator i = selection->tracks.begin(); i != selection->tracks.end(); ++i) {
8011 RouteTimeAxisView *rtav = dynamic_cast<RouteTimeAxisView *>(*i);
8017 boost::shared_ptr<MidiTrack> mt = rtav->midi_track();
8020 rl->push_back (rtav->route());
8021 onoff = !mt->input_active();
8025 _session->set_exclusive_input_active (rl, onoff, flip_others);
8028 static bool ok_fine (GdkEventAny*) { return true; }
8034 lock_dialog = new Gtk::Dialog (string_compose (_("%1: Locked"), PROGRAM_NAME), true);
8036 Gtk::Image* padlock = manage (new Gtk::Image (ARDOUR_UI_UTILS::get_icon ("padlock_closed")));
8037 lock_dialog->get_vbox()->pack_start (*padlock);
8038 lock_dialog->signal_delete_event ().connect (sigc::ptr_fun (ok_fine));
8040 ArdourButton* b = manage (new ArdourButton);
8041 b->set_name ("lock button");
8042 b->set_text (_("Click to unlock"));
8043 b->signal_clicked.connect (sigc::mem_fun (*this, &Editor::unlock));
8044 lock_dialog->get_vbox()->pack_start (*b);
8046 lock_dialog->get_vbox()->show_all ();
8047 lock_dialog->set_size_request (200, 200);
8050 delete _main_menu_disabler;
8051 _main_menu_disabler = new MainMenuDisabler;
8053 lock_dialog->present ();
8055 lock_dialog->get_window()->set_decorations (Gdk::WMDecoration (0));
8061 lock_dialog->hide ();
8063 delete _main_menu_disabler;
8064 _main_menu_disabler = 0;
8066 if (UIConfiguration::instance().get_lock_gui_after_seconds()) {
8067 start_lock_event_timing ();
8072 Editor::bring_in_callback (Gtk::Label* label, uint32_t n, uint32_t total, string name)
8074 Gtkmm2ext::UI::instance()->call_slot (invalidator (*this), boost::bind (&Editor::update_bring_in_message, this, label, n, total, name));
8078 Editor::update_bring_in_message (Gtk::Label* label, uint32_t n, uint32_t total, string name)
8080 Timers::TimerSuspender t;
8081 label->set_text (string_compose ("Copying %1, %2 of %3", name, n, total));
8082 Gtkmm2ext::UI::instance()->flush_pending (1);
8086 Editor::bring_all_sources_into_session ()
8093 ArdourDialog w (_("Moving embedded files into session folder"));
8094 w.get_vbox()->pack_start (msg);
8097 /* flush all pending GUI events because we're about to start copying
8101 Timers::TimerSuspender t;
8102 Gtkmm2ext::UI::instance()->flush_pending (3);
8106 _session->bring_all_sources_into_session (boost::bind (&Editor::bring_in_callback, this, &msg, _1, _2, _3));