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"
85 #include "mixer_strip.h"
86 #include "mouse_cursors.h"
87 #include "normalize_dialog.h"
89 #include "paste_context.h"
90 #include "patch_change_dialog.h"
91 #include "quantize_dialog.h"
92 #include "region_gain_line.h"
93 #include "rgb_macros.h"
94 #include "route_time_axis.h"
95 #include "selection.h"
96 #include "selection_templates.h"
97 #include "streamview.h"
98 #include "strip_silence_dialog.h"
99 #include "time_axis_view.h"
101 #include "transpose_dialog.h"
102 #include "transform_dialog.h"
103 #include "ui_config.h"
105 #include "pbd/i18n.h"
108 using namespace ARDOUR;
111 using namespace Gtkmm2ext;
112 using namespace Editing;
113 using Gtkmm2ext::Keyboard;
115 /***********************************************************************
117 ***********************************************************************/
120 Editor::undo (uint32_t n)
122 if (_session && _session->actively_recording()) {
123 /* no undo allowed while recording. Session will check also,
124 but we don't even want to get to that.
129 if (_drags->active ()) {
135 if (_session->undo_depth() == 0) {
136 undo_action->set_sensitive(false);
138 redo_action->set_sensitive(true);
139 begin_selection_op_history ();
144 Editor::redo (uint32_t n)
146 if (_session && _session->actively_recording()) {
147 /* no redo allowed while recording. Session will check also,
148 but we don't even want to get to that.
153 if (_drags->active ()) {
159 if (_session->redo_depth() == 0) {
160 redo_action->set_sensitive(false);
162 undo_action->set_sensitive(true);
163 begin_selection_op_history ();
168 Editor::split_regions_at (framepos_t where, RegionSelection& regions, const int32_t sub_num)
172 RegionSelection pre_selected_regions = selection->regions;
173 bool working_on_selection = !pre_selected_regions.empty();
175 list<boost::shared_ptr<Playlist> > used_playlists;
176 list<RouteTimeAxisView*> used_trackviews;
178 if (regions.empty()) {
182 begin_reversible_command (_("split"));
184 // if splitting a single region, and snap-to is using
185 // region boundaries, don't pay attention to them
187 if (regions.size() == 1) {
188 switch (_snap_type) {
189 case SnapToRegionStart:
190 case SnapToRegionSync:
191 case SnapToRegionEnd:
200 EditorFreeze(); /* Emit Signal */
203 for (RegionSelection::iterator a = regions.begin(); a != regions.end(); ) {
205 RegionSelection::iterator tmp;
207 /* XXX this test needs to be more complicated, to make sure we really
208 have something to split.
211 if (!(*a)->region()->covers (where)) {
219 boost::shared_ptr<Playlist> pl = (*a)->region()->playlist();
227 /* we haven't seen this playlist before */
229 /* remember used playlists so we can thaw them later */
230 used_playlists.push_back(pl);
232 TimeAxisView& tv = (*a)->get_time_axis_view();
233 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (&tv);
235 used_trackviews.push_back (rtv);
242 pl->clear_changes ();
243 pl->split_region ((*a)->region(), where, sub_num);
244 _session->add_command (new StatefulDiffCommand (pl));
250 latest_regionviews.clear ();
252 vector<sigc::connection> region_added_connections;
254 for (list<RouteTimeAxisView*>::iterator i = used_trackviews.begin(); i != used_trackviews.end(); ++i) {
255 region_added_connections.push_back ((*i)->view()->RegionViewAdded.connect (sigc::mem_fun(*this, &Editor::collect_new_region_view)));
258 while (used_playlists.size() > 0) {
259 list <boost::shared_ptr<Playlist > >::iterator i = used_playlists.begin();
261 used_playlists.pop_front();
264 for (vector<sigc::connection>::iterator c = region_added_connections.begin(); c != region_added_connections.end(); ++c) {
269 EditorThaw(); /* Emit Signal */
272 if (working_on_selection) {
273 // IFF we were working on selected regions, try to reinstate the other region selections that existed before the freeze/thaw.
275 _ignore_follow_edits = true; // a split will change the region selection in mysterious ways; it's not practical or wanted to follow this edit
276 RegionSelectionAfterSplit rsas = Config->get_region_selection_after_split();
277 /* There are three classes of regions that we might want selected after
278 splitting selected regions:
279 - regions selected before the split operation, and unaffected by it
280 - newly-created regions before the split
281 - newly-created regions after the split
284 if (rsas & Existing) {
285 // region selections that existed before the split.
286 selection->add ( pre_selected_regions );
289 for (RegionSelection::iterator ri = latest_regionviews.begin(); ri != latest_regionviews.end(); ri++) {
290 if ((*ri)->region()->position() < where) {
291 // new regions created before the split
292 if (rsas & NewlyCreatedLeft) {
293 selection->add (*ri);
296 // new regions created after the split
297 if (rsas & NewlyCreatedRight) {
298 selection->add (*ri);
302 _ignore_follow_edits = false;
304 _ignore_follow_edits = true;
305 if( working_on_selection ) {
306 selection->add (latest_regionviews); //these are the new regions created after the split
308 _ignore_follow_edits = false;
311 commit_reversible_command ();
314 /** Move one extreme of the current range selection. If more than one range is selected,
315 * the start of the earliest range or the end of the latest range is moved.
317 * @param move_end true to move the end of the current range selection, false to move
319 * @param next true to move the extreme to the next region boundary, false to move to
323 Editor::move_range_selection_start_or_end_to_region_boundary (bool move_end, bool next)
325 if (selection->time.start() == selection->time.end_frame()) {
329 framepos_t start = selection->time.start ();
330 framepos_t end = selection->time.end_frame ();
332 /* the position of the thing we may move */
333 framepos_t pos = move_end ? end : start;
334 int dir = next ? 1 : -1;
336 /* so we don't find the current region again */
337 if (dir > 0 || pos > 0) {
341 framepos_t const target = get_region_boundary (pos, dir, true, false);
356 begin_reversible_selection_op (_("alter selection"));
357 selection->set_preserving_all_ranges (start, end);
358 commit_reversible_selection_op ();
362 Editor::nudge_forward_release (GdkEventButton* ev)
364 if (ev->state & Keyboard::PrimaryModifier) {
365 nudge_forward (false, true);
367 nudge_forward (false, false);
373 Editor::nudge_backward_release (GdkEventButton* ev)
375 if (ev->state & Keyboard::PrimaryModifier) {
376 nudge_backward (false, true);
378 nudge_backward (false, false);
385 Editor::nudge_forward (bool next, bool force_playhead)
388 framepos_t next_distance;
394 RegionSelection rs = get_regions_from_selection_and_entered ();
396 if (!force_playhead && !rs.empty()) {
398 begin_reversible_command (_("nudge regions forward"));
400 for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
401 boost::shared_ptr<Region> r ((*i)->region());
403 distance = get_nudge_distance (r->position(), next_distance);
406 distance = next_distance;
410 r->set_position (r->position() + distance);
411 _session->add_command (new StatefulDiffCommand (r));
414 commit_reversible_command ();
417 } else if (!force_playhead && !selection->markers.empty()) {
420 bool in_command = false;
422 for (MarkerSelection::iterator i = selection->markers.begin(); i != selection->markers.end(); ++i) {
424 Location* loc = find_location_from_marker ((*i), is_start);
428 XMLNode& before (loc->get_state());
431 distance = get_nudge_distance (loc->start(), next_distance);
433 distance = next_distance;
435 if (max_framepos - distance > loc->start() + loc->length()) {
436 loc->set_start (loc->start() + distance);
438 loc->set_start (max_framepos - loc->length());
441 distance = get_nudge_distance (loc->end(), next_distance);
443 distance = next_distance;
445 if (max_framepos - distance > loc->end()) {
446 loc->set_end (loc->end() + distance);
448 loc->set_end (max_framepos);
450 if (loc->is_session_range()) {
451 _session->set_end_is_free (false);
455 begin_reversible_command (_("nudge location forward"));
458 XMLNode& after (loc->get_state());
459 _session->add_command (new MementoCommand<Location>(*loc, &before, &after));
464 commit_reversible_command ();
467 distance = get_nudge_distance (playhead_cursor->current_frame (), next_distance);
468 _session->request_locate (playhead_cursor->current_frame () + distance);
473 Editor::nudge_backward (bool next, bool force_playhead)
476 framepos_t next_distance;
482 RegionSelection rs = get_regions_from_selection_and_entered ();
484 if (!force_playhead && !rs.empty()) {
486 begin_reversible_command (_("nudge regions backward"));
488 for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
489 boost::shared_ptr<Region> r ((*i)->region());
491 distance = get_nudge_distance (r->position(), next_distance);
494 distance = next_distance;
499 if (r->position() > distance) {
500 r->set_position (r->position() - distance);
504 _session->add_command (new StatefulDiffCommand (r));
507 commit_reversible_command ();
509 } else if (!force_playhead && !selection->markers.empty()) {
512 bool in_command = false;
514 for (MarkerSelection::iterator i = selection->markers.begin(); i != selection->markers.end(); ++i) {
516 Location* loc = find_location_from_marker ((*i), is_start);
520 XMLNode& before (loc->get_state());
523 distance = get_nudge_distance (loc->start(), next_distance);
525 distance = next_distance;
527 if (distance < loc->start()) {
528 loc->set_start (loc->start() - distance);
533 distance = get_nudge_distance (loc->end(), next_distance);
536 distance = next_distance;
539 if (distance < loc->end() - loc->length()) {
540 loc->set_end (loc->end() - distance);
542 loc->set_end (loc->length());
544 if (loc->is_session_range()) {
545 _session->set_end_is_free (false);
549 begin_reversible_command (_("nudge location forward"));
552 XMLNode& after (loc->get_state());
553 _session->add_command (new MementoCommand<Location>(*loc, &before, &after));
557 commit_reversible_command ();
562 distance = get_nudge_distance (playhead_cursor->current_frame (), next_distance);
564 if (playhead_cursor->current_frame () > distance) {
565 _session->request_locate (playhead_cursor->current_frame () - distance);
567 _session->goto_start();
573 Editor::nudge_forward_capture_offset ()
575 RegionSelection rs = get_regions_from_selection_and_entered ();
577 if (!_session || rs.empty()) {
581 begin_reversible_command (_("nudge forward"));
583 framepos_t const distance = _session->worst_output_latency();
585 for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
586 boost::shared_ptr<Region> r ((*i)->region());
589 r->set_position (r->position() + distance);
590 _session->add_command(new StatefulDiffCommand (r));
593 commit_reversible_command ();
597 Editor::nudge_backward_capture_offset ()
599 RegionSelection rs = get_regions_from_selection_and_entered ();
601 if (!_session || rs.empty()) {
605 begin_reversible_command (_("nudge backward"));
607 framepos_t const distance = _session->worst_output_latency();
609 for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
610 boost::shared_ptr<Region> r ((*i)->region());
614 if (r->position() > distance) {
615 r->set_position (r->position() - distance);
619 _session->add_command(new StatefulDiffCommand (r));
622 commit_reversible_command ();
625 struct RegionSelectionPositionSorter {
626 bool operator() (RegionView* a, RegionView* b) {
627 return a->region()->position() < b->region()->position();
632 Editor::sequence_regions ()
635 framepos_t r_end_prev;
643 RegionSelection rs = get_regions_from_selection_and_entered ();
644 rs.sort(RegionSelectionPositionSorter());
648 bool in_command = false;
650 for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
651 boost::shared_ptr<Region> r ((*i)->region());
659 if(r->position_locked())
666 r->set_position(r_end_prev);
670 begin_reversible_command (_("sequence regions"));
673 _session->add_command (new StatefulDiffCommand (r));
675 r_end=r->position() + r->length();
681 commit_reversible_command ();
690 Editor::move_to_start ()
692 _session->goto_start ();
696 Editor::move_to_end ()
699 _session->request_locate (_session->current_end_frame());
703 Editor::build_region_boundary_cache ()
706 vector<RegionPoint> interesting_points;
707 boost::shared_ptr<Region> r;
708 TrackViewList tracks;
711 region_boundary_cache.clear ();
717 switch (_snap_type) {
718 case SnapToRegionStart:
719 interesting_points.push_back (Start);
721 case SnapToRegionEnd:
722 interesting_points.push_back (End);
724 case SnapToRegionSync:
725 interesting_points.push_back (SyncPoint);
727 case SnapToRegionBoundary:
728 interesting_points.push_back (Start);
729 interesting_points.push_back (End);
732 fatal << string_compose (_("build_region_boundary_cache called with snap_type = %1"), _snap_type) << endmsg;
733 abort(); /*NOTREACHED*/
737 TimeAxisView *ontrack = 0;
740 if (!selection->tracks.empty()) {
741 tlist = selection->tracks.filter_to_unique_playlists ();
743 tlist = track_views.filter_to_unique_playlists ();
746 while (pos < _session->current_end_frame() && !at_end) {
749 framepos_t lpos = max_framepos;
751 for (vector<RegionPoint>::iterator p = interesting_points.begin(); p != interesting_points.end(); ++p) {
753 if ((r = find_next_region (pos, *p, 1, tlist, &ontrack)) == 0) {
754 if (*p == interesting_points.back()) {
757 /* move to next point type */
763 rpos = r->first_frame();
767 rpos = r->last_frame();
771 rpos = r->sync_position ();
779 RouteTimeAxisView *rtav;
781 if (ontrack != 0 && (rtav = dynamic_cast<RouteTimeAxisView*>(ontrack)) != 0 ) {
782 if (rtav->track() != 0) {
783 speed = rtav->track()->speed();
787 rpos = track_frame_to_session_frame (rpos, speed);
793 /* prevent duplicates, but we don't use set<> because we want to be able
797 vector<framepos_t>::iterator ri;
799 for (ri = region_boundary_cache.begin(); ri != region_boundary_cache.end(); ++ri) {
805 if (ri == region_boundary_cache.end()) {
806 region_boundary_cache.push_back (rpos);
813 /* finally sort to be sure that the order is correct */
815 sort (region_boundary_cache.begin(), region_boundary_cache.end());
818 boost::shared_ptr<Region>
819 Editor::find_next_region (framepos_t frame, RegionPoint point, int32_t dir, TrackViewList& tracks, TimeAxisView **ontrack)
821 TrackViewList::iterator i;
822 framepos_t closest = max_framepos;
823 boost::shared_ptr<Region> ret;
827 framepos_t track_frame;
828 RouteTimeAxisView *rtav;
830 for (i = tracks.begin(); i != tracks.end(); ++i) {
833 boost::shared_ptr<Region> r;
836 if ( (rtav = dynamic_cast<RouteTimeAxisView*>(*i)) != 0 ) {
837 if (rtav->track()!=0)
838 track_speed = rtav->track()->speed();
841 track_frame = session_frame_to_track_frame(frame, track_speed);
843 if ((r = (*i)->find_next_region (track_frame, point, dir)) == 0) {
849 rpos = r->first_frame ();
853 rpos = r->last_frame ();
857 rpos = r->sync_position ();
861 // rpos is a "track frame", converting it to "_session frame"
862 rpos = track_frame_to_session_frame(rpos, track_speed);
865 distance = rpos - frame;
867 distance = frame - rpos;
870 if (distance < closest) {
882 Editor::find_next_region_boundary (framepos_t pos, int32_t dir, const TrackViewList& tracks)
884 framecnt_t distance = max_framepos;
885 framepos_t current_nearest = -1;
887 for (TrackViewList::const_iterator i = tracks.begin(); i != tracks.end(); ++i) {
888 framepos_t contender;
891 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (*i);
897 if ((contender = rtv->find_next_region_boundary (pos, dir)) < 0) {
901 d = ::llabs (pos - contender);
904 current_nearest = contender;
909 return current_nearest;
913 Editor::get_region_boundary (framepos_t pos, int32_t dir, bool with_selection, bool only_onscreen)
918 if (with_selection && Config->get_region_boundaries_from_selected_tracks()) {
920 if (!selection->tracks.empty()) {
922 target = find_next_region_boundary (pos, dir, selection->tracks);
926 if (only_onscreen || Config->get_region_boundaries_from_onscreen_tracks()) {
927 get_onscreen_tracks (tvl);
928 target = find_next_region_boundary (pos, dir, tvl);
930 target = find_next_region_boundary (pos, dir, track_views);
936 if (only_onscreen || Config->get_region_boundaries_from_onscreen_tracks()) {
937 get_onscreen_tracks (tvl);
938 target = find_next_region_boundary (pos, dir, tvl);
940 target = find_next_region_boundary (pos, dir, track_views);
948 Editor::cursor_to_region_boundary (bool with_selection, int32_t dir)
950 framepos_t pos = playhead_cursor->current_frame ();
957 // so we don't find the current region again..
958 if (dir > 0 || pos > 0) {
962 if ((target = get_region_boundary (pos, dir, with_selection, false)) < 0) {
966 _session->request_locate (target);
970 Editor::cursor_to_next_region_boundary (bool with_selection)
972 cursor_to_region_boundary (with_selection, 1);
976 Editor::cursor_to_previous_region_boundary (bool with_selection)
978 cursor_to_region_boundary (with_selection, -1);
982 Editor::cursor_to_region_point (EditorCursor* cursor, RegionPoint point, int32_t dir)
984 boost::shared_ptr<Region> r;
985 framepos_t pos = cursor->current_frame ();
991 TimeAxisView *ontrack = 0;
993 // so we don't find the current region again..
997 if (!selection->tracks.empty()) {
999 r = find_next_region (pos, point, dir, selection->tracks, &ontrack);
1001 } else if (clicked_axisview) {
1004 t.push_back (clicked_axisview);
1006 r = find_next_region (pos, point, dir, t, &ontrack);
1010 r = find_next_region (pos, point, dir, track_views, &ontrack);
1019 pos = r->first_frame ();
1023 pos = r->last_frame ();
1027 pos = r->sync_position ();
1032 RouteTimeAxisView *rtav;
1034 if ( ontrack != 0 && (rtav = dynamic_cast<RouteTimeAxisView*>(ontrack)) != 0 ) {
1035 if (rtav->track() != 0) {
1036 speed = rtav->track()->speed();
1040 pos = track_frame_to_session_frame(pos, speed);
1042 if (cursor == playhead_cursor) {
1043 _session->request_locate (pos);
1045 cursor->set_position (pos);
1050 Editor::cursor_to_next_region_point (EditorCursor* cursor, RegionPoint point)
1052 cursor_to_region_point (cursor, point, 1);
1056 Editor::cursor_to_previous_region_point (EditorCursor* cursor, RegionPoint point)
1058 cursor_to_region_point (cursor, point, -1);
1062 Editor::cursor_to_selection_start (EditorCursor *cursor)
1066 switch (mouse_mode) {
1068 if (!selection->regions.empty()) {
1069 pos = selection->regions.start();
1074 if (!selection->time.empty()) {
1075 pos = selection->time.start ();
1083 if (cursor == playhead_cursor) {
1084 _session->request_locate (pos);
1086 cursor->set_position (pos);
1091 Editor::cursor_to_selection_end (EditorCursor *cursor)
1095 switch (mouse_mode) {
1097 if (!selection->regions.empty()) {
1098 pos = selection->regions.end_frame();
1103 if (!selection->time.empty()) {
1104 pos = selection->time.end_frame ();
1112 if (cursor == playhead_cursor) {
1113 _session->request_locate (pos);
1115 cursor->set_position (pos);
1120 Editor::selected_marker_to_region_boundary (bool with_selection, int32_t dir)
1130 if (selection->markers.empty()) {
1134 if (!mouse_frame (mouse, ignored)) {
1138 add_location_mark (mouse);
1141 if ((loc = find_location_from_marker (selection->markers.front(), ignored)) == 0) {
1145 framepos_t pos = loc->start();
1147 // so we don't find the current region again..
1148 if (dir > 0 || pos > 0) {
1152 if ((target = get_region_boundary (pos, dir, with_selection, false)) < 0) {
1156 loc->move_to (target);
1160 Editor::selected_marker_to_next_region_boundary (bool with_selection)
1162 selected_marker_to_region_boundary (with_selection, 1);
1166 Editor::selected_marker_to_previous_region_boundary (bool with_selection)
1168 selected_marker_to_region_boundary (with_selection, -1);
1172 Editor::selected_marker_to_region_point (RegionPoint point, int32_t dir)
1174 boost::shared_ptr<Region> r;
1179 if (!_session || selection->markers.empty()) {
1183 if ((loc = find_location_from_marker (selection->markers.front(), ignored)) == 0) {
1187 TimeAxisView *ontrack = 0;
1191 // so we don't find the current region again..
1195 if (!selection->tracks.empty()) {
1197 r = find_next_region (pos, point, dir, selection->tracks, &ontrack);
1201 r = find_next_region (pos, point, dir, track_views, &ontrack);
1210 pos = r->first_frame ();
1214 pos = r->last_frame ();
1218 pos = r->adjust_to_sync (r->first_frame());
1223 RouteTimeAxisView *rtav;
1225 if (ontrack != 0 && (rtav = dynamic_cast<RouteTimeAxisView*>(ontrack)) != 0) {
1226 if (rtav->track() != 0) {
1227 speed = rtav->track()->speed();
1231 pos = track_frame_to_session_frame(pos, speed);
1237 Editor::selected_marker_to_next_region_point (RegionPoint point)
1239 selected_marker_to_region_point (point, 1);
1243 Editor::selected_marker_to_previous_region_point (RegionPoint point)
1245 selected_marker_to_region_point (point, -1);
1249 Editor::selected_marker_to_selection_start ()
1255 if (!_session || selection->markers.empty()) {
1259 if ((loc = find_location_from_marker (selection->markers.front(), ignored)) == 0) {
1263 switch (mouse_mode) {
1265 if (!selection->regions.empty()) {
1266 pos = selection->regions.start();
1271 if (!selection->time.empty()) {
1272 pos = selection->time.start ();
1284 Editor::selected_marker_to_selection_end ()
1290 if (!_session || selection->markers.empty()) {
1294 if ((loc = find_location_from_marker (selection->markers.front(), ignored)) == 0) {
1298 switch (mouse_mode) {
1300 if (!selection->regions.empty()) {
1301 pos = selection->regions.end_frame();
1306 if (!selection->time.empty()) {
1307 pos = selection->time.end_frame ();
1319 Editor::scroll_playhead (bool forward)
1321 framepos_t pos = playhead_cursor->current_frame ();
1322 framecnt_t delta = (framecnt_t) floor (current_page_samples() / 0.8);
1325 if (pos == max_framepos) {
1329 if (pos < max_framepos - delta) {
1348 _session->request_locate (pos);
1352 Editor::cursor_align (bool playhead_to_edit)
1358 if (playhead_to_edit) {
1360 if (selection->markers.empty()) {
1364 _session->request_locate (selection->markers.front()->position(), _session->transport_rolling());
1367 /* move selected markers to playhead */
1369 for (MarkerSelection::iterator i = selection->markers.begin(); i != selection->markers.end(); ++i) {
1372 Location* loc = find_location_from_marker (*i, ignored);
1374 if (loc->is_mark()) {
1375 loc->set_start (playhead_cursor->current_frame ());
1377 loc->set (playhead_cursor->current_frame (),
1378 playhead_cursor->current_frame () + loc->length());
1385 Editor::scroll_backward (float pages)
1387 framepos_t const one_page = (framepos_t) rint (_visible_canvas_width * samples_per_pixel);
1388 framepos_t const cnt = (framepos_t) floor (pages * one_page);
1391 if (leftmost_frame < cnt) {
1394 frame = leftmost_frame - cnt;
1397 reset_x_origin (frame);
1401 Editor::scroll_forward (float pages)
1403 framepos_t const one_page = (framepos_t) rint (_visible_canvas_width * samples_per_pixel);
1404 framepos_t const cnt = (framepos_t) floor (pages * one_page);
1407 if (max_framepos - cnt < leftmost_frame) {
1408 frame = max_framepos - cnt;
1410 frame = leftmost_frame + cnt;
1413 reset_x_origin (frame);
1417 Editor::scroll_tracks_down ()
1419 double vert_value = vertical_adjustment.get_value() + vertical_adjustment.get_page_size();
1420 if (vert_value > vertical_adjustment.get_upper() - _visible_canvas_height) {
1421 vert_value = vertical_adjustment.get_upper() - _visible_canvas_height;
1424 vertical_adjustment.set_value (vert_value);
1428 Editor::scroll_tracks_up ()
1430 vertical_adjustment.set_value (vertical_adjustment.get_value() - vertical_adjustment.get_page_size());
1434 Editor::scroll_tracks_down_line ()
1436 double vert_value = vertical_adjustment.get_value() + 60;
1438 if (vert_value > vertical_adjustment.get_upper() - _visible_canvas_height) {
1439 vert_value = vertical_adjustment.get_upper() - _visible_canvas_height;
1442 vertical_adjustment.set_value (vert_value);
1446 Editor::scroll_tracks_up_line ()
1448 reset_y_origin (vertical_adjustment.get_value() - 60);
1452 Editor::scroll_down_one_track (bool skip_child_views)
1454 TrackViewList::reverse_iterator next = track_views.rend();
1455 const double top_of_trackviews = vertical_adjustment.get_value();
1457 for (TrackViewList::reverse_iterator t = track_views.rbegin(); t != track_views.rend(); ++t) {
1458 if ((*t)->hidden()) {
1462 /* If this is the upper-most visible trackview, we want to display
1463 * the one above it (next)
1465 * Note that covers_y_position() is recursive and includes child views
1467 std::pair<TimeAxisView*,double> res = (*t)->covers_y_position (top_of_trackviews);
1470 if (skip_child_views) {
1473 /* automation lane (one level, non-recursive)
1475 * - if no automation lane exists -> move to next tack
1476 * - if the first (here: bottom-most) matches -> move to next tack
1477 * - if no y-axis match is found -> the current track is at the top
1478 * -> move to last (here: top-most) automation lane
1480 TimeAxisView::Children kids = (*t)->get_child_list();
1481 TimeAxisView::Children::reverse_iterator nkid = kids.rend();
1483 for (TimeAxisView::Children::reverse_iterator ci = kids.rbegin(); ci != kids.rend(); ++ci) {
1484 if ((*ci)->hidden()) {
1488 std::pair<TimeAxisView*,double> dev;
1489 dev = (*ci)->covers_y_position (top_of_trackviews);
1491 /* some automation lane is currently at the top */
1492 if (ci == kids.rbegin()) {
1493 /* first (bottom-most) autmation lane is at the top.
1494 * -> move to next track
1503 if (nkid != kids.rend()) {
1504 ensure_time_axis_view_is_visible (**nkid, true);
1512 /* move to the track below the first one that covers the */
1514 if (next != track_views.rend()) {
1515 ensure_time_axis_view_is_visible (**next, true);
1523 Editor::scroll_up_one_track (bool skip_child_views)
1525 TrackViewList::iterator prev = track_views.end();
1526 double top_of_trackviews = vertical_adjustment.get_value ();
1528 for (TrackViewList::iterator t = track_views.begin(); t != track_views.end(); ++t) {
1530 if ((*t)->hidden()) {
1534 /* find the trackview at the top of the trackview group
1536 * Note that covers_y_position() is recursive and includes child views
1538 std::pair<TimeAxisView*,double> res = (*t)->covers_y_position (top_of_trackviews);
1541 if (skip_child_views) {
1544 /* automation lane (one level, non-recursive)
1546 * - if no automation lane exists -> move to prev tack
1547 * - if no y-axis match is found -> the current track is at the top -> move to prev track
1548 * (actually last automation lane of previous track, see below)
1549 * - if first (top-most) lane is at the top -> move to this track
1550 * - else move up one lane
1552 TimeAxisView::Children kids = (*t)->get_child_list();
1553 TimeAxisView::Children::iterator pkid = kids.end();
1555 for (TimeAxisView::Children::iterator ci = kids.begin(); ci != kids.end(); ++ci) {
1556 if ((*ci)->hidden()) {
1560 std::pair<TimeAxisView*,double> dev;
1561 dev = (*ci)->covers_y_position (top_of_trackviews);
1563 /* some automation lane is currently at the top */
1564 if (ci == kids.begin()) {
1565 /* first (top-most) autmation lane is at the top.
1566 * jump directly to this track's top
1568 ensure_time_axis_view_is_visible (**t, true);
1571 else if (pkid != kids.end()) {
1572 /* some other automation lane is at the top.
1573 * move up to prev automation lane.
1575 ensure_time_axis_view_is_visible (**pkid, true);
1578 assert(0); // not reached
1589 if (prev != track_views.end()) {
1590 // move to bottom-most automation-lane of the previous track
1591 TimeAxisView::Children kids = (*prev)->get_child_list();
1592 TimeAxisView::Children::reverse_iterator pkid = kids.rend();
1593 if (!skip_child_views) {
1594 // find the last visible lane
1595 for (TimeAxisView::Children::reverse_iterator ci = kids.rbegin(); ci != kids.rend(); ++ci) {
1596 if (!(*ci)->hidden()) {
1602 if (pkid != kids.rend()) {
1603 ensure_time_axis_view_is_visible (**pkid, true);
1605 ensure_time_axis_view_is_visible (**prev, true);
1614 Editor::scroll_left_step ()
1616 framepos_t xdelta = (current_page_samples() / 8);
1618 if (leftmost_frame > xdelta) {
1619 reset_x_origin (leftmost_frame - xdelta);
1627 Editor::scroll_right_step ()
1629 framepos_t xdelta = (current_page_samples() / 8);
1631 if (max_framepos - xdelta > leftmost_frame) {
1632 reset_x_origin (leftmost_frame + xdelta);
1634 reset_x_origin (max_framepos - current_page_samples());
1639 Editor::scroll_left_half_page ()
1641 framepos_t xdelta = (current_page_samples() / 2);
1642 if (leftmost_frame > xdelta) {
1643 reset_x_origin (leftmost_frame - xdelta);
1650 Editor::scroll_right_half_page ()
1652 framepos_t xdelta = (current_page_samples() / 2);
1653 if (max_framepos - xdelta > leftmost_frame) {
1654 reset_x_origin (leftmost_frame + xdelta);
1656 reset_x_origin (max_framepos - current_page_samples());
1663 Editor::tav_zoom_step (bool coarser)
1665 DisplaySuspender ds;
1669 if (selection->tracks.empty()) {
1672 ts = &selection->tracks;
1675 for (TrackViewList::iterator i = ts->begin(); i != ts->end(); ++i) {
1676 TimeAxisView *tv = (static_cast<TimeAxisView*>(*i));
1677 tv->step_height (coarser);
1682 Editor::tav_zoom_smooth (bool coarser, bool force_all)
1684 DisplaySuspender ds;
1688 if (selection->tracks.empty() || force_all) {
1691 ts = &selection->tracks;
1694 for (TrackViewList::iterator i = ts->begin(); i != ts->end(); ++i) {
1695 TimeAxisView *tv = (static_cast<TimeAxisView*>(*i));
1696 uint32_t h = tv->current_height ();
1701 if (h >= TimeAxisView::preset_height (HeightSmall)) {
1706 tv->set_height (h + 5);
1712 Editor::temporal_zoom_step_mouse_focus (bool coarser)
1714 Editing::ZoomFocus temp_focus = zoom_focus;
1715 zoom_focus = Editing::ZoomFocusMouse;
1716 temporal_zoom_step (coarser);
1717 zoom_focus = temp_focus;
1721 Editor::temporal_zoom_step (bool coarser)
1723 ENSURE_GUI_THREAD (*this, &Editor::temporal_zoom_step, coarser)
1725 framecnt_t nspp = samples_per_pixel;
1733 temporal_zoom (nspp);
1737 Editor::temporal_zoom (framecnt_t fpp)
1743 framepos_t current_page = current_page_samples();
1744 framepos_t current_leftmost = leftmost_frame;
1745 framepos_t current_rightmost;
1746 framepos_t current_center;
1747 framepos_t new_page_size;
1748 framepos_t half_page_size;
1749 framepos_t leftmost_after_zoom = 0;
1751 bool in_track_canvas;
1755 if (fpp == samples_per_pixel) {
1759 // Imposing an arbitrary limit to zoom out as too much zoom out produces
1760 // segfaults for lack of memory. If somebody decides this is not high enough I
1761 // believe it can be raisen to higher values but some limit must be in place.
1763 // This constant represents 1 day @ 48kHz on a 1600 pixel wide display
1764 // all of which is used for the editor track displays. The whole day
1765 // would be 4147200000 samples, so 2592000 samples per pixel.
1767 nfpp = min (fpp, (framecnt_t) 2592000);
1768 nfpp = max ((framecnt_t) 1, nfpp);
1770 new_page_size = (framepos_t) floor (_visible_canvas_width * nfpp);
1771 half_page_size = new_page_size / 2;
1773 switch (zoom_focus) {
1775 leftmost_after_zoom = current_leftmost;
1778 case ZoomFocusRight:
1779 current_rightmost = leftmost_frame + current_page;
1780 if (current_rightmost < new_page_size) {
1781 leftmost_after_zoom = 0;
1783 leftmost_after_zoom = current_rightmost - new_page_size;
1787 case ZoomFocusCenter:
1788 current_center = current_leftmost + (current_page/2);
1789 if (current_center < half_page_size) {
1790 leftmost_after_zoom = 0;
1792 leftmost_after_zoom = current_center - half_page_size;
1796 case ZoomFocusPlayhead:
1797 /* centre playhead */
1798 l = playhead_cursor->current_frame () - (new_page_size * 0.5);
1801 leftmost_after_zoom = 0;
1802 } else if (l > max_framepos) {
1803 leftmost_after_zoom = max_framepos - new_page_size;
1805 leftmost_after_zoom = (framepos_t) l;
1809 case ZoomFocusMouse:
1810 /* try to keep the mouse over the same point in the display */
1812 if (!mouse_frame (where, in_track_canvas)) {
1813 /* use playhead instead */
1814 where = playhead_cursor->current_frame ();
1816 if (where < half_page_size) {
1817 leftmost_after_zoom = 0;
1819 leftmost_after_zoom = where - half_page_size;
1824 l = - ((new_page_size * ((where - current_leftmost)/(double)current_page)) - where);
1827 leftmost_after_zoom = 0;
1828 } else if (l > max_framepos) {
1829 leftmost_after_zoom = max_framepos - new_page_size;
1831 leftmost_after_zoom = (framepos_t) l;
1838 /* try to keep the edit point in the same place */
1839 where = get_preferred_edit_position ();
1843 double 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;
1854 /* edit point not defined */
1861 // leftmost_after_zoom = min (leftmost_after_zoom, _session->current_end_frame());
1863 reposition_and_zoom (leftmost_after_zoom, nfpp);
1867 Editor::calc_extra_zoom_edges(framepos_t &start, framepos_t &end)
1869 /* this func helps make sure we leave a little space
1870 at each end of the editor so that the zoom doesn't fit the region
1871 precisely to the screen.
1874 GdkScreen* screen = gdk_screen_get_default ();
1875 const gint pixwidth = gdk_screen_get_width (screen);
1876 const gint mmwidth = gdk_screen_get_width_mm (screen);
1877 const double pix_per_mm = (double) pixwidth/ (double) mmwidth;
1878 const double one_centimeter_in_pixels = pix_per_mm * 10.0;
1880 const framepos_t range = end - start;
1881 const framecnt_t new_fpp = (framecnt_t) ceil ((double) range / (double) _visible_canvas_width);
1882 const framepos_t extra_samples = (framepos_t) floor (one_centimeter_in_pixels * new_fpp);
1884 if (start > extra_samples) {
1885 start -= extra_samples;
1890 if (max_framepos - extra_samples > end) {
1891 end += extra_samples;
1898 Editor::temporal_zoom_region (bool both_axes)
1900 framepos_t start = max_framepos;
1902 set<TimeAxisView*> tracks;
1904 if ( !get_selection_extents(start, end) )
1907 calc_extra_zoom_edges (start, end);
1909 /* if we're zooming on both axes we need to save track heights etc.
1912 undo_visual_stack.push_back (current_visual_state (both_axes));
1914 PBD::Unwinder<bool> nsv (no_save_visual, true);
1916 temporal_zoom_by_frame (start, end);
1919 uint32_t per_track_height = (uint32_t) floor ((_visible_canvas_height - 10.0) / tracks.size());
1921 /* set visible track heights appropriately */
1923 for (set<TimeAxisView*>::iterator t = tracks.begin(); t != tracks.end(); ++t) {
1924 (*t)->set_height (per_track_height);
1927 /* hide irrelevant tracks */
1929 DisplaySuspender ds;
1931 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
1932 if (find (tracks.begin(), tracks.end(), (*i)) == tracks.end()) {
1933 hide_track_in_display (*i);
1937 vertical_adjustment.set_value (0.0);
1940 redo_visual_stack.push_back (current_visual_state (both_axes));
1945 Editor::get_selection_extents (framepos_t &start, framepos_t &end) const
1947 start = max_framepos;
1951 //ToDo: if notes are selected, set extents to that selection
1953 //ToDo: if control points are selected, set extents to that selection
1955 if ( !selection->regions.empty() ) {
1956 RegionSelection rs = get_regions_from_selection_and_entered ();
1958 for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
1960 if ((*i)->region()->position() < start) {
1961 start = (*i)->region()->position();
1964 if ((*i)->region()->last_frame() + 1 > end) {
1965 end = (*i)->region()->last_frame() + 1;
1969 } else if (!selection->time.empty()) {
1970 start = selection->time.start();
1971 end = selection->time.end_frame();
1973 ret = false; //no selection found
1976 if ((start == 0 && end == 0) || end < start) {
1985 Editor::temporal_zoom_selection (bool both_axes)
1987 if (!selection) return;
1989 //ToDo: if notes are selected, zoom to that
1991 //ToDo: if control points are selected, zoom to that
1993 //if region(s) are selected, zoom to that
1994 if ( !selection->regions.empty() )
1995 temporal_zoom_region (both_axes);
1997 //if a range is selected, zoom to that
1998 if (!selection->time.empty()) {
2000 framepos_t start, end;
2001 if (get_selection_extents (start, end)) {
2002 calc_extra_zoom_edges(start, end);
2003 temporal_zoom_by_frame (start, end);
2013 Editor::temporal_zoom_session ()
2015 ENSURE_GUI_THREAD (*this, &Editor::temporal_zoom_session)
2018 framecnt_t start = _session->current_start_frame();
2019 framecnt_t end = _session->current_end_frame();
2021 if (_session->actively_recording () ) {
2022 framepos_t cur = playhead_cursor->current_frame ();
2024 /* recording beyond the end marker; zoom out
2025 * by 5 seconds more so that if 'follow
2026 * playhead' is active we don't immediately
2029 end = cur + _session->frame_rate() * 5;
2033 if ((start == 0 && end == 0) || end < start) {
2037 calc_extra_zoom_edges(start, end);
2039 temporal_zoom_by_frame (start, end);
2044 Editor::temporal_zoom_by_frame (framepos_t start, framepos_t end)
2046 if (!_session) return;
2048 if ((start == 0 && end == 0) || end < start) {
2052 framepos_t range = end - start;
2054 const framecnt_t new_fpp = (framecnt_t) ceil ((double) range / (double) _visible_canvas_width);
2056 framepos_t new_page = range;
2057 framepos_t middle = (framepos_t) floor ((double) start + ((double) range / 2.0f));
2058 framepos_t new_leftmost = (framepos_t) floor ((double) middle - ((double) new_page / 2.0f));
2060 if (new_leftmost > middle) {
2064 if (new_leftmost < 0) {
2068 reposition_and_zoom (new_leftmost, new_fpp);
2072 Editor::temporal_zoom_to_frame (bool coarser, framepos_t frame)
2078 framecnt_t range_before = frame - leftmost_frame;
2082 if (samples_per_pixel <= 1) {
2085 new_spp = samples_per_pixel + (samples_per_pixel/2);
2087 range_before += range_before/2;
2089 if (samples_per_pixel >= 1) {
2090 new_spp = samples_per_pixel - (samples_per_pixel/2);
2092 /* could bail out here since we cannot zoom any finer,
2093 but leave that to the equality test below
2095 new_spp = samples_per_pixel;
2098 range_before -= range_before/2;
2101 if (new_spp == samples_per_pixel) {
2105 /* zoom focus is automatically taken as @param frame when this
2109 framepos_t new_leftmost = frame - (framepos_t)range_before;
2111 if (new_leftmost > frame) {
2115 if (new_leftmost < 0) {
2119 reposition_and_zoom (new_leftmost, new_spp);
2124 Editor::choose_new_marker_name(string &name) {
2126 if (!UIConfiguration::instance().get_name_new_markers()) {
2127 /* don't prompt user for a new name */
2131 ArdourPrompter dialog (true);
2133 dialog.set_prompt (_("New Name:"));
2135 dialog.set_title (_("New Location Marker"));
2137 dialog.set_name ("MarkNameWindow");
2138 dialog.set_size_request (250, -1);
2139 dialog.set_position (Gtk::WIN_POS_MOUSE);
2141 dialog.add_button (Stock::OK, RESPONSE_ACCEPT);
2142 dialog.set_initial_text (name);
2146 switch (dialog.run ()) {
2147 case RESPONSE_ACCEPT:
2153 dialog.get_result(name);
2160 Editor::add_location_from_selection ()
2164 if (selection->time.empty()) {
2168 if (_session == 0 || clicked_axisview == 0) {
2172 framepos_t start = selection->time[clicked_selection].start;
2173 framepos_t end = selection->time[clicked_selection].end;
2175 _session->locations()->next_available_name(rangename,"selection");
2176 Location *location = new Location (*_session, start, end, rangename, Location::IsRangeMarker);
2178 begin_reversible_command (_("add marker"));
2180 XMLNode &before = _session->locations()->get_state();
2181 _session->locations()->add (location, true);
2182 XMLNode &after = _session->locations()->get_state();
2183 _session->add_command(new MementoCommand<Locations>(*(_session->locations()), &before, &after));
2185 commit_reversible_command ();
2189 Editor::add_location_mark (framepos_t where)
2193 select_new_marker = true;
2195 _session->locations()->next_available_name(markername,"mark");
2196 if (!choose_new_marker_name(markername)) {
2199 Location *location = new Location (*_session, where, where, markername, Location::IsMark);
2200 begin_reversible_command (_("add marker"));
2202 XMLNode &before = _session->locations()->get_state();
2203 _session->locations()->add (location, true);
2204 XMLNode &after = _session->locations()->get_state();
2205 _session->add_command(new MementoCommand<Locations>(*(_session->locations()), &before, &after));
2207 commit_reversible_command ();
2211 Editor::set_session_start_from_playhead ()
2217 if ((loc = _session->locations()->session_range_location()) == 0) { //should never happen
2218 _session->set_session_extents ( _session->audible_frame(), _session->audible_frame() );
2220 XMLNode &before = loc->get_state();
2222 _session->set_session_extents ( _session->audible_frame(), loc->end() );
2224 XMLNode &after = loc->get_state();
2226 begin_reversible_command (_("Set session start"));
2228 _session->add_command (new MementoCommand<Location>(*loc, &before, &after));
2230 commit_reversible_command ();
2235 Editor::set_session_end_from_playhead ()
2241 if ((loc = _session->locations()->session_range_location()) == 0) { //should never happen
2242 _session->set_session_extents ( _session->audible_frame(), _session->audible_frame() );
2244 XMLNode &before = loc->get_state();
2246 _session->set_session_extents ( loc->start(), _session->audible_frame() );
2248 XMLNode &after = loc->get_state();
2250 begin_reversible_command (_("Set session start"));
2252 _session->add_command (new MementoCommand<Location>(*loc, &before, &after));
2254 commit_reversible_command ();
2257 _session->set_end_is_free (false);
2262 Editor::toggle_location_at_playhead_cursor ()
2264 if (!do_remove_location_at_playhead_cursor())
2266 add_location_from_playhead_cursor();
2271 Editor::add_location_from_playhead_cursor ()
2273 add_location_mark (_session->audible_frame());
2277 Editor::do_remove_location_at_playhead_cursor ()
2279 bool removed = false;
2282 XMLNode &before = _session->locations()->get_state();
2284 //find location(s) at this time
2285 Locations::LocationList locs;
2286 _session->locations()->find_all_between (_session->audible_frame(), _session->audible_frame()+1, locs, Location::Flags(0));
2287 for (Locations::LocationList::iterator i = locs.begin(); i != locs.end(); ++i) {
2288 if ((*i)->is_mark()) {
2289 _session->locations()->remove (*i);
2296 begin_reversible_command (_("remove marker"));
2297 XMLNode &after = _session->locations()->get_state();
2298 _session->add_command(new MementoCommand<Locations>(*(_session->locations()), &before, &after));
2299 commit_reversible_command ();
2306 Editor::remove_location_at_playhead_cursor ()
2308 do_remove_location_at_playhead_cursor ();
2311 /** Add a range marker around each selected region */
2313 Editor::add_locations_from_region ()
2315 RegionSelection rs = get_regions_from_selection_and_entered ();
2320 bool commit = false;
2322 XMLNode &before = _session->locations()->get_state();
2324 for (RegionSelection::iterator i = rs.begin (); i != rs.end (); ++i) {
2326 boost::shared_ptr<Region> region = (*i)->region ();
2328 Location *location = new Location (*_session, region->position(), region->last_frame(), region->name(), Location::IsRangeMarker);
2330 _session->locations()->add (location, true);
2335 begin_reversible_command (selection->regions.size () > 1 ? _("add markers") : _("add marker"));
2336 XMLNode &after = _session->locations()->get_state();
2337 _session->add_command (new MementoCommand<Locations>(*(_session->locations()), &before, &after));
2338 commit_reversible_command ();
2342 /** Add a single range marker around all selected regions */
2344 Editor::add_location_from_region ()
2346 RegionSelection rs = get_regions_from_selection_and_entered ();
2352 XMLNode &before = _session->locations()->get_state();
2356 if (rs.size() > 1) {
2357 _session->locations()->next_available_name(markername, "regions");
2359 RegionView* rv = *(rs.begin());
2360 boost::shared_ptr<Region> region = rv->region();
2361 markername = region->name();
2364 if (!choose_new_marker_name(markername)) {
2368 // single range spanning all selected
2369 Location *location = new Location (*_session, selection->regions.start(), selection->regions.end_frame(), markername, Location::IsRangeMarker);
2370 _session->locations()->add (location, true);
2372 begin_reversible_command (_("add marker"));
2373 XMLNode &after = _session->locations()->get_state();
2374 _session->add_command (new MementoCommand<Locations>(*(_session->locations()), &before, &after));
2375 commit_reversible_command ();
2381 Editor::jump_forward_to_mark ()
2387 framepos_t pos = _session->locations()->first_mark_after (playhead_cursor->current_frame());
2393 _session->request_locate (pos, _session->transport_rolling());
2397 Editor::jump_backward_to_mark ()
2403 framepos_t pos = _session->locations()->first_mark_before (playhead_cursor->current_frame());
2409 _session->request_locate (pos, _session->transport_rolling());
2415 framepos_t const pos = _session->audible_frame ();
2418 _session->locations()->next_available_name (markername, "mark");
2420 if (!choose_new_marker_name (markername)) {
2424 _session->locations()->add (new Location (*_session, pos, 0, markername, Location::IsMark), true);
2428 Editor::clear_markers ()
2431 begin_reversible_command (_("clear markers"));
2433 XMLNode &before = _session->locations()->get_state();
2434 _session->locations()->clear_markers ();
2435 XMLNode &after = _session->locations()->get_state();
2436 _session->add_command(new MementoCommand<Locations>(*(_session->locations()), &before, &after));
2438 commit_reversible_command ();
2443 Editor::clear_ranges ()
2446 begin_reversible_command (_("clear ranges"));
2448 XMLNode &before = _session->locations()->get_state();
2450 _session->locations()->clear_ranges ();
2452 XMLNode &after = _session->locations()->get_state();
2453 _session->add_command(new MementoCommand<Locations>(*(_session->locations()), &before, &after));
2455 commit_reversible_command ();
2460 Editor::clear_locations ()
2462 begin_reversible_command (_("clear locations"));
2464 XMLNode &before = _session->locations()->get_state();
2465 _session->locations()->clear ();
2466 XMLNode &after = _session->locations()->get_state();
2467 _session->add_command(new MementoCommand<Locations>(*(_session->locations()), &before, &after));
2469 commit_reversible_command ();
2473 Editor::unhide_markers ()
2475 for (LocationMarkerMap::iterator i = location_markers.begin(); i != location_markers.end(); ++i) {
2476 Location *l = (*i).first;
2477 if (l->is_hidden() && l->is_mark()) {
2478 l->set_hidden(false, this);
2484 Editor::unhide_ranges ()
2486 for (LocationMarkerMap::iterator i = location_markers.begin(); i != location_markers.end(); ++i) {
2487 Location *l = (*i).first;
2488 if (l->is_hidden() && l->is_range_marker()) {
2489 l->set_hidden(false, this);
2494 /* INSERT/REPLACE */
2497 Editor::insert_region_list_selection (float times)
2499 RouteTimeAxisView *tv = 0;
2500 boost::shared_ptr<Playlist> playlist;
2502 if (clicked_routeview != 0) {
2503 tv = clicked_routeview;
2504 } else if (!selection->tracks.empty()) {
2505 if ((tv = dynamic_cast<RouteTimeAxisView*>(selection->tracks.front())) == 0) {
2508 } else if (entered_track != 0) {
2509 if ((tv = dynamic_cast<RouteTimeAxisView*>(entered_track)) == 0) {
2516 if ((playlist = tv->playlist()) == 0) {
2520 boost::shared_ptr<Region> region = _regions->get_single_selection ();
2525 begin_reversible_command (_("insert region"));
2526 playlist->clear_changes ();
2527 playlist->add_region ((RegionFactory::create (region, true)), get_preferred_edit_position(), times);
2528 if (Config->get_edit_mode() == Ripple)
2529 playlist->ripple (get_preferred_edit_position(), region->length() * times, boost::shared_ptr<Region>());
2531 _session->add_command(new StatefulDiffCommand (playlist));
2532 commit_reversible_command ();
2535 /* BUILT-IN EFFECTS */
2538 Editor::reverse_selection ()
2543 /* GAIN ENVELOPE EDITING */
2546 Editor::edit_envelope ()
2553 Editor::transition_to_rolling (bool fwd)
2559 if (_session->config.get_external_sync()) {
2560 switch (Config->get_sync_source()) {
2564 /* transport controlled by the master */
2569 if (_session->is_auditioning()) {
2570 _session->cancel_audition ();
2574 _session->request_transport_speed (fwd ? 1.0f : -1.0f);
2578 Editor::play_from_start ()
2580 _session->request_locate (_session->current_start_frame(), true);
2584 Editor::play_from_edit_point ()
2586 _session->request_locate (get_preferred_edit_position(), true);
2590 Editor::play_from_edit_point_and_return ()
2592 framepos_t start_frame;
2593 framepos_t return_frame;
2595 start_frame = get_preferred_edit_position ( EDIT_IGNORE_PHEAD );
2597 if (_session->transport_rolling()) {
2598 _session->request_locate (start_frame, false);
2602 /* don't reset the return frame if its already set */
2604 if ((return_frame = _session->requested_return_frame()) < 0) {
2605 return_frame = _session->audible_frame();
2608 if (start_frame >= 0) {
2609 _session->request_roll_at_and_return (start_frame, return_frame);
2614 Editor::play_selection ()
2616 framepos_t start, end;
2617 if (!get_selection_extents ( start, end))
2620 AudioRange ar (start, end, 0);
2621 list<AudioRange> lar;
2624 _session->request_play_range (&lar, true);
2628 Editor::get_preroll ()
2630 return Config->get_preroll_seconds() * _session->frame_rate();
2635 Editor::maybe_locate_with_edit_preroll ( framepos_t location )
2637 if ( _session->transport_rolling() || !UIConfiguration::instance().get_follow_edits() || _ignore_follow_edits || _session->config.get_external_sync() )
2640 location -= get_preroll();
2642 //don't try to locate before the beginning of time
2646 //if follow_playhead is on, keep the playhead on the screen
2647 if ( _follow_playhead )
2648 if ( location < leftmost_frame )
2649 location = leftmost_frame;
2651 _session->request_locate( location );
2655 Editor::play_with_preroll ()
2658 framepos_t preroll = get_preroll();
2660 framepos_t start, end;
2661 if (!get_selection_extents ( start, end))
2664 if (start > preroll)
2665 start = start - preroll;
2667 end = end + preroll; //"post-roll"
2669 AudioRange ar (start, end, 0);
2670 list<AudioRange> lar;
2673 _session->request_play_range (&lar, true);
2678 Editor::play_location (Location& location)
2680 if (location.start() <= location.end()) {
2684 _session->request_bounded_roll (location.start(), location.end());
2688 Editor::loop_location (Location& location)
2690 if (location.start() <= location.end()) {
2696 if ((tll = transport_loop_location()) != 0) {
2697 tll->set (location.start(), location.end());
2699 // enable looping, reposition and start rolling
2700 _session->request_locate (tll->start(), true);
2701 _session->request_play_loop (true);
2706 Editor::do_layer_operation (LayerOperation op)
2708 if (selection->regions.empty ()) {
2712 bool const multiple = selection->regions.size() > 1;
2716 begin_reversible_command (_("raise regions"));
2718 begin_reversible_command (_("raise region"));
2724 begin_reversible_command (_("raise regions to top"));
2726 begin_reversible_command (_("raise region to top"));
2732 begin_reversible_command (_("lower regions"));
2734 begin_reversible_command (_("lower region"));
2740 begin_reversible_command (_("lower regions to bottom"));
2742 begin_reversible_command (_("lower region"));
2747 set<boost::shared_ptr<Playlist> > playlists = selection->regions.playlists ();
2748 for (set<boost::shared_ptr<Playlist> >::iterator i = playlists.begin(); i != playlists.end(); ++i) {
2749 (*i)->clear_owned_changes ();
2752 for (RegionSelection::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
2753 boost::shared_ptr<Region> r = (*i)->region ();
2765 r->lower_to_bottom ();
2769 for (set<boost::shared_ptr<Playlist> >::iterator i = playlists.begin(); i != playlists.end(); ++i) {
2770 vector<Command*> cmds;
2772 _session->add_commands (cmds);
2775 commit_reversible_command ();
2779 Editor::raise_region ()
2781 do_layer_operation (Raise);
2785 Editor::raise_region_to_top ()
2787 do_layer_operation (RaiseToTop);
2791 Editor::lower_region ()
2793 do_layer_operation (Lower);
2797 Editor::lower_region_to_bottom ()
2799 do_layer_operation (LowerToBottom);
2802 /** Show the region editor for the selected regions */
2804 Editor::show_region_properties ()
2806 selection->foreach_regionview (&RegionView::show_region_editor);
2809 /** Show the midi list editor for the selected MIDI regions */
2811 Editor::show_midi_list_editor ()
2813 selection->foreach_midi_regionview (&MidiRegionView::show_list_editor);
2817 Editor::rename_region ()
2819 RegionSelection rs = get_regions_from_selection_and_entered ();
2825 ArdourDialog d (_("Rename Region"), true, false);
2827 Label label (_("New name:"));
2830 hbox.set_spacing (6);
2831 hbox.pack_start (label, false, false);
2832 hbox.pack_start (entry, true, true);
2834 d.get_vbox()->set_border_width (12);
2835 d.get_vbox()->pack_start (hbox, false, false);
2837 d.add_button(Gtk::Stock::CANCEL, Gtk::RESPONSE_CANCEL);
2838 d.add_button(Gtk::Stock::OK, Gtk::RESPONSE_OK);
2840 d.set_size_request (300, -1);
2842 entry.set_text (rs.front()->region()->name());
2843 entry.select_region (0, -1);
2845 entry.signal_activate().connect (sigc::bind (sigc::mem_fun (d, &Dialog::response), RESPONSE_OK));
2851 int const ret = d.run();
2855 if (ret != RESPONSE_OK) {
2859 std::string str = entry.get_text();
2860 strip_whitespace_edges (str);
2862 rs.front()->region()->set_name (str);
2863 _regions->redisplay ();
2867 /** Start an audition of the first selected region */
2869 Editor::play_edit_range ()
2871 framepos_t start, end;
2873 if (get_edit_op_range (start, end)) {
2874 _session->request_bounded_roll (start, end);
2879 Editor::play_selected_region ()
2881 framepos_t start = max_framepos;
2884 RegionSelection rs = get_regions_from_selection_and_entered ();
2890 for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
2891 if ((*i)->region()->position() < start) {
2892 start = (*i)->region()->position();
2894 if ((*i)->region()->last_frame() + 1 > end) {
2895 end = (*i)->region()->last_frame() + 1;
2899 _session->request_bounded_roll (start, end);
2903 Editor::audition_playlist_region_standalone (boost::shared_ptr<Region> region)
2905 _session->audition_region (region);
2909 Editor::region_from_selection ()
2911 if (clicked_axisview == 0) {
2915 if (selection->time.empty()) {
2919 framepos_t start = selection->time[clicked_selection].start;
2920 framepos_t end = selection->time[clicked_selection].end;
2922 TrackViewList tracks = get_tracks_for_range_action ();
2924 framepos_t selection_cnt = end - start + 1;
2926 for (TrackSelection::iterator i = tracks.begin(); i != tracks.end(); ++i) {
2927 boost::shared_ptr<Region> current;
2928 boost::shared_ptr<Playlist> pl;
2929 framepos_t internal_start;
2932 if ((pl = (*i)->playlist()) == 0) {
2936 if ((current = pl->top_region_at (start)) == 0) {
2940 internal_start = start - current->position();
2941 RegionFactory::region_name (new_name, current->name(), true);
2945 plist.add (ARDOUR::Properties::start, current->start() + internal_start);
2946 plist.add (ARDOUR::Properties::length, selection_cnt);
2947 plist.add (ARDOUR::Properties::name, new_name);
2948 plist.add (ARDOUR::Properties::layer, 0);
2950 boost::shared_ptr<Region> region (RegionFactory::create (current, plist));
2955 Editor::create_region_from_selection (vector<boost::shared_ptr<Region> >& new_regions)
2957 if (selection->time.empty() || selection->tracks.empty()) {
2961 framepos_t start, end;
2962 if (clicked_selection) {
2963 start = selection->time[clicked_selection].start;
2964 end = selection->time[clicked_selection].end;
2966 start = selection->time.start();
2967 end = selection->time.end_frame();
2970 TrackViewList ts = selection->tracks.filter_to_unique_playlists ();
2971 sort_track_selection (ts);
2973 for (TrackSelection::iterator i = ts.begin(); i != ts.end(); ++i) {
2974 boost::shared_ptr<Region> current;
2975 boost::shared_ptr<Playlist> playlist;
2976 framepos_t internal_start;
2979 if ((playlist = (*i)->playlist()) == 0) {
2983 if ((current = playlist->top_region_at(start)) == 0) {
2987 internal_start = start - current->position();
2988 RegionFactory::region_name (new_name, current->name(), true);
2992 plist.add (ARDOUR::Properties::start, current->start() + internal_start);
2993 plist.add (ARDOUR::Properties::length, end - start + 1);
2994 plist.add (ARDOUR::Properties::name, new_name);
2996 new_regions.push_back (RegionFactory::create (current, plist));
3001 Editor::split_multichannel_region ()
3003 RegionSelection rs = get_regions_from_selection_and_entered ();
3009 vector< boost::shared_ptr<Region> > v;
3011 for (list<RegionView*>::iterator x = rs.begin(); x != rs.end(); ++x) {
3012 (*x)->region()->separate_by_channel (*_session, v);
3017 Editor::new_region_from_selection ()
3019 region_from_selection ();
3020 cancel_selection ();
3024 add_if_covered (RegionView* rv, const AudioRange* ar, RegionSelection* rs)
3026 switch (rv->region()->coverage (ar->start, ar->end - 1)) {
3027 // n.b. -1 because AudioRange::end is one past the end, but coverage expects inclusive ranges
3028 case Evoral::OverlapNone:
3036 * - selected tracks, or if there are none...
3037 * - tracks containing selected regions, or if there are none...
3042 Editor::get_tracks_for_range_action () const
3046 if (selection->tracks.empty()) {
3048 /* use tracks with selected regions */
3050 RegionSelection rs = selection->regions;
3052 for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
3053 TimeAxisView* tv = &(*i)->get_time_axis_view();
3055 if (!t.contains (tv)) {
3061 /* no regions and no tracks: use all tracks */
3067 t = selection->tracks;
3070 return t.filter_to_unique_playlists();
3074 Editor::separate_regions_between (const TimeSelection& ts)
3076 bool in_command = false;
3077 boost::shared_ptr<Playlist> playlist;
3078 RegionSelection new_selection;
3080 TrackViewList tmptracks = get_tracks_for_range_action ();
3081 sort_track_selection (tmptracks);
3083 for (TrackSelection::iterator i = tmptracks.begin(); i != tmptracks.end(); ++i) {
3085 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> ((*i));
3091 if (!rtv->is_track()) {
3095 /* no edits to destructive tracks */
3097 if (rtv->track()->destructive()) {
3101 if ((playlist = rtv->playlist()) != 0) {
3103 playlist->clear_changes ();
3105 /* XXX need to consider musical time selections here at some point */
3107 double speed = rtv->track()->speed();
3109 for (list<AudioRange>::const_iterator t = ts.begin(); t != ts.end(); ++t) {
3111 sigc::connection c = rtv->view()->RegionViewAdded.connect (
3112 sigc::mem_fun(*this, &Editor::collect_new_region_view));
3114 latest_regionviews.clear ();
3116 playlist->partition ((framepos_t)((*t).start * speed),
3117 (framepos_t)((*t).end * speed), false);
3121 if (!latest_regionviews.empty()) {
3123 rtv->view()->foreach_regionview (sigc::bind (
3124 sigc::ptr_fun (add_if_covered),
3125 &(*t), &new_selection));
3128 begin_reversible_command (_("separate"));
3132 /* pick up changes to existing regions */
3134 vector<Command*> cmds;
3135 playlist->rdiff (cmds);
3136 _session->add_commands (cmds);
3138 /* pick up changes to the playlist itself (adds/removes)
3141 _session->add_command(new StatefulDiffCommand (playlist));
3148 // selection->set (new_selection);
3150 commit_reversible_command ();
3154 struct PlaylistState {
3155 boost::shared_ptr<Playlist> playlist;
3159 /** Take tracks from get_tracks_for_range_action and cut any regions
3160 * on those tracks so that the tracks are empty over the time
3164 Editor::separate_region_from_selection ()
3166 /* preferentially use *all* ranges in the time selection if we're in range mode
3167 to allow discontiguous operation, since get_edit_op_range() currently
3168 returns a single range.
3171 if (!selection->time.empty()) {
3173 separate_regions_between (selection->time);
3180 if (get_edit_op_range (start, end)) {
3182 AudioRange ar (start, end, 1);
3186 separate_regions_between (ts);
3192 Editor::separate_region_from_punch ()
3194 Location* loc = _session->locations()->auto_punch_location();
3196 separate_regions_using_location (*loc);
3201 Editor::separate_region_from_loop ()
3203 Location* loc = _session->locations()->auto_loop_location();
3205 separate_regions_using_location (*loc);
3210 Editor::separate_regions_using_location (Location& loc)
3212 if (loc.is_mark()) {
3216 AudioRange ar (loc.start(), loc.end(), 1);
3221 separate_regions_between (ts);
3224 /** Separate regions under the selected region */
3226 Editor::separate_under_selected_regions ()
3228 vector<PlaylistState> playlists;
3232 rs = get_regions_from_selection_and_entered();
3234 if (!_session || rs.empty()) {
3238 begin_reversible_command (_("separate region under"));
3240 list<boost::shared_ptr<Region> > regions_to_remove;
3242 for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
3243 // we can't just remove the region(s) in this loop because
3244 // this removes them from the RegionSelection, and they thus
3245 // disappear from underneath the iterator, and the ++i above
3246 // SEGVs in a puzzling fashion.
3248 // so, first iterate over the regions to be removed from rs and
3249 // add them to the regions_to_remove list, and then
3250 // iterate over the list to actually remove them.
3252 regions_to_remove.push_back ((*i)->region());
3255 for (list<boost::shared_ptr<Region> >::iterator rl = regions_to_remove.begin(); rl != regions_to_remove.end(); ++rl) {
3257 boost::shared_ptr<Playlist> playlist = (*rl)->playlist();
3260 // is this check necessary?
3264 vector<PlaylistState>::iterator i;
3266 //only take state if this is a new playlist.
3267 for (i = playlists.begin(); i != playlists.end(); ++i) {
3268 if ((*i).playlist == playlist) {
3273 if (i == playlists.end()) {
3275 PlaylistState before;
3276 before.playlist = playlist;
3277 before.before = &playlist->get_state();
3279 playlist->freeze ();
3280 playlists.push_back(before);
3283 //Partition on the region bounds
3284 playlist->partition ((*rl)->first_frame() - 1, (*rl)->last_frame() + 1, true);
3286 //Re-add region that was just removed due to the partition operation
3287 playlist->add_region( (*rl), (*rl)->first_frame() );
3290 vector<PlaylistState>::iterator pl;
3292 for (pl = playlists.begin(); pl != playlists.end(); ++pl) {
3293 (*pl).playlist->thaw ();
3294 _session->add_command(new MementoCommand<Playlist>(*(*pl).playlist, (*pl).before, &(*pl).playlist->get_state()));
3297 commit_reversible_command ();
3301 Editor::crop_region_to_selection ()
3303 if (!selection->time.empty()) {
3305 crop_region_to (selection->time.start(), selection->time.end_frame());
3312 if (get_edit_op_range (start, end)) {
3313 crop_region_to (start, end);
3320 Editor::crop_region_to (framepos_t start, framepos_t end)
3322 vector<boost::shared_ptr<Playlist> > playlists;
3323 boost::shared_ptr<Playlist> playlist;
3326 if (selection->tracks.empty()) {
3327 ts = track_views.filter_to_unique_playlists();
3329 ts = selection->tracks.filter_to_unique_playlists ();
3332 sort_track_selection (ts);
3334 for (TrackSelection::iterator i = ts.begin(); i != ts.end(); ++i) {
3336 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> ((*i));
3342 boost::shared_ptr<Track> t = rtv->track();
3344 if (t != 0 && ! t->destructive()) {
3346 if ((playlist = rtv->playlist()) != 0) {
3347 playlists.push_back (playlist);
3352 if (playlists.empty()) {
3357 framepos_t new_start;
3359 framecnt_t new_length;
3360 bool in_command = false;
3362 for (vector<boost::shared_ptr<Playlist> >::iterator i = playlists.begin(); i != playlists.end(); ++i) {
3364 /* Only the top regions at start and end have to be cropped */
3365 boost::shared_ptr<Region> region_at_start = (*i)->top_region_at(start);
3366 boost::shared_ptr<Region> region_at_end = (*i)->top_region_at(end);
3368 vector<boost::shared_ptr<Region> > regions;
3370 if (region_at_start != 0) {
3371 regions.push_back (region_at_start);
3373 if (region_at_end != 0) {
3374 regions.push_back (region_at_end);
3377 /* now adjust lengths */
3378 for (vector<boost::shared_ptr<Region> >::iterator i = regions.begin(); i != regions.end(); ++i) {
3380 pos = (*i)->position();
3381 new_start = max (start, pos);
3382 if (max_framepos - pos > (*i)->length()) {
3383 new_end = pos + (*i)->length() - 1;
3385 new_end = max_framepos;
3387 new_end = min (end, new_end);
3388 new_length = new_end - new_start + 1;
3391 begin_reversible_command (_("trim to selection"));
3394 (*i)->clear_changes ();
3395 (*i)->trim_to (new_start, new_length);
3396 _session->add_command (new StatefulDiffCommand (*i));
3401 commit_reversible_command ();
3406 Editor::region_fill_track ()
3408 boost::shared_ptr<Playlist> playlist;
3409 RegionSelection regions = get_regions_from_selection_and_entered ();
3410 RegionSelection foo;
3412 framepos_t const end = _session->current_end_frame ();
3414 if (regions.empty () || regions.end_frame () + 1 >= end) {
3418 framepos_t const start_frame = regions.start ();
3419 framepos_t const end_frame = regions.end_frame ();
3420 framecnt_t const gap = end_frame - start_frame + 1;
3422 begin_reversible_command (Operations::region_fill);
3424 selection->clear_regions ();
3426 for (RegionSelection::iterator i = regions.begin(); i != regions.end(); ++i) {
3428 boost::shared_ptr<Region> r ((*i)->region());
3430 TimeAxisView& tv = (*i)->get_time_axis_view();
3431 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (&tv);
3432 latest_regionviews.clear ();
3433 sigc::connection c = rtv->view()->RegionViewAdded.connect (sigc::mem_fun(*this, &Editor::collect_new_region_view));
3435 framepos_t const position = end_frame + (r->first_frame() - start_frame + 1);
3436 playlist = (*i)->region()->playlist();
3437 playlist->clear_changes ();
3438 playlist->duplicate_until (r, position, gap, end);
3439 _session->add_command(new StatefulDiffCommand (playlist));
3443 foo.insert (foo.end(), latest_regionviews.begin(), latest_regionviews.end());
3447 selection->set (foo);
3450 commit_reversible_command ();
3454 Editor::set_region_sync_position ()
3456 set_sync_point (get_preferred_edit_position (), get_regions_from_selection_and_edit_point ());
3460 Editor::set_sync_point (framepos_t where, const RegionSelection& rs)
3462 bool in_command = false;
3464 for (RegionSelection::const_iterator r = rs.begin(); r != rs.end(); ++r) {
3466 if (!(*r)->region()->covers (where)) {
3470 boost::shared_ptr<Region> region ((*r)->region());
3473 begin_reversible_command (_("set sync point"));
3477 region->clear_changes ();
3478 region->set_sync_position (where);
3479 _session->add_command(new StatefulDiffCommand (region));
3483 commit_reversible_command ();
3487 /** Remove the sync positions of the selection */
3489 Editor::remove_region_sync ()
3491 RegionSelection rs = get_regions_from_selection_and_entered ();
3497 begin_reversible_command (_("remove region sync"));
3499 for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
3501 (*i)->region()->clear_changes ();
3502 (*i)->region()->clear_sync_position ();
3503 _session->add_command(new StatefulDiffCommand ((*i)->region()));
3506 commit_reversible_command ();
3510 Editor::naturalize_region ()
3512 RegionSelection rs = get_regions_from_selection_and_entered ();
3518 if (rs.size() > 1) {
3519 begin_reversible_command (_("move regions to original position"));
3521 begin_reversible_command (_("move region to original position"));
3524 for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
3525 (*i)->region()->clear_changes ();
3526 (*i)->region()->move_to_natural_position ();
3527 _session->add_command (new StatefulDiffCommand ((*i)->region()));
3530 commit_reversible_command ();
3534 Editor::align_regions (RegionPoint what)
3536 RegionSelection const rs = get_regions_from_selection_and_edit_point ();
3542 begin_reversible_command (_("align selection"));
3544 framepos_t const position = get_preferred_edit_position ();
3546 for (RegionSelection::const_iterator i = rs.begin(); i != rs.end(); ++i) {
3547 align_region_internal ((*i)->region(), what, position);
3550 commit_reversible_command ();
3553 struct RegionSortByTime {
3554 bool operator() (const RegionView* a, const RegionView* b) {
3555 return a->region()->position() < b->region()->position();
3560 Editor::align_regions_relative (RegionPoint point)
3562 RegionSelection const rs = get_regions_from_selection_and_edit_point ();
3568 framepos_t const position = get_preferred_edit_position ();
3570 framepos_t distance = 0;
3574 list<RegionView*> sorted;
3575 rs.by_position (sorted);
3577 boost::shared_ptr<Region> r ((*sorted.begin())->region());
3582 if (position > r->position()) {
3583 distance = position - r->position();
3585 distance = r->position() - position;
3591 if (position > r->last_frame()) {
3592 distance = position - r->last_frame();
3593 pos = r->position() + distance;
3595 distance = r->last_frame() - position;
3596 pos = r->position() - distance;
3602 pos = r->adjust_to_sync (position);
3603 if (pos > r->position()) {
3604 distance = pos - r->position();
3606 distance = r->position() - pos;
3612 if (pos == r->position()) {
3616 begin_reversible_command (_("align selection (relative)"));
3618 /* move first one specially */
3620 r->clear_changes ();
3621 r->set_position (pos);
3622 _session->add_command(new StatefulDiffCommand (r));
3624 /* move rest by the same amount */
3628 for (list<RegionView*>::iterator i = sorted.begin(); i != sorted.end(); ++i) {
3630 boost::shared_ptr<Region> region ((*i)->region());
3632 region->clear_changes ();
3635 region->set_position (region->position() + distance);
3637 region->set_position (region->position() - distance);
3640 _session->add_command(new StatefulDiffCommand (region));
3644 commit_reversible_command ();
3648 Editor::align_region (boost::shared_ptr<Region> region, RegionPoint point, framepos_t position)
3650 begin_reversible_command (_("align region"));
3651 align_region_internal (region, point, position);
3652 commit_reversible_command ();
3656 Editor::align_region_internal (boost::shared_ptr<Region> region, RegionPoint point, framepos_t position)
3658 region->clear_changes ();
3662 region->set_position (region->adjust_to_sync (position));
3666 if (position > region->length()) {
3667 region->set_position (position - region->length());
3672 region->set_position (position);
3676 _session->add_command(new StatefulDiffCommand (region));
3680 Editor::trim_region_front ()
3686 Editor::trim_region_back ()
3688 trim_region (false);
3692 Editor::trim_region (bool front)
3694 framepos_t where = get_preferred_edit_position();
3695 RegionSelection rs = get_regions_from_selection_and_edit_point ();
3701 begin_reversible_command (front ? _("trim front") : _("trim back"));
3703 for (list<RegionView*>::const_iterator i = rs.by_layer().begin(); i != rs.by_layer().end(); ++i) {
3704 if (!(*i)->region()->locked()) {
3706 (*i)->region()->clear_changes ();
3709 (*i)->region()->trim_front (where);
3710 maybe_locate_with_edit_preroll ( where );
3712 (*i)->region()->trim_end (where);
3713 maybe_locate_with_edit_preroll ( where );
3716 _session->add_command (new StatefulDiffCommand ((*i)->region()));
3720 commit_reversible_command ();
3723 /** Trim the end of the selected regions to the position of the edit cursor */
3725 Editor::trim_region_to_loop ()
3727 Location* loc = _session->locations()->auto_loop_location();
3731 trim_region_to_location (*loc, _("trim to loop"));
3735 Editor::trim_region_to_punch ()
3737 Location* loc = _session->locations()->auto_punch_location();
3741 trim_region_to_location (*loc, _("trim to punch"));
3745 Editor::trim_region_to_location (const Location& loc, const char* str)
3747 RegionSelection rs = get_regions_from_selection_and_entered ();
3748 bool in_command = false;
3750 for (RegionSelection::iterator x = rs.begin(); x != rs.end(); ++x) {
3751 RegionView* rv = (*x);
3753 /* require region to span proposed trim */
3754 switch (rv->region()->coverage (loc.start(), loc.end())) {
3755 case Evoral::OverlapInternal:
3761 RouteTimeAxisView* tav = dynamic_cast<RouteTimeAxisView*> (&rv->get_time_axis_view());
3770 if (tav->track() != 0) {
3771 speed = tav->track()->speed();
3774 start = session_frame_to_track_frame (loc.start(), speed);
3775 end = session_frame_to_track_frame (loc.end(), speed);
3777 rv->region()->clear_changes ();
3778 rv->region()->trim_to (start, (end - start));
3781 begin_reversible_command (str);
3784 _session->add_command(new StatefulDiffCommand (rv->region()));
3788 commit_reversible_command ();
3793 Editor::trim_region_to_previous_region_end ()
3795 return trim_to_region(false);
3799 Editor::trim_region_to_next_region_start ()
3801 return trim_to_region(true);
3805 Editor::trim_to_region(bool forward)
3807 RegionSelection rs = get_regions_from_selection_and_entered ();
3808 bool in_command = false;
3810 boost::shared_ptr<Region> next_region;
3812 for (RegionSelection::iterator x = rs.begin(); x != rs.end(); ++x) {
3814 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (*x);
3820 AudioTimeAxisView* atav = dynamic_cast<AudioTimeAxisView*> (&arv->get_time_axis_view());
3828 if (atav->track() != 0) {
3829 speed = atav->track()->speed();
3833 boost::shared_ptr<Region> region = arv->region();
3834 boost::shared_ptr<Playlist> playlist (region->playlist());
3836 region->clear_changes ();
3840 next_region = playlist->find_next_region (region->first_frame(), Start, 1);
3846 region->trim_end((framepos_t) ( (next_region->first_frame() - 1) * speed));
3847 arv->region_changed (PropertyChange (ARDOUR::Properties::length));
3851 next_region = playlist->find_next_region (region->first_frame(), Start, 0);
3857 region->trim_front((framepos_t) ((next_region->last_frame() + 1) * speed));
3859 arv->region_changed (ARDOUR::bounds_change);
3863 begin_reversible_command (_("trim to region"));
3866 _session->add_command(new StatefulDiffCommand (region));
3870 commit_reversible_command ();
3875 Editor::unfreeze_route ()
3877 if (clicked_routeview == 0 || !clicked_routeview->is_track()) {
3881 clicked_routeview->track()->unfreeze ();
3885 Editor::_freeze_thread (void* arg)
3887 return static_cast<Editor*>(arg)->freeze_thread ();
3891 Editor::freeze_thread ()
3893 /* create event pool because we may need to talk to the session */
3894 SessionEvent::create_per_thread_pool ("freeze events", 64);
3895 /* create per-thread buffers for process() tree to use */
3896 clicked_routeview->audio_track()->freeze_me (*current_interthread_info);
3897 current_interthread_info->done = true;
3902 Editor::freeze_route ()
3908 /* stop transport before we start. this is important */
3910 _session->request_transport_speed (0.0);
3912 /* wait for just a little while, because the above call is asynchronous */
3914 Glib::usleep (250000);
3916 if (clicked_routeview == 0 || !clicked_routeview->is_audio_track()) {
3920 if (!clicked_routeview->track()->bounceable (clicked_routeview->track()->main_outs(), true)) {
3922 _("This track/bus cannot be frozen because the signal adds or loses channels before reaching the outputs.\n"
3923 "This is typically caused by plugins that generate stereo output from mono input or vice versa.")
3925 d.set_title (_("Cannot freeze"));
3930 if (clicked_routeview->track()->has_external_redirects()) {
3931 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"
3932 "Freezing will only process the signal as far as the first send/insert/return."),
3933 clicked_routeview->track()->name()), true, MESSAGE_INFO, BUTTONS_NONE, true);
3935 d.add_button (_("Freeze anyway"), Gtk::RESPONSE_OK);
3936 d.add_button (_("Don't freeze"), Gtk::RESPONSE_CANCEL);
3937 d.set_title (_("Freeze Limits"));
3939 int response = d.run ();
3942 case Gtk::RESPONSE_CANCEL:
3949 InterThreadInfo itt;
3950 current_interthread_info = &itt;
3952 InterthreadProgressWindow ipw (current_interthread_info, _("Freeze"), _("Cancel Freeze"));
3954 pthread_create_and_store (X_("freezer"), &itt.thread, _freeze_thread, this);
3956 CursorContext::Handle cursor_ctx = CursorContext::create(*this, _cursors->wait);
3958 while (!itt.done && !itt.cancel) {
3959 gtk_main_iteration ();
3962 pthread_join (itt.thread, 0);
3963 current_interthread_info = 0;
3967 Editor::bounce_range_selection (bool replace, bool enable_processing)
3969 if (selection->time.empty()) {
3973 TrackSelection views = selection->tracks;
3975 for (TrackViewList::iterator i = views.begin(); i != views.end(); ++i) {
3977 if (enable_processing) {
3979 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (*i);
3981 if (rtv && rtv->track() && replace && enable_processing && !rtv->track()->bounceable (rtv->track()->main_outs(), false)) {
3983 _("You can't perform this operation because the processing of the signal "
3984 "will cause one or more of the tracks to end up with a region with more channels than this track has inputs.\n\n"
3985 "You can do this without processing, which is a different operation.")
3987 d.set_title (_("Cannot bounce"));
3994 framepos_t start = selection->time[clicked_selection].start;
3995 framepos_t end = selection->time[clicked_selection].end;
3996 framepos_t cnt = end - start + 1;
3997 bool in_command = false;
3999 for (TrackViewList::iterator i = views.begin(); i != views.end(); ++i) {
4001 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (*i);
4007 boost::shared_ptr<Playlist> playlist;
4009 if ((playlist = rtv->playlist()) == 0) {
4013 InterThreadInfo itt;
4015 playlist->clear_changes ();
4016 playlist->clear_owned_changes ();
4018 boost::shared_ptr<Region> r;
4020 if (enable_processing) {
4021 r = rtv->track()->bounce_range (start, start+cnt, itt, rtv->track()->main_outs(), false);
4023 r = rtv->track()->bounce_range (start, start+cnt, itt, boost::shared_ptr<Processor>(), false);
4031 list<AudioRange> ranges;
4032 ranges.push_back (AudioRange (start, start+cnt, 0));
4033 playlist->cut (ranges); // discard result
4034 playlist->add_region (r, start);
4038 begin_reversible_command (_("bounce range"));
4041 vector<Command*> cmds;
4042 playlist->rdiff (cmds);
4043 _session->add_commands (cmds);
4045 _session->add_command (new StatefulDiffCommand (playlist));
4049 commit_reversible_command ();
4053 /** Delete selected regions, automation points or a time range */
4057 //special case: if the user is pointing in the editor/mixer strip, they may be trying to delete a plugin.
4058 //we need this because the editor-mixer strip is in the editor window, so it doesn't get the bindings from the mix window
4059 bool deleted = false;
4060 if ( current_mixer_strip && current_mixer_strip == MixerStrip::entered_mixer_strip() )
4061 deleted = current_mixer_strip->delete_processors ();
4067 /** Cut selected regions, automation points or a time range */
4074 /** Copy selected regions, automation points or a time range */
4082 /** @return true if a Cut, Copy or Clear is possible */
4084 Editor::can_cut_copy () const
4086 if (!selection->time.empty() || !selection->regions.empty() || !selection->points.empty())
4093 /** Cut, copy or clear selected regions, automation points or a time range.
4094 * @param op Operation (Delete, Cut, Copy or Clear)
4097 Editor::cut_copy (CutCopyOp op)
4099 /* only cancel selection if cut/copy is successful.*/
4105 opname = _("delete");
4114 opname = _("clear");
4118 /* if we're deleting something, and the mouse is still pressed,
4119 the thing we started a drag for will be gone when we release
4120 the mouse button(s). avoid this. see part 2 at the end of
4124 if (op == Delete || op == Cut || op == Clear) {
4125 if (_drags->active ()) {
4130 if ( op != Delete ) //"Delete" doesn't change copy/paste buf
4131 cut_buffer->clear ();
4133 if (entered_marker) {
4135 /* cut/delete op while pointing at a marker */
4138 Location* loc = find_location_from_marker (entered_marker, ignored);
4140 if (_session && loc) {
4141 entered_marker = NULL;
4142 Glib::signal_idle().connect (sigc::bind (sigc::mem_fun(*this, &Editor::really_remove_marker), loc));
4149 switch (mouse_mode) {
4152 begin_reversible_command (opname + ' ' + X_("MIDI"));
4154 commit_reversible_command ();
4160 bool did_edit = false;
4162 if (!selection->regions.empty() || !selection->points.empty()) {
4163 begin_reversible_command (opname + ' ' + _("objects"));
4166 if (!selection->regions.empty()) {
4167 cut_copy_regions (op, selection->regions);
4169 if (op == Cut || op == Delete) {
4170 selection->clear_regions ();
4174 if (!selection->points.empty()) {
4175 cut_copy_points (op);
4177 if (op == Cut || op == Delete) {
4178 selection->clear_points ();
4181 } else if (selection->time.empty()) {
4182 framepos_t start, end;
4183 /* no time selection, see if we can get an edit range
4186 if (get_edit_op_range (start, end)) {
4187 selection->set (start, end);
4189 } else if (!selection->time.empty()) {
4190 begin_reversible_command (opname + ' ' + _("range"));
4193 cut_copy_ranges (op);
4195 if (op == Cut || op == Delete) {
4196 selection->clear_time ();
4201 /* reset repeated paste state */
4204 commit_reversible_command ();
4207 if (op == Delete || op == Cut || op == Clear) {
4213 struct AutomationRecord {
4214 AutomationRecord () : state (0) , line(NULL) {}
4215 AutomationRecord (XMLNode* s, const AutomationLine* l) : state (s) , line (l) {}
4217 XMLNode* state; ///< state before any operation
4218 const AutomationLine* line; ///< line this came from
4219 boost::shared_ptr<Evoral::ControlList> copy; ///< copied events for the cut buffer
4221 struct PointsSelectionPositionSorter {
4222 bool operator() (ControlPoint* a, ControlPoint* b) {
4223 return (*(a->model()))->when < (*(b->model()))->when;
4226 /** Cut, copy or clear selected automation points.
4227 * @param op Operation (Cut, Copy or Clear)
4230 Editor::cut_copy_points (Editing::CutCopyOp op, Evoral::Beats earliest, bool midi)
4232 if (selection->points.empty ()) {
4236 /* XXX: not ideal, as there may be more than one track involved in the point selection */
4237 _last_cut_copy_source_track = &selection->points.front()->line().trackview;
4239 /* Keep a record of the AutomationLists that we end up using in this operation */
4240 typedef std::map<boost::shared_ptr<AutomationList>, AutomationRecord> Lists;
4243 /* user could select points in any order */
4244 selection->points.sort(PointsSelectionPositionSorter ());
4246 /* Go through all selected points, making an AutomationRecord for each distinct AutomationList */
4247 for (PointSelection::iterator sel_point = selection->points.begin(); sel_point != selection->points.end(); ++sel_point) {
4248 const AutomationLine& line = (*sel_point)->line();
4249 const boost::shared_ptr<AutomationList> al = line.the_list();
4250 if (lists.find (al) == lists.end ()) {
4251 /* We haven't seen this list yet, so make a record for it. This includes
4252 taking a copy of its current state, in case this is needed for undo later.
4254 lists[al] = AutomationRecord (&al->get_state (), &line);
4258 if (op == Cut || op == Copy) {
4259 /* This operation will involve putting things in the cut buffer, so create an empty
4260 ControlList for each of our source lists to put the cut buffer data in.
4262 for (Lists::iterator i = lists.begin(); i != lists.end(); ++i) {
4263 i->second.copy = i->first->create (i->first->parameter (), i->first->descriptor());
4266 /* Add all selected points to the relevant copy ControlLists */
4267 framepos_t start = std::numeric_limits<framepos_t>::max();
4268 for (PointSelection::iterator sel_point = selection->points.begin(); sel_point != selection->points.end(); ++sel_point) {
4269 boost::shared_ptr<AutomationList> al = (*sel_point)->line().the_list();
4270 AutomationList::const_iterator ctrl_evt = (*sel_point)->model ();
4272 lists[al].copy->fast_simple_add ((*ctrl_evt)->when, (*ctrl_evt)->value);
4274 /* Update earliest MIDI start time in beats */
4275 earliest = std::min(earliest, Evoral::Beats((*ctrl_evt)->when));
4277 /* Update earliest session start time in frames */
4278 start = std::min(start, (*sel_point)->line().session_position(ctrl_evt));
4282 /* Snap start time backwards, so copy/paste is snap aligned. */
4284 if (earliest == Evoral::Beats::max()) {
4285 earliest = Evoral::Beats(); // Weird... don't offset
4287 earliest.round_down_to_beat();
4289 if (start == std::numeric_limits<double>::max()) {
4290 start = 0; // Weird... don't offset
4292 snap_to(start, RoundDownMaybe);
4295 const double line_offset = midi ? earliest.to_double() : start;
4296 for (Lists::iterator i = lists.begin(); i != lists.end(); ++i) {
4297 /* Correct this copy list so that it is relative to the earliest
4298 start time, so relative ordering between points is preserved
4299 when copying from several lists and the paste starts at the
4300 earliest copied piece of data. */
4301 boost::shared_ptr<Evoral::ControlList> &al_cpy = i->second.copy;
4302 for (AutomationList::iterator ctrl_evt = al_cpy->begin(); ctrl_evt != al_cpy->end(); ++ctrl_evt) {
4303 (*ctrl_evt)->when -= line_offset;
4306 /* And add it to the cut buffer */
4307 cut_buffer->add (al_cpy);
4311 if (op == Delete || op == Cut) {
4312 /* This operation needs to remove things from the main AutomationList, so do that now */
4314 for (Lists::iterator i = lists.begin(); i != lists.end(); ++i) {
4315 i->first->freeze ();
4318 /* Remove each selected point from its AutomationList */
4319 for (PointSelection::iterator sel_point = selection->points.begin(); sel_point != selection->points.end(); ++sel_point) {
4320 AutomationLine& line = (*sel_point)->line ();
4321 boost::shared_ptr<AutomationList> al = line.the_list();
4325 if (dynamic_cast<AudioRegionGainLine*> (&line)) {
4326 /* removing of first and last gain point in region gain lines is prohibited*/
4327 if (line.is_last_point (*(*sel_point)) || line.is_first_point (*(*sel_point))) {
4333 al->erase ((*sel_point)->model ());
4337 /* Thaw the lists and add undo records for them */
4338 for (Lists::iterator i = lists.begin(); i != lists.end(); ++i) {
4339 boost::shared_ptr<AutomationList> al = i->first;
4341 _session->add_command (new MementoCommand<AutomationList> (*al.get(), i->second.state, &(al->get_state ())));
4346 /** Cut, copy or clear selected automation points.
4347 * @param op Operation (Cut, Copy or Clear)
4350 Editor::cut_copy_midi (CutCopyOp op)
4352 Evoral::Beats earliest = Evoral::Beats::max();
4353 for (MidiRegionSelection::iterator i = selection->midi_regions.begin(); i != selection->midi_regions.end(); ++i) {
4354 MidiRegionView* mrv = dynamic_cast<MidiRegionView*>(*i);
4356 if (!mrv->selection().empty()) {
4357 earliest = std::min(earliest, (*mrv->selection().begin())->note()->time());
4359 mrv->cut_copy_clear (op);
4361 /* XXX: not ideal, as there may be more than one track involved in the selection */
4362 _last_cut_copy_source_track = &mrv->get_time_axis_view();
4366 if (!selection->points.empty()) {
4367 cut_copy_points (op, earliest, true);
4368 if (op == Cut || op == Delete) {
4369 selection->clear_points ();
4374 struct lt_playlist {
4375 bool operator () (const PlaylistState& a, const PlaylistState& b) {
4376 return a.playlist < b.playlist;
4380 struct PlaylistMapping {
4382 boost::shared_ptr<Playlist> pl;
4384 PlaylistMapping (TimeAxisView* tvp) : tv (tvp) {}
4387 /** Remove `clicked_regionview' */
4389 Editor::remove_clicked_region ()
4391 if (clicked_routeview == 0 || clicked_regionview == 0) {
4395 begin_reversible_command (_("remove region"));
4397 boost::shared_ptr<Playlist> playlist = clicked_routeview->playlist();
4399 playlist->clear_changes ();
4400 playlist->clear_owned_changes ();
4401 playlist->remove_region (clicked_regionview->region());
4402 if (Config->get_edit_mode() == Ripple)
4403 playlist->ripple (clicked_regionview->region()->position(), -clicked_regionview->region()->length(), boost::shared_ptr<Region>());
4405 /* We might have removed regions, which alters other regions' layering_index,
4406 so we need to do a recursive diff here.
4408 vector<Command*> cmds;
4409 playlist->rdiff (cmds);
4410 _session->add_commands (cmds);
4412 _session->add_command(new StatefulDiffCommand (playlist));
4413 commit_reversible_command ();
4417 /** Remove the selected regions */
4419 Editor::remove_selected_regions ()
4421 RegionSelection rs = get_regions_from_selection_and_entered ();
4423 if (!_session || rs.empty()) {
4427 list<boost::shared_ptr<Region> > regions_to_remove;
4429 for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
4430 // we can't just remove the region(s) in this loop because
4431 // this removes them from the RegionSelection, and they thus
4432 // disappear from underneath the iterator, and the ++i above
4433 // SEGVs in a puzzling fashion.
4435 // so, first iterate over the regions to be removed from rs and
4436 // add them to the regions_to_remove list, and then
4437 // iterate over the list to actually remove them.
4439 regions_to_remove.push_back ((*i)->region());
4442 vector<boost::shared_ptr<Playlist> > playlists;
4444 for (list<boost::shared_ptr<Region> >::iterator rl = regions_to_remove.begin(); rl != regions_to_remove.end(); ++rl) {
4446 boost::shared_ptr<Playlist> playlist = (*rl)->playlist();
4449 // is this check necessary?
4453 /* get_regions_from_selection_and_entered() guarantees that
4454 the playlists involved are unique, so there is no need
4458 playlists.push_back (playlist);
4460 playlist->clear_changes ();
4461 playlist->clear_owned_changes ();
4462 playlist->freeze ();
4463 playlist->remove_region (*rl);
4464 if (Config->get_edit_mode() == Ripple)
4465 playlist->ripple ((*rl)->position(), -(*rl)->length(), boost::shared_ptr<Region>());
4469 vector<boost::shared_ptr<Playlist> >::iterator pl;
4470 bool in_command = false;
4472 for (pl = playlists.begin(); pl != playlists.end(); ++pl) {
4475 /* We might have removed regions, which alters other regions' layering_index,
4476 so we need to do a recursive diff here.
4480 begin_reversible_command (_("remove region"));
4483 vector<Command*> cmds;
4484 (*pl)->rdiff (cmds);
4485 _session->add_commands (cmds);
4487 _session->add_command(new StatefulDiffCommand (*pl));
4491 commit_reversible_command ();
4495 /** Cut, copy or clear selected regions.
4496 * @param op Operation (Cut, Copy or Clear)
4499 Editor::cut_copy_regions (CutCopyOp op, RegionSelection& rs)
4501 /* we can't use a std::map here because the ordering is important, and we can't trivially sort
4502 a map when we want ordered access to both elements. i think.
4505 vector<PlaylistMapping> pmap;
4507 framepos_t first_position = max_framepos;
4509 typedef set<boost::shared_ptr<Playlist> > FreezeList;
4510 FreezeList freezelist;
4512 /* get ordering correct before we cut/copy */
4514 rs.sort_by_position_and_track ();
4516 for (RegionSelection::iterator x = rs.begin(); x != rs.end(); ++x) {
4518 first_position = min ((framepos_t) (*x)->region()->position(), first_position);
4520 if (op == Cut || op == Clear || op == Delete) {
4521 boost::shared_ptr<Playlist> pl = (*x)->region()->playlist();
4524 FreezeList::iterator fl;
4526 // only take state if this is a new playlist.
4527 for (fl = freezelist.begin(); fl != freezelist.end(); ++fl) {
4533 if (fl == freezelist.end()) {
4534 pl->clear_changes();
4535 pl->clear_owned_changes ();
4537 freezelist.insert (pl);
4542 TimeAxisView* tv = &(*x)->get_time_axis_view();
4543 vector<PlaylistMapping>::iterator z;
4545 for (z = pmap.begin(); z != pmap.end(); ++z) {
4546 if ((*z).tv == tv) {
4551 if (z == pmap.end()) {
4552 pmap.push_back (PlaylistMapping (tv));
4556 for (RegionSelection::iterator x = rs.begin(); x != rs.end(); ) {
4558 boost::shared_ptr<Playlist> pl = (*x)->region()->playlist();
4561 /* region not yet associated with a playlist (e.g. unfinished
4568 TimeAxisView& tv = (*x)->get_time_axis_view();
4569 boost::shared_ptr<Playlist> npl;
4570 RegionSelection::iterator tmp;
4577 vector<PlaylistMapping>::iterator z;
4579 for (z = pmap.begin(); z != pmap.end(); ++z) {
4580 if ((*z).tv == &tv) {
4585 assert (z != pmap.end());
4588 npl = PlaylistFactory::create (pl->data_type(), *_session, "cutlist", true);
4596 boost::shared_ptr<Region> r = (*x)->region();
4597 boost::shared_ptr<Region> _xx;
4603 pl->remove_region (r);
4604 if (Config->get_edit_mode() == Ripple)
4605 pl->ripple (r->position(), -r->length(), boost::shared_ptr<Region>());
4609 _xx = RegionFactory::create (r);
4610 npl->add_region (_xx, r->position() - first_position);
4611 pl->remove_region (r);
4612 if (Config->get_edit_mode() == Ripple)
4613 pl->ripple (r->position(), -r->length(), boost::shared_ptr<Region>());
4617 /* copy region before adding, so we're not putting same object into two different playlists */
4618 npl->add_region (RegionFactory::create (r), r->position() - first_position);
4622 pl->remove_region (r);
4623 if (Config->get_edit_mode() == Ripple)
4624 pl->ripple (r->position(), -r->length(), boost::shared_ptr<Region>());
4633 list<boost::shared_ptr<Playlist> > foo;
4635 /* the pmap is in the same order as the tracks in which selected regions occurred */
4637 for (vector<PlaylistMapping>::iterator i = pmap.begin(); i != pmap.end(); ++i) {
4640 foo.push_back ((*i).pl);
4645 cut_buffer->set (foo);
4649 _last_cut_copy_source_track = 0;
4651 _last_cut_copy_source_track = pmap.front().tv;
4655 for (FreezeList::iterator pl = freezelist.begin(); pl != freezelist.end(); ++pl) {
4658 /* We might have removed regions, which alters other regions' layering_index,
4659 so we need to do a recursive diff here.
4661 vector<Command*> cmds;
4662 (*pl)->rdiff (cmds);
4663 _session->add_commands (cmds);
4665 _session->add_command (new StatefulDiffCommand (*pl));
4670 Editor::cut_copy_ranges (CutCopyOp op)
4672 TrackViewList ts = selection->tracks.filter_to_unique_playlists ();
4674 /* Sort the track selection now, so that it if is used, the playlists
4675 selected by the calls below to cut_copy_clear are in the order that
4676 their tracks appear in the editor. This makes things like paste
4677 of ranges work properly.
4680 sort_track_selection (ts);
4683 if (!entered_track) {
4686 ts.push_back (entered_track);
4689 for (TrackViewList::iterator i = ts.begin(); i != ts.end(); ++i) {
4690 (*i)->cut_copy_clear (*selection, op);
4695 Editor::paste (float times, bool from_context)
4697 DEBUG_TRACE (DEBUG::CutNPaste, "paste to preferred edit pos\n");
4699 paste_internal (get_preferred_edit_position (EDIT_IGNORE_NONE, from_context), times, get_grid_music_divisions (0));
4703 Editor::mouse_paste ()
4708 if (!mouse_frame (where, ignored)) {
4713 paste_internal (where, 1, get_grid_music_divisions (0));
4717 Editor::paste_internal (framepos_t position, float times, const int32_t sub_num)
4719 DEBUG_TRACE (DEBUG::CutNPaste, string_compose ("apparent paste position is %1\n", position));
4721 if (cut_buffer->empty(internal_editing())) {
4725 if (position == max_framepos) {
4726 position = get_preferred_edit_position();
4727 DEBUG_TRACE (DEBUG::CutNPaste, string_compose ("preferred edit position is %1\n", position));
4730 if (position == last_paste_pos) {
4731 /* repeated paste in the same position */
4734 /* paste in new location, reset repeated paste state */
4736 last_paste_pos = position;
4739 /* get everything in the correct order */
4742 if (!selection->tracks.empty()) {
4743 /* If there is a track selection, paste into exactly those tracks and
4744 only those tracks. This allows the user to be explicit and override
4745 the below "do the reasonable thing" logic. */
4746 ts = selection->tracks.filter_to_unique_playlists ();
4747 sort_track_selection (ts);
4749 /* Figure out which track to base the paste at. */
4750 TimeAxisView* base_track = NULL;
4751 if (_edit_point == Editing::EditAtMouse && entered_track) {
4752 /* With the mouse edit point, paste onto the track under the mouse. */
4753 base_track = entered_track;
4754 } else if (_edit_point == Editing::EditAtMouse && entered_regionview) {
4755 /* With the mouse edit point, paste onto the track of the region under the mouse. */
4756 base_track = &entered_regionview->get_time_axis_view();
4757 } else if (_last_cut_copy_source_track) {
4758 /* Paste to the track that the cut/copy came from (see mantis #333). */
4759 base_track = _last_cut_copy_source_track;
4761 /* This is "impossible" since we've copied... well, do nothing. */
4765 /* Walk up to parent if necessary, so base track is a route. */
4766 while (base_track->get_parent()) {
4767 base_track = base_track->get_parent();
4770 /* Add base track and all tracks below it. The paste logic will select
4771 the appropriate object types from the cut buffer in relative order. */
4772 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
4773 if ((*i)->order() >= base_track->order()) {
4778 /* Sort tracks so the nth track of type T will pick the nth object of type T. */
4779 sort_track_selection (ts);
4781 /* Add automation children of each track in order, for pasting several lines. */
4782 for (TrackViewList::iterator i = ts.begin(); i != ts.end();) {
4783 /* Add any automation children for pasting several lines */
4784 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*>(*i++);
4789 typedef RouteTimeAxisView::AutomationTracks ATracks;
4790 const ATracks& atracks = rtv->automation_tracks();
4791 for (ATracks::const_iterator a = atracks.begin(); a != atracks.end(); ++a) {
4792 i = ts.insert(i, a->second.get());
4797 /* We now have a list of trackviews starting at base_track, including
4798 automation children, in the order shown in the editor, e.g. R1,
4799 R1.A1, R1.A2, R2, R2.A1, ... */
4802 begin_reversible_command (Operations::paste);
4804 if (ts.size() == 1 && cut_buffer->lines.size() == 1 &&
4805 dynamic_cast<AutomationTimeAxisView*>(ts.front())) {
4806 /* Only one line copied, and one automation track selected. Do a
4807 "greedy" paste from one automation type to another. */
4809 PasteContext ctx(paste_count, times, ItemCounts(), true);
4810 ts.front()->paste (position, *cut_buffer, ctx, sub_num);
4814 /* Paste into tracks */
4816 PasteContext ctx(paste_count, times, ItemCounts(), false);
4817 for (TrackViewList::iterator i = ts.begin(); i != ts.end(); ++i) {
4818 (*i)->paste (position, *cut_buffer, ctx, sub_num);
4822 commit_reversible_command ();
4826 Editor::duplicate_some_regions (RegionSelection& regions, float times)
4828 if (regions.empty ()) {
4832 boost::shared_ptr<Playlist> playlist;
4833 RegionSelection sel = regions; // clear (below) may clear the argument list if its the current region selection
4834 RegionSelection foo;
4836 framepos_t const start_frame = regions.start ();
4837 framepos_t const end_frame = regions.end_frame ();
4838 framecnt_t const gap = end_frame - start_frame + 1;
4840 begin_reversible_command (Operations::duplicate_region);
4842 selection->clear_regions ();
4844 for (RegionSelection::iterator i = sel.begin(); i != sel.end(); ++i) {
4846 boost::shared_ptr<Region> r ((*i)->region());
4848 TimeAxisView& tv = (*i)->get_time_axis_view();
4849 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (&tv);
4850 latest_regionviews.clear ();
4851 sigc::connection c = rtv->view()->RegionViewAdded.connect (sigc::mem_fun(*this, &Editor::collect_new_region_view));
4853 framepos_t const position = end_frame + (r->first_frame() - start_frame + 1);
4854 playlist = (*i)->region()->playlist();
4855 playlist->clear_changes ();
4856 playlist->duplicate (r, position, gap, times);
4857 _session->add_command(new StatefulDiffCommand (playlist));
4861 foo.insert (foo.end(), latest_regionviews.begin(), latest_regionviews.end());
4865 selection->set (foo);
4868 commit_reversible_command ();
4872 Editor::duplicate_selection (float times)
4874 if (selection->time.empty() || selection->tracks.empty()) {
4878 boost::shared_ptr<Playlist> playlist;
4880 TrackViewList ts = selection->tracks.filter_to_unique_playlists ();
4882 bool in_command = false;
4884 for (TrackViewList::iterator i = ts.begin(); i != ts.end(); ++i) {
4885 if ((playlist = (*i)->playlist()) == 0) {
4888 playlist->clear_changes ();
4890 if (clicked_selection) {
4891 playlist->duplicate_range (selection->time[clicked_selection], times);
4893 playlist->duplicate_ranges (selection->time, times);
4897 begin_reversible_command (_("duplicate range selection"));
4900 _session->add_command (new StatefulDiffCommand (playlist));
4905 // now "move" range selection to after the current range selection
4906 framecnt_t distance = 0;
4908 if (clicked_selection) {
4909 distance = selection->time[clicked_selection].end -
4910 selection->time[clicked_selection].start;
4912 distance = selection->time.end_frame() - selection->time.start();
4915 selection->move_time (distance);
4917 commit_reversible_command ();
4921 /** Reset all selected points to the relevant default value */
4923 Editor::reset_point_selection ()
4925 for (PointSelection::iterator i = selection->points.begin(); i != selection->points.end(); ++i) {
4926 ARDOUR::AutomationList::iterator j = (*i)->model ();
4927 (*j)->value = (*i)->line().the_list()->default_value ();
4932 Editor::center_playhead ()
4934 float const page = _visible_canvas_width * samples_per_pixel;
4935 center_screen_internal (playhead_cursor->current_frame (), page);
4939 Editor::center_edit_point ()
4941 float const page = _visible_canvas_width * samples_per_pixel;
4942 center_screen_internal (get_preferred_edit_position(), page);
4945 /** Caller must begin and commit a reversible command */
4947 Editor::clear_playlist (boost::shared_ptr<Playlist> playlist)
4949 playlist->clear_changes ();
4951 _session->add_command (new StatefulDiffCommand (playlist));
4955 Editor::nudge_track (bool use_edit, bool forwards)
4957 boost::shared_ptr<Playlist> playlist;
4958 framepos_t distance;
4959 framepos_t next_distance;
4963 start = get_preferred_edit_position();
4968 if ((distance = get_nudge_distance (start, next_distance)) == 0) {
4972 if (selection->tracks.empty()) {
4976 TrackViewList ts = selection->tracks.filter_to_unique_playlists ();
4977 bool in_command = false;
4979 for (TrackViewList::iterator i = ts.begin(); i != ts.end(); ++i) {
4981 if ((playlist = (*i)->playlist()) == 0) {
4985 playlist->clear_changes ();
4986 playlist->clear_owned_changes ();
4988 playlist->nudge_after (start, distance, forwards);
4991 begin_reversible_command (_("nudge track"));
4994 vector<Command*> cmds;
4996 playlist->rdiff (cmds);
4997 _session->add_commands (cmds);
4999 _session->add_command (new StatefulDiffCommand (playlist));
5003 commit_reversible_command ();
5008 Editor::remove_last_capture ()
5010 vector<string> choices;
5017 if (Config->get_verify_remove_last_capture()) {
5018 prompt = _("Do you really want to destroy the last capture?"
5019 "\n(This is destructive and cannot be undone)");
5021 choices.push_back (_("No, do nothing."));
5022 choices.push_back (_("Yes, destroy it."));
5024 Gtkmm2ext::Choice prompter (_("Destroy last capture"), prompt, choices);
5026 if (prompter.run () == 1) {
5027 _session->remove_last_capture ();
5028 _regions->redisplay ();
5032 _session->remove_last_capture();
5033 _regions->redisplay ();
5038 Editor::normalize_region ()
5044 RegionSelection rs = get_regions_from_selection_and_entered ();
5050 NormalizeDialog dialog (rs.size() > 1);
5052 if (dialog.run () == RESPONSE_CANCEL) {
5056 CursorContext::Handle cursor_ctx = CursorContext::create(*this, _cursors->wait);
5059 /* XXX: should really only count audio regions here */
5060 int const regions = rs.size ();
5062 /* Make a list of the selected audio regions' maximum amplitudes, and also
5063 obtain the maximum amplitude of them all.
5065 list<double> max_amps;
5066 list<double> rms_vals;
5069 bool use_rms = dialog.constrain_rms ();
5071 for (RegionSelection::const_iterator i = rs.begin(); i != rs.end(); ++i) {
5072 AudioRegionView const * arv = dynamic_cast<AudioRegionView const *> (*i);
5076 dialog.descend (1.0 / regions);
5077 double const a = arv->audio_region()->maximum_amplitude (&dialog);
5079 double r = arv->audio_region()->rms (&dialog);
5080 max_rms = max (max_rms, r);
5081 rms_vals.push_back (r);
5085 /* the user cancelled the operation */
5089 max_amps.push_back (a);
5090 max_amp = max (max_amp, a);
5094 list<double>::const_iterator a = max_amps.begin ();
5095 list<double>::const_iterator l = rms_vals.begin ();
5096 bool in_command = false;
5098 for (RegionSelection::iterator r = rs.begin(); r != rs.end(); ++r) {
5099 AudioRegionView* const arv = dynamic_cast<AudioRegionView*> (*r);
5104 arv->region()->clear_changes ();
5106 double amp = dialog.normalize_individually() ? *a : max_amp;
5107 double target = dialog.target_peak (); // dB
5110 double const amp_rms = dialog.normalize_individually() ? *l : max_rms;
5111 const double t_rms = dialog.target_rms ();
5112 const gain_t c_peak = dB_to_coefficient (target);
5113 const gain_t c_rms = dB_to_coefficient (t_rms);
5114 if ((amp_rms / c_rms) > (amp / c_peak)) {
5120 arv->audio_region()->normalize (amp, target);
5123 begin_reversible_command (_("normalize"));
5126 _session->add_command (new StatefulDiffCommand (arv->region()));
5133 commit_reversible_command ();
5139 Editor::reset_region_scale_amplitude ()
5145 RegionSelection rs = get_regions_from_selection_and_entered ();
5151 bool in_command = false;
5153 for (RegionSelection::iterator r = rs.begin(); r != rs.end(); ++r) {
5154 AudioRegionView* const arv = dynamic_cast<AudioRegionView*>(*r);
5157 arv->region()->clear_changes ();
5158 arv->audio_region()->set_scale_amplitude (1.0f);
5161 begin_reversible_command ("reset gain");
5164 _session->add_command (new StatefulDiffCommand (arv->region()));
5168 commit_reversible_command ();
5173 Editor::adjust_region_gain (bool up)
5175 RegionSelection rs = get_regions_from_selection_and_entered ();
5177 if (!_session || rs.empty()) {
5181 bool in_command = false;
5183 for (RegionSelection::iterator r = rs.begin(); r != rs.end(); ++r) {
5184 AudioRegionView* const arv = dynamic_cast<AudioRegionView*>(*r);
5189 arv->region()->clear_changes ();
5191 double dB = accurate_coefficient_to_dB (arv->audio_region()->scale_amplitude ());
5199 arv->audio_region()->set_scale_amplitude (dB_to_coefficient (dB));
5202 begin_reversible_command ("adjust region gain");
5205 _session->add_command (new StatefulDiffCommand (arv->region()));
5209 commit_reversible_command ();
5215 Editor::reverse_region ()
5221 Reverse rev (*_session);
5222 apply_filter (rev, _("reverse regions"));
5226 Editor::strip_region_silence ()
5232 RegionSelection rs = get_regions_from_selection_and_entered ();
5238 std::list<RegionView*> audio_only;
5240 for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
5241 AudioRegionView* const arv = dynamic_cast<AudioRegionView*> (*i);
5243 audio_only.push_back (arv);
5247 assert (!audio_only.empty());
5249 StripSilenceDialog d (_session, audio_only);
5250 int const r = d.run ();
5254 if (r == Gtk::RESPONSE_OK) {
5255 ARDOUR::AudioIntervalMap silences;
5256 d.silences (silences);
5257 StripSilence s (*_session, silences, d.fade_length());
5259 apply_filter (s, _("strip silence"), &d);
5264 Editor::apply_midi_note_edit_op_to_region (MidiOperator& op, MidiRegionView& mrv)
5266 Evoral::Sequence<Evoral::Beats>::Notes selected;
5267 mrv.selection_as_notelist (selected, true);
5269 vector<Evoral::Sequence<Evoral::Beats>::Notes> v;
5270 v.push_back (selected);
5272 Evoral::Beats pos_beats = Evoral::Beats (mrv.midi_region()->beat()) - mrv.midi_region()->start_beats();
5274 return op (mrv.midi_region()->model(), pos_beats, v);
5278 Editor::apply_midi_note_edit_op (MidiOperator& op, const RegionSelection& rs)
5284 bool in_command = false;
5286 for (RegionSelection::const_iterator r = rs.begin(); r != rs.end(); ) {
5287 RegionSelection::const_iterator tmp = r;
5290 MidiRegionView* const mrv = dynamic_cast<MidiRegionView*> (*r);
5293 Command* cmd = apply_midi_note_edit_op_to_region (op, *mrv);
5296 begin_reversible_command (op.name ());
5300 _session->add_command (cmd);
5308 commit_reversible_command ();
5313 Editor::fork_region ()
5315 RegionSelection rs = get_regions_from_selection_and_entered ();
5321 CursorContext::Handle cursor_ctx = CursorContext::create(*this, _cursors->wait);
5322 bool in_command = false;
5326 for (RegionSelection::iterator r = rs.begin(); r != rs.end(); ) {
5327 RegionSelection::iterator tmp = r;
5330 MidiRegionView* const mrv = dynamic_cast<MidiRegionView*>(*r);
5334 boost::shared_ptr<Playlist> playlist = mrv->region()->playlist();
5335 boost::shared_ptr<MidiSource> new_source = _session->create_midi_source_by_stealing_name (mrv->midi_view()->track());
5336 boost::shared_ptr<MidiRegion> newregion = mrv->midi_region()->clone (new_source);
5339 begin_reversible_command (_("Fork Region(s)"));
5342 playlist->clear_changes ();
5343 playlist->replace_region (mrv->region(), newregion, mrv->region()->position());
5344 _session->add_command(new StatefulDiffCommand (playlist));
5346 error << string_compose (_("Could not unlink %1"), mrv->region()->name()) << endmsg;
5354 commit_reversible_command ();
5359 Editor::quantize_region ()
5362 quantize_regions(get_regions_from_selection_and_entered ());
5367 Editor::quantize_regions (const RegionSelection& rs)
5369 if (rs.n_midi_regions() == 0) {
5373 if (!quantize_dialog) {
5374 quantize_dialog = new QuantizeDialog (*this);
5377 quantize_dialog->present ();
5378 const int r = quantize_dialog->run ();
5379 quantize_dialog->hide ();
5381 if (r == Gtk::RESPONSE_OK) {
5382 Quantize quant (quantize_dialog->snap_start(),
5383 quantize_dialog->snap_end(),
5384 quantize_dialog->start_grid_size(),
5385 quantize_dialog->end_grid_size(),
5386 quantize_dialog->strength(),
5387 quantize_dialog->swing(),
5388 quantize_dialog->threshold());
5390 apply_midi_note_edit_op (quant, rs);
5395 Editor::legatize_region (bool shrink_only)
5398 legatize_regions(get_regions_from_selection_and_entered (), shrink_only);
5403 Editor::legatize_regions (const RegionSelection& rs, bool shrink_only)
5405 if (rs.n_midi_regions() == 0) {
5409 Legatize legatize(shrink_only);
5410 apply_midi_note_edit_op (legatize, rs);
5414 Editor::transform_region ()
5417 transform_regions(get_regions_from_selection_and_entered ());
5422 Editor::transform_regions (const RegionSelection& rs)
5424 if (rs.n_midi_regions() == 0) {
5431 const int r = td.run();
5434 if (r == Gtk::RESPONSE_OK) {
5435 Transform transform(td.get());
5436 apply_midi_note_edit_op(transform, rs);
5441 Editor::transpose_region ()
5444 transpose_regions(get_regions_from_selection_and_entered ());
5449 Editor::transpose_regions (const RegionSelection& rs)
5451 if (rs.n_midi_regions() == 0) {
5456 int const r = d.run ();
5458 if (r == RESPONSE_ACCEPT) {
5459 Transpose transpose(d.semitones ());
5460 apply_midi_note_edit_op (transpose, rs);
5465 Editor::insert_patch_change (bool from_context)
5467 RegionSelection rs = get_regions_from_selection_and_entered ();
5473 const framepos_t p = get_preferred_edit_position (EDIT_IGNORE_NONE, from_context);
5475 /* XXX: bit of a hack; use the MIDNAM from the first selected region;
5476 there may be more than one, but the PatchChangeDialog can only offer
5477 one set of patch menus.
5479 MidiRegionView* first = dynamic_cast<MidiRegionView*> (rs.front ());
5481 Evoral::PatchChange<Evoral::Beats> empty (Evoral::Beats(), 0, 0, 0);
5482 PatchChangeDialog d (0, _session, empty, first->instrument_info(), Gtk::Stock::ADD);
5484 if (d.run() == RESPONSE_CANCEL) {
5488 for (RegionSelection::iterator i = rs.begin (); i != rs.end(); ++i) {
5489 MidiRegionView* const mrv = dynamic_cast<MidiRegionView*> (*i);
5491 if (p >= mrv->region()->first_frame() && p <= mrv->region()->last_frame()) {
5492 mrv->add_patch_change (p - mrv->region()->position(), d.patch ());
5499 Editor::apply_filter (Filter& filter, string command, ProgressReporter* progress)
5501 RegionSelection rs = get_regions_from_selection_and_entered ();
5507 CursorContext::Handle cursor_ctx = CursorContext::create(*this, _cursors->wait);
5508 bool in_command = false;
5513 int const N = rs.size ();
5515 for (RegionSelection::iterator r = rs.begin(); r != rs.end(); ) {
5516 RegionSelection::iterator tmp = r;
5519 AudioRegionView* const arv = dynamic_cast<AudioRegionView*>(*r);
5521 boost::shared_ptr<Playlist> playlist = arv->region()->playlist();
5524 progress->descend (1.0 / N);
5527 if (arv->audio_region()->apply (filter, progress) == 0) {
5529 playlist->clear_changes ();
5530 playlist->clear_owned_changes ();
5533 begin_reversible_command (command);
5537 if (filter.results.empty ()) {
5539 /* no regions returned; remove the old one */
5540 playlist->remove_region (arv->region ());
5544 std::vector<boost::shared_ptr<Region> >::iterator res = filter.results.begin ();
5546 /* first region replaces the old one */
5547 playlist->replace_region (arv->region(), *res, (*res)->position());
5551 while (res != filter.results.end()) {
5552 playlist->add_region (*res, (*res)->position());
5558 /* We might have removed regions, which alters other regions' layering_index,
5559 so we need to do a recursive diff here.
5561 vector<Command*> cmds;
5562 playlist->rdiff (cmds);
5563 _session->add_commands (cmds);
5565 _session->add_command(new StatefulDiffCommand (playlist));
5569 progress->ascend ();
5578 commit_reversible_command ();
5583 Editor::external_edit_region ()
5589 Editor::reset_region_gain_envelopes ()
5591 RegionSelection rs = get_regions_from_selection_and_entered ();
5593 if (!_session || rs.empty()) {
5597 bool in_command = false;
5599 for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
5600 AudioRegionView* const arv = dynamic_cast<AudioRegionView*>(*i);
5602 boost::shared_ptr<AutomationList> alist (arv->audio_region()->envelope());
5603 XMLNode& before (alist->get_state());
5605 arv->audio_region()->set_default_envelope ();
5608 begin_reversible_command (_("reset region gain"));
5611 _session->add_command (new MementoCommand<AutomationList>(*arv->audio_region()->envelope().get(), &before, &alist->get_state()));
5616 commit_reversible_command ();
5621 Editor::set_region_gain_visibility (RegionView* rv)
5623 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (rv);
5625 arv->update_envelope_visibility();
5630 Editor::set_gain_envelope_visibility ()
5636 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
5637 AudioTimeAxisView* v = dynamic_cast<AudioTimeAxisView*>(*i);
5639 v->audio_view()->foreach_regionview (sigc::mem_fun (this, &Editor::set_region_gain_visibility));
5645 Editor::toggle_gain_envelope_active ()
5647 if (_ignore_region_action) {
5651 RegionSelection rs = get_regions_from_selection_and_entered ();
5653 if (!_session || rs.empty()) {
5657 bool in_command = false;
5659 for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
5660 AudioRegionView* const arv = dynamic_cast<AudioRegionView*>(*i);
5662 arv->region()->clear_changes ();
5663 arv->audio_region()->set_envelope_active (!arv->audio_region()->envelope_active());
5666 begin_reversible_command (_("region gain envelope active"));
5669 _session->add_command (new StatefulDiffCommand (arv->region()));
5674 commit_reversible_command ();
5679 Editor::toggle_region_lock ()
5681 if (_ignore_region_action) {
5685 RegionSelection rs = get_regions_from_selection_and_entered ();
5687 if (!_session || rs.empty()) {
5691 begin_reversible_command (_("toggle region lock"));
5693 for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
5694 (*i)->region()->clear_changes ();
5695 (*i)->region()->set_locked (!(*i)->region()->locked());
5696 _session->add_command (new StatefulDiffCommand ((*i)->region()));
5699 commit_reversible_command ();
5703 Editor::toggle_region_video_lock ()
5705 if (_ignore_region_action) {
5709 RegionSelection rs = get_regions_from_selection_and_entered ();
5711 if (!_session || rs.empty()) {
5715 begin_reversible_command (_("Toggle Video Lock"));
5717 for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
5718 (*i)->region()->clear_changes ();
5719 (*i)->region()->set_video_locked (!(*i)->region()->video_locked());
5720 _session->add_command (new StatefulDiffCommand ((*i)->region()));
5723 commit_reversible_command ();
5727 Editor::toggle_region_lock_style ()
5729 if (_ignore_region_action) {
5733 RegionSelection rs = get_regions_from_selection_and_entered ();
5735 if (!_session || rs.empty()) {
5739 begin_reversible_command (_("region lock style"));
5741 for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
5742 (*i)->region()->clear_changes ();
5743 PositionLockStyle const ns = (*i)->region()->position_lock_style() == AudioTime ? MusicTime : AudioTime;
5744 (*i)->region()->set_position_lock_style (ns);
5745 _session->add_command (new StatefulDiffCommand ((*i)->region()));
5748 commit_reversible_command ();
5752 Editor::toggle_opaque_region ()
5754 if (_ignore_region_action) {
5758 RegionSelection rs = get_regions_from_selection_and_entered ();
5760 if (!_session || rs.empty()) {
5764 begin_reversible_command (_("change region opacity"));
5766 for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
5767 (*i)->region()->clear_changes ();
5768 (*i)->region()->set_opaque (!(*i)->region()->opaque());
5769 _session->add_command (new StatefulDiffCommand ((*i)->region()));
5772 commit_reversible_command ();
5776 Editor::toggle_record_enable ()
5778 bool new_state = false;
5780 for (TrackSelection::iterator i = selection->tracks.begin(); i != selection->tracks.end(); ++i) {
5781 RouteTimeAxisView *rtav = dynamic_cast<RouteTimeAxisView *>(*i);
5784 if (!rtav->is_track())
5788 new_state = !rtav->track()->rec_enable_control()->get_value();
5792 rtav->track()->rec_enable_control()->set_value (new_state, Controllable::UseGroup);
5797 Editor::toggle_solo ()
5799 bool new_state = false;
5801 boost::shared_ptr<ControlList> cl (new ControlList);
5803 for (TrackSelection::iterator i = selection->tracks.begin(); i != selection->tracks.end(); ++i) {
5804 RouteTimeAxisView *rtav = dynamic_cast<RouteTimeAxisView *>(*i);
5811 new_state = !rtav->route()->soloed ();
5815 cl->push_back (rtav->route()->solo_control());
5818 _session->set_controls (cl, new_state ? 1.0 : 0.0, Controllable::UseGroup);
5822 Editor::toggle_mute ()
5824 bool new_state = false;
5826 boost::shared_ptr<RouteList> rl (new RouteList);
5828 for (TrackSelection::iterator i = selection->tracks.begin(); i != selection->tracks.end(); ++i) {
5829 RouteTimeAxisView *rtav = dynamic_cast<RouteTimeAxisView *>(*i);
5836 new_state = !rtav->route()->muted();
5840 rl->push_back (rtav->route());
5843 _session->set_controls (route_list_to_control_list (rl, &Stripable::mute_control), new_state, Controllable::UseGroup);
5847 Editor::toggle_solo_isolate ()
5853 Editor::fade_range ()
5855 TrackViewList ts = selection->tracks.filter_to_unique_playlists ();
5857 begin_reversible_command (_("fade range"));
5859 for (TrackViewList::iterator i = ts.begin(); i != ts.end(); ++i) {
5860 (*i)->fade_range (selection->time);
5863 commit_reversible_command ();
5868 Editor::set_fade_length (bool in)
5870 RegionSelection rs = get_regions_from_selection_and_entered ();
5876 /* we need a region to measure the offset from the start */
5878 RegionView* rv = rs.front ();
5880 framepos_t pos = get_preferred_edit_position();
5884 if (pos > rv->region()->last_frame() || pos < rv->region()->first_frame()) {
5885 /* edit point is outside the relevant region */
5890 if (pos <= rv->region()->position()) {
5894 len = pos - rv->region()->position();
5895 cmd = _("set fade in length");
5897 if (pos >= rv->region()->last_frame()) {
5901 len = rv->region()->last_frame() - pos;
5902 cmd = _("set fade out length");
5905 bool in_command = false;
5907 for (RegionSelection::iterator x = rs.begin(); x != rs.end(); ++x) {
5908 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (*x);
5914 boost::shared_ptr<AutomationList> alist;
5916 alist = tmp->audio_region()->fade_in();
5918 alist = tmp->audio_region()->fade_out();
5921 XMLNode &before = alist->get_state();
5924 tmp->audio_region()->set_fade_in_length (len);
5925 tmp->audio_region()->set_fade_in_active (true);
5927 tmp->audio_region()->set_fade_out_length (len);
5928 tmp->audio_region()->set_fade_out_active (true);
5932 begin_reversible_command (cmd);
5935 XMLNode &after = alist->get_state();
5936 _session->add_command(new MementoCommand<AutomationList>(*alist, &before, &after));
5940 commit_reversible_command ();
5945 Editor::set_fade_in_shape (FadeShape shape)
5947 RegionSelection rs = get_regions_from_selection_and_entered ();
5952 bool in_command = false;
5954 for (RegionSelection::iterator x = rs.begin(); x != rs.end(); ++x) {
5955 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (*x);
5961 boost::shared_ptr<AutomationList> alist = tmp->audio_region()->fade_in();
5962 XMLNode &before = alist->get_state();
5964 tmp->audio_region()->set_fade_in_shape (shape);
5967 begin_reversible_command (_("set fade in shape"));
5970 XMLNode &after = alist->get_state();
5971 _session->add_command(new MementoCommand<AutomationList>(*alist.get(), &before, &after));
5975 commit_reversible_command ();
5980 Editor::set_fade_out_shape (FadeShape shape)
5982 RegionSelection rs = get_regions_from_selection_and_entered ();
5987 bool in_command = false;
5989 for (RegionSelection::iterator x = rs.begin(); x != rs.end(); ++x) {
5990 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (*x);
5996 boost::shared_ptr<AutomationList> alist = tmp->audio_region()->fade_out();
5997 XMLNode &before = alist->get_state();
5999 tmp->audio_region()->set_fade_out_shape (shape);
6002 begin_reversible_command (_("set fade out shape"));
6005 XMLNode &after = alist->get_state();
6006 _session->add_command(new MementoCommand<AutomationList>(*alist.get(), &before, &after));
6010 commit_reversible_command ();
6015 Editor::set_fade_in_active (bool yn)
6017 RegionSelection rs = get_regions_from_selection_and_entered ();
6022 bool in_command = false;
6024 for (RegionSelection::iterator x = rs.begin(); x != rs.end(); ++x) {
6025 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (*x);
6032 boost::shared_ptr<AudioRegion> ar (tmp->audio_region());
6034 ar->clear_changes ();
6035 ar->set_fade_in_active (yn);
6038 begin_reversible_command (_("set fade in active"));
6041 _session->add_command (new StatefulDiffCommand (ar));
6045 commit_reversible_command ();
6050 Editor::set_fade_out_active (bool yn)
6052 RegionSelection rs = get_regions_from_selection_and_entered ();
6057 bool in_command = false;
6059 for (RegionSelection::iterator x = rs.begin(); x != rs.end(); ++x) {
6060 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (*x);
6066 boost::shared_ptr<AudioRegion> ar (tmp->audio_region());
6068 ar->clear_changes ();
6069 ar->set_fade_out_active (yn);
6072 begin_reversible_command (_("set fade out active"));
6075 _session->add_command(new StatefulDiffCommand (ar));
6079 commit_reversible_command ();
6084 Editor::toggle_region_fades (int dir)
6086 if (_ignore_region_action) {
6090 boost::shared_ptr<AudioRegion> ar;
6093 RegionSelection rs = get_regions_from_selection_and_entered ();
6099 RegionSelection::iterator i;
6100 for (i = rs.begin(); i != rs.end(); ++i) {
6101 if ((ar = boost::dynamic_pointer_cast<AudioRegion>((*i)->region())) != 0) {
6103 yn = ar->fade_out_active ();
6105 yn = ar->fade_in_active ();
6111 if (i == rs.end()) {
6115 /* XXX should this undo-able? */
6116 bool in_command = false;
6118 for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
6119 if ((ar = boost::dynamic_pointer_cast<AudioRegion>((*i)->region())) == 0) {
6122 ar->clear_changes ();
6124 if (dir == 1 || dir == 0) {
6125 ar->set_fade_in_active (!yn);
6128 if (dir == -1 || dir == 0) {
6129 ar->set_fade_out_active (!yn);
6132 begin_reversible_command (_("toggle fade active"));
6135 _session->add_command(new StatefulDiffCommand (ar));
6139 commit_reversible_command ();
6144 /** Update region fade visibility after its configuration has been changed */
6146 Editor::update_region_fade_visibility ()
6148 bool _fade_visibility = _session->config.get_show_region_fades ();
6150 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
6151 AudioTimeAxisView* v = dynamic_cast<AudioTimeAxisView*>(*i);
6153 if (_fade_visibility) {
6154 v->audio_view()->show_all_fades ();
6156 v->audio_view()->hide_all_fades ();
6163 Editor::set_edit_point ()
6168 if (!mouse_frame (where, ignored)) {
6174 if (selection->markers.empty()) {
6176 mouse_add_new_marker (where);
6181 Location* loc = find_location_from_marker (selection->markers.front(), ignored);
6184 loc->move_to (where);
6190 Editor::set_playhead_cursor ()
6192 if (entered_marker) {
6193 _session->request_locate (entered_marker->position(), _session->transport_rolling());
6198 if (!mouse_frame (where, ignored)) {
6205 _session->request_locate (where, _session->transport_rolling());
6209 if (UIConfiguration::instance().get_follow_edits() && (!_session || !_session->config.get_external_sync())) {
6210 cancel_time_selection();
6215 Editor::split_region ()
6217 if (_drags->active ()) {
6221 //if a range is selected, separate it
6222 if ( !selection->time.empty()) {
6223 separate_regions_between (selection->time);
6227 //if no range was selected, try to find some regions to split
6228 if (current_mouse_mode() == MouseObject) { //don't try this for Internal Edit, Stretch, Draw, etc.
6230 RegionSelection rs = get_regions_from_selection_and_edit_point ();
6232 framepos_t where = get_preferred_edit_position ();
6238 if (snap_musical()) {
6239 split_regions_at (where, rs, get_grid_music_divisions (0));
6241 split_regions_at (where, rs, 0);
6247 Editor::select_next_route()
6249 if (selection->tracks.empty()) {
6250 selection->set (track_views.front());
6254 TimeAxisView* current = selection->tracks.front();
6258 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
6260 if (*i == current) {
6262 if (i != track_views.end()) {
6265 current = (*(track_views.begin()));
6266 //selection->set (*(track_views.begin()));
6272 rui = dynamic_cast<RouteUI *>(current);
6274 } while (current->hidden() || (rui == NULL) || !rui->route()->active());
6276 selection->set (current);
6278 ensure_time_axis_view_is_visible (*current, false);
6282 Editor::select_prev_route()
6284 if (selection->tracks.empty()) {
6285 selection->set (track_views.front());
6289 TimeAxisView* current = selection->tracks.front();
6293 for (TrackViewList::reverse_iterator i = track_views.rbegin(); i != track_views.rend(); ++i) {
6295 if (*i == current) {
6297 if (i != track_views.rend()) {
6300 current = *(track_views.rbegin());
6305 rui = dynamic_cast<RouteUI *>(current);
6307 } while (current->hidden() || (rui == NULL) || !rui->route()->active());
6309 selection->set (current);
6311 ensure_time_axis_view_is_visible (*current, false);
6315 Editor::set_loop_from_selection (bool play)
6317 if (_session == 0) {
6321 framepos_t start, end;
6322 if (!get_selection_extents ( start, end))
6325 set_loop_range (start, end, _("set loop range from selection"));
6328 _session->request_play_loop (true, true);
6333 Editor::set_loop_from_region (bool play)
6335 framepos_t start, end;
6336 if (!get_selection_extents ( start, end))
6339 set_loop_range (start, end, _("set loop range from region"));
6342 _session->request_locate (start, true);
6343 _session->request_play_loop (true);
6348 Editor::set_punch_from_selection ()
6350 if (_session == 0) {
6354 framepos_t start, end;
6355 if (!get_selection_extents ( start, end))
6358 set_punch_range (start, end, _("set punch range from selection"));
6362 Editor::set_session_extents_from_selection ()
6364 if (_session == 0) {
6368 framepos_t start, end;
6369 if (!get_selection_extents ( start, end))
6373 if ((loc = _session->locations()->session_range_location()) == 0) {
6374 _session->set_session_extents (start, end); // this will create a new session range; no need for UNDO
6376 XMLNode &before = loc->get_state();
6378 _session->set_session_extents (start, end);
6380 XMLNode &after = loc->get_state();
6382 begin_reversible_command (_("set session start/end from selection"));
6384 _session->add_command (new MementoCommand<Location>(*loc, &before, &after));
6386 commit_reversible_command ();
6389 _session->set_end_is_free (false);
6393 Editor::set_punch_start_from_edit_point ()
6397 framepos_t start = 0;
6398 framepos_t end = max_framepos;
6400 //use the existing punch end, if any
6401 Location* tpl = transport_punch_location();
6406 if ((_edit_point == EditAtPlayhead) && _session->transport_rolling()) {
6407 start = _session->audible_frame();
6409 start = get_preferred_edit_position();
6412 //snap the selection start/end
6415 //if there's not already a sensible selection endpoint, go "forever"
6416 if ( start > end ) {
6420 set_punch_range (start, end, _("set punch start from EP"));
6426 Editor::set_punch_end_from_edit_point ()
6430 framepos_t start = 0;
6431 framepos_t end = max_framepos;
6433 //use the existing punch start, if any
6434 Location* tpl = transport_punch_location();
6436 start = tpl->start();
6439 if ((_edit_point == EditAtPlayhead) && _session->transport_rolling()) {
6440 end = _session->audible_frame();
6442 end = get_preferred_edit_position();
6445 //snap the selection start/end
6448 set_punch_range (start, end, _("set punch end from EP"));
6454 Editor::set_loop_start_from_edit_point ()
6458 framepos_t start = 0;
6459 framepos_t end = max_framepos;
6461 //use the existing loop end, if any
6462 Location* tpl = transport_loop_location();
6467 if ((_edit_point == EditAtPlayhead) && _session->transport_rolling()) {
6468 start = _session->audible_frame();
6470 start = get_preferred_edit_position();
6473 //snap the selection start/end
6476 //if there's not already a sensible selection endpoint, go "forever"
6477 if ( start > end ) {
6481 set_loop_range (start, end, _("set loop start from EP"));
6487 Editor::set_loop_end_from_edit_point ()
6491 framepos_t start = 0;
6492 framepos_t end = max_framepos;
6494 //use the existing loop start, if any
6495 Location* tpl = transport_loop_location();
6497 start = tpl->start();
6500 if ((_edit_point == EditAtPlayhead) && _session->transport_rolling()) {
6501 end = _session->audible_frame();
6503 end = get_preferred_edit_position();
6506 //snap the selection start/end
6509 set_loop_range (start, end, _("set loop end from EP"));
6514 Editor::set_punch_from_region ()
6516 framepos_t start, end;
6517 if (!get_selection_extents ( start, end))
6520 set_punch_range (start, end, _("set punch range from region"));
6524 Editor::pitch_shift_region ()
6526 RegionSelection rs = get_regions_from_selection_and_entered ();
6528 RegionSelection audio_rs;
6529 for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
6530 if (dynamic_cast<AudioRegionView*> (*i)) {
6531 audio_rs.push_back (*i);
6535 if (audio_rs.empty()) {
6539 pitch_shift (audio_rs, 1.2);
6543 Editor::set_tempo_from_region ()
6545 RegionSelection rs = get_regions_from_selection_and_entered ();
6547 if (!_session || rs.empty()) {
6551 RegionView* rv = rs.front();
6553 define_one_bar (rv->region()->position(), rv->region()->last_frame() + 1);
6557 Editor::use_range_as_bar ()
6559 framepos_t start, end;
6560 if (get_edit_op_range (start, end)) {
6561 define_one_bar (start, end);
6566 Editor::define_one_bar (framepos_t start, framepos_t end)
6568 framepos_t length = end - start;
6570 const Meter& m (_session->tempo_map().meter_at_frame (start));
6572 /* length = 1 bar */
6574 /* We're going to deliver a constant tempo here,
6575 so we can use frames per beat to determine length.
6576 now we want frames per beat.
6577 we have frames per bar, and beats per bar, so ...
6580 /* XXXX METER MATH */
6582 double frames_per_beat = length / m.divisions_per_bar();
6584 /* beats per minute = */
6586 double beats_per_minute = (_session->frame_rate() * 60.0) / frames_per_beat;
6588 /* now decide whether to:
6590 (a) set global tempo
6591 (b) add a new tempo marker
6595 const TempoSection& t (_session->tempo_map().tempo_section_at_frame (start));
6597 bool do_global = false;
6599 if ((_session->tempo_map().n_tempos() == 1) && (_session->tempo_map().n_meters() == 1)) {
6601 /* only 1 tempo & 1 meter: ask if the user wants to set the tempo
6602 at the start, or create a new marker
6605 vector<string> options;
6606 options.push_back (_("Cancel"));
6607 options.push_back (_("Add new marker"));
6608 options.push_back (_("Set global tempo"));
6611 _("Define one bar"),
6612 _("Do you want to set the global tempo or add a new tempo marker?"),
6616 c.set_default_response (2);
6632 /* more than 1 tempo and/or meter section already, go ahead do the "usual":
6633 if the marker is at the region starter, change it, otherwise add
6638 begin_reversible_command (_("set tempo from region"));
6639 XMLNode& before (_session->tempo_map().get_state());
6642 _session->tempo_map().change_initial_tempo (beats_per_minute, t.note_type());
6643 } else if (t.frame() == start) {
6644 _session->tempo_map().change_existing_tempo_at (start, beats_per_minute, t.note_type());
6646 const Tempo tempo (beats_per_minute, t.note_type());
6647 _session->tempo_map().add_tempo (tempo, 0.0, start, TempoSection::Constant, AudioTime);
6650 XMLNode& after (_session->tempo_map().get_state());
6652 _session->add_command (new MementoCommand<TempoMap>(_session->tempo_map(), &before, &after));
6653 commit_reversible_command ();
6657 Editor::split_region_at_transients ()
6659 AnalysisFeatureList positions;
6661 RegionSelection rs = get_regions_from_selection_and_entered ();
6663 if (!_session || rs.empty()) {
6667 begin_reversible_command (_("split regions"));
6669 for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ) {
6671 RegionSelection::iterator tmp;
6676 boost::shared_ptr<AudioRegion> ar = boost::dynamic_pointer_cast<AudioRegion> ((*i)->region());
6679 ar->transients (positions);
6680 split_region_at_points ((*i)->region(), positions, true);
6687 commit_reversible_command ();
6692 Editor::split_region_at_points (boost::shared_ptr<Region> r, AnalysisFeatureList& positions, bool can_ferret, bool select_new)
6694 bool use_rhythmic_rodent = false;
6696 boost::shared_ptr<Playlist> pl = r->playlist();
6698 list<boost::shared_ptr<Region> > new_regions;
6704 if (positions.empty()) {
6708 if (positions.size() > 20 && can_ferret) {
6709 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);
6710 MessageDialog msg (msgstr,
6713 Gtk::BUTTONS_OK_CANCEL);
6716 msg.add_button (_("Call for the Ferret!"), RESPONSE_APPLY);
6717 msg.set_secondary_text (_("Press OK to continue with this split operation\nor ask the Ferret dialog to tune the analysis"));
6719 msg.set_secondary_text (_("Press OK to continue with this split operation"));
6722 msg.set_title (_("Excessive split?"));
6725 int response = msg.run();
6731 case RESPONSE_APPLY:
6732 use_rhythmic_rodent = true;
6739 if (use_rhythmic_rodent) {
6740 show_rhythm_ferret ();
6744 AnalysisFeatureList::const_iterator x;
6746 pl->clear_changes ();
6747 pl->clear_owned_changes ();
6749 x = positions.begin();
6751 if (x == positions.end()) {
6756 pl->remove_region (r);
6760 framepos_t rstart = r->first_frame ();
6761 framepos_t rend = r->last_frame ();
6763 while (x != positions.end()) {
6765 /* deal with positons that are out of scope of present region bounds */
6766 if (*x <= rstart || *x > rend) {
6771 /* file start = original start + how far we from the initial position ? */
6773 framepos_t file_start = r->start() + pos;
6775 /* length = next position - current position */
6777 framepos_t len = (*x) - pos - rstart;
6779 /* XXX we do we really want to allow even single-sample regions?
6780 * shouldn't we have some kind of lower limit on region size?
6789 if (RegionFactory::region_name (new_name, r->name())) {
6793 /* do NOT announce new regions 1 by one, just wait till they are all done */
6797 plist.add (ARDOUR::Properties::start, file_start);
6798 plist.add (ARDOUR::Properties::length, len);
6799 plist.add (ARDOUR::Properties::name, new_name);
6800 plist.add (ARDOUR::Properties::layer, 0);
6801 // TODO set transients_offset
6803 boost::shared_ptr<Region> nr = RegionFactory::create (r->sources(), plist, false);
6804 /* because we set annouce to false, manually add the new region to the
6807 RegionFactory::map_add (nr);
6809 pl->add_region (nr, rstart + pos);
6812 new_regions.push_front(nr);
6821 RegionFactory::region_name (new_name, r->name());
6823 /* Add the final region */
6826 plist.add (ARDOUR::Properties::start, r->start() + pos);
6827 plist.add (ARDOUR::Properties::length, r->last_frame() - (r->position() + pos) + 1);
6828 plist.add (ARDOUR::Properties::name, new_name);
6829 plist.add (ARDOUR::Properties::layer, 0);
6831 boost::shared_ptr<Region> nr = RegionFactory::create (r->sources(), plist, false);
6832 /* because we set annouce to false, manually add the new region to the
6835 RegionFactory::map_add (nr);
6836 pl->add_region (nr, r->position() + pos);
6839 new_regions.push_front(nr);
6844 /* We might have removed regions, which alters other regions' layering_index,
6845 so we need to do a recursive diff here.
6847 vector<Command*> cmds;
6849 _session->add_commands (cmds);
6851 _session->add_command (new StatefulDiffCommand (pl));
6855 for (list<boost::shared_ptr<Region> >::iterator i = new_regions.begin(); i != new_regions.end(); ++i){
6856 set_selected_regionview_from_region_list ((*i), Selection::Add);
6862 Editor::place_transient()
6868 RegionSelection rs = get_regions_from_selection_and_edit_point ();
6874 framepos_t where = get_preferred_edit_position();
6876 begin_reversible_command (_("place transient"));
6878 for (RegionSelection::iterator r = rs.begin(); r != rs.end(); ++r) {
6879 (*r)->region()->add_transient(where);
6882 commit_reversible_command ();
6886 Editor::remove_transient(ArdourCanvas::Item* item)
6892 ArdourCanvas::Line* _line = reinterpret_cast<ArdourCanvas::Line*> (item);
6895 AudioRegionView* _arv = reinterpret_cast<AudioRegionView*> (item->get_data ("regionview"));
6896 _arv->remove_transient (*(float*) _line->get_data ("position"));
6900 Editor::snap_regions_to_grid ()
6902 list <boost::shared_ptr<Playlist > > used_playlists;
6904 RegionSelection rs = get_regions_from_selection_and_entered ();
6906 if (!_session || rs.empty()) {
6910 begin_reversible_command (_("snap regions to grid"));
6912 for (RegionSelection::iterator r = rs.begin(); r != rs.end(); ++r) {
6914 boost::shared_ptr<Playlist> pl = (*r)->region()->playlist();
6916 if (!pl->frozen()) {
6917 /* we haven't seen this playlist before */
6919 /* remember used playlists so we can thaw them later */
6920 used_playlists.push_back(pl);
6924 framepos_t start_frame = (*r)->region()->first_frame ();
6925 snap_to (start_frame);
6926 (*r)->region()->set_position (start_frame);
6929 while (used_playlists.size() > 0) {
6930 list <boost::shared_ptr<Playlist > >::iterator i = used_playlists.begin();
6932 used_playlists.pop_front();
6935 commit_reversible_command ();
6939 Editor::close_region_gaps ()
6941 list <boost::shared_ptr<Playlist > > used_playlists;
6943 RegionSelection rs = get_regions_from_selection_and_entered ();
6945 if (!_session || rs.empty()) {
6949 Dialog dialog (_("Close Region Gaps"));
6952 table.set_spacings (12);
6953 table.set_border_width (12);
6954 Label* l = manage (left_aligned_label (_("Crossfade length")));
6955 table.attach (*l, 0, 1, 0, 1);
6957 SpinButton spin_crossfade (1, 0);
6958 spin_crossfade.set_range (0, 15);
6959 spin_crossfade.set_increments (1, 1);
6960 spin_crossfade.set_value (5);
6961 table.attach (spin_crossfade, 1, 2, 0, 1);
6963 table.attach (*manage (new Label (_("ms"))), 2, 3, 0, 1);
6965 l = manage (left_aligned_label (_("Pull-back length")));
6966 table.attach (*l, 0, 1, 1, 2);
6968 SpinButton spin_pullback (1, 0);
6969 spin_pullback.set_range (0, 100);
6970 spin_pullback.set_increments (1, 1);
6971 spin_pullback.set_value(30);
6972 table.attach (spin_pullback, 1, 2, 1, 2);
6974 table.attach (*manage (new Label (_("ms"))), 2, 3, 1, 2);
6976 dialog.get_vbox()->pack_start (table);
6977 dialog.add_button (Stock::CANCEL, RESPONSE_CANCEL);
6978 dialog.add_button (_("Ok"), RESPONSE_ACCEPT);
6981 if (dialog.run () == RESPONSE_CANCEL) {
6985 framepos_t crossfade_len = spin_crossfade.get_value();
6986 framepos_t pull_back_frames = spin_pullback.get_value();
6988 crossfade_len = lrintf (crossfade_len * _session->frame_rate()/1000);
6989 pull_back_frames = lrintf (pull_back_frames * _session->frame_rate()/1000);
6991 /* Iterate over the region list and make adjacent regions overlap by crossfade_len_ms */
6993 begin_reversible_command (_("close region gaps"));
6996 boost::shared_ptr<Region> last_region;
6998 rs.sort_by_position_and_track();
7000 for (RegionSelection::iterator r = rs.begin(); r != rs.end(); ++r) {
7002 boost::shared_ptr<Playlist> pl = (*r)->region()->playlist();
7004 if (!pl->frozen()) {
7005 /* we haven't seen this playlist before */
7007 /* remember used playlists so we can thaw them later */
7008 used_playlists.push_back(pl);
7012 framepos_t position = (*r)->region()->position();
7014 if (idx == 0 || position < last_region->position()){
7015 last_region = (*r)->region();
7020 (*r)->region()->trim_front( (position - pull_back_frames));
7021 last_region->trim_end( (position - pull_back_frames + crossfade_len));
7023 last_region = (*r)->region();
7028 while (used_playlists.size() > 0) {
7029 list <boost::shared_ptr<Playlist > >::iterator i = used_playlists.begin();
7031 used_playlists.pop_front();
7034 commit_reversible_command ();
7038 Editor::tab_to_transient (bool forward)
7040 AnalysisFeatureList positions;
7042 RegionSelection rs = get_regions_from_selection_and_entered ();
7048 framepos_t pos = _session->audible_frame ();
7050 if (!selection->tracks.empty()) {
7052 /* don't waste time searching for transients in duplicate playlists.
7055 TrackViewList ts = selection->tracks.filter_to_unique_playlists ();
7057 for (TrackViewList::iterator t = ts.begin(); t != ts.end(); ++t) {
7059 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (*t);
7062 boost::shared_ptr<Track> tr = rtv->track();
7064 boost::shared_ptr<Playlist> pl = tr->playlist ();
7066 framepos_t result = pl->find_next_transient (pos, forward ? 1 : -1);
7069 positions.push_back (result);
7082 for (RegionSelection::iterator r = rs.begin(); r != rs.end(); ++r) {
7083 (*r)->region()->get_transients (positions);
7087 TransientDetector::cleanup_transients (positions, _session->frame_rate(), 3.0);
7090 AnalysisFeatureList::iterator x;
7092 for (x = positions.begin(); x != positions.end(); ++x) {
7098 if (x != positions.end ()) {
7099 _session->request_locate (*x);
7103 AnalysisFeatureList::reverse_iterator x;
7105 for (x = positions.rbegin(); x != positions.rend(); ++x) {
7111 if (x != positions.rend ()) {
7112 _session->request_locate (*x);
7118 Editor::playhead_forward_to_grid ()
7124 framepos_t pos = playhead_cursor->current_frame ();
7125 if (pos < max_framepos - 1) {
7127 snap_to_internal (pos, RoundUpAlways, false);
7128 _session->request_locate (pos);
7134 Editor::playhead_backward_to_grid ()
7140 framepos_t pos = playhead_cursor->current_frame ();
7143 snap_to_internal (pos, RoundDownAlways, false);
7144 _session->request_locate (pos);
7149 Editor::set_track_height (Height h)
7151 TrackSelection& ts (selection->tracks);
7153 for (TrackSelection::iterator x = ts.begin(); x != ts.end(); ++x) {
7154 (*x)->set_height_enum (h);
7159 Editor::toggle_tracks_active ()
7161 TrackSelection& ts (selection->tracks);
7163 bool target = false;
7169 for (TrackSelection::iterator x = ts.begin(); x != ts.end(); ++x) {
7170 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*>(*x);
7174 target = !rtv->_route->active();
7177 rtv->_route->set_active (target, this);
7183 Editor::remove_tracks ()
7185 /* this will delete GUI objects that may be the subject of an event
7186 handler in which this method is called. Defer actual deletion to the
7187 next idle callback, when all event handling is finished.
7189 Glib::signal_idle().connect (sigc::mem_fun (*this, &Editor::idle_remove_tracks));
7193 Editor::idle_remove_tracks ()
7195 Session::StateProtector sp (_session);
7197 return false; /* do not call again */
7201 Editor::_remove_tracks ()
7203 TrackSelection& ts (selection->tracks);
7209 vector<string> choices;
7213 const char* trackstr;
7215 vector<boost::shared_ptr<Route> > routes;
7216 bool special_bus = false;
7218 for (TrackSelection::iterator x = ts.begin(); x != ts.end(); ++x) {
7219 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (*x);
7223 if (rtv->is_track()) {
7228 routes.push_back (rtv->_route);
7230 if (rtv->route()->is_master() || rtv->route()->is_monitor()) {
7235 if (special_bus && !Config->get_allow_special_bus_removal()) {
7236 MessageDialog msg (_("That would be bad news ...."),
7240 msg.set_secondary_text (string_compose (_(
7241 "Removing the master or monitor bus is such a bad idea\n\
7242 that %1 is not going to allow it.\n\
7244 If you really want to do this sort of thing\n\
7245 edit your ardour.rc file to set the\n\
7246 \"allow-special-bus-removal\" option to be \"yes\""), PROGRAM_NAME));
7253 if (ntracks + nbusses == 0) {
7257 trackstr = P_("track", "tracks", ntracks);
7258 busstr = P_("bus", "busses", nbusses);
7262 prompt = string_compose (_("Do you really want to remove %1 %2 and %3 %4?\n"
7263 "(You may also lose the playlists associated with the %2)\n\n"
7264 "This action cannot be undone, and the session file will be overwritten!"),
7265 ntracks, trackstr, nbusses, busstr);
7267 prompt = string_compose (_("Do you really want to remove %1 %2?\n"
7268 "(You may also lose the playlists associated with the %2)\n\n"
7269 "This action cannot be undone, and the session file will be overwritten!"),
7272 } else if (nbusses) {
7273 prompt = string_compose (_("Do you really want to remove %1 %2?\n\n"
7274 "This action cannot be undone, and the session file will be overwritten"),
7278 choices.push_back (_("No, do nothing."));
7279 if (ntracks + nbusses > 1) {
7280 choices.push_back (_("Yes, remove them."));
7282 choices.push_back (_("Yes, remove it."));
7287 title = string_compose (_("Remove %1"), trackstr);
7289 title = string_compose (_("Remove %1"), busstr);
7292 Choice prompter (title, prompt, choices);
7294 if (prompter.run () != 1) {
7299 DisplaySuspender ds;
7300 boost::shared_ptr<RouteList> rl (new RouteList);
7301 for (vector<boost::shared_ptr<Route> >::iterator x = routes.begin(); x != routes.end(); ++x) {
7304 _session->remove_routes (rl);
7306 /* TrackSelection and RouteList leave scope,
7307 * destructors are called,
7308 * diskstream drops references, save_state is called (again for every track)
7313 Editor::do_insert_time ()
7315 if (selection->tracks.empty()) {
7319 InsertRemoveTimeDialog d (*this);
7320 int response = d.run ();
7322 if (response != RESPONSE_OK) {
7326 if (d.distance() == 0) {
7331 get_preferred_edit_position (EDIT_IGNORE_MOUSE),
7333 d.intersected_region_action (),
7337 d.move_glued_markers(),
7338 d.move_locked_markers(),
7344 Editor::insert_time (
7345 framepos_t pos, framecnt_t frames, InsertTimeOption opt,
7346 bool all_playlists, bool ignore_music_glue, bool markers_too, bool glued_markers_too, bool locked_markers_too, bool tempo_too
7350 if (Config->get_edit_mode() == Lock) {
7353 bool in_command = false;
7355 TrackViewList ts = selection->tracks.filter_to_unique_playlists ();
7357 for (TrackViewList::iterator x = ts.begin(); x != ts.end(); ++x) {
7361 /* don't operate on any playlist more than once, which could
7362 * happen if "all playlists" is enabled, but there is more
7363 * than 1 track using playlists "from" a given track.
7366 set<boost::shared_ptr<Playlist> > pl;
7368 if (all_playlists) {
7369 RouteTimeAxisView* rtav = dynamic_cast<RouteTimeAxisView*> (*x);
7370 if (rtav && rtav->track ()) {
7371 vector<boost::shared_ptr<Playlist> > all = _session->playlists->playlists_for_track (rtav->track ());
7372 for (vector<boost::shared_ptr<Playlist> >::iterator p = all.begin(); p != all.end(); ++p) {
7377 if ((*x)->playlist ()) {
7378 pl.insert ((*x)->playlist ());
7382 for (set<boost::shared_ptr<Playlist> >::iterator i = pl.begin(); i != pl.end(); ++i) {
7384 (*i)->clear_changes ();
7385 (*i)->clear_owned_changes ();
7387 if (opt == SplitIntersected) {
7388 /* non musical split */
7389 (*i)->split (pos, 0);
7392 (*i)->shift (pos, frames, (opt == MoveIntersected), ignore_music_glue);
7395 begin_reversible_command (_("insert time"));
7398 vector<Command*> cmds;
7400 _session->add_commands (cmds);
7402 _session->add_command (new StatefulDiffCommand (*i));
7406 RouteTimeAxisView* rtav = dynamic_cast<RouteTimeAxisView*> (*x);
7409 begin_reversible_command (_("insert time"));
7412 rtav->route ()->shift (pos, frames);
7419 XMLNode& before (_session->locations()->get_state());
7420 Locations::LocationList copy (_session->locations()->list());
7422 for (Locations::LocationList::iterator i = copy.begin(); i != copy.end(); ++i) {
7424 Locations::LocationList::const_iterator tmp;
7426 if ((*i)->position_lock_style() == AudioTime || glued_markers_too) {
7427 bool const was_locked = (*i)->locked ();
7428 if (locked_markers_too) {
7432 if ((*i)->start() >= pos) {
7433 // move end first, in case we're moving by more than the length of the range
7434 if (!(*i)->is_mark()) {
7435 (*i)->set_end ((*i)->end() + frames);
7437 (*i)->set_start ((*i)->start() + frames);
7449 begin_reversible_command (_("insert time"));
7452 XMLNode& after (_session->locations()->get_state());
7453 _session->add_command (new MementoCommand<Locations>(*_session->locations(), &before, &after));
7459 begin_reversible_command (_("insert time"));
7462 XMLNode& before (_session->tempo_map().get_state());
7463 _session->tempo_map().insert_time (pos, frames);
7464 XMLNode& after (_session->tempo_map().get_state());
7465 _session->add_command (new MementoCommand<TempoMap>(_session->tempo_map(), &before, &after));
7469 commit_reversible_command ();
7474 Editor::do_remove_time ()
7476 if (selection->tracks.empty()) {
7480 framepos_t pos = get_preferred_edit_position (EDIT_IGNORE_MOUSE);
7481 InsertRemoveTimeDialog d (*this, true);
7483 int response = d.run ();
7485 if (response != RESPONSE_OK) {
7489 framecnt_t distance = d.distance();
7491 if (distance == 0) {
7501 d.move_glued_markers(),
7502 d.move_locked_markers(),
7508 Editor::remove_time (framepos_t pos, framecnt_t frames, InsertTimeOption opt,
7509 bool ignore_music_glue, bool markers_too, bool glued_markers_too, bool locked_markers_too, bool tempo_too)
7511 if (Config->get_edit_mode() == Lock) {
7512 error << (_("Cannot insert or delete time when in Lock edit.")) << endmsg;
7515 bool in_command = false;
7517 for (TrackSelection::iterator x = selection->tracks.begin(); x != selection->tracks.end(); ++x) {
7519 boost::shared_ptr<Playlist> pl = (*x)->playlist();
7523 XMLNode &before = pl->get_state();
7525 std::list<AudioRange> rl;
7526 AudioRange ar(pos, pos+frames, 0);
7529 pl->shift (pos, -frames, true, ignore_music_glue);
7532 begin_reversible_command (_("remove time"));
7535 XMLNode &after = pl->get_state();
7537 _session->add_command (new MementoCommand<Playlist> (*pl, &before, &after));
7541 RouteTimeAxisView* rtav = dynamic_cast<RouteTimeAxisView*> (*x);
7544 begin_reversible_command (_("remove time"));
7547 rtav->route ()->shift (pos, -frames);
7551 std::list<Location*> loc_kill_list;
7556 XMLNode& before (_session->locations()->get_state());
7557 Locations::LocationList copy (_session->locations()->list());
7559 for (Locations::LocationList::iterator i = copy.begin(); i != copy.end(); ++i) {
7560 if ((*i)->position_lock_style() == AudioTime || glued_markers_too) {
7562 bool const was_locked = (*i)->locked ();
7563 if (locked_markers_too) {
7567 if (!(*i)->is_mark()) { // it's a range; have to handle both start and end
7568 if ((*i)->end() >= pos
7569 && (*i)->end() < pos+frames
7570 && (*i)->start() >= pos
7571 && (*i)->end() < pos+frames) { // range is completely enclosed; kill it
7573 loc_kill_list.push_back(*i);
7574 } else { // only start or end is included, try to do the right thing
7575 // move start before moving end, to avoid trying to move the end to before the start
7576 // if we're removing more time than the length of the range
7577 if ((*i)->start() >= pos && (*i)->start() < pos+frames) {
7578 // start is within cut
7579 (*i)->set_start (pos); // bring the start marker to the beginning of the cut
7581 } else if ((*i)->start() >= pos+frames) {
7582 // start (and thus entire range) lies beyond end of cut
7583 (*i)->set_start ((*i)->start() - frames); // slip the start marker back
7586 if ((*i)->end() >= pos && (*i)->end() < pos+frames) {
7587 // end is inside cut
7588 (*i)->set_end (pos); // bring the end to the cut
7590 } else if ((*i)->end() >= pos+frames) {
7591 // end is beyond end of cut
7592 (*i)->set_end ((*i)->end() - frames); // slip the end marker back
7597 } else if ((*i)->start() >= pos && (*i)->start() < pos+frames ) {
7598 loc_kill_list.push_back(*i);
7600 } else if ((*i)->start() >= pos) {
7601 (*i)->set_start ((*i)->start() -frames);
7611 for (list<Location*>::iterator i = loc_kill_list.begin(); i != loc_kill_list.end(); ++i) {
7612 _session->locations()->remove( *i );
7617 begin_reversible_command (_("remove time"));
7620 XMLNode& after (_session->locations()->get_state());
7621 _session->add_command (new MementoCommand<Locations>(*_session->locations(), &before, &after));
7626 XMLNode& before (_session->tempo_map().get_state());
7628 if (_session->tempo_map().remove_time (pos, frames) ) {
7630 begin_reversible_command (_("remove time"));
7633 XMLNode& after (_session->tempo_map().get_state());
7634 _session->add_command (new MementoCommand<TempoMap>(_session->tempo_map(), &before, &after));
7639 commit_reversible_command ();
7644 Editor::fit_selection ()
7646 if (!selection->tracks.empty()) {
7647 fit_tracks (selection->tracks);
7651 /* no selected tracks - use tracks with selected regions */
7653 if (!selection->regions.empty()) {
7654 for (RegionSelection::iterator r = selection->regions.begin(); r != selection->regions.end(); ++r) {
7655 tvl.push_back (&(*r)->get_time_axis_view ());
7661 } else if (internal_editing()) {
7662 /* no selected tracks, or regions, but in internal edit mode, so follow the mouse and use
7665 if (entered_track) {
7666 tvl.push_back (entered_track);
7675 Editor::fit_tracks (TrackViewList & tracks)
7677 if (tracks.empty()) {
7681 uint32_t child_heights = 0;
7682 int visible_tracks = 0;
7684 for (TrackSelection::iterator t = tracks.begin(); t != tracks.end(); ++t) {
7686 if (!(*t)->marked_for_display()) {
7690 child_heights += (*t)->effective_height() - (*t)->current_height();
7694 /* compute the per-track height from:
7696 total canvas visible height -
7697 height that will be taken by visible children of selected
7698 tracks - height of the ruler/hscroll area
7700 uint32_t h = (uint32_t) floor ((trackviews_height() - child_heights) / visible_tracks);
7701 double first_y_pos = DBL_MAX;
7703 if (h < TimeAxisView::preset_height (HeightSmall)) {
7704 MessageDialog msg (_("There are too many tracks to fit in the current window"));
7705 /* too small to be displayed */
7709 undo_visual_stack.push_back (current_visual_state (true));
7710 PBD::Unwinder<bool> nsv (no_save_visual, true);
7712 /* build a list of all tracks, including children */
7715 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
7717 TimeAxisView::Children c = (*i)->get_child_list ();
7718 for (TimeAxisView::Children::iterator j = c.begin(); j != c.end(); ++j) {
7719 all.push_back (j->get());
7724 // find selection range.
7725 // if someone knows how to user TrackViewList::iterator for this
7727 int selected_top = -1;
7728 int selected_bottom = -1;
7730 for (TrackViewList::iterator t = all.begin(); t != all.end(); ++t, ++i) {
7731 if ((*t)->marked_for_display ()) {
7732 if (tracks.contains(*t)) {
7733 if (selected_top == -1) {
7736 selected_bottom = i;
7742 for (TrackViewList::iterator t = all.begin(); t != all.end(); ++t, ++i) {
7743 if ((*t)->marked_for_display ()) {
7744 if (tracks.contains(*t)) {
7745 (*t)->set_height (h);
7746 first_y_pos = std::min ((*t)->y_position (), first_y_pos);
7748 if (i > selected_top && i < selected_bottom) {
7749 hide_track_in_display (*t);
7756 set the controls_layout height now, because waiting for its size
7757 request signal handler will cause the vertical adjustment setting to fail
7760 controls_layout.property_height () = _full_canvas_height;
7761 vertical_adjustment.set_value (first_y_pos);
7763 redo_visual_stack.push_back (current_visual_state (true));
7765 visible_tracks_selector.set_text (_("Sel"));
7769 Editor::save_visual_state (uint32_t n)
7771 while (visual_states.size() <= n) {
7772 visual_states.push_back (0);
7775 if (visual_states[n] != 0) {
7776 delete visual_states[n];
7779 visual_states[n] = current_visual_state (true);
7784 Editor::goto_visual_state (uint32_t n)
7786 if (visual_states.size() <= n) {
7790 if (visual_states[n] == 0) {
7794 use_visual_state (*visual_states[n]);
7798 Editor::start_visual_state_op (uint32_t n)
7800 save_visual_state (n);
7802 PopUp* pup = new PopUp (WIN_POS_MOUSE, 1000, true);
7804 snprintf (buf, sizeof (buf), _("Saved view %u"), n+1);
7805 pup->set_text (buf);
7810 Editor::cancel_visual_state_op (uint32_t n)
7812 goto_visual_state (n);
7816 Editor::toggle_region_mute ()
7818 if (_ignore_region_action) {
7822 RegionSelection rs = get_regions_from_selection_and_entered ();
7828 if (rs.size() > 1) {
7829 begin_reversible_command (_("mute regions"));
7831 begin_reversible_command (_("mute region"));
7834 for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
7836 (*i)->region()->playlist()->clear_changes ();
7837 (*i)->region()->set_muted (!(*i)->region()->muted ());
7838 _session->add_command (new StatefulDiffCommand ((*i)->region()));
7842 commit_reversible_command ();
7846 Editor::combine_regions ()
7848 /* foreach track with selected regions, take all selected regions
7849 and join them into a new region containing the subregions (as a
7853 typedef set<RouteTimeAxisView*> RTVS;
7856 if (selection->regions.empty()) {
7860 for (RegionSelection::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
7861 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*>(&(*i)->get_time_axis_view());
7864 tracks.insert (rtv);
7868 begin_reversible_command (_("combine regions"));
7870 vector<RegionView*> new_selection;
7872 for (RTVS::iterator i = tracks.begin(); i != tracks.end(); ++i) {
7875 if ((rv = (*i)->combine_regions ()) != 0) {
7876 new_selection.push_back (rv);
7880 selection->clear_regions ();
7881 for (vector<RegionView*>::iterator i = new_selection.begin(); i != new_selection.end(); ++i) {
7882 selection->add (*i);
7885 commit_reversible_command ();
7889 Editor::uncombine_regions ()
7891 typedef set<RouteTimeAxisView*> RTVS;
7894 if (selection->regions.empty()) {
7898 for (RegionSelection::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
7899 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*>(&(*i)->get_time_axis_view());
7902 tracks.insert (rtv);
7906 begin_reversible_command (_("uncombine regions"));
7908 for (RTVS::iterator i = tracks.begin(); i != tracks.end(); ++i) {
7909 (*i)->uncombine_regions ();
7912 commit_reversible_command ();
7916 Editor::toggle_midi_input_active (bool flip_others)
7919 boost::shared_ptr<RouteList> rl (new RouteList);
7921 for (TrackSelection::iterator i = selection->tracks.begin(); i != selection->tracks.end(); ++i) {
7922 RouteTimeAxisView *rtav = dynamic_cast<RouteTimeAxisView *>(*i);
7928 boost::shared_ptr<MidiTrack> mt = rtav->midi_track();
7931 rl->push_back (rtav->route());
7932 onoff = !mt->input_active();
7936 _session->set_exclusive_input_active (rl, onoff, flip_others);
7943 lock_dialog = new Gtk::Dialog (string_compose (_("%1: Locked"), PROGRAM_NAME), true);
7945 Gtk::Image* padlock = manage (new Gtk::Image (ARDOUR_UI_UTILS::get_icon ("padlock_closed")));
7946 lock_dialog->get_vbox()->pack_start (*padlock);
7948 ArdourButton* b = manage (new ArdourButton);
7949 b->set_name ("lock button");
7950 b->set_text (_("Click to unlock"));
7951 b->signal_clicked.connect (sigc::mem_fun (*this, &Editor::unlock));
7952 lock_dialog->get_vbox()->pack_start (*b);
7954 lock_dialog->get_vbox()->show_all ();
7955 lock_dialog->set_size_request (200, 200);
7958 delete _main_menu_disabler;
7959 _main_menu_disabler = new MainMenuDisabler;
7961 lock_dialog->present ();
7967 lock_dialog->hide ();
7969 delete _main_menu_disabler;
7970 _main_menu_disabler = 0;
7972 if (UIConfiguration::instance().get_lock_gui_after_seconds()) {
7973 start_lock_event_timing ();
7978 Editor::bring_in_callback (Gtk::Label* label, uint32_t n, uint32_t total, string name)
7980 Gtkmm2ext::UI::instance()->call_slot (invalidator (*this), boost::bind (&Editor::update_bring_in_message, this, label, n, total, name));
7984 Editor::update_bring_in_message (Gtk::Label* label, uint32_t n, uint32_t total, string name)
7986 Timers::TimerSuspender t;
7987 label->set_text (string_compose ("Copying %1, %2 of %3", name, n, total));
7988 Gtkmm2ext::UI::instance()->flush_pending (1);
7992 Editor::bring_all_sources_into_session ()
7999 ArdourDialog w (_("Moving embedded files into session folder"));
8000 w.get_vbox()->pack_start (msg);
8003 /* flush all pending GUI events because we're about to start copying
8007 Timers::TimerSuspender t;
8008 Gtkmm2ext::UI::instance()->flush_pending (3);
8012 _session->bring_all_sources_into_session (boost::bind (&Editor::bring_in_callback, this, &msg, _1, _2, _3));