2 Copyright (C) 2000-2004 Paul Davis
4 This program is free software; you can redistribute it and/or modify
5 it under the terms of the GNU General Public License as published by
6 the Free Software Foundation; either version 2 of the License, or
7 (at your option) any later version.
9 This program is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 GNU General Public License for more details.
14 You should have received a copy of the GNU General Public License
15 along with this program; if not, write to the Free Software
16 Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
20 /* Note: public Editor methods are documented in public_editor.h */
31 #include <gtkmm/messagedialog.h>
33 #include "pbd/error.h"
34 #include "pbd/basename.h"
35 #include "pbd/pthread_utils.h"
36 #include "pbd/memento_command.h"
37 #include "pbd/unwind.h"
38 #include "pbd/whitespace.h"
39 #include "pbd/stateful_diff_command.h"
41 #include "gtkmm2ext/utils.h"
43 #include "widgets/choice.h"
44 #include "widgets/popup.h"
45 #include "widgets/prompter.h"
47 #include "ardour/audio_track.h"
48 #include "ardour/audioregion.h"
49 #include "ardour/boost_debug.h"
50 #include "ardour/dB.h"
51 #include "ardour/location.h"
52 #include "ardour/midi_region.h"
53 #include "ardour/midi_track.h"
54 #include "ardour/operations.h"
55 #include "ardour/playlist_factory.h"
56 #include "ardour/profile.h"
57 #include "ardour/quantize.h"
58 #include "ardour/legatize.h"
59 #include "ardour/region_factory.h"
60 #include "ardour/reverse.h"
61 #include "ardour/session.h"
62 #include "ardour/session_playlists.h"
63 #include "ardour/strip_silence.h"
64 #include "ardour/transient_detector.h"
65 #include "ardour/transpose.h"
66 #include "ardour/vca_manager.h"
68 #include "canvas/canvas.h"
71 #include "audio_region_view.h"
72 #include "audio_streamview.h"
73 #include "audio_time_axis.h"
74 #include "automation_region_view.h"
75 #include "automation_time_axis.h"
76 #include "control_point.h"
80 #include "editor_cursors.h"
81 #include "editor_drag.h"
82 #include "editor_regions.h"
83 #include "editor_routes.h"
84 #include "gui_thread.h"
85 #include "insert_remove_time_dialog.h"
86 #include "interthread_progress_window.h"
87 #include "item_counts.h"
89 #include "midi_region_view.h"
91 #include "mixer_strip.h"
92 #include "mouse_cursors.h"
93 #include "normalize_dialog.h"
95 #include "paste_context.h"
96 #include "patch_change_dialog.h"
97 #include "quantize_dialog.h"
98 #include "region_gain_line.h"
99 #include "rgb_macros.h"
100 #include "route_time_axis.h"
101 #include "selection.h"
102 #include "selection_templates.h"
103 #include "streamview.h"
104 #include "strip_silence_dialog.h"
105 #include "time_axis_view.h"
107 #include "transpose_dialog.h"
108 #include "transform_dialog.h"
109 #include "ui_config.h"
110 #include "vca_time_axis.h"
112 #include "pbd/i18n.h"
115 using namespace ARDOUR;
118 using namespace Gtkmm2ext;
119 using namespace ArdourWidgets;
120 using namespace Editing;
121 using Gtkmm2ext::Keyboard;
123 /***********************************************************************
125 ***********************************************************************/
128 Editor::undo (uint32_t n)
130 if (_session && _session->actively_recording()) {
131 /* no undo allowed while recording. Session will check also,
132 but we don't even want to get to that.
137 if (_drags->active ()) {
143 if (_session->undo_depth() == 0) {
144 undo_action->set_sensitive(false);
146 redo_action->set_sensitive(true);
147 begin_selection_op_history ();
152 Editor::redo (uint32_t n)
154 if (_session && _session->actively_recording()) {
155 /* no redo allowed while recording. Session will check also,
156 but we don't even want to get to that.
161 if (_drags->active ()) {
167 if (_session->redo_depth() == 0) {
168 redo_action->set_sensitive(false);
170 undo_action->set_sensitive(true);
171 begin_selection_op_history ();
176 Editor::split_regions_at (MusicFrame where, RegionSelection& regions, bool snap_frame)
180 RegionSelection pre_selected_regions = selection->regions;
181 bool working_on_selection = !pre_selected_regions.empty();
183 list<boost::shared_ptr<Playlist> > used_playlists;
184 list<RouteTimeAxisView*> used_trackviews;
186 if (regions.empty()) {
190 begin_reversible_command (_("split"));
192 // if splitting a single region, and snap-to is using
193 // region boundaries, don't pay attention to them
195 if (regions.size() == 1) {
196 switch (_snap_type) {
197 case SnapToRegionStart:
198 case SnapToRegionSync:
199 case SnapToRegionEnd:
212 EditorFreeze(); /* Emit Signal */
215 for (RegionSelection::iterator a = regions.begin(); a != regions.end(); ) {
217 RegionSelection::iterator tmp;
219 /* XXX this test needs to be more complicated, to make sure we really
220 have something to split.
223 if (!(*a)->region()->covers (where.frame)) {
231 boost::shared_ptr<Playlist> pl = (*a)->region()->playlist();
239 /* we haven't seen this playlist before */
241 /* remember used playlists so we can thaw them later */
242 used_playlists.push_back(pl);
244 TimeAxisView& tv = (*a)->get_time_axis_view();
245 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (&tv);
247 used_trackviews.push_back (rtv);
254 pl->clear_changes ();
255 pl->split_region ((*a)->region(), where);
256 _session->add_command (new StatefulDiffCommand (pl));
262 latest_regionviews.clear ();
264 vector<sigc::connection> region_added_connections;
266 for (list<RouteTimeAxisView*>::iterator i = used_trackviews.begin(); i != used_trackviews.end(); ++i) {
267 region_added_connections.push_back ((*i)->view()->RegionViewAdded.connect (sigc::mem_fun(*this, &Editor::collect_new_region_view)));
270 while (used_playlists.size() > 0) {
271 list <boost::shared_ptr<Playlist > >::iterator i = used_playlists.begin();
273 used_playlists.pop_front();
276 for (vector<sigc::connection>::iterator c = region_added_connections.begin(); c != region_added_connections.end(); ++c) {
281 EditorThaw(); /* Emit Signal */
284 if (working_on_selection) {
285 // IFF we were working on selected regions, try to reinstate the other region selections that existed before the freeze/thaw.
287 RegionSelectionAfterSplit rsas = Config->get_region_selection_after_split();
288 /* There are three classes of regions that we might want selected after
289 splitting selected regions:
290 - regions selected before the split operation, and unaffected by it
291 - newly-created regions before the split
292 - newly-created regions after the split
295 if (rsas & Existing) {
296 // region selections that existed before the split.
297 selection->add ( pre_selected_regions );
300 for (RegionSelection::iterator ri = latest_regionviews.begin(); ri != latest_regionviews.end(); ri++) {
301 if ((*ri)->region()->position() < where.frame) {
302 // new regions created before the split
303 if (rsas & NewlyCreatedLeft) {
304 selection->add (*ri);
307 // new regions created after the split
308 if (rsas & NewlyCreatedRight) {
309 selection->add (*ri);
314 if( working_on_selection ) {
315 selection->add (latest_regionviews); //these are the new regions created after the split
319 commit_reversible_command ();
322 /** Move one extreme of the current range selection. If more than one range is selected,
323 * the start of the earliest range or the end of the latest range is moved.
325 * @param move_end true to move the end of the current range selection, false to move
327 * @param next true to move the extreme to the next region boundary, false to move to
331 Editor::move_range_selection_start_or_end_to_region_boundary (bool move_end, bool next)
333 if (selection->time.start() == selection->time.end_frame()) {
337 framepos_t start = selection->time.start ();
338 framepos_t end = selection->time.end_frame ();
340 /* the position of the thing we may move */
341 framepos_t pos = move_end ? end : start;
342 int dir = next ? 1 : -1;
344 /* so we don't find the current region again */
345 if (dir > 0 || pos > 0) {
349 framepos_t const target = get_region_boundary (pos, dir, true, false);
364 begin_reversible_selection_op (_("alter selection"));
365 selection->set_preserving_all_ranges (start, end);
366 commit_reversible_selection_op ();
370 Editor::nudge_forward_release (GdkEventButton* ev)
372 if (ev->state & Keyboard::PrimaryModifier) {
373 nudge_forward (false, true);
375 nudge_forward (false, false);
381 Editor::nudge_backward_release (GdkEventButton* ev)
383 if (ev->state & Keyboard::PrimaryModifier) {
384 nudge_backward (false, true);
386 nudge_backward (false, false);
393 Editor::nudge_forward (bool next, bool force_playhead)
396 framepos_t next_distance;
402 RegionSelection rs = get_regions_from_selection_and_entered ();
404 if (!force_playhead && !rs.empty()) {
406 begin_reversible_command (_("nudge regions forward"));
408 for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
409 boost::shared_ptr<Region> r ((*i)->region());
411 distance = get_nudge_distance (r->position(), next_distance);
414 distance = next_distance;
418 r->set_position (r->position() + distance);
419 _session->add_command (new StatefulDiffCommand (r));
422 commit_reversible_command ();
425 } else if (!force_playhead && !selection->markers.empty()) {
428 bool in_command = false;
429 const int32_t divisions = get_grid_music_divisions (0);
431 for (MarkerSelection::iterator i = selection->markers.begin(); i != selection->markers.end(); ++i) {
433 Location* loc = find_location_from_marker ((*i), is_start);
437 XMLNode& before (loc->get_state());
440 distance = get_nudge_distance (loc->start(), next_distance);
442 distance = next_distance;
444 if (max_framepos - distance > loc->start() + loc->length()) {
445 loc->set_start (loc->start() + distance, false, true, divisions);
447 loc->set_start (max_framepos - loc->length(), false, true, divisions);
450 distance = get_nudge_distance (loc->end(), next_distance);
452 distance = next_distance;
454 if (max_framepos - distance > loc->end()) {
455 loc->set_end (loc->end() + distance, false, true, divisions);
457 loc->set_end (max_framepos, false, true, divisions);
459 if (loc->is_session_range()) {
460 _session->set_end_is_free (false);
464 begin_reversible_command (_("nudge location forward"));
467 XMLNode& after (loc->get_state());
468 _session->add_command (new MementoCommand<Location>(*loc, &before, &after));
473 commit_reversible_command ();
476 distance = get_nudge_distance (playhead_cursor->current_frame (), next_distance);
477 _session->request_locate (playhead_cursor->current_frame () + distance);
482 Editor::nudge_backward (bool next, bool force_playhead)
485 framepos_t next_distance;
491 RegionSelection rs = get_regions_from_selection_and_entered ();
493 if (!force_playhead && !rs.empty()) {
495 begin_reversible_command (_("nudge regions backward"));
497 for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
498 boost::shared_ptr<Region> r ((*i)->region());
500 distance = get_nudge_distance (r->position(), next_distance);
503 distance = next_distance;
508 if (r->position() > distance) {
509 r->set_position (r->position() - distance);
513 _session->add_command (new StatefulDiffCommand (r));
516 commit_reversible_command ();
518 } else if (!force_playhead && !selection->markers.empty()) {
521 bool in_command = false;
523 for (MarkerSelection::iterator i = selection->markers.begin(); i != selection->markers.end(); ++i) {
525 Location* loc = find_location_from_marker ((*i), is_start);
529 XMLNode& before (loc->get_state());
532 distance = get_nudge_distance (loc->start(), next_distance);
534 distance = next_distance;
536 if (distance < loc->start()) {
537 loc->set_start (loc->start() - distance, false, true, get_grid_music_divisions(0));
539 loc->set_start (0, false, true, get_grid_music_divisions(0));
542 distance = get_nudge_distance (loc->end(), next_distance);
545 distance = next_distance;
548 if (distance < loc->end() - loc->length()) {
549 loc->set_end (loc->end() - distance, false, true, get_grid_music_divisions(0));
551 loc->set_end (loc->length(), false, true, get_grid_music_divisions(0));
553 if (loc->is_session_range()) {
554 _session->set_end_is_free (false);
558 begin_reversible_command (_("nudge location forward"));
561 XMLNode& after (loc->get_state());
562 _session->add_command (new MementoCommand<Location>(*loc, &before, &after));
566 commit_reversible_command ();
571 distance = get_nudge_distance (playhead_cursor->current_frame (), next_distance);
573 if (playhead_cursor->current_frame () > distance) {
574 _session->request_locate (playhead_cursor->current_frame () - distance);
576 _session->goto_start();
582 Editor::nudge_forward_capture_offset ()
584 RegionSelection rs = get_regions_from_selection_and_entered ();
586 if (!_session || rs.empty()) {
590 begin_reversible_command (_("nudge forward"));
592 framepos_t const distance = _session->worst_output_latency();
594 for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
595 boost::shared_ptr<Region> r ((*i)->region());
598 r->set_position (r->position() + distance);
599 _session->add_command(new StatefulDiffCommand (r));
602 commit_reversible_command ();
606 Editor::nudge_backward_capture_offset ()
608 RegionSelection rs = get_regions_from_selection_and_entered ();
610 if (!_session || rs.empty()) {
614 begin_reversible_command (_("nudge backward"));
616 framepos_t const distance = _session->worst_output_latency();
618 for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
619 boost::shared_ptr<Region> r ((*i)->region());
623 if (r->position() > distance) {
624 r->set_position (r->position() - distance);
628 _session->add_command(new StatefulDiffCommand (r));
631 commit_reversible_command ();
634 struct RegionSelectionPositionSorter {
635 bool operator() (RegionView* a, RegionView* b) {
636 return a->region()->position() < b->region()->position();
641 Editor::sequence_regions ()
644 framepos_t r_end_prev;
652 RegionSelection rs = get_regions_from_selection_and_entered ();
653 rs.sort(RegionSelectionPositionSorter());
657 bool in_command = false;
659 for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
660 boost::shared_ptr<Region> r ((*i)->region());
668 if(r->position_locked())
675 r->set_position(r_end_prev);
679 begin_reversible_command (_("sequence regions"));
682 _session->add_command (new StatefulDiffCommand (r));
684 r_end=r->position() + r->length();
690 commit_reversible_command ();
699 Editor::move_to_start ()
701 _session->goto_start ();
705 Editor::move_to_end ()
708 _session->request_locate (_session->current_end_frame());
712 Editor::build_region_boundary_cache ()
715 vector<RegionPoint> interesting_points;
716 boost::shared_ptr<Region> r;
717 TrackViewList tracks;
720 region_boundary_cache.clear ();
726 bool maybe_first_frame = false;
728 switch (_snap_type) {
729 case SnapToRegionStart:
730 interesting_points.push_back (Start);
731 maybe_first_frame = true;
733 case SnapToRegionEnd:
734 interesting_points.push_back (End);
736 case SnapToRegionSync:
737 interesting_points.push_back (SyncPoint);
739 case SnapToRegionBoundary:
740 interesting_points.push_back (Start);
741 interesting_points.push_back (End);
742 maybe_first_frame = true;
745 fatal << string_compose (_("build_region_boundary_cache called with snap_type = %1"), _snap_type) << endmsg;
746 abort(); /*NOTREACHED*/
750 TimeAxisView *ontrack = 0;
753 if (!selection->tracks.empty()) {
754 tlist = selection->tracks.filter_to_unique_playlists ();
756 tlist = track_views.filter_to_unique_playlists ();
759 if (maybe_first_frame) {
760 TrackViewList::const_iterator i;
761 for (i = tlist.begin(); i != tlist.end(); ++i) {
762 boost::shared_ptr<Playlist> pl = (*i)->playlist();
763 if (pl && pl->count_regions_at (0)) {
764 region_boundary_cache.push_back (0);
770 while (pos < _session->current_end_frame() && !at_end) {
773 framepos_t lpos = max_framepos;
775 for (vector<RegionPoint>::iterator p = interesting_points.begin(); p != interesting_points.end(); ++p) {
777 if ((r = find_next_region (pos, *p, 1, tlist, &ontrack)) == 0) {
778 if (*p == interesting_points.back()) {
781 /* move to next point type */
787 rpos = r->first_frame();
791 rpos = r->last_frame();
795 rpos = r->sync_position ();
806 /* prevent duplicates, but we don't use set<> because we want to be able
810 vector<framepos_t>::iterator ri;
812 for (ri = region_boundary_cache.begin(); ri != region_boundary_cache.end(); ++ri) {
818 if (ri == region_boundary_cache.end()) {
819 region_boundary_cache.push_back (rpos);
826 /* finally sort to be sure that the order is correct */
828 sort (region_boundary_cache.begin(), region_boundary_cache.end());
831 boost::shared_ptr<Region>
832 Editor::find_next_region (framepos_t frame, RegionPoint point, int32_t dir, TrackViewList& tracks, TimeAxisView **ontrack)
834 TrackViewList::iterator i;
835 framepos_t closest = max_framepos;
836 boost::shared_ptr<Region> ret;
839 framepos_t track_frame;
840 RouteTimeAxisView *rtav;
842 for (i = tracks.begin(); i != tracks.end(); ++i) {
845 boost::shared_ptr<Region> r;
849 if ((r = (*i)->find_next_region (track_frame, point, dir)) == 0) {
855 rpos = r->first_frame ();
859 rpos = r->last_frame ();
863 rpos = r->sync_position ();
868 distance = rpos - frame;
870 distance = frame - rpos;
873 if (distance < closest) {
885 Editor::find_next_region_boundary (framepos_t pos, int32_t dir, const TrackViewList& tracks)
887 framecnt_t distance = max_framepos;
888 framepos_t current_nearest = -1;
890 for (TrackViewList::const_iterator i = tracks.begin(); i != tracks.end(); ++i) {
891 framepos_t contender;
894 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (*i);
900 if ((contender = rtv->find_next_region_boundary (pos, dir)) < 0) {
904 d = ::llabs (pos - contender);
907 current_nearest = contender;
912 return current_nearest;
916 Editor::get_region_boundary (framepos_t pos, int32_t dir, bool with_selection, bool only_onscreen)
921 if (with_selection && Config->get_region_boundaries_from_selected_tracks()) {
923 if (!selection->tracks.empty()) {
925 target = find_next_region_boundary (pos, dir, selection->tracks);
929 if (only_onscreen || Config->get_region_boundaries_from_onscreen_tracks()) {
930 get_onscreen_tracks (tvl);
931 target = find_next_region_boundary (pos, dir, tvl);
933 target = find_next_region_boundary (pos, dir, track_views);
939 if (only_onscreen || Config->get_region_boundaries_from_onscreen_tracks()) {
940 get_onscreen_tracks (tvl);
941 target = find_next_region_boundary (pos, dir, tvl);
943 target = find_next_region_boundary (pos, dir, track_views);
951 Editor::cursor_to_region_boundary (bool with_selection, int32_t dir)
953 framepos_t pos = playhead_cursor->current_frame ();
960 // so we don't find the current region again..
961 if (dir > 0 || pos > 0) {
965 if ((target = get_region_boundary (pos, dir, with_selection, false)) < 0) {
969 _session->request_locate (target);
973 Editor::cursor_to_next_region_boundary (bool with_selection)
975 cursor_to_region_boundary (with_selection, 1);
979 Editor::cursor_to_previous_region_boundary (bool with_selection)
981 cursor_to_region_boundary (with_selection, -1);
985 Editor::cursor_to_region_point (EditorCursor* cursor, RegionPoint point, int32_t dir)
987 boost::shared_ptr<Region> r;
988 framepos_t pos = cursor->current_frame ();
994 TimeAxisView *ontrack = 0;
996 // so we don't find the current region again..
1000 if (!selection->tracks.empty()) {
1002 r = find_next_region (pos, point, dir, selection->tracks, &ontrack);
1004 } else if (clicked_axisview) {
1007 t.push_back (clicked_axisview);
1009 r = find_next_region (pos, point, dir, t, &ontrack);
1013 r = find_next_region (pos, point, dir, track_views, &ontrack);
1022 pos = r->first_frame ();
1026 pos = r->last_frame ();
1030 pos = r->sync_position ();
1034 if (cursor == playhead_cursor) {
1035 _session->request_locate (pos);
1037 cursor->set_position (pos);
1042 Editor::cursor_to_next_region_point (EditorCursor* cursor, RegionPoint point)
1044 cursor_to_region_point (cursor, point, 1);
1048 Editor::cursor_to_previous_region_point (EditorCursor* cursor, RegionPoint point)
1050 cursor_to_region_point (cursor, point, -1);
1054 Editor::cursor_to_selection_start (EditorCursor *cursor)
1058 switch (mouse_mode) {
1060 if (!selection->regions.empty()) {
1061 pos = selection->regions.start();
1066 if (!selection->time.empty()) {
1067 pos = selection->time.start ();
1075 if (cursor == playhead_cursor) {
1076 _session->request_locate (pos);
1078 cursor->set_position (pos);
1083 Editor::cursor_to_selection_end (EditorCursor *cursor)
1087 switch (mouse_mode) {
1089 if (!selection->regions.empty()) {
1090 pos = selection->regions.end_frame();
1095 if (!selection->time.empty()) {
1096 pos = selection->time.end_frame ();
1104 if (cursor == playhead_cursor) {
1105 _session->request_locate (pos);
1107 cursor->set_position (pos);
1112 Editor::selected_marker_to_region_boundary (bool with_selection, int32_t dir)
1122 if (selection->markers.empty()) {
1126 if (!mouse_frame (mouse, ignored)) {
1130 add_location_mark (mouse);
1133 if ((loc = find_location_from_marker (selection->markers.front(), ignored)) == 0) {
1137 framepos_t pos = loc->start();
1139 // so we don't find the current region again..
1140 if (dir > 0 || pos > 0) {
1144 if ((target = get_region_boundary (pos, dir, with_selection, false)) < 0) {
1148 loc->move_to (target, 0);
1152 Editor::selected_marker_to_next_region_boundary (bool with_selection)
1154 selected_marker_to_region_boundary (with_selection, 1);
1158 Editor::selected_marker_to_previous_region_boundary (bool with_selection)
1160 selected_marker_to_region_boundary (with_selection, -1);
1164 Editor::selected_marker_to_region_point (RegionPoint point, int32_t dir)
1166 boost::shared_ptr<Region> r;
1171 if (!_session || selection->markers.empty()) {
1175 if ((loc = find_location_from_marker (selection->markers.front(), ignored)) == 0) {
1179 TimeAxisView *ontrack = 0;
1183 // so we don't find the current region again..
1187 if (!selection->tracks.empty()) {
1189 r = find_next_region (pos, point, dir, selection->tracks, &ontrack);
1193 r = find_next_region (pos, point, dir, track_views, &ontrack);
1202 pos = r->first_frame ();
1206 pos = r->last_frame ();
1210 pos = r->adjust_to_sync (r->first_frame());
1214 loc->move_to (pos, 0);
1218 Editor::selected_marker_to_next_region_point (RegionPoint point)
1220 selected_marker_to_region_point (point, 1);
1224 Editor::selected_marker_to_previous_region_point (RegionPoint point)
1226 selected_marker_to_region_point (point, -1);
1230 Editor::selected_marker_to_selection_start ()
1236 if (!_session || selection->markers.empty()) {
1240 if ((loc = find_location_from_marker (selection->markers.front(), ignored)) == 0) {
1244 switch (mouse_mode) {
1246 if (!selection->regions.empty()) {
1247 pos = selection->regions.start();
1252 if (!selection->time.empty()) {
1253 pos = selection->time.start ();
1261 loc->move_to (pos, 0);
1265 Editor::selected_marker_to_selection_end ()
1271 if (!_session || selection->markers.empty()) {
1275 if ((loc = find_location_from_marker (selection->markers.front(), ignored)) == 0) {
1279 switch (mouse_mode) {
1281 if (!selection->regions.empty()) {
1282 pos = selection->regions.end_frame();
1287 if (!selection->time.empty()) {
1288 pos = selection->time.end_frame ();
1296 loc->move_to (pos, 0);
1300 Editor::scroll_playhead (bool forward)
1302 framepos_t pos = playhead_cursor->current_frame ();
1303 framecnt_t delta = (framecnt_t) floor (current_page_samples() / 0.8);
1306 if (pos == max_framepos) {
1310 if (pos < max_framepos - delta) {
1329 _session->request_locate (pos);
1333 Editor::cursor_align (bool playhead_to_edit)
1339 if (playhead_to_edit) {
1341 if (selection->markers.empty()) {
1345 _session->request_locate (selection->markers.front()->position(), _session->transport_rolling());
1348 const int32_t divisions = get_grid_music_divisions (0);
1349 /* move selected markers to playhead */
1351 for (MarkerSelection::iterator i = selection->markers.begin(); i != selection->markers.end(); ++i) {
1354 Location* loc = find_location_from_marker (*i, ignored);
1356 if (loc->is_mark()) {
1357 loc->set_start (playhead_cursor->current_frame (), false, true, divisions);
1359 loc->set (playhead_cursor->current_frame (),
1360 playhead_cursor->current_frame () + loc->length(), true, divisions);
1367 Editor::scroll_backward (float pages)
1369 framepos_t const one_page = (framepos_t) rint (_visible_canvas_width * samples_per_pixel);
1370 framepos_t const cnt = (framepos_t) floor (pages * one_page);
1373 if (leftmost_frame < cnt) {
1376 frame = leftmost_frame - cnt;
1379 reset_x_origin (frame);
1383 Editor::scroll_forward (float pages)
1385 framepos_t const one_page = (framepos_t) rint (_visible_canvas_width * samples_per_pixel);
1386 framepos_t const cnt = (framepos_t) floor (pages * one_page);
1389 if (max_framepos - cnt < leftmost_frame) {
1390 frame = max_framepos - cnt;
1392 frame = leftmost_frame + cnt;
1395 reset_x_origin (frame);
1399 Editor::scroll_tracks_down ()
1401 double vert_value = vertical_adjustment.get_value() + vertical_adjustment.get_page_size();
1402 if (vert_value > vertical_adjustment.get_upper() - _visible_canvas_height) {
1403 vert_value = vertical_adjustment.get_upper() - _visible_canvas_height;
1406 vertical_adjustment.set_value (vert_value);
1410 Editor::scroll_tracks_up ()
1412 vertical_adjustment.set_value (vertical_adjustment.get_value() - vertical_adjustment.get_page_size());
1416 Editor::scroll_tracks_down_line ()
1418 double vert_value = vertical_adjustment.get_value() + 60;
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_line ()
1430 reset_y_origin (vertical_adjustment.get_value() - 60);
1434 Editor::select_topmost_track ()
1436 const double top_of_trackviews = vertical_adjustment.get_value();
1437 for (TrackViewList::iterator t = track_views.begin(); t != track_views.end(); ++t) {
1438 if ((*t)->hidden()) {
1441 std::pair<TimeAxisView*,double> res = (*t)->covers_y_position (top_of_trackviews);
1443 selection->set (*t);
1450 Editor::scroll_down_one_track (bool skip_child_views)
1452 TrackViewList::reverse_iterator next = track_views.rend();
1453 const double top_of_trackviews = vertical_adjustment.get_value();
1455 for (TrackViewList::reverse_iterator t = track_views.rbegin(); t != track_views.rend(); ++t) {
1456 if ((*t)->hidden()) {
1460 /* If this is the upper-most visible trackview, we want to display
1461 * the one above it (next)
1463 * Note that covers_y_position() is recursive and includes child views
1465 std::pair<TimeAxisView*,double> res = (*t)->covers_y_position (top_of_trackviews);
1468 if (skip_child_views) {
1471 /* automation lane (one level, non-recursive)
1473 * - if no automation lane exists -> move to next tack
1474 * - if the first (here: bottom-most) matches -> move to next tack
1475 * - if no y-axis match is found -> the current track is at the top
1476 * -> move to last (here: top-most) automation lane
1478 TimeAxisView::Children kids = (*t)->get_child_list();
1479 TimeAxisView::Children::reverse_iterator nkid = kids.rend();
1481 for (TimeAxisView::Children::reverse_iterator ci = kids.rbegin(); ci != kids.rend(); ++ci) {
1482 if ((*ci)->hidden()) {
1486 std::pair<TimeAxisView*,double> dev;
1487 dev = (*ci)->covers_y_position (top_of_trackviews);
1489 /* some automation lane is currently at the top */
1490 if (ci == kids.rbegin()) {
1491 /* first (bottom-most) autmation lane is at the top.
1492 * -> move to next track
1501 if (nkid != kids.rend()) {
1502 ensure_time_axis_view_is_visible (**nkid, true);
1510 /* move to the track below the first one that covers the */
1512 if (next != track_views.rend()) {
1513 ensure_time_axis_view_is_visible (**next, true);
1521 Editor::scroll_up_one_track (bool skip_child_views)
1523 TrackViewList::iterator prev = track_views.end();
1524 double top_of_trackviews = vertical_adjustment.get_value ();
1526 for (TrackViewList::iterator t = track_views.begin(); t != track_views.end(); ++t) {
1528 if ((*t)->hidden()) {
1532 /* find the trackview at the top of the trackview group
1534 * Note that covers_y_position() is recursive and includes child views
1536 std::pair<TimeAxisView*,double> res = (*t)->covers_y_position (top_of_trackviews);
1539 if (skip_child_views) {
1542 /* automation lane (one level, non-recursive)
1544 * - if no automation lane exists -> move to prev tack
1545 * - if no y-axis match is found -> the current track is at the top -> move to prev track
1546 * (actually last automation lane of previous track, see below)
1547 * - if first (top-most) lane is at the top -> move to this track
1548 * - else move up one lane
1550 TimeAxisView::Children kids = (*t)->get_child_list();
1551 TimeAxisView::Children::iterator pkid = kids.end();
1553 for (TimeAxisView::Children::iterator ci = kids.begin(); ci != kids.end(); ++ci) {
1554 if ((*ci)->hidden()) {
1558 std::pair<TimeAxisView*,double> dev;
1559 dev = (*ci)->covers_y_position (top_of_trackviews);
1561 /* some automation lane is currently at the top */
1562 if (ci == kids.begin()) {
1563 /* first (top-most) autmation lane is at the top.
1564 * jump directly to this track's top
1566 ensure_time_axis_view_is_visible (**t, true);
1569 else if (pkid != kids.end()) {
1570 /* some other automation lane is at the top.
1571 * move up to prev automation lane.
1573 ensure_time_axis_view_is_visible (**pkid, true);
1576 assert(0); // not reached
1587 if (prev != track_views.end()) {
1588 // move to bottom-most automation-lane of the previous track
1589 TimeAxisView::Children kids = (*prev)->get_child_list();
1590 TimeAxisView::Children::reverse_iterator pkid = kids.rend();
1591 if (!skip_child_views) {
1592 // find the last visible lane
1593 for (TimeAxisView::Children::reverse_iterator ci = kids.rbegin(); ci != kids.rend(); ++ci) {
1594 if (!(*ci)->hidden()) {
1600 if (pkid != kids.rend()) {
1601 ensure_time_axis_view_is_visible (**pkid, true);
1603 ensure_time_axis_view_is_visible (**prev, true);
1612 Editor::scroll_left_step ()
1614 framepos_t xdelta = (current_page_samples() / 8);
1616 if (leftmost_frame > xdelta) {
1617 reset_x_origin (leftmost_frame - xdelta);
1625 Editor::scroll_right_step ()
1627 framepos_t xdelta = (current_page_samples() / 8);
1629 if (max_framepos - xdelta > leftmost_frame) {
1630 reset_x_origin (leftmost_frame + xdelta);
1632 reset_x_origin (max_framepos - current_page_samples());
1637 Editor::scroll_left_half_page ()
1639 framepos_t xdelta = (current_page_samples() / 2);
1640 if (leftmost_frame > xdelta) {
1641 reset_x_origin (leftmost_frame - xdelta);
1648 Editor::scroll_right_half_page ()
1650 framepos_t xdelta = (current_page_samples() / 2);
1651 if (max_framepos - xdelta > leftmost_frame) {
1652 reset_x_origin (leftmost_frame + xdelta);
1654 reset_x_origin (max_framepos - current_page_samples());
1661 Editor::tav_zoom_step (bool coarser)
1663 DisplaySuspender ds;
1667 if (selection->tracks.empty()) {
1670 ts = &selection->tracks;
1673 for (TrackViewList::iterator i = ts->begin(); i != ts->end(); ++i) {
1674 TimeAxisView *tv = (static_cast<TimeAxisView*>(*i));
1675 tv->step_height (coarser);
1680 Editor::tav_zoom_smooth (bool coarser, bool force_all)
1682 DisplaySuspender ds;
1686 if (selection->tracks.empty() || force_all) {
1689 ts = &selection->tracks;
1692 for (TrackViewList::iterator i = ts->begin(); i != ts->end(); ++i) {
1693 TimeAxisView *tv = (static_cast<TimeAxisView*>(*i));
1694 uint32_t h = tv->current_height ();
1699 if (h >= TimeAxisView::preset_height (HeightSmall)) {
1704 tv->set_height (h + 5);
1710 Editor::temporal_zoom_step_mouse_focus_scale (bool zoom_out, double scale)
1712 Editing::ZoomFocus temp_focus = zoom_focus;
1713 zoom_focus = Editing::ZoomFocusMouse;
1714 temporal_zoom_step_scale (zoom_out, scale);
1715 zoom_focus = temp_focus;
1719 Editor::temporal_zoom_step_mouse_focus (bool zoom_out)
1721 temporal_zoom_step_mouse_focus_scale (zoom_out, 2.0);
1725 Editor::temporal_zoom_step (bool zoom_out)
1727 temporal_zoom_step_scale (zoom_out, 2.0);
1731 Editor::temporal_zoom_step_scale (bool zoom_out, double scale)
1733 ENSURE_GUI_THREAD (*this, &Editor::temporal_zoom_step, zoom_out, scale)
1735 framecnt_t nspp = samples_per_pixel;
1739 if (nspp == samples_per_pixel) {
1744 if (nspp == samples_per_pixel) {
1749 //zoom-behavior-tweaks
1750 //limit our maximum zoom to the session gui extents value
1751 std::pair<framepos_t, framepos_t> ext = session_gui_extents();
1752 framecnt_t session_extents_pp = ( ext.second - ext.first ) / _visible_canvas_width;
1753 if (nspp > session_extents_pp)
1754 nspp = session_extents_pp;
1756 temporal_zoom (nspp);
1760 Editor::temporal_zoom (framecnt_t fpp)
1766 framepos_t current_page = current_page_samples();
1767 framepos_t current_leftmost = leftmost_frame;
1768 framepos_t current_rightmost;
1769 framepos_t current_center;
1770 framepos_t new_page_size;
1771 framepos_t half_page_size;
1772 framepos_t leftmost_after_zoom = 0;
1774 bool in_track_canvas;
1775 bool use_mouse_frame = true;
1779 if (fpp == samples_per_pixel) {
1783 // Imposing an arbitrary limit to zoom out as too much zoom out produces
1784 // segfaults for lack of memory. If somebody decides this is not high enough I
1785 // believe it can be raisen to higher values but some limit must be in place.
1787 // This constant represents 1 day @ 48kHz on a 1600 pixel wide display
1788 // all of which is used for the editor track displays. The whole day
1789 // would be 4147200000 samples, so 2592000 samples per pixel.
1791 nfpp = min (fpp, (framecnt_t) 2592000);
1792 nfpp = max ((framecnt_t) 1, nfpp);
1794 new_page_size = (framepos_t) floor (_visible_canvas_width * nfpp);
1795 half_page_size = new_page_size / 2;
1797 switch (zoom_focus) {
1799 leftmost_after_zoom = current_leftmost;
1802 case ZoomFocusRight:
1803 current_rightmost = leftmost_frame + current_page;
1804 if (current_rightmost < new_page_size) {
1805 leftmost_after_zoom = 0;
1807 leftmost_after_zoom = current_rightmost - new_page_size;
1811 case ZoomFocusCenter:
1812 current_center = current_leftmost + (current_page/2);
1813 if (current_center < half_page_size) {
1814 leftmost_after_zoom = 0;
1816 leftmost_after_zoom = current_center - half_page_size;
1820 case ZoomFocusPlayhead:
1821 /* centre playhead */
1822 l = playhead_cursor->current_frame () - (new_page_size * 0.5);
1825 leftmost_after_zoom = 0;
1826 } else if (l > max_framepos) {
1827 leftmost_after_zoom = max_framepos - new_page_size;
1829 leftmost_after_zoom = (framepos_t) l;
1833 case ZoomFocusMouse:
1834 /* try to keep the mouse over the same point in the display */
1836 if (_drags->active()) {
1837 where = _drags->current_pointer_frame ();
1838 } else if (!mouse_frame (where, in_track_canvas)) {
1839 use_mouse_frame = false;
1842 if (use_mouse_frame) {
1843 l = - ((new_page_size * ((where - current_leftmost)/(double)current_page)) - where);
1846 leftmost_after_zoom = 0;
1847 } else if (l > max_framepos) {
1848 leftmost_after_zoom = max_framepos - new_page_size;
1850 leftmost_after_zoom = (framepos_t) l;
1853 /* use playhead instead */
1854 where = playhead_cursor->current_frame ();
1856 if (where < half_page_size) {
1857 leftmost_after_zoom = 0;
1859 leftmost_after_zoom = where - half_page_size;
1865 /* try to keep the edit point in the same place */
1866 where = get_preferred_edit_position ();
1870 double l = - ((new_page_size * ((where - current_leftmost)/(double)current_page)) - where);
1873 leftmost_after_zoom = 0;
1874 } else if (l > max_framepos) {
1875 leftmost_after_zoom = max_framepos - new_page_size;
1877 leftmost_after_zoom = (framepos_t) l;
1881 /* edit point not defined */
1888 // leftmost_after_zoom = min (leftmost_after_zoom, _session->current_end_frame());
1890 reposition_and_zoom (leftmost_after_zoom, nfpp);
1894 Editor::calc_extra_zoom_edges(framepos_t &start, framepos_t &end)
1896 /* this func helps make sure we leave a little space
1897 at each end of the editor so that the zoom doesn't fit the region
1898 precisely to the screen.
1901 GdkScreen* screen = gdk_screen_get_default ();
1902 const gint pixwidth = gdk_screen_get_width (screen);
1903 const gint mmwidth = gdk_screen_get_width_mm (screen);
1904 const double pix_per_mm = (double) pixwidth/ (double) mmwidth;
1905 const double one_centimeter_in_pixels = pix_per_mm * 10.0;
1907 const framepos_t range = end - start;
1908 const framecnt_t new_fpp = (framecnt_t) ceil ((double) range / (double) _visible_canvas_width);
1909 const framepos_t extra_samples = (framepos_t) floor (one_centimeter_in_pixels * new_fpp);
1911 if (start > extra_samples) {
1912 start -= extra_samples;
1917 if (max_framepos - extra_samples > end) {
1918 end += extra_samples;
1925 Editor::get_selection_extents (framepos_t &start, framepos_t &end) const
1927 start = max_framepos;
1931 //ToDo: if notes are selected, set extents to that selection
1933 //ToDo: if control points are selected, set extents to that selection
1935 if ( !selection->regions.empty() ) {
1936 RegionSelection rs = get_regions_from_selection_and_entered ();
1938 for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
1940 if ((*i)->region()->position() < start) {
1941 start = (*i)->region()->position();
1944 if ((*i)->region()->last_frame() + 1 > end) {
1945 end = (*i)->region()->last_frame() + 1;
1949 } else if (!selection->time.empty()) {
1950 start = selection->time.start();
1951 end = selection->time.end_frame();
1953 ret = false; //no selection found
1956 if ((start == 0 && end == 0) || end < start) {
1965 Editor::temporal_zoom_selection (Editing::ZoomAxis axes)
1967 if (!selection) return;
1969 //ToDo: if notes are selected, zoom to that
1971 //ToDo: if control points are selected, zoom to that
1973 if (axes == Horizontal || axes == Both) {
1975 framepos_t start, end;
1976 if (get_selection_extents (start, end)) {
1977 calc_extra_zoom_edges (start, end);
1978 temporal_zoom_by_frame (start, end);
1982 if (axes == Vertical || axes == Both) {
1988 Editor::temporal_zoom_session ()
1990 ENSURE_GUI_THREAD (*this, &Editor::temporal_zoom_session)
1993 framecnt_t start = _session->current_start_frame();
1994 framecnt_t end = _session->current_end_frame();
1996 if (_session->actively_recording () ) {
1997 framepos_t cur = playhead_cursor->current_frame ();
1999 /* recording beyond the end marker; zoom out
2000 * by 5 seconds more so that if 'follow
2001 * playhead' is active we don't immediately
2004 end = cur + _session->frame_rate() * 5;
2008 if ((start == 0 && end == 0) || end < start) {
2012 calc_extra_zoom_edges(start, end);
2014 temporal_zoom_by_frame (start, end);
2019 Editor::temporal_zoom_extents ()
2021 ENSURE_GUI_THREAD (*this, &Editor::temporal_zoom_extents)
2024 std::pair<framepos_t, framepos_t> ext = session_gui_extents( false ); //in this case we want to zoom to the extents explicitly; ignore the users prefs for extra padding
2026 framecnt_t start = ext.first;
2027 framecnt_t end = ext.second;
2029 if (_session->actively_recording () ) {
2030 framepos_t cur = playhead_cursor->current_frame ();
2032 /* recording beyond the end marker; zoom out
2033 * by 5 seconds more so that if 'follow
2034 * playhead' is active we don't immediately
2037 end = cur + _session->frame_rate() * 5;
2041 if ((start == 0 && end == 0) || end < start) {
2045 calc_extra_zoom_edges(start, end);
2047 temporal_zoom_by_frame (start, end);
2052 Editor::temporal_zoom_by_frame (framepos_t start, framepos_t end)
2054 if (!_session) return;
2056 if ((start == 0 && end == 0) || end < start) {
2060 framepos_t range = end - start;
2062 const framecnt_t new_fpp = (framecnt_t) ceil ((double) range / (double) _visible_canvas_width);
2064 framepos_t new_page = range;
2065 framepos_t middle = (framepos_t) floor ((double) start + ((double) range / 2.0f));
2066 framepos_t new_leftmost = (framepos_t) floor ((double) middle - ((double) new_page / 2.0f));
2068 if (new_leftmost > middle) {
2072 if (new_leftmost < 0) {
2076 reposition_and_zoom (new_leftmost, new_fpp);
2080 Editor::temporal_zoom_to_frame (bool coarser, framepos_t frame)
2086 framecnt_t range_before = frame - leftmost_frame;
2090 if (samples_per_pixel <= 1) {
2093 new_spp = samples_per_pixel + (samples_per_pixel/2);
2095 range_before += range_before/2;
2097 if (samples_per_pixel >= 1) {
2098 new_spp = samples_per_pixel - (samples_per_pixel/2);
2100 /* could bail out here since we cannot zoom any finer,
2101 but leave that to the equality test below
2103 new_spp = samples_per_pixel;
2106 range_before -= range_before/2;
2109 if (new_spp == samples_per_pixel) {
2113 /* zoom focus is automatically taken as @param frame when this
2117 framepos_t new_leftmost = frame - (framepos_t)range_before;
2119 if (new_leftmost > frame) {
2123 if (new_leftmost < 0) {
2127 reposition_and_zoom (new_leftmost, new_spp);
2132 Editor::choose_new_marker_name(string &name) {
2134 if (!UIConfiguration::instance().get_name_new_markers()) {
2135 /* don't prompt user for a new name */
2139 Prompter dialog (true);
2141 dialog.set_prompt (_("New Name:"));
2143 dialog.set_title (_("New Location Marker"));
2145 dialog.set_name ("MarkNameWindow");
2146 dialog.set_size_request (250, -1);
2147 dialog.set_position (Gtk::WIN_POS_MOUSE);
2149 dialog.add_button (Stock::OK, RESPONSE_ACCEPT);
2150 dialog.set_initial_text (name);
2154 switch (dialog.run ()) {
2155 case RESPONSE_ACCEPT:
2161 dialog.get_result(name);
2168 Editor::add_location_from_selection ()
2172 if (selection->time.empty()) {
2176 if (_session == 0 || clicked_axisview == 0) {
2180 framepos_t start = selection->time[clicked_selection].start;
2181 framepos_t end = selection->time[clicked_selection].end;
2183 _session->locations()->next_available_name(rangename,"selection");
2184 Location *location = new Location (*_session, start, end, rangename, Location::IsRangeMarker, get_grid_music_divisions(0));
2186 begin_reversible_command (_("add marker"));
2188 XMLNode &before = _session->locations()->get_state();
2189 _session->locations()->add (location, true);
2190 XMLNode &after = _session->locations()->get_state();
2191 _session->add_command(new MementoCommand<Locations>(*(_session->locations()), &before, &after));
2193 commit_reversible_command ();
2197 Editor::add_location_mark (framepos_t where)
2201 select_new_marker = true;
2203 _session->locations()->next_available_name(markername,"mark");
2204 if (!choose_new_marker_name(markername)) {
2207 Location *location = new Location (*_session, where, where, markername, Location::IsMark, get_grid_music_divisions (0));
2208 begin_reversible_command (_("add marker"));
2210 XMLNode &before = _session->locations()->get_state();
2211 _session->locations()->add (location, true);
2212 XMLNode &after = _session->locations()->get_state();
2213 _session->add_command(new MementoCommand<Locations>(*(_session->locations()), &before, &after));
2215 commit_reversible_command ();
2219 Editor::set_session_start_from_playhead ()
2225 if ((loc = _session->locations()->session_range_location()) == 0) { //should never happen
2226 _session->set_session_extents ( _session->audible_frame(), _session->audible_frame() );
2228 XMLNode &before = loc->get_state();
2230 _session->set_session_extents ( _session->audible_frame(), loc->end() );
2232 XMLNode &after = loc->get_state();
2234 begin_reversible_command (_("Set session start"));
2236 _session->add_command (new MementoCommand<Location>(*loc, &before, &after));
2238 commit_reversible_command ();
2243 Editor::set_session_end_from_playhead ()
2249 if ((loc = _session->locations()->session_range_location()) == 0) { //should never happen
2250 _session->set_session_extents ( _session->audible_frame(), _session->audible_frame() );
2252 XMLNode &before = loc->get_state();
2254 _session->set_session_extents ( loc->start(), _session->audible_frame() );
2256 XMLNode &after = loc->get_state();
2258 begin_reversible_command (_("Set session start"));
2260 _session->add_command (new MementoCommand<Location>(*loc, &before, &after));
2262 commit_reversible_command ();
2265 _session->set_end_is_free (false);
2270 Editor::toggle_location_at_playhead_cursor ()
2272 if (!do_remove_location_at_playhead_cursor())
2274 add_location_from_playhead_cursor();
2279 Editor::add_location_from_playhead_cursor ()
2281 add_location_mark (_session->audible_frame());
2285 Editor::do_remove_location_at_playhead_cursor ()
2287 bool removed = false;
2290 XMLNode &before = _session->locations()->get_state();
2292 //find location(s) at this time
2293 Locations::LocationList locs;
2294 _session->locations()->find_all_between (_session->audible_frame(), _session->audible_frame()+1, locs, Location::Flags(0));
2295 for (Locations::LocationList::iterator i = locs.begin(); i != locs.end(); ++i) {
2296 if ((*i)->is_mark()) {
2297 _session->locations()->remove (*i);
2304 begin_reversible_command (_("remove marker"));
2305 XMLNode &after = _session->locations()->get_state();
2306 _session->add_command(new MementoCommand<Locations>(*(_session->locations()), &before, &after));
2307 commit_reversible_command ();
2314 Editor::remove_location_at_playhead_cursor ()
2316 do_remove_location_at_playhead_cursor ();
2319 /** Add a range marker around each selected region */
2321 Editor::add_locations_from_region ()
2323 RegionSelection rs = get_regions_from_selection_and_entered ();
2328 bool commit = false;
2330 XMLNode &before = _session->locations()->get_state();
2332 for (RegionSelection::iterator i = rs.begin (); i != rs.end (); ++i) {
2334 boost::shared_ptr<Region> region = (*i)->region ();
2336 Location *location = new Location (*_session, region->position(), region->last_frame(), region->name(), Location::IsRangeMarker, 0);
2338 _session->locations()->add (location, true);
2343 begin_reversible_command (selection->regions.size () > 1 ? _("add markers") : _("add marker"));
2344 XMLNode &after = _session->locations()->get_state();
2345 _session->add_command (new MementoCommand<Locations>(*(_session->locations()), &before, &after));
2346 commit_reversible_command ();
2350 /** Add a single range marker around all selected regions */
2352 Editor::add_location_from_region ()
2354 RegionSelection rs = get_regions_from_selection_and_entered ();
2360 XMLNode &before = _session->locations()->get_state();
2364 if (rs.size() > 1) {
2365 _session->locations()->next_available_name(markername, "regions");
2367 RegionView* rv = *(rs.begin());
2368 boost::shared_ptr<Region> region = rv->region();
2369 markername = region->name();
2372 if (!choose_new_marker_name(markername)) {
2376 // single range spanning all selected
2377 Location *location = new Location (*_session, selection->regions.start(), selection->regions.end_frame(), markername, Location::IsRangeMarker, 0);
2378 _session->locations()->add (location, true);
2380 begin_reversible_command (_("add marker"));
2381 XMLNode &after = _session->locations()->get_state();
2382 _session->add_command (new MementoCommand<Locations>(*(_session->locations()), &before, &after));
2383 commit_reversible_command ();
2389 Editor::jump_forward_to_mark ()
2395 framepos_t pos = _session->locations()->first_mark_after (playhead_cursor->current_frame());
2401 _session->request_locate (pos, _session->transport_rolling());
2405 Editor::jump_backward_to_mark ()
2411 framepos_t pos = _session->locations()->first_mark_before (playhead_cursor->current_frame());
2413 //handle the case where we are rolling, and we're less than one-half second past the mark, we want to go to the prior mark...
2414 if ( _session->transport_rolling() ) {
2415 if ( (playhead_cursor->current_frame() - pos) < _session->frame_rate()/2 ) {
2416 framepos_t prior = _session->locations()->first_mark_before ( pos );
2425 _session->request_locate (pos, _session->transport_rolling());
2431 framepos_t const pos = _session->audible_frame ();
2434 _session->locations()->next_available_name (markername, "mark");
2436 if (!choose_new_marker_name (markername)) {
2440 _session->locations()->add (new Location (*_session, pos, 0, markername, Location::IsMark, 0), true);
2444 Editor::clear_markers ()
2447 begin_reversible_command (_("clear markers"));
2449 XMLNode &before = _session->locations()->get_state();
2450 _session->locations()->clear_markers ();
2451 XMLNode &after = _session->locations()->get_state();
2452 _session->add_command(new MementoCommand<Locations>(*(_session->locations()), &before, &after));
2454 commit_reversible_command ();
2459 Editor::clear_ranges ()
2462 begin_reversible_command (_("clear ranges"));
2464 XMLNode &before = _session->locations()->get_state();
2466 _session->locations()->clear_ranges ();
2468 XMLNode &after = _session->locations()->get_state();
2469 _session->add_command(new MementoCommand<Locations>(*(_session->locations()), &before, &after));
2471 commit_reversible_command ();
2476 Editor::clear_locations ()
2478 begin_reversible_command (_("clear locations"));
2480 XMLNode &before = _session->locations()->get_state();
2481 _session->locations()->clear ();
2482 XMLNode &after = _session->locations()->get_state();
2483 _session->add_command(new MementoCommand<Locations>(*(_session->locations()), &before, &after));
2485 commit_reversible_command ();
2489 Editor::unhide_markers ()
2491 for (LocationMarkerMap::iterator i = location_markers.begin(); i != location_markers.end(); ++i) {
2492 Location *l = (*i).first;
2493 if (l->is_hidden() && l->is_mark()) {
2494 l->set_hidden(false, this);
2500 Editor::unhide_ranges ()
2502 for (LocationMarkerMap::iterator i = location_markers.begin(); i != location_markers.end(); ++i) {
2503 Location *l = (*i).first;
2504 if (l->is_hidden() && l->is_range_marker()) {
2505 l->set_hidden(false, this);
2510 /* INSERT/REPLACE */
2513 Editor::insert_region_list_selection (float times)
2515 RouteTimeAxisView *tv = 0;
2516 boost::shared_ptr<Playlist> playlist;
2518 if (clicked_routeview != 0) {
2519 tv = clicked_routeview;
2520 } else if (!selection->tracks.empty()) {
2521 if ((tv = dynamic_cast<RouteTimeAxisView*>(selection->tracks.front())) == 0) {
2524 } else if (entered_track != 0) {
2525 if ((tv = dynamic_cast<RouteTimeAxisView*>(entered_track)) == 0) {
2532 if ((playlist = tv->playlist()) == 0) {
2536 boost::shared_ptr<Region> region = _regions->get_single_selection ();
2541 begin_reversible_command (_("insert region"));
2542 playlist->clear_changes ();
2543 playlist->add_region ((RegionFactory::create (region, true)), get_preferred_edit_position(), times);
2544 if (Config->get_edit_mode() == Ripple)
2545 playlist->ripple (get_preferred_edit_position(), region->length() * times, boost::shared_ptr<Region>());
2547 _session->add_command(new StatefulDiffCommand (playlist));
2548 commit_reversible_command ();
2551 /* BUILT-IN EFFECTS */
2554 Editor::reverse_selection ()
2559 /* GAIN ENVELOPE EDITING */
2562 Editor::edit_envelope ()
2569 Editor::transition_to_rolling (bool fwd)
2575 if (_session->config.get_external_sync()) {
2576 switch (Config->get_sync_source()) {
2580 /* transport controlled by the master */
2585 if (_session->is_auditioning()) {
2586 _session->cancel_audition ();
2590 _session->request_transport_speed (fwd ? 1.0f : -1.0f);
2594 Editor::play_from_start ()
2596 _session->request_locate (_session->current_start_frame(), true);
2600 Editor::play_from_edit_point ()
2602 _session->request_locate (get_preferred_edit_position(), true);
2606 Editor::play_from_edit_point_and_return ()
2608 framepos_t start_frame;
2609 framepos_t return_frame;
2611 start_frame = get_preferred_edit_position ( EDIT_IGNORE_PHEAD );
2613 if (_session->transport_rolling()) {
2614 _session->request_locate (start_frame, false);
2618 /* don't reset the return frame if its already set */
2620 if ((return_frame = _session->requested_return_frame()) < 0) {
2621 return_frame = _session->audible_frame();
2624 if (start_frame >= 0) {
2625 _session->request_roll_at_and_return (start_frame, return_frame);
2630 Editor::play_selection ()
2632 framepos_t start, end;
2633 if (!get_selection_extents ( start, end))
2636 AudioRange ar (start, end, 0);
2637 list<AudioRange> lar;
2640 _session->request_play_range (&lar, true);
2645 Editor::maybe_locate_with_edit_preroll (framepos_t location)
2647 if ( _session->transport_rolling() || !UIConfiguration::instance().get_follow_edits() || _session->config.get_external_sync() )
2650 location -= _session->preroll_samples (location);
2652 //don't try to locate before the beginning of time
2657 //if follow_playhead is on, keep the playhead on the screen
2658 if ( _follow_playhead )
2659 if ( location < leftmost_frame )
2660 location = leftmost_frame;
2662 _session->request_locate( location );
2666 Editor::play_with_preroll ()
2668 framepos_t start, end;
2669 if ( UIConfiguration::instance().get_follow_edits() && get_selection_extents ( start, end) ) {
2670 const framepos_t preroll = _session->preroll_samples (start);
2672 framepos_t ret = start;
2674 if (start > preroll) {
2675 start = start - preroll;
2678 end = end + preroll; //"post-roll"
2680 AudioRange ar (start, end, 0);
2681 list<AudioRange> lar;
2684 _session->request_play_range (&lar, true);
2685 _session->set_requested_return_frame (ret); //force auto-return to return to range start, without the preroll
2687 framepos_t ph = playhead_cursor->current_frame ();
2688 const framepos_t preroll = _session->preroll_samples (ph);
2691 start = ph - preroll;
2695 _session->request_locate (start, true);
2696 _session->set_requested_return_frame (ph); //force auto-return to return to playhead location, without the preroll
2701 Editor::rec_with_preroll ()
2703 framepos_t ph = playhead_cursor->current_frame ();
2704 framepos_t preroll = _session->preroll_samples (ph);
2705 _session->request_preroll_record_trim (ph, preroll);
2709 Editor::rec_with_count_in ()
2711 _session->request_count_in_record ();
2715 Editor::play_location (Location& location)
2717 if (location.start() <= location.end()) {
2721 _session->request_bounded_roll (location.start(), location.end());
2725 Editor::loop_location (Location& location)
2727 if (location.start() <= location.end()) {
2733 if ((tll = transport_loop_location()) != 0) {
2734 tll->set (location.start(), location.end());
2736 // enable looping, reposition and start rolling
2737 _session->request_locate (tll->start(), true);
2738 _session->request_play_loop (true);
2743 Editor::do_layer_operation (LayerOperation op)
2745 if (selection->regions.empty ()) {
2749 bool const multiple = selection->regions.size() > 1;
2753 begin_reversible_command (_("raise regions"));
2755 begin_reversible_command (_("raise region"));
2761 begin_reversible_command (_("raise regions to top"));
2763 begin_reversible_command (_("raise region to top"));
2769 begin_reversible_command (_("lower regions"));
2771 begin_reversible_command (_("lower region"));
2777 begin_reversible_command (_("lower regions to bottom"));
2779 begin_reversible_command (_("lower region"));
2784 set<boost::shared_ptr<Playlist> > playlists = selection->regions.playlists ();
2785 for (set<boost::shared_ptr<Playlist> >::iterator i = playlists.begin(); i != playlists.end(); ++i) {
2786 (*i)->clear_owned_changes ();
2789 for (RegionSelection::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
2790 boost::shared_ptr<Region> r = (*i)->region ();
2802 r->lower_to_bottom ();
2806 for (set<boost::shared_ptr<Playlist> >::iterator i = playlists.begin(); i != playlists.end(); ++i) {
2807 vector<Command*> cmds;
2809 _session->add_commands (cmds);
2812 commit_reversible_command ();
2816 Editor::raise_region ()
2818 do_layer_operation (Raise);
2822 Editor::raise_region_to_top ()
2824 do_layer_operation (RaiseToTop);
2828 Editor::lower_region ()
2830 do_layer_operation (Lower);
2834 Editor::lower_region_to_bottom ()
2836 do_layer_operation (LowerToBottom);
2839 /** Show the region editor for the selected regions */
2841 Editor::show_region_properties ()
2843 selection->foreach_regionview (&RegionView::show_region_editor);
2846 /** Show the midi list editor for the selected MIDI regions */
2848 Editor::show_midi_list_editor ()
2850 selection->foreach_midi_regionview (&MidiRegionView::show_list_editor);
2854 Editor::rename_region ()
2856 RegionSelection rs = get_regions_from_selection_and_entered ();
2862 ArdourDialog d (_("Rename Region"), true, false);
2864 Label label (_("New name:"));
2867 hbox.set_spacing (6);
2868 hbox.pack_start (label, false, false);
2869 hbox.pack_start (entry, true, true);
2871 d.get_vbox()->set_border_width (12);
2872 d.get_vbox()->pack_start (hbox, false, false);
2874 d.add_button(Gtk::Stock::CANCEL, Gtk::RESPONSE_CANCEL);
2875 d.add_button(Gtk::Stock::OK, Gtk::RESPONSE_OK);
2877 d.set_size_request (300, -1);
2879 entry.set_text (rs.front()->region()->name());
2880 entry.select_region (0, -1);
2882 entry.signal_activate().connect (sigc::bind (sigc::mem_fun (d, &Dialog::response), RESPONSE_OK));
2888 int const ret = d.run();
2892 if (ret != RESPONSE_OK) {
2896 std::string str = entry.get_text();
2897 strip_whitespace_edges (str);
2899 rs.front()->region()->set_name (str);
2900 _regions->redisplay ();
2904 /** Start an audition of the first selected region */
2906 Editor::play_edit_range ()
2908 framepos_t start, end;
2910 if (get_edit_op_range (start, end)) {
2911 _session->request_bounded_roll (start, end);
2916 Editor::play_selected_region ()
2918 framepos_t start = max_framepos;
2921 RegionSelection rs = get_regions_from_selection_and_entered ();
2927 for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
2928 if ((*i)->region()->position() < start) {
2929 start = (*i)->region()->position();
2931 if ((*i)->region()->last_frame() + 1 > end) {
2932 end = (*i)->region()->last_frame() + 1;
2936 _session->request_bounded_roll (start, end);
2940 Editor::audition_playlist_region_standalone (boost::shared_ptr<Region> region)
2942 _session->audition_region (region);
2946 Editor::region_from_selection ()
2948 if (clicked_axisview == 0) {
2952 if (selection->time.empty()) {
2956 framepos_t start = selection->time[clicked_selection].start;
2957 framepos_t end = selection->time[clicked_selection].end;
2959 TrackViewList tracks = get_tracks_for_range_action ();
2961 framepos_t selection_cnt = end - start + 1;
2963 for (TrackSelection::iterator i = tracks.begin(); i != tracks.end(); ++i) {
2964 boost::shared_ptr<Region> current;
2965 boost::shared_ptr<Playlist> pl;
2966 framepos_t internal_start;
2969 if ((pl = (*i)->playlist()) == 0) {
2973 if ((current = pl->top_region_at (start)) == 0) {
2977 internal_start = start - current->position();
2978 RegionFactory::region_name (new_name, current->name(), true);
2982 plist.add (ARDOUR::Properties::start, current->start() + internal_start);
2983 plist.add (ARDOUR::Properties::length, selection_cnt);
2984 plist.add (ARDOUR::Properties::name, new_name);
2985 plist.add (ARDOUR::Properties::layer, 0);
2987 boost::shared_ptr<Region> region (RegionFactory::create (current, plist));
2992 Editor::create_region_from_selection (vector<boost::shared_ptr<Region> >& new_regions)
2994 if (selection->time.empty() || selection->tracks.empty()) {
2998 framepos_t start, end;
2999 if (clicked_selection) {
3000 start = selection->time[clicked_selection].start;
3001 end = selection->time[clicked_selection].end;
3003 start = selection->time.start();
3004 end = selection->time.end_frame();
3007 TrackViewList ts = selection->tracks.filter_to_unique_playlists ();
3008 sort_track_selection (ts);
3010 for (TrackSelection::iterator i = ts.begin(); i != ts.end(); ++i) {
3011 boost::shared_ptr<Region> current;
3012 boost::shared_ptr<Playlist> playlist;
3013 framepos_t internal_start;
3016 if ((playlist = (*i)->playlist()) == 0) {
3020 if ((current = playlist->top_region_at(start)) == 0) {
3024 internal_start = start - current->position();
3025 RegionFactory::region_name (new_name, current->name(), true);
3029 plist.add (ARDOUR::Properties::start, current->start() + internal_start);
3030 plist.add (ARDOUR::Properties::length, end - start + 1);
3031 plist.add (ARDOUR::Properties::name, new_name);
3033 new_regions.push_back (RegionFactory::create (current, plist));
3038 Editor::split_multichannel_region ()
3040 RegionSelection rs = get_regions_from_selection_and_entered ();
3046 vector< boost::shared_ptr<Region> > v;
3048 for (list<RegionView*>::iterator x = rs.begin(); x != rs.end(); ++x) {
3049 (*x)->region()->separate_by_channel (v);
3054 Editor::new_region_from_selection ()
3056 region_from_selection ();
3057 cancel_selection ();
3061 add_if_covered (RegionView* rv, const AudioRange* ar, RegionSelection* rs)
3063 switch (rv->region()->coverage (ar->start, ar->end - 1)) {
3064 // n.b. -1 because AudioRange::end is one past the end, but coverage expects inclusive ranges
3065 case Evoral::OverlapNone:
3073 * - selected tracks, or if there are none...
3074 * - tracks containing selected regions, or if there are none...
3079 Editor::get_tracks_for_range_action () const
3083 if (selection->tracks.empty()) {
3085 /* use tracks with selected regions */
3087 RegionSelection rs = selection->regions;
3089 for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
3090 TimeAxisView* tv = &(*i)->get_time_axis_view();
3092 if (!t.contains (tv)) {
3098 /* no regions and no tracks: use all tracks */
3104 t = selection->tracks;
3107 return t.filter_to_unique_playlists();
3111 Editor::separate_regions_between (const TimeSelection& ts)
3113 bool in_command = false;
3114 boost::shared_ptr<Playlist> playlist;
3115 RegionSelection new_selection;
3117 TrackViewList tmptracks = get_tracks_for_range_action ();
3118 sort_track_selection (tmptracks);
3120 for (TrackSelection::iterator i = tmptracks.begin(); i != tmptracks.end(); ++i) {
3122 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> ((*i));
3128 if (!rtv->is_track()) {
3132 /* no edits to destructive tracks */
3134 if (rtv->track()->destructive()) {
3138 if ((playlist = rtv->playlist()) != 0) {
3140 playlist->clear_changes ();
3142 /* XXX need to consider musical time selections here at some point */
3144 for (list<AudioRange>::const_iterator t = ts.begin(); t != ts.end(); ++t) {
3146 sigc::connection c = rtv->view()->RegionViewAdded.connect (
3147 sigc::mem_fun(*this, &Editor::collect_new_region_view));
3149 latest_regionviews.clear ();
3151 playlist->partition ((*t).start, (*t).end, false);
3155 if (!latest_regionviews.empty()) {
3157 rtv->view()->foreach_regionview (sigc::bind (
3158 sigc::ptr_fun (add_if_covered),
3159 &(*t), &new_selection));
3162 begin_reversible_command (_("separate"));
3166 /* pick up changes to existing regions */
3168 vector<Command*> cmds;
3169 playlist->rdiff (cmds);
3170 _session->add_commands (cmds);
3172 /* pick up changes to the playlist itself (adds/removes)
3175 _session->add_command(new StatefulDiffCommand (playlist));
3182 // selection->set (new_selection);
3184 commit_reversible_command ();
3188 struct PlaylistState {
3189 boost::shared_ptr<Playlist> playlist;
3193 /** Take tracks from get_tracks_for_range_action and cut any regions
3194 * on those tracks so that the tracks are empty over the time
3198 Editor::separate_region_from_selection ()
3200 /* preferentially use *all* ranges in the time selection if we're in range mode
3201 to allow discontiguous operation, since get_edit_op_range() currently
3202 returns a single range.
3205 if (!selection->time.empty()) {
3207 separate_regions_between (selection->time);
3214 if (get_edit_op_range (start, end)) {
3216 AudioRange ar (start, end, 1);
3220 separate_regions_between (ts);
3226 Editor::separate_region_from_punch ()
3228 Location* loc = _session->locations()->auto_punch_location();
3230 separate_regions_using_location (*loc);
3235 Editor::separate_region_from_loop ()
3237 Location* loc = _session->locations()->auto_loop_location();
3239 separate_regions_using_location (*loc);
3244 Editor::separate_regions_using_location (Location& loc)
3246 if (loc.is_mark()) {
3250 AudioRange ar (loc.start(), loc.end(), 1);
3255 separate_regions_between (ts);
3258 /** Separate regions under the selected region */
3260 Editor::separate_under_selected_regions ()
3262 vector<PlaylistState> playlists;
3266 rs = get_regions_from_selection_and_entered();
3268 if (!_session || rs.empty()) {
3272 begin_reversible_command (_("separate region under"));
3274 list<boost::shared_ptr<Region> > regions_to_remove;
3276 for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
3277 // we can't just remove the region(s) in this loop because
3278 // this removes them from the RegionSelection, and they thus
3279 // disappear from underneath the iterator, and the ++i above
3280 // SEGVs in a puzzling fashion.
3282 // so, first iterate over the regions to be removed from rs and
3283 // add them to the regions_to_remove list, and then
3284 // iterate over the list to actually remove them.
3286 regions_to_remove.push_back ((*i)->region());
3289 for (list<boost::shared_ptr<Region> >::iterator rl = regions_to_remove.begin(); rl != regions_to_remove.end(); ++rl) {
3291 boost::shared_ptr<Playlist> playlist = (*rl)->playlist();
3294 // is this check necessary?
3298 vector<PlaylistState>::iterator i;
3300 //only take state if this is a new playlist.
3301 for (i = playlists.begin(); i != playlists.end(); ++i) {
3302 if ((*i).playlist == playlist) {
3307 if (i == playlists.end()) {
3309 PlaylistState before;
3310 before.playlist = playlist;
3311 before.before = &playlist->get_state();
3312 playlist->clear_changes ();
3313 playlist->freeze ();
3314 playlists.push_back(before);
3317 //Partition on the region bounds
3318 playlist->partition ((*rl)->first_frame() - 1, (*rl)->last_frame() + 1, true);
3320 //Re-add region that was just removed due to the partition operation
3321 playlist->add_region( (*rl), (*rl)->first_frame() );
3324 vector<PlaylistState>::iterator pl;
3326 for (pl = playlists.begin(); pl != playlists.end(); ++pl) {
3327 (*pl).playlist->thaw ();
3328 _session->add_command(new MementoCommand<Playlist>(*(*pl).playlist, (*pl).before, &(*pl).playlist->get_state()));
3331 commit_reversible_command ();
3335 Editor::crop_region_to_selection ()
3337 if (!selection->time.empty()) {
3339 begin_reversible_command (_("Crop Regions to Time Selection"));
3340 for (std::list<AudioRange>::iterator i = selection->time.begin(); i != selection->time.end(); ++i) {
3341 crop_region_to ((*i).start, (*i).end);
3343 commit_reversible_command();
3349 if (get_edit_op_range (start, end)) {
3350 begin_reversible_command (_("Crop Regions to Edit Range"));
3352 crop_region_to (start, end);
3354 commit_reversible_command();
3361 Editor::crop_region_to (framepos_t start, framepos_t end)
3363 vector<boost::shared_ptr<Playlist> > playlists;
3364 boost::shared_ptr<Playlist> playlist;
3367 if (selection->tracks.empty()) {
3368 ts = track_views.filter_to_unique_playlists();
3370 ts = selection->tracks.filter_to_unique_playlists ();
3373 sort_track_selection (ts);
3375 for (TrackSelection::iterator i = ts.begin(); i != ts.end(); ++i) {
3377 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> ((*i));
3383 boost::shared_ptr<Track> t = rtv->track();
3385 if (t != 0 && ! t->destructive()) {
3387 if ((playlist = rtv->playlist()) != 0) {
3388 playlists.push_back (playlist);
3393 if (playlists.empty()) {
3398 framepos_t new_start;
3400 framecnt_t new_length;
3402 for (vector<boost::shared_ptr<Playlist> >::iterator i = playlists.begin(); i != playlists.end(); ++i) {
3404 /* Only the top regions at start and end have to be cropped */
3405 boost::shared_ptr<Region> region_at_start = (*i)->top_region_at(start);
3406 boost::shared_ptr<Region> region_at_end = (*i)->top_region_at(end);
3408 vector<boost::shared_ptr<Region> > regions;
3410 if (region_at_start != 0) {
3411 regions.push_back (region_at_start);
3413 if (region_at_end != 0) {
3414 regions.push_back (region_at_end);
3417 /* now adjust lengths */
3418 for (vector<boost::shared_ptr<Region> >::iterator i = regions.begin(); i != regions.end(); ++i) {
3420 pos = (*i)->position();
3421 new_start = max (start, pos);
3422 if (max_framepos - pos > (*i)->length()) {
3423 new_end = pos + (*i)->length() - 1;
3425 new_end = max_framepos;
3427 new_end = min (end, new_end);
3428 new_length = new_end - new_start + 1;
3430 (*i)->clear_changes ();
3431 (*i)->trim_to (new_start, new_length);
3432 _session->add_command (new StatefulDiffCommand (*i));
3438 Editor::region_fill_track ()
3440 boost::shared_ptr<Playlist> playlist;
3441 RegionSelection regions = get_regions_from_selection_and_entered ();
3442 RegionSelection foo;
3444 framepos_t const end = _session->current_end_frame ();
3446 if (regions.empty () || regions.end_frame () + 1 >= end) {
3450 framepos_t const start_frame = regions.start ();
3451 framepos_t const end_frame = regions.end_frame ();
3452 framecnt_t const gap = end_frame - start_frame + 1;
3454 begin_reversible_command (Operations::region_fill);
3456 selection->clear_regions ();
3458 for (RegionSelection::iterator i = regions.begin(); i != regions.end(); ++i) {
3460 boost::shared_ptr<Region> r ((*i)->region());
3462 TimeAxisView& tv = (*i)->get_time_axis_view();
3463 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (&tv);
3464 latest_regionviews.clear ();
3465 sigc::connection c = rtv->view()->RegionViewAdded.connect (sigc::mem_fun(*this, &Editor::collect_new_region_view));
3467 framepos_t const position = end_frame + (r->first_frame() - start_frame + 1);
3468 playlist = (*i)->region()->playlist();
3469 playlist->clear_changes ();
3470 playlist->duplicate_until (r, position, gap, end);
3471 _session->add_command(new StatefulDiffCommand (playlist));
3475 foo.insert (foo.end(), latest_regionviews.begin(), latest_regionviews.end());
3479 selection->set (foo);
3482 commit_reversible_command ();
3486 Editor::set_region_sync_position ()
3488 set_sync_point (get_preferred_edit_position (), get_regions_from_selection_and_edit_point ());
3492 Editor::set_sync_point (framepos_t where, const RegionSelection& rs)
3494 bool in_command = false;
3496 for (RegionSelection::const_iterator r = rs.begin(); r != rs.end(); ++r) {
3498 if (!(*r)->region()->covers (where)) {
3502 boost::shared_ptr<Region> region ((*r)->region());
3505 begin_reversible_command (_("set sync point"));
3509 region->clear_changes ();
3510 region->set_sync_position (where);
3511 _session->add_command(new StatefulDiffCommand (region));
3515 commit_reversible_command ();
3519 /** Remove the sync positions of the selection */
3521 Editor::remove_region_sync ()
3523 RegionSelection rs = get_regions_from_selection_and_entered ();
3529 begin_reversible_command (_("remove region sync"));
3531 for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
3533 (*i)->region()->clear_changes ();
3534 (*i)->region()->clear_sync_position ();
3535 _session->add_command(new StatefulDiffCommand ((*i)->region()));
3538 commit_reversible_command ();
3542 Editor::naturalize_region ()
3544 RegionSelection rs = get_regions_from_selection_and_entered ();
3550 if (rs.size() > 1) {
3551 begin_reversible_command (_("move regions to original position"));
3553 begin_reversible_command (_("move region to original position"));
3556 for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
3557 (*i)->region()->clear_changes ();
3558 (*i)->region()->move_to_natural_position ();
3559 _session->add_command (new StatefulDiffCommand ((*i)->region()));
3562 commit_reversible_command ();
3566 Editor::align_regions (RegionPoint what)
3568 RegionSelection const rs = get_regions_from_selection_and_edit_point ();
3574 begin_reversible_command (_("align selection"));
3576 framepos_t const position = get_preferred_edit_position ();
3578 for (RegionSelection::const_iterator i = rs.begin(); i != rs.end(); ++i) {
3579 align_region_internal ((*i)->region(), what, position);
3582 commit_reversible_command ();
3585 struct RegionSortByTime {
3586 bool operator() (const RegionView* a, const RegionView* b) {
3587 return a->region()->position() < b->region()->position();
3592 Editor::align_regions_relative (RegionPoint point)
3594 RegionSelection const rs = get_regions_from_selection_and_edit_point ();
3600 framepos_t const position = get_preferred_edit_position ();
3602 framepos_t distance = 0;
3606 list<RegionView*> sorted;
3607 rs.by_position (sorted);
3609 boost::shared_ptr<Region> r ((*sorted.begin())->region());
3614 if (position > r->position()) {
3615 distance = position - r->position();
3617 distance = r->position() - position;
3623 if (position > r->last_frame()) {
3624 distance = position - r->last_frame();
3625 pos = r->position() + distance;
3627 distance = r->last_frame() - position;
3628 pos = r->position() - distance;
3634 pos = r->adjust_to_sync (position);
3635 if (pos > r->position()) {
3636 distance = pos - r->position();
3638 distance = r->position() - pos;
3644 if (pos == r->position()) {
3648 begin_reversible_command (_("align selection (relative)"));
3650 /* move first one specially */
3652 r->clear_changes ();
3653 r->set_position (pos);
3654 _session->add_command(new StatefulDiffCommand (r));
3656 /* move rest by the same amount */
3660 for (list<RegionView*>::iterator i = sorted.begin(); i != sorted.end(); ++i) {
3662 boost::shared_ptr<Region> region ((*i)->region());
3664 region->clear_changes ();
3667 region->set_position (region->position() + distance);
3669 region->set_position (region->position() - distance);
3672 _session->add_command(new StatefulDiffCommand (region));
3676 commit_reversible_command ();
3680 Editor::align_region (boost::shared_ptr<Region> region, RegionPoint point, framepos_t position)
3682 begin_reversible_command (_("align region"));
3683 align_region_internal (region, point, position);
3684 commit_reversible_command ();
3688 Editor::align_region_internal (boost::shared_ptr<Region> region, RegionPoint point, framepos_t position)
3690 region->clear_changes ();
3694 region->set_position (region->adjust_to_sync (position));
3698 if (position > region->length()) {
3699 region->set_position (position - region->length());
3704 region->set_position (position);
3708 _session->add_command(new StatefulDiffCommand (region));
3712 Editor::trim_region_front ()
3718 Editor::trim_region_back ()
3720 trim_region (false);
3724 Editor::trim_region (bool front)
3726 framepos_t where = get_preferred_edit_position();
3727 RegionSelection rs = get_regions_from_selection_and_edit_point ();
3733 begin_reversible_command (front ? _("trim front") : _("trim back"));
3735 for (list<RegionView*>::const_iterator i = rs.by_layer().begin(); i != rs.by_layer().end(); ++i) {
3736 if (!(*i)->region()->locked()) {
3738 (*i)->region()->clear_changes ();
3741 (*i)->region()->trim_front (where);
3743 (*i)->region()->trim_end (where);
3746 _session->add_command (new StatefulDiffCommand ((*i)->region()));
3750 commit_reversible_command ();
3753 /** Trim the end of the selected regions to the position of the edit cursor */
3755 Editor::trim_region_to_loop ()
3757 Location* loc = _session->locations()->auto_loop_location();
3761 trim_region_to_location (*loc, _("trim to loop"));
3765 Editor::trim_region_to_punch ()
3767 Location* loc = _session->locations()->auto_punch_location();
3771 trim_region_to_location (*loc, _("trim to punch"));
3775 Editor::trim_region_to_location (const Location& loc, const char* str)
3777 RegionSelection rs = get_regions_from_selection_and_entered ();
3778 bool in_command = false;
3780 for (RegionSelection::iterator x = rs.begin(); x != rs.end(); ++x) {
3781 RegionView* rv = (*x);
3783 /* require region to span proposed trim */
3784 switch (rv->region()->coverage (loc.start(), loc.end())) {
3785 case Evoral::OverlapInternal:
3791 RouteTimeAxisView* tav = dynamic_cast<RouteTimeAxisView*> (&rv->get_time_axis_view());
3799 start = loc.start();
3802 rv->region()->clear_changes ();
3803 rv->region()->trim_to (start, (end - start));
3806 begin_reversible_command (str);
3809 _session->add_command(new StatefulDiffCommand (rv->region()));
3813 commit_reversible_command ();
3818 Editor::trim_region_to_previous_region_end ()
3820 return trim_to_region(false);
3824 Editor::trim_region_to_next_region_start ()
3826 return trim_to_region(true);
3830 Editor::trim_to_region(bool forward)
3832 RegionSelection rs = get_regions_from_selection_and_entered ();
3833 bool in_command = false;
3835 boost::shared_ptr<Region> next_region;
3837 for (RegionSelection::iterator x = rs.begin(); x != rs.end(); ++x) {
3839 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (*x);
3845 AudioTimeAxisView* atav = dynamic_cast<AudioTimeAxisView*> (&arv->get_time_axis_view());
3851 boost::shared_ptr<Region> region = arv->region();
3852 boost::shared_ptr<Playlist> playlist (region->playlist());
3854 region->clear_changes ();
3858 next_region = playlist->find_next_region (region->first_frame(), Start, 1);
3864 region->trim_end (next_region->first_frame() - 1);
3865 arv->region_changed (PropertyChange (ARDOUR::Properties::length));
3869 next_region = playlist->find_next_region (region->first_frame(), Start, 0);
3875 region->trim_front (next_region->last_frame() + 1);
3876 arv->region_changed (ARDOUR::bounds_change);
3880 begin_reversible_command (_("trim to region"));
3883 _session->add_command(new StatefulDiffCommand (region));
3887 commit_reversible_command ();
3892 Editor::unfreeze_route ()
3894 if (clicked_routeview == 0 || !clicked_routeview->is_track()) {
3898 clicked_routeview->track()->unfreeze ();
3902 Editor::_freeze_thread (void* arg)
3904 return static_cast<Editor*>(arg)->freeze_thread ();
3908 Editor::freeze_thread ()
3910 /* create event pool because we may need to talk to the session */
3911 SessionEvent::create_per_thread_pool ("freeze events", 64);
3912 /* create per-thread buffers for process() tree to use */
3913 clicked_routeview->audio_track()->freeze_me (*current_interthread_info);
3914 current_interthread_info->done = true;
3919 Editor::freeze_route ()
3925 /* stop transport before we start. this is important */
3927 _session->request_transport_speed (0.0);
3929 /* wait for just a little while, because the above call is asynchronous */
3931 Glib::usleep (250000);
3933 if (clicked_routeview == 0 || !clicked_routeview->is_audio_track()) {
3937 if (!clicked_routeview->track()->bounceable (clicked_routeview->track()->main_outs(), true)) {
3939 _("This track/bus cannot be frozen because the signal adds or loses channels before reaching the outputs.\n"
3940 "This is typically caused by plugins that generate stereo output from mono input or vice versa.")
3942 d.set_title (_("Cannot freeze"));
3947 if (clicked_routeview->track()->has_external_redirects()) {
3948 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"
3949 "Freezing will only process the signal as far as the first send/insert/return."),
3950 clicked_routeview->track()->name()), true, MESSAGE_INFO, BUTTONS_NONE, true);
3952 d.add_button (_("Freeze anyway"), Gtk::RESPONSE_OK);
3953 d.add_button (_("Don't freeze"), Gtk::RESPONSE_CANCEL);
3954 d.set_title (_("Freeze Limits"));
3956 int response = d.run ();
3959 case Gtk::RESPONSE_CANCEL:
3966 InterThreadInfo itt;
3967 current_interthread_info = &itt;
3969 InterthreadProgressWindow ipw (current_interthread_info, _("Freeze"), _("Cancel Freeze"));
3971 pthread_create_and_store (X_("freezer"), &itt.thread, _freeze_thread, this);
3973 CursorContext::Handle cursor_ctx = CursorContext::create(*this, _cursors->wait);
3975 while (!itt.done && !itt.cancel) {
3976 gtk_main_iteration ();
3979 pthread_join (itt.thread, 0);
3980 current_interthread_info = 0;
3984 Editor::bounce_range_selection (bool replace, bool enable_processing)
3986 if (selection->time.empty()) {
3990 TrackSelection views = selection->tracks;
3992 for (TrackViewList::iterator i = views.begin(); i != views.end(); ++i) {
3994 if (enable_processing) {
3996 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (*i);
3998 if (rtv && rtv->track() && replace && enable_processing && !rtv->track()->bounceable (rtv->track()->main_outs(), false)) {
4000 _("You can't perform this operation because the processing of the signal "
4001 "will cause one or more of the tracks to end up with a region with more channels than this track has inputs.\n\n"
4002 "You can do this without processing, which is a different operation.")
4004 d.set_title (_("Cannot bounce"));
4011 framepos_t start = selection->time[clicked_selection].start;
4012 framepos_t end = selection->time[clicked_selection].end;
4013 framepos_t cnt = end - start + 1;
4014 bool in_command = false;
4016 for (TrackViewList::iterator i = views.begin(); i != views.end(); ++i) {
4018 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (*i);
4024 boost::shared_ptr<Playlist> playlist;
4026 if ((playlist = rtv->playlist()) == 0) {
4030 InterThreadInfo itt;
4032 playlist->clear_changes ();
4033 playlist->clear_owned_changes ();
4035 boost::shared_ptr<Region> r;
4037 if (enable_processing) {
4038 r = rtv->track()->bounce_range (start, start+cnt, itt, rtv->track()->main_outs(), false);
4040 r = rtv->track()->bounce_range (start, start+cnt, itt, boost::shared_ptr<Processor>(), false);
4048 list<AudioRange> ranges;
4049 ranges.push_back (AudioRange (start, start+cnt, 0));
4050 playlist->cut (ranges); // discard result
4051 playlist->add_region (r, start);
4055 begin_reversible_command (_("bounce range"));
4058 vector<Command*> cmds;
4059 playlist->rdiff (cmds);
4060 _session->add_commands (cmds);
4062 _session->add_command (new StatefulDiffCommand (playlist));
4066 commit_reversible_command ();
4070 /** Delete selected regions, automation points or a time range */
4074 //special case: if the user is pointing in the editor/mixer strip, they may be trying to delete a plugin.
4075 //we need this because the editor-mixer strip is in the editor window, so it doesn't get the bindings from the mix window
4076 bool deleted = false;
4077 if ( current_mixer_strip && current_mixer_strip == MixerStrip::entered_mixer_strip() )
4078 deleted = current_mixer_strip->delete_processors ();
4084 /** Cut selected regions, automation points or a time range */
4091 /** Copy selected regions, automation points or a time range */
4099 /** @return true if a Cut, Copy or Clear is possible */
4101 Editor::can_cut_copy () const
4103 if (!selection->time.empty() || !selection->regions.empty() || !selection->points.empty())
4110 /** Cut, copy or clear selected regions, automation points or a time range.
4111 * @param op Operation (Delete, Cut, Copy or Clear)
4114 Editor::cut_copy (CutCopyOp op)
4116 /* only cancel selection if cut/copy is successful.*/
4122 opname = _("delete");
4131 opname = _("clear");
4135 /* if we're deleting something, and the mouse is still pressed,
4136 the thing we started a drag for will be gone when we release
4137 the mouse button(s). avoid this. see part 2 at the end of
4141 if (op == Delete || op == Cut || op == Clear) {
4142 if (_drags->active ()) {
4147 if ( op != Delete ) { //"Delete" doesn't change copy/paste buf
4148 cut_buffer->clear ();
4151 if (entered_marker) {
4153 /* cut/delete op while pointing at a marker */
4156 Location* loc = find_location_from_marker (entered_marker, ignored);
4158 if (_session && loc) {
4159 entered_marker = NULL;
4160 Glib::signal_idle().connect (sigc::bind (sigc::mem_fun(*this, &Editor::really_remove_marker), loc));
4167 switch (mouse_mode) {
4170 begin_reversible_command (opname + ' ' + X_("MIDI"));
4172 commit_reversible_command ();
4178 bool did_edit = false;
4180 if (!selection->regions.empty() || !selection->points.empty()) {
4181 begin_reversible_command (opname + ' ' + _("objects"));
4184 if (!selection->regions.empty()) {
4185 cut_copy_regions (op, selection->regions);
4187 if (op == Cut || op == Delete) {
4188 selection->clear_regions ();
4192 if (!selection->points.empty()) {
4193 cut_copy_points (op);
4195 if (op == Cut || op == Delete) {
4196 selection->clear_points ();
4199 } else if (selection->time.empty()) {
4200 framepos_t start, end;
4201 /* no time selection, see if we can get an edit range
4204 if (get_edit_op_range (start, end)) {
4205 selection->set (start, end);
4207 } else if (!selection->time.empty()) {
4208 begin_reversible_command (opname + ' ' + _("range"));
4211 cut_copy_ranges (op);
4213 if (op == Cut || op == Delete) {
4214 selection->clear_time ();
4219 /* reset repeated paste state */
4221 last_paste_pos = -1;
4222 commit_reversible_command ();
4225 if (op == Delete || op == Cut || op == Clear) {
4231 struct AutomationRecord {
4232 AutomationRecord () : state (0) , line(NULL) {}
4233 AutomationRecord (XMLNode* s, const AutomationLine* l) : state (s) , line (l) {}
4235 XMLNode* state; ///< state before any operation
4236 const AutomationLine* line; ///< line this came from
4237 boost::shared_ptr<Evoral::ControlList> copy; ///< copied events for the cut buffer
4240 struct PointsSelectionPositionSorter {
4241 bool operator() (ControlPoint* a, ControlPoint* b) {
4242 return (*(a->model()))->when < (*(b->model()))->when;
4246 /** Cut, copy or clear selected automation points.
4247 * @param op Operation (Cut, Copy or Clear)
4250 Editor::cut_copy_points (Editing::CutCopyOp op, Evoral::Beats earliest, bool midi)
4252 if (selection->points.empty ()) {
4256 /* XXX: not ideal, as there may be more than one track involved in the point selection */
4257 _last_cut_copy_source_track = &selection->points.front()->line().trackview;
4259 /* Keep a record of the AutomationLists that we end up using in this operation */
4260 typedef std::map<boost::shared_ptr<AutomationList>, AutomationRecord> Lists;
4263 /* user could select points in any order */
4264 selection->points.sort(PointsSelectionPositionSorter ());
4266 /* Go through all selected points, making an AutomationRecord for each distinct AutomationList */
4267 for (PointSelection::iterator sel_point = selection->points.begin(); sel_point != selection->points.end(); ++sel_point) {
4268 const AutomationLine& line = (*sel_point)->line();
4269 const boost::shared_ptr<AutomationList> al = line.the_list();
4270 if (lists.find (al) == lists.end ()) {
4271 /* We haven't seen this list yet, so make a record for it. This includes
4272 taking a copy of its current state, in case this is needed for undo later.
4274 lists[al] = AutomationRecord (&al->get_state (), &line);
4278 if (op == Cut || op == Copy) {
4279 /* This operation will involve putting things in the cut buffer, so create an empty
4280 ControlList for each of our source lists to put the cut buffer data in.
4282 for (Lists::iterator i = lists.begin(); i != lists.end(); ++i) {
4283 i->second.copy = i->first->create (i->first->parameter (), i->first->descriptor());
4286 /* Add all selected points to the relevant copy ControlLists */
4287 MusicFrame start (std::numeric_limits<framepos_t>::max(), 0);
4288 for (PointSelection::iterator sel_point = selection->points.begin(); sel_point != selection->points.end(); ++sel_point) {
4289 boost::shared_ptr<AutomationList> al = (*sel_point)->line().the_list();
4290 AutomationList::const_iterator ctrl_evt = (*sel_point)->model ();
4292 lists[al].copy->fast_simple_add ((*ctrl_evt)->when, (*ctrl_evt)->value);
4294 /* Update earliest MIDI start time in beats */
4295 earliest = std::min(earliest, Evoral::Beats((*ctrl_evt)->when));
4297 /* Update earliest session start time in frames */
4298 start.frame = std::min(start.frame, (*sel_point)->line().session_position(ctrl_evt));
4302 /* Snap start time backwards, so copy/paste is snap aligned. */
4304 if (earliest == Evoral::Beats::max()) {
4305 earliest = Evoral::Beats(); // Weird... don't offset
4307 earliest.round_down_to_beat();
4309 if (start.frame == std::numeric_limits<double>::max()) {
4310 start.frame = 0; // Weird... don't offset
4312 snap_to(start, RoundDownMaybe);
4315 const double line_offset = midi ? earliest.to_double() : start.frame;
4316 for (Lists::iterator i = lists.begin(); i != lists.end(); ++i) {
4317 /* Correct this copy list so that it is relative to the earliest
4318 start time, so relative ordering between points is preserved
4319 when copying from several lists and the paste starts at the
4320 earliest copied piece of data. */
4321 boost::shared_ptr<Evoral::ControlList> &al_cpy = i->second.copy;
4322 for (AutomationList::iterator ctrl_evt = al_cpy->begin(); ctrl_evt != al_cpy->end(); ++ctrl_evt) {
4323 (*ctrl_evt)->when -= line_offset;
4326 /* And add it to the cut buffer */
4327 cut_buffer->add (al_cpy);
4331 if (op == Delete || op == Cut) {
4332 /* This operation needs to remove things from the main AutomationList, so do that now */
4334 for (Lists::iterator i = lists.begin(); i != lists.end(); ++i) {
4335 i->first->freeze ();
4338 /* Remove each selected point from its AutomationList */
4339 for (PointSelection::iterator sel_point = selection->points.begin(); sel_point != selection->points.end(); ++sel_point) {
4340 AutomationLine& line = (*sel_point)->line ();
4341 boost::shared_ptr<AutomationList> al = line.the_list();
4345 if (dynamic_cast<AudioRegionGainLine*> (&line)) {
4346 /* removing of first and last gain point in region gain lines is prohibited*/
4347 if (line.is_last_point (*(*sel_point)) || line.is_first_point (*(*sel_point))) {
4353 al->erase ((*sel_point)->model ());
4357 /* Thaw the lists and add undo records for them */
4358 for (Lists::iterator i = lists.begin(); i != lists.end(); ++i) {
4359 boost::shared_ptr<AutomationList> al = i->first;
4361 _session->add_command (new MementoCommand<AutomationList> (*al.get(), i->second.state, &(al->get_state ())));
4366 /** Cut, copy or clear selected automation points.
4367 * @param op Operation (Cut, Copy or Clear)
4370 Editor::cut_copy_midi (CutCopyOp op)
4372 Evoral::Beats earliest = Evoral::Beats::max();
4373 for (MidiRegionSelection::iterator i = selection->midi_regions.begin(); i != selection->midi_regions.end(); ++i) {
4374 MidiRegionView* mrv = dynamic_cast<MidiRegionView*>(*i);
4376 if (!mrv->selection().empty()) {
4377 earliest = std::min(earliest, (*mrv->selection().begin())->note()->time());
4379 mrv->cut_copy_clear (op);
4381 /* XXX: not ideal, as there may be more than one track involved in the selection */
4382 _last_cut_copy_source_track = &mrv->get_time_axis_view();
4386 if (!selection->points.empty()) {
4387 cut_copy_points (op, earliest, true);
4388 if (op == Cut || op == Delete) {
4389 selection->clear_points ();
4394 struct lt_playlist {
4395 bool operator () (const PlaylistState& a, const PlaylistState& b) {
4396 return a.playlist < b.playlist;
4400 struct PlaylistMapping {
4402 boost::shared_ptr<Playlist> pl;
4404 PlaylistMapping (TimeAxisView* tvp) : tv (tvp) {}
4407 /** Remove `clicked_regionview' */
4409 Editor::remove_clicked_region ()
4411 if (clicked_routeview == 0 || clicked_regionview == 0) {
4415 begin_reversible_command (_("remove region"));
4417 boost::shared_ptr<Playlist> playlist = clicked_routeview->playlist();
4419 playlist->clear_changes ();
4420 playlist->clear_owned_changes ();
4421 playlist->remove_region (clicked_regionview->region());
4422 if (Config->get_edit_mode() == Ripple)
4423 playlist->ripple (clicked_regionview->region()->position(), -clicked_regionview->region()->length(), boost::shared_ptr<Region>());
4425 /* We might have removed regions, which alters other regions' layering_index,
4426 so we need to do a recursive diff here.
4428 vector<Command*> cmds;
4429 playlist->rdiff (cmds);
4430 _session->add_commands (cmds);
4432 _session->add_command(new StatefulDiffCommand (playlist));
4433 commit_reversible_command ();
4437 /** Remove the selected regions */
4439 Editor::remove_selected_regions ()
4441 RegionSelection rs = get_regions_from_selection_and_entered ();
4443 if (!_session || rs.empty()) {
4447 list<boost::shared_ptr<Region> > regions_to_remove;
4449 for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
4450 // we can't just remove the region(s) in this loop because
4451 // this removes them from the RegionSelection, and they thus
4452 // disappear from underneath the iterator, and the ++i above
4453 // SEGVs in a puzzling fashion.
4455 // so, first iterate over the regions to be removed from rs and
4456 // add them to the regions_to_remove list, and then
4457 // iterate over the list to actually remove them.
4459 regions_to_remove.push_back ((*i)->region());
4462 vector<boost::shared_ptr<Playlist> > playlists;
4464 for (list<boost::shared_ptr<Region> >::iterator rl = regions_to_remove.begin(); rl != regions_to_remove.end(); ++rl) {
4466 boost::shared_ptr<Playlist> playlist = (*rl)->playlist();
4469 // is this check necessary?
4473 /* get_regions_from_selection_and_entered() guarantees that
4474 the playlists involved are unique, so there is no need
4478 playlists.push_back (playlist);
4480 playlist->clear_changes ();
4481 playlist->clear_owned_changes ();
4482 playlist->freeze ();
4483 playlist->remove_region (*rl);
4484 if (Config->get_edit_mode() == Ripple)
4485 playlist->ripple ((*rl)->position(), -(*rl)->length(), boost::shared_ptr<Region>());
4489 vector<boost::shared_ptr<Playlist> >::iterator pl;
4490 bool in_command = false;
4492 for (pl = playlists.begin(); pl != playlists.end(); ++pl) {
4495 /* We might have removed regions, which alters other regions' layering_index,
4496 so we need to do a recursive diff here.
4500 begin_reversible_command (_("remove region"));
4503 vector<Command*> cmds;
4504 (*pl)->rdiff (cmds);
4505 _session->add_commands (cmds);
4507 _session->add_command(new StatefulDiffCommand (*pl));
4511 commit_reversible_command ();
4515 /** Cut, copy or clear selected regions.
4516 * @param op Operation (Cut, Copy or Clear)
4519 Editor::cut_copy_regions (CutCopyOp op, RegionSelection& rs)
4521 /* we can't use a std::map here because the ordering is important, and we can't trivially sort
4522 a map when we want ordered access to both elements. i think.
4525 vector<PlaylistMapping> pmap;
4527 framepos_t first_position = max_framepos;
4529 typedef set<boost::shared_ptr<Playlist> > FreezeList;
4530 FreezeList freezelist;
4532 /* get ordering correct before we cut/copy */
4534 rs.sort_by_position_and_track ();
4536 for (RegionSelection::iterator x = rs.begin(); x != rs.end(); ++x) {
4538 first_position = min ((framepos_t) (*x)->region()->position(), first_position);
4540 if (op == Cut || op == Clear || op == Delete) {
4541 boost::shared_ptr<Playlist> pl = (*x)->region()->playlist();
4544 FreezeList::iterator fl;
4546 // only take state if this is a new playlist.
4547 for (fl = freezelist.begin(); fl != freezelist.end(); ++fl) {
4553 if (fl == freezelist.end()) {
4554 pl->clear_changes();
4555 pl->clear_owned_changes ();
4557 freezelist.insert (pl);
4562 TimeAxisView* tv = &(*x)->get_time_axis_view();
4563 vector<PlaylistMapping>::iterator z;
4565 for (z = pmap.begin(); z != pmap.end(); ++z) {
4566 if ((*z).tv == tv) {
4571 if (z == pmap.end()) {
4572 pmap.push_back (PlaylistMapping (tv));
4576 for (RegionSelection::iterator x = rs.begin(); x != rs.end(); ) {
4578 boost::shared_ptr<Playlist> pl = (*x)->region()->playlist();
4581 /* region not yet associated with a playlist (e.g. unfinished
4588 TimeAxisView& tv = (*x)->get_time_axis_view();
4589 boost::shared_ptr<Playlist> npl;
4590 RegionSelection::iterator tmp;
4597 vector<PlaylistMapping>::iterator z;
4599 for (z = pmap.begin(); z != pmap.end(); ++z) {
4600 if ((*z).tv == &tv) {
4605 assert (z != pmap.end());
4608 npl = PlaylistFactory::create (pl->data_type(), *_session, "cutlist", true);
4616 boost::shared_ptr<Region> r = (*x)->region();
4617 boost::shared_ptr<Region> _xx;
4623 pl->remove_region (r);
4624 if (Config->get_edit_mode() == Ripple)
4625 pl->ripple (r->position(), -r->length(), boost::shared_ptr<Region>());
4629 _xx = RegionFactory::create (r);
4630 npl->add_region (_xx, r->position() - first_position);
4631 pl->remove_region (r);
4632 if (Config->get_edit_mode() == Ripple)
4633 pl->ripple (r->position(), -r->length(), boost::shared_ptr<Region>());
4637 /* copy region before adding, so we're not putting same object into two different playlists */
4638 npl->add_region (RegionFactory::create (r), r->position() - first_position);
4642 pl->remove_region (r);
4643 if (Config->get_edit_mode() == Ripple)
4644 pl->ripple (r->position(), -r->length(), boost::shared_ptr<Region>());
4653 list<boost::shared_ptr<Playlist> > foo;
4655 /* the pmap is in the same order as the tracks in which selected regions occurred */
4657 for (vector<PlaylistMapping>::iterator i = pmap.begin(); i != pmap.end(); ++i) {
4660 foo.push_back ((*i).pl);
4665 cut_buffer->set (foo);
4669 _last_cut_copy_source_track = 0;
4671 _last_cut_copy_source_track = pmap.front().tv;
4675 for (FreezeList::iterator pl = freezelist.begin(); pl != freezelist.end(); ++pl) {
4678 /* We might have removed regions, which alters other regions' layering_index,
4679 so we need to do a recursive diff here.
4681 vector<Command*> cmds;
4682 (*pl)->rdiff (cmds);
4683 _session->add_commands (cmds);
4685 _session->add_command (new StatefulDiffCommand (*pl));
4690 Editor::cut_copy_ranges (CutCopyOp op)
4692 TrackViewList ts = selection->tracks.filter_to_unique_playlists ();
4694 /* Sort the track selection now, so that it if is used, the playlists
4695 selected by the calls below to cut_copy_clear are in the order that
4696 their tracks appear in the editor. This makes things like paste
4697 of ranges work properly.
4700 sort_track_selection (ts);
4703 if (!entered_track) {
4706 ts.push_back (entered_track);
4709 for (TrackViewList::iterator i = ts.begin(); i != ts.end(); ++i) {
4710 (*i)->cut_copy_clear (*selection, op);
4715 Editor::paste (float times, bool from_context)
4717 DEBUG_TRACE (DEBUG::CutNPaste, "paste to preferred edit pos\n");
4718 MusicFrame where (get_preferred_edit_position (EDIT_IGNORE_NONE, from_context), 0);
4719 paste_internal (where.frame, times, 0);
4723 Editor::mouse_paste ()
4725 MusicFrame where (0, 0);
4727 if (!mouse_frame (where.frame, ignored)) {
4732 paste_internal (where.frame, 1, where.division);
4736 Editor::paste_internal (framepos_t position, float times, const int32_t sub_num)
4738 DEBUG_TRACE (DEBUG::CutNPaste, string_compose ("apparent paste position is %1\n", position));
4740 if (cut_buffer->empty(internal_editing())) {
4744 if (position == max_framepos) {
4745 position = get_preferred_edit_position();
4746 DEBUG_TRACE (DEBUG::CutNPaste, string_compose ("preferred edit position is %1\n", position));
4749 if (position == last_paste_pos) {
4750 /* repeated paste in the same position */
4753 /* paste in new location, reset repeated paste state */
4755 last_paste_pos = position;
4758 /* get everything in the correct order */
4761 if (!selection->tracks.empty()) {
4762 /* If there is a track selection, paste into exactly those tracks and
4763 * only those tracks. This allows the user to be explicit and override
4764 * the below "do the reasonable thing" logic. */
4765 ts = selection->tracks.filter_to_unique_playlists ();
4766 sort_track_selection (ts);
4768 /* Figure out which track to base the paste at. */
4769 TimeAxisView* base_track = NULL;
4770 if (_edit_point == Editing::EditAtMouse && entered_track) {
4771 /* With the mouse edit point, paste onto the track under the mouse. */
4772 base_track = entered_track;
4773 } else if (_edit_point == Editing::EditAtMouse && entered_regionview) {
4774 /* With the mouse edit point, paste onto the track of the region under the mouse. */
4775 base_track = &entered_regionview->get_time_axis_view();
4776 } else if (_last_cut_copy_source_track) {
4777 /* Paste to the track that the cut/copy came from (see mantis #333). */
4778 base_track = _last_cut_copy_source_track;
4780 /* This is "impossible" since we've copied... well, do nothing. */
4784 /* Walk up to parent if necessary, so base track is a route. */
4785 while (base_track->get_parent()) {
4786 base_track = base_track->get_parent();
4789 /* Add base track and all tracks below it. The paste logic will select
4790 the appropriate object types from the cut buffer in relative order. */
4791 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
4792 if ((*i)->order() >= base_track->order()) {
4797 /* Sort tracks so the nth track of type T will pick the nth object of type T. */
4798 sort_track_selection (ts);
4800 /* Add automation children of each track in order, for pasting several lines. */
4801 for (TrackViewList::iterator i = ts.begin(); i != ts.end();) {
4802 /* Add any automation children for pasting several lines */
4803 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*>(*i++);
4808 typedef RouteTimeAxisView::AutomationTracks ATracks;
4809 const ATracks& atracks = rtv->automation_tracks();
4810 for (ATracks::const_iterator a = atracks.begin(); a != atracks.end(); ++a) {
4811 i = ts.insert(i, a->second.get());
4816 /* We now have a list of trackviews starting at base_track, including
4817 automation children, in the order shown in the editor, e.g. R1,
4818 R1.A1, R1.A2, R2, R2.A1, ... */
4821 begin_reversible_command (Operations::paste);
4823 if (ts.size() == 1 && cut_buffer->lines.size() == 1 &&
4824 dynamic_cast<AutomationTimeAxisView*>(ts.front())) {
4825 /* Only one line copied, and one automation track selected. Do a
4826 "greedy" paste from one automation type to another. */
4828 PasteContext ctx(paste_count, times, ItemCounts(), true);
4829 ts.front()->paste (position, *cut_buffer, ctx, sub_num);
4833 /* Paste into tracks */
4835 PasteContext ctx(paste_count, times, ItemCounts(), false);
4836 for (TrackViewList::iterator i = ts.begin(); i != ts.end(); ++i) {
4837 (*i)->paste (position, *cut_buffer, ctx, sub_num);
4841 commit_reversible_command ();
4845 Editor::duplicate_regions (float times)
4847 RegionSelection rs (get_regions_from_selection_and_entered());
4848 duplicate_some_regions (rs, times);
4852 Editor::duplicate_some_regions (RegionSelection& regions, float times)
4854 if (regions.empty ()) {
4858 boost::shared_ptr<Playlist> playlist;
4859 RegionSelection sel = regions; // clear (below) may clear the argument list if its the current region selection
4860 RegionSelection foo;
4862 framepos_t const start_frame = regions.start ();
4863 framepos_t const end_frame = regions.end_frame ();
4864 framecnt_t const gap = end_frame - start_frame + 1;
4866 begin_reversible_command (Operations::duplicate_region);
4868 selection->clear_regions ();
4870 for (RegionSelection::iterator i = sel.begin(); i != sel.end(); ++i) {
4872 boost::shared_ptr<Region> r ((*i)->region());
4874 TimeAxisView& tv = (*i)->get_time_axis_view();
4875 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (&tv);
4876 latest_regionviews.clear ();
4877 sigc::connection c = rtv->view()->RegionViewAdded.connect (sigc::mem_fun(*this, &Editor::collect_new_region_view));
4879 framepos_t const position = end_frame + (r->first_frame() - start_frame + 1);
4880 playlist = (*i)->region()->playlist();
4881 playlist->clear_changes ();
4882 playlist->duplicate (r, position, gap, times);
4883 _session->add_command(new StatefulDiffCommand (playlist));
4887 foo.insert (foo.end(), latest_regionviews.begin(), latest_regionviews.end());
4891 selection->set (foo);
4894 commit_reversible_command ();
4898 Editor::duplicate_selection (float times)
4900 if (selection->time.empty() || selection->tracks.empty()) {
4904 boost::shared_ptr<Playlist> playlist;
4906 TrackViewList ts = selection->tracks.filter_to_unique_playlists ();
4908 bool in_command = false;
4910 for (TrackViewList::iterator i = ts.begin(); i != ts.end(); ++i) {
4911 if ((playlist = (*i)->playlist()) == 0) {
4914 playlist->clear_changes ();
4916 if (clicked_selection) {
4917 playlist->duplicate_range (selection->time[clicked_selection], times);
4919 playlist->duplicate_ranges (selection->time, times);
4923 begin_reversible_command (_("duplicate range selection"));
4926 _session->add_command (new StatefulDiffCommand (playlist));
4931 if (times == 1.0f) {
4932 // now "move" range selection to after the current range selection
4933 framecnt_t distance = 0;
4935 if (clicked_selection) {
4937 selection->time[clicked_selection].end - selection->time[clicked_selection].start;
4939 distance = selection->time.end_frame () - selection->time.start ();
4942 selection->move_time (distance);
4944 commit_reversible_command ();
4948 /** Reset all selected points to the relevant default value */
4950 Editor::reset_point_selection ()
4952 for (PointSelection::iterator i = selection->points.begin(); i != selection->points.end(); ++i) {
4953 ARDOUR::AutomationList::iterator j = (*i)->model ();
4954 (*j)->value = (*i)->line().the_list()->descriptor ().normal;
4959 Editor::center_playhead ()
4961 float const page = _visible_canvas_width * samples_per_pixel;
4962 center_screen_internal (playhead_cursor->current_frame (), page);
4966 Editor::center_edit_point ()
4968 float const page = _visible_canvas_width * samples_per_pixel;
4969 center_screen_internal (get_preferred_edit_position(), page);
4972 /** Caller must begin and commit a reversible command */
4974 Editor::clear_playlist (boost::shared_ptr<Playlist> playlist)
4976 playlist->clear_changes ();
4978 _session->add_command (new StatefulDiffCommand (playlist));
4982 Editor::nudge_track (bool use_edit, bool forwards)
4984 boost::shared_ptr<Playlist> playlist;
4985 framepos_t distance;
4986 framepos_t next_distance;
4990 start = get_preferred_edit_position();
4995 if ((distance = get_nudge_distance (start, next_distance)) == 0) {
4999 if (selection->tracks.empty()) {
5003 TrackViewList ts = selection->tracks.filter_to_unique_playlists ();
5004 bool in_command = false;
5006 for (TrackViewList::iterator i = ts.begin(); i != ts.end(); ++i) {
5008 if ((playlist = (*i)->playlist()) == 0) {
5012 playlist->clear_changes ();
5013 playlist->clear_owned_changes ();
5015 playlist->nudge_after (start, distance, forwards);
5018 begin_reversible_command (_("nudge track"));
5021 vector<Command*> cmds;
5023 playlist->rdiff (cmds);
5024 _session->add_commands (cmds);
5026 _session->add_command (new StatefulDiffCommand (playlist));
5030 commit_reversible_command ();
5035 Editor::remove_last_capture ()
5037 vector<string> choices;
5044 if (Config->get_verify_remove_last_capture()) {
5045 prompt = _("Do you really want to destroy the last capture?"
5046 "\n(This is destructive and cannot be undone)");
5048 choices.push_back (_("No, do nothing."));
5049 choices.push_back (_("Yes, destroy it."));
5051 Choice prompter (_("Destroy last capture"), prompt, choices);
5053 if (prompter.run () == 1) {
5054 _session->remove_last_capture ();
5055 _regions->redisplay ();
5059 _session->remove_last_capture();
5060 _regions->redisplay ();
5065 Editor::normalize_region ()
5071 RegionSelection rs = get_regions_from_selection_and_entered ();
5077 NormalizeDialog dialog (rs.size() > 1);
5079 if (dialog.run () != RESPONSE_ACCEPT) {
5083 CursorContext::Handle cursor_ctx = CursorContext::create(*this, _cursors->wait);
5086 /* XXX: should really only count audio regions here */
5087 int const regions = rs.size ();
5089 /* Make a list of the selected audio regions' maximum amplitudes, and also
5090 obtain the maximum amplitude of them all.
5092 list<double> max_amps;
5093 list<double> rms_vals;
5096 bool use_rms = dialog.constrain_rms ();
5098 for (RegionSelection::const_iterator i = rs.begin(); i != rs.end(); ++i) {
5099 AudioRegionView const * arv = dynamic_cast<AudioRegionView const *> (*i);
5103 dialog.descend (1.0 / regions);
5104 double const a = arv->audio_region()->maximum_amplitude (&dialog);
5106 double r = arv->audio_region()->rms (&dialog);
5107 max_rms = max (max_rms, r);
5108 rms_vals.push_back (r);
5112 /* the user cancelled the operation */
5116 max_amps.push_back (a);
5117 max_amp = max (max_amp, a);
5121 list<double>::const_iterator a = max_amps.begin ();
5122 list<double>::const_iterator l = rms_vals.begin ();
5123 bool in_command = false;
5125 for (RegionSelection::iterator r = rs.begin(); r != rs.end(); ++r) {
5126 AudioRegionView* const arv = dynamic_cast<AudioRegionView*> (*r);
5131 arv->region()->clear_changes ();
5133 double amp = dialog.normalize_individually() ? *a : max_amp;
5134 double target = dialog.target_peak (); // dB
5137 double const amp_rms = dialog.normalize_individually() ? *l : max_rms;
5138 const double t_rms = dialog.target_rms ();
5139 const gain_t c_peak = dB_to_coefficient (target);
5140 const gain_t c_rms = dB_to_coefficient (t_rms);
5141 if ((amp_rms / c_rms) > (amp / c_peak)) {
5147 arv->audio_region()->normalize (amp, target);
5150 begin_reversible_command (_("normalize"));
5153 _session->add_command (new StatefulDiffCommand (arv->region()));
5160 commit_reversible_command ();
5166 Editor::reset_region_scale_amplitude ()
5172 RegionSelection rs = get_regions_from_selection_and_entered ();
5178 bool in_command = false;
5180 for (RegionSelection::iterator r = rs.begin(); r != rs.end(); ++r) {
5181 AudioRegionView* const arv = dynamic_cast<AudioRegionView*>(*r);
5184 arv->region()->clear_changes ();
5185 arv->audio_region()->set_scale_amplitude (1.0f);
5188 begin_reversible_command ("reset gain");
5191 _session->add_command (new StatefulDiffCommand (arv->region()));
5195 commit_reversible_command ();
5200 Editor::adjust_region_gain (bool up)
5202 RegionSelection rs = get_regions_from_selection_and_entered ();
5204 if (!_session || rs.empty()) {
5208 bool in_command = false;
5210 for (RegionSelection::iterator r = rs.begin(); r != rs.end(); ++r) {
5211 AudioRegionView* const arv = dynamic_cast<AudioRegionView*>(*r);
5216 arv->region()->clear_changes ();
5218 double dB = accurate_coefficient_to_dB (arv->audio_region()->scale_amplitude ());
5226 arv->audio_region()->set_scale_amplitude (dB_to_coefficient (dB));
5229 begin_reversible_command ("adjust region gain");
5232 _session->add_command (new StatefulDiffCommand (arv->region()));
5236 commit_reversible_command ();
5241 Editor::reset_region_gain ()
5243 RegionSelection rs = get_regions_from_selection_and_entered ();
5245 if (!_session || rs.empty()) {
5249 bool in_command = false;
5251 for (RegionSelection::iterator r = rs.begin(); r != rs.end(); ++r) {
5252 AudioRegionView* const arv = dynamic_cast<AudioRegionView*>(*r);
5257 arv->region()->clear_changes ();
5259 arv->audio_region()->set_scale_amplitude (1.0f);
5262 begin_reversible_command ("reset region gain");
5265 _session->add_command (new StatefulDiffCommand (arv->region()));
5269 commit_reversible_command ();
5274 Editor::reverse_region ()
5280 Reverse rev (*_session);
5281 apply_filter (rev, _("reverse regions"));
5285 Editor::strip_region_silence ()
5291 RegionSelection rs = get_regions_from_selection_and_entered ();
5297 std::list<RegionView*> audio_only;
5299 for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
5300 AudioRegionView* const arv = dynamic_cast<AudioRegionView*> (*i);
5302 audio_only.push_back (arv);
5306 assert (!audio_only.empty());
5308 StripSilenceDialog d (_session, audio_only);
5309 int const r = d.run ();
5313 if (r == Gtk::RESPONSE_OK) {
5314 ARDOUR::AudioIntervalMap silences;
5315 d.silences (silences);
5316 StripSilence s (*_session, silences, d.fade_length());
5318 apply_filter (s, _("strip silence"), &d);
5323 Editor::apply_midi_note_edit_op_to_region (MidiOperator& op, MidiRegionView& mrv)
5325 Evoral::Sequence<Evoral::Beats>::Notes selected;
5326 mrv.selection_as_notelist (selected, true);
5328 vector<Evoral::Sequence<Evoral::Beats>::Notes> v;
5329 v.push_back (selected);
5331 Evoral::Beats pos_beats = Evoral::Beats (mrv.midi_region()->beat()) - mrv.midi_region()->start_beats();
5333 return op (mrv.midi_region()->model(), pos_beats, v);
5337 Editor::apply_midi_note_edit_op (MidiOperator& op, const RegionSelection& rs)
5343 bool in_command = false;
5345 for (RegionSelection::const_iterator r = rs.begin(); r != rs.end(); ) {
5346 RegionSelection::const_iterator tmp = r;
5349 MidiRegionView* const mrv = dynamic_cast<MidiRegionView*> (*r);
5352 Command* cmd = apply_midi_note_edit_op_to_region (op, *mrv);
5355 begin_reversible_command (op.name ());
5359 _session->add_command (cmd);
5367 commit_reversible_command ();
5368 _session->set_dirty ();
5373 Editor::fork_region ()
5375 RegionSelection rs = get_regions_from_selection_and_entered ();
5381 CursorContext::Handle cursor_ctx = CursorContext::create(*this, _cursors->wait);
5382 bool in_command = false;
5386 for (RegionSelection::iterator r = rs.begin(); r != rs.end(); ) {
5387 RegionSelection::iterator tmp = r;
5390 MidiRegionView* const mrv = dynamic_cast<MidiRegionView*>(*r);
5394 boost::shared_ptr<Playlist> playlist = mrv->region()->playlist();
5395 boost::shared_ptr<MidiSource> new_source = _session->create_midi_source_by_stealing_name (mrv->midi_view()->track());
5396 boost::shared_ptr<MidiRegion> newregion = mrv->midi_region()->clone (new_source);
5399 begin_reversible_command (_("Fork Region(s)"));
5402 playlist->clear_changes ();
5403 playlist->replace_region (mrv->region(), newregion, mrv->region()->position());
5404 _session->add_command(new StatefulDiffCommand (playlist));
5406 error << string_compose (_("Could not unlink %1"), mrv->region()->name()) << endmsg;
5414 commit_reversible_command ();
5419 Editor::quantize_region ()
5422 quantize_regions(get_regions_from_selection_and_entered ());
5427 Editor::quantize_regions (const RegionSelection& rs)
5429 if (rs.n_midi_regions() == 0) {
5433 if (!quantize_dialog) {
5434 quantize_dialog = new QuantizeDialog (*this);
5437 if (quantize_dialog->is_mapped()) {
5438 /* in progress already */
5442 quantize_dialog->present ();
5443 const int r = quantize_dialog->run ();
5444 quantize_dialog->hide ();
5446 if (r == Gtk::RESPONSE_OK) {
5447 Quantize quant (quantize_dialog->snap_start(),
5448 quantize_dialog->snap_end(),
5449 quantize_dialog->start_grid_size(),
5450 quantize_dialog->end_grid_size(),
5451 quantize_dialog->strength(),
5452 quantize_dialog->swing(),
5453 quantize_dialog->threshold());
5455 apply_midi_note_edit_op (quant, rs);
5460 Editor::legatize_region (bool shrink_only)
5463 legatize_regions(get_regions_from_selection_and_entered (), shrink_only);
5468 Editor::legatize_regions (const RegionSelection& rs, bool shrink_only)
5470 if (rs.n_midi_regions() == 0) {
5474 Legatize legatize(shrink_only);
5475 apply_midi_note_edit_op (legatize, rs);
5479 Editor::transform_region ()
5482 transform_regions(get_regions_from_selection_and_entered ());
5487 Editor::transform_regions (const RegionSelection& rs)
5489 if (rs.n_midi_regions() == 0) {
5496 const int r = td.run();
5499 if (r == Gtk::RESPONSE_OK) {
5500 Transform transform(td.get());
5501 apply_midi_note_edit_op(transform, rs);
5506 Editor::transpose_region ()
5509 transpose_regions(get_regions_from_selection_and_entered ());
5514 Editor::transpose_regions (const RegionSelection& rs)
5516 if (rs.n_midi_regions() == 0) {
5521 int const r = d.run ();
5523 if (r == RESPONSE_ACCEPT) {
5524 Transpose transpose(d.semitones ());
5525 apply_midi_note_edit_op (transpose, rs);
5530 Editor::insert_patch_change (bool from_context)
5532 RegionSelection rs = get_regions_from_selection_and_entered ();
5538 const framepos_t p = get_preferred_edit_position (EDIT_IGNORE_NONE, from_context);
5540 /* XXX: bit of a hack; use the MIDNAM from the first selected region;
5541 there may be more than one, but the PatchChangeDialog can only offer
5542 one set of patch menus.
5544 MidiRegionView* first = dynamic_cast<MidiRegionView*> (rs.front ());
5546 Evoral::PatchChange<Evoral::Beats> empty (Evoral::Beats(), 0, 0, 0);
5547 PatchChangeDialog d (0, _session, empty, first->instrument_info(), Gtk::Stock::ADD);
5549 if (d.run() == RESPONSE_CANCEL) {
5553 for (RegionSelection::iterator i = rs.begin (); i != rs.end(); ++i) {
5554 MidiRegionView* const mrv = dynamic_cast<MidiRegionView*> (*i);
5556 if (p >= mrv->region()->first_frame() && p <= mrv->region()->last_frame()) {
5557 mrv->add_patch_change (p - mrv->region()->position(), d.patch ());
5564 Editor::apply_filter (Filter& filter, string command, ProgressReporter* progress)
5566 RegionSelection rs = get_regions_from_selection_and_entered ();
5572 CursorContext::Handle cursor_ctx = CursorContext::create(*this, _cursors->wait);
5573 bool in_command = false;
5578 int const N = rs.size ();
5580 for (RegionSelection::iterator r = rs.begin(); r != rs.end(); ) {
5581 RegionSelection::iterator tmp = r;
5584 AudioRegionView* const arv = dynamic_cast<AudioRegionView*>(*r);
5586 boost::shared_ptr<Playlist> playlist = arv->region()->playlist();
5589 progress->descend (1.0 / N);
5592 if (arv->audio_region()->apply (filter, progress) == 0) {
5594 playlist->clear_changes ();
5595 playlist->clear_owned_changes ();
5598 begin_reversible_command (command);
5602 if (filter.results.empty ()) {
5604 /* no regions returned; remove the old one */
5605 playlist->remove_region (arv->region ());
5609 std::vector<boost::shared_ptr<Region> >::iterator res = filter.results.begin ();
5611 /* first region replaces the old one */
5612 playlist->replace_region (arv->region(), *res, (*res)->position());
5616 while (res != filter.results.end()) {
5617 playlist->add_region (*res, (*res)->position());
5623 /* We might have removed regions, which alters other regions' layering_index,
5624 so we need to do a recursive diff here.
5626 vector<Command*> cmds;
5627 playlist->rdiff (cmds);
5628 _session->add_commands (cmds);
5630 _session->add_command(new StatefulDiffCommand (playlist));
5634 progress->ascend ();
5643 commit_reversible_command ();
5648 Editor::external_edit_region ()
5654 Editor::reset_region_gain_envelopes ()
5656 RegionSelection rs = get_regions_from_selection_and_entered ();
5658 if (!_session || rs.empty()) {
5662 bool in_command = false;
5664 for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
5665 AudioRegionView* const arv = dynamic_cast<AudioRegionView*>(*i);
5667 boost::shared_ptr<AutomationList> alist (arv->audio_region()->envelope());
5668 XMLNode& before (alist->get_state());
5670 arv->audio_region()->set_default_envelope ();
5673 begin_reversible_command (_("reset region gain"));
5676 _session->add_command (new MementoCommand<AutomationList>(*arv->audio_region()->envelope().get(), &before, &alist->get_state()));
5681 commit_reversible_command ();
5686 Editor::set_region_gain_visibility (RegionView* rv)
5688 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (rv);
5690 arv->update_envelope_visibility();
5695 Editor::set_gain_envelope_visibility ()
5701 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
5702 AudioTimeAxisView* v = dynamic_cast<AudioTimeAxisView*>(*i);
5704 v->audio_view()->foreach_regionview (sigc::mem_fun (this, &Editor::set_region_gain_visibility));
5710 Editor::toggle_gain_envelope_active ()
5712 if (_ignore_region_action) {
5716 RegionSelection rs = get_regions_from_selection_and_entered ();
5718 if (!_session || rs.empty()) {
5722 bool in_command = false;
5724 for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
5725 AudioRegionView* const arv = dynamic_cast<AudioRegionView*>(*i);
5727 arv->region()->clear_changes ();
5728 arv->audio_region()->set_envelope_active (!arv->audio_region()->envelope_active());
5731 begin_reversible_command (_("region gain envelope active"));
5734 _session->add_command (new StatefulDiffCommand (arv->region()));
5739 commit_reversible_command ();
5744 Editor::toggle_region_lock ()
5746 if (_ignore_region_action) {
5750 RegionSelection rs = get_regions_from_selection_and_entered ();
5752 if (!_session || rs.empty()) {
5756 begin_reversible_command (_("toggle region lock"));
5758 for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
5759 (*i)->region()->clear_changes ();
5760 (*i)->region()->set_locked (!(*i)->region()->locked());
5761 _session->add_command (new StatefulDiffCommand ((*i)->region()));
5764 commit_reversible_command ();
5768 Editor::toggle_region_video_lock ()
5770 if (_ignore_region_action) {
5774 RegionSelection rs = get_regions_from_selection_and_entered ();
5776 if (!_session || rs.empty()) {
5780 begin_reversible_command (_("Toggle Video Lock"));
5782 for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
5783 (*i)->region()->clear_changes ();
5784 (*i)->region()->set_video_locked (!(*i)->region()->video_locked());
5785 _session->add_command (new StatefulDiffCommand ((*i)->region()));
5788 commit_reversible_command ();
5792 Editor::toggle_region_lock_style ()
5794 if (_ignore_region_action) {
5798 RegionSelection rs = get_regions_from_selection_and_entered ();
5800 if (!_session || rs.empty()) {
5804 Glib::RefPtr<ToggleAction> a = Glib::RefPtr<ToggleAction>::cast_dynamic (_region_actions->get_action("toggle-region-lock-style"));
5805 vector<Widget*> proxies = a->get_proxies();
5806 Gtk::CheckMenuItem* cmi = dynamic_cast<Gtk::CheckMenuItem*> (proxies.front());
5810 begin_reversible_command (_("toggle region lock style"));
5812 for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
5813 (*i)->region()->clear_changes ();
5814 PositionLockStyle const ns = ((*i)->region()->position_lock_style() == AudioTime && !cmi->get_inconsistent()) ? MusicTime : AudioTime;
5815 (*i)->region()->set_position_lock_style (ns);
5816 _session->add_command (new StatefulDiffCommand ((*i)->region()));
5819 commit_reversible_command ();
5823 Editor::toggle_opaque_region ()
5825 if (_ignore_region_action) {
5829 RegionSelection rs = get_regions_from_selection_and_entered ();
5831 if (!_session || rs.empty()) {
5835 begin_reversible_command (_("change region opacity"));
5837 for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
5838 (*i)->region()->clear_changes ();
5839 (*i)->region()->set_opaque (!(*i)->region()->opaque());
5840 _session->add_command (new StatefulDiffCommand ((*i)->region()));
5843 commit_reversible_command ();
5847 Editor::toggle_record_enable ()
5849 bool new_state = false;
5851 for (TrackSelection::iterator i = selection->tracks.begin(); i != selection->tracks.end(); ++i) {
5852 RouteTimeAxisView *rtav = dynamic_cast<RouteTimeAxisView *>(*i);
5855 if (!rtav->is_track())
5859 new_state = !rtav->track()->rec_enable_control()->get_value();
5863 rtav->track()->rec_enable_control()->set_value (new_state, Controllable::UseGroup);
5868 Editor::toggle_solo ()
5870 bool new_state = false;
5872 boost::shared_ptr<ControlList> cl (new ControlList);
5874 for (TrackSelection::iterator i = selection->tracks.begin(); i != selection->tracks.end(); ++i) {
5875 StripableTimeAxisView *stav = dynamic_cast<StripableTimeAxisView *>(*i);
5877 if (!stav || !stav->stripable()->solo_control()) {
5882 new_state = !stav->stripable()->solo_control()->soloed ();
5886 cl->push_back (stav->stripable()->solo_control());
5889 _session->set_controls (cl, new_state ? 1.0 : 0.0, Controllable::UseGroup);
5893 Editor::toggle_mute ()
5895 bool new_state = false;
5897 boost::shared_ptr<ControlList> cl (new ControlList);
5899 for (TrackSelection::iterator i = selection->tracks.begin(); i != selection->tracks.end(); ++i) {
5900 StripableTimeAxisView *stav = dynamic_cast<StripableTimeAxisView *>(*i);
5902 if (!stav || !stav->stripable()->mute_control()) {
5907 new_state = !stav->stripable()->mute_control()->muted();
5911 cl->push_back (stav->stripable()->mute_control());
5914 _session->set_controls (cl, new_state, Controllable::UseGroup);
5918 Editor::toggle_solo_isolate ()
5924 Editor::fade_range ()
5926 TrackViewList ts = selection->tracks.filter_to_unique_playlists ();
5928 begin_reversible_command (_("fade range"));
5930 for (TrackViewList::iterator i = ts.begin(); i != ts.end(); ++i) {
5931 (*i)->fade_range (selection->time);
5934 commit_reversible_command ();
5939 Editor::set_fade_length (bool in)
5941 RegionSelection rs = get_regions_from_selection_and_entered ();
5947 /* we need a region to measure the offset from the start */
5949 RegionView* rv = rs.front ();
5951 framepos_t pos = get_preferred_edit_position();
5955 if (pos > rv->region()->last_frame() || pos < rv->region()->first_frame()) {
5956 /* edit point is outside the relevant region */
5961 if (pos <= rv->region()->position()) {
5965 len = pos - rv->region()->position();
5966 cmd = _("set fade in length");
5968 if (pos >= rv->region()->last_frame()) {
5972 len = rv->region()->last_frame() - pos;
5973 cmd = _("set fade out length");
5976 bool in_command = false;
5978 for (RegionSelection::iterator x = rs.begin(); x != rs.end(); ++x) {
5979 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (*x);
5985 boost::shared_ptr<AutomationList> alist;
5987 alist = tmp->audio_region()->fade_in();
5989 alist = tmp->audio_region()->fade_out();
5992 XMLNode &before = alist->get_state();
5995 tmp->audio_region()->set_fade_in_length (len);
5996 tmp->audio_region()->set_fade_in_active (true);
5998 tmp->audio_region()->set_fade_out_length (len);
5999 tmp->audio_region()->set_fade_out_active (true);
6003 begin_reversible_command (cmd);
6006 XMLNode &after = alist->get_state();
6007 _session->add_command(new MementoCommand<AutomationList>(*alist, &before, &after));
6011 commit_reversible_command ();
6016 Editor::set_fade_in_shape (FadeShape shape)
6018 RegionSelection rs = get_regions_from_selection_and_entered ();
6023 bool in_command = false;
6025 for (RegionSelection::iterator x = rs.begin(); x != rs.end(); ++x) {
6026 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (*x);
6032 boost::shared_ptr<AutomationList> alist = tmp->audio_region()->fade_in();
6033 XMLNode &before = alist->get_state();
6035 tmp->audio_region()->set_fade_in_shape (shape);
6038 begin_reversible_command (_("set fade in shape"));
6041 XMLNode &after = alist->get_state();
6042 _session->add_command(new MementoCommand<AutomationList>(*alist.get(), &before, &after));
6046 commit_reversible_command ();
6051 Editor::set_fade_out_shape (FadeShape shape)
6053 RegionSelection rs = get_regions_from_selection_and_entered ();
6058 bool in_command = false;
6060 for (RegionSelection::iterator x = rs.begin(); x != rs.end(); ++x) {
6061 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (*x);
6067 boost::shared_ptr<AutomationList> alist = tmp->audio_region()->fade_out();
6068 XMLNode &before = alist->get_state();
6070 tmp->audio_region()->set_fade_out_shape (shape);
6073 begin_reversible_command (_("set fade out shape"));
6076 XMLNode &after = alist->get_state();
6077 _session->add_command(new MementoCommand<AutomationList>(*alist.get(), &before, &after));
6081 commit_reversible_command ();
6086 Editor::set_fade_in_active (bool yn)
6088 RegionSelection rs = get_regions_from_selection_and_entered ();
6093 bool in_command = false;
6095 for (RegionSelection::iterator x = rs.begin(); x != rs.end(); ++x) {
6096 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (*x);
6103 boost::shared_ptr<AudioRegion> ar (tmp->audio_region());
6105 ar->clear_changes ();
6106 ar->set_fade_in_active (yn);
6109 begin_reversible_command (_("set fade in active"));
6112 _session->add_command (new StatefulDiffCommand (ar));
6116 commit_reversible_command ();
6121 Editor::set_fade_out_active (bool yn)
6123 RegionSelection rs = get_regions_from_selection_and_entered ();
6128 bool in_command = false;
6130 for (RegionSelection::iterator x = rs.begin(); x != rs.end(); ++x) {
6131 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (*x);
6137 boost::shared_ptr<AudioRegion> ar (tmp->audio_region());
6139 ar->clear_changes ();
6140 ar->set_fade_out_active (yn);
6143 begin_reversible_command (_("set fade out active"));
6146 _session->add_command(new StatefulDiffCommand (ar));
6150 commit_reversible_command ();
6155 Editor::toggle_region_fades (int dir)
6157 if (_ignore_region_action) {
6161 boost::shared_ptr<AudioRegion> ar;
6164 RegionSelection rs = get_regions_from_selection_and_entered ();
6170 RegionSelection::iterator i;
6171 for (i = rs.begin(); i != rs.end(); ++i) {
6172 if ((ar = boost::dynamic_pointer_cast<AudioRegion>((*i)->region())) != 0) {
6174 yn = ar->fade_out_active ();
6176 yn = ar->fade_in_active ();
6182 if (i == rs.end()) {
6186 /* XXX should this undo-able? */
6187 bool in_command = false;
6189 for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
6190 if ((ar = boost::dynamic_pointer_cast<AudioRegion>((*i)->region())) == 0) {
6193 ar->clear_changes ();
6195 if (dir == 1 || dir == 0) {
6196 ar->set_fade_in_active (!yn);
6199 if (dir == -1 || dir == 0) {
6200 ar->set_fade_out_active (!yn);
6203 begin_reversible_command (_("toggle fade active"));
6206 _session->add_command(new StatefulDiffCommand (ar));
6210 commit_reversible_command ();
6215 /** Update region fade visibility after its configuration has been changed */
6217 Editor::update_region_fade_visibility ()
6219 bool _fade_visibility = _session->config.get_show_region_fades ();
6221 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
6222 AudioTimeAxisView* v = dynamic_cast<AudioTimeAxisView*>(*i);
6224 if (_fade_visibility) {
6225 v->audio_view()->show_all_fades ();
6227 v->audio_view()->hide_all_fades ();
6234 Editor::set_edit_point ()
6237 MusicFrame where (0, 0);
6239 if (!mouse_frame (where.frame, ignored)) {
6245 if (selection->markers.empty()) {
6247 mouse_add_new_marker (where.frame);
6252 Location* loc = find_location_from_marker (selection->markers.front(), ignored);
6255 loc->move_to (where.frame, where.division);
6261 Editor::set_playhead_cursor ()
6263 if (entered_marker) {
6264 _session->request_locate (entered_marker->position(), _session->transport_rolling());
6266 MusicFrame where (0, 0);
6269 if (!mouse_frame (where.frame, ignored)) {
6276 _session->request_locate (where.frame, _session->transport_rolling());
6280 //not sure what this was for; remove it for now.
6281 // if (UIConfiguration::instance().get_follow_edits() && (!_session || !_session->config.get_external_sync())) {
6282 // cancel_time_selection();
6288 Editor::split_region ()
6290 if (_drags->active ()) {
6294 //if a range is selected, separate it
6295 if ( !selection->time.empty()) {
6296 separate_regions_between (selection->time);
6300 //if no range was selected, try to find some regions to split
6301 if (current_mouse_mode() == MouseObject) { //don't try this for Internal Edit, Stretch, Draw, etc.
6303 RegionSelection rs = get_regions_from_selection_and_edit_point ();
6304 const framepos_t pos = get_preferred_edit_position();
6305 const int32_t division = get_grid_music_divisions (0);
6306 MusicFrame where (pos, division);
6312 split_regions_at (where, rs);
6318 Editor::select_next_stripable (bool routes_only)
6320 if (selection->tracks.empty()) {
6321 selection->set (track_views.front());
6325 TimeAxisView* current = selection->tracks.front();
6329 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
6331 if (*i == current) {
6333 if (i != track_views.end()) {
6336 current = (*(track_views.begin()));
6337 //selection->set (*(track_views.begin()));
6344 RouteUI* rui = dynamic_cast<RouteUI *>(current);
6345 valid = rui && rui->route()->active();
6347 valid = 0 != current->stripable ().get();
6350 } while (current->hidden() || !valid);
6352 selection->set (current);
6354 ensure_time_axis_view_is_visible (*current, false);
6358 Editor::select_prev_stripable (bool routes_only)
6360 if (selection->tracks.empty()) {
6361 selection->set (track_views.front());
6365 TimeAxisView* current = selection->tracks.front();
6369 for (TrackViewList::reverse_iterator i = track_views.rbegin(); i != track_views.rend(); ++i) {
6371 if (*i == current) {
6373 if (i != track_views.rend()) {
6376 current = *(track_views.rbegin());
6382 RouteUI* rui = dynamic_cast<RouteUI *>(current);
6383 valid = rui && rui->route()->active();
6385 valid = 0 != current->stripable ().get();
6388 } while (current->hidden() || !valid);
6390 selection->set (current);
6392 ensure_time_axis_view_is_visible (*current, false);
6396 Editor::set_loop_from_selection (bool play)
6398 if (_session == 0) {
6402 framepos_t start, end;
6403 if (!get_selection_extents ( start, end))
6406 set_loop_range (start, end, _("set loop range from selection"));
6409 _session->request_play_loop (true, true);
6414 Editor::set_loop_from_region (bool play)
6416 framepos_t start, end;
6417 if (!get_selection_extents ( start, end))
6420 set_loop_range (start, end, _("set loop range from region"));
6423 _session->request_locate (start, true);
6424 _session->request_play_loop (true);
6429 Editor::set_punch_from_selection ()
6431 if (_session == 0) {
6435 framepos_t start, end;
6436 if (!get_selection_extents ( start, end))
6439 set_punch_range (start, end, _("set punch range from selection"));
6443 Editor::set_auto_punch_range ()
6445 // auto punch in/out button from a single button
6446 // If Punch In is unset, set punch range from playhead to end, enable punch in
6447 // If Punch In is set, the next punch sets Punch Out, unless the playhead has been
6448 // rewound beyond the Punch In marker, in which case that marker will be moved back
6449 // to the current playhead position.
6450 // If punch out is set, it clears the punch range and Punch In/Out buttons
6452 if (_session == 0) {
6456 Location* tpl = transport_punch_location();
6457 framepos_t now = playhead_cursor->current_frame();
6458 framepos_t begin = now;
6459 framepos_t end = _session->current_end_frame();
6461 if (!_session->config.get_punch_in()) {
6462 // First Press - set punch in and create range from here to eternity
6463 set_punch_range (begin, end, _("Auto Punch In"));
6464 _session->config.set_punch_in(true);
6465 } else if (tpl && !_session->config.get_punch_out()) {
6466 // Second press - update end range marker and set punch_out
6467 if (now < tpl->start()) {
6468 // playhead has been rewound - move start back and pretend nothing happened
6470 set_punch_range (begin, end, _("Auto Punch In/Out"));
6472 // normal case for 2nd press - set the punch out
6473 end = playhead_cursor->current_frame ();
6474 set_punch_range (tpl->start(), now, _("Auto Punch In/Out"));
6475 _session->config.set_punch_out(true);
6478 if (_session->config.get_punch_out()) {
6479 _session->config.set_punch_out(false);
6482 if (_session->config.get_punch_in()) {
6483 _session->config.set_punch_in(false);
6488 // third press - unset punch in/out and remove range
6489 _session->locations()->remove(tpl);
6496 Editor::set_session_extents_from_selection ()
6498 if (_session == 0) {
6502 framepos_t start, end;
6503 if (!get_selection_extents ( start, end))
6507 if ((loc = _session->locations()->session_range_location()) == 0) {
6508 _session->set_session_extents (start, end); // this will create a new session range; no need for UNDO
6510 XMLNode &before = loc->get_state();
6512 _session->set_session_extents (start, end);
6514 XMLNode &after = loc->get_state();
6516 begin_reversible_command (_("set session start/end from selection"));
6518 _session->add_command (new MementoCommand<Location>(*loc, &before, &after));
6520 commit_reversible_command ();
6523 _session->set_end_is_free (false);
6527 Editor::set_punch_start_from_edit_point ()
6531 MusicFrame start (0, 0);
6532 framepos_t end = max_framepos;
6534 //use the existing punch end, if any
6535 Location* tpl = transport_punch_location();
6540 if ((_edit_point == EditAtPlayhead) && _session->transport_rolling()) {
6541 start.frame = _session->audible_frame();
6543 start.frame = get_preferred_edit_position();
6546 //snap the selection start/end
6549 //if there's not already a sensible selection endpoint, go "forever"
6550 if (start.frame > end ) {
6554 set_punch_range (start.frame, end, _("set punch start from EP"));
6560 Editor::set_punch_end_from_edit_point ()
6564 framepos_t start = 0;
6565 MusicFrame end (max_framepos, 0);
6567 //use the existing punch start, if any
6568 Location* tpl = transport_punch_location();
6570 start = tpl->start();
6573 if ((_edit_point == EditAtPlayhead) && _session->transport_rolling()) {
6574 end.frame = _session->audible_frame();
6576 end.frame = get_preferred_edit_position();
6579 //snap the selection start/end
6582 set_punch_range (start, end.frame, _("set punch end from EP"));
6588 Editor::set_loop_start_from_edit_point ()
6592 MusicFrame start (0, 0);
6593 framepos_t end = max_framepos;
6595 //use the existing loop end, if any
6596 Location* tpl = transport_loop_location();
6601 if ((_edit_point == EditAtPlayhead) && _session->transport_rolling()) {
6602 start.frame = _session->audible_frame();
6604 start.frame = get_preferred_edit_position();
6607 //snap the selection start/end
6610 //if there's not already a sensible selection endpoint, go "forever"
6611 if (start.frame > end ) {
6615 set_loop_range (start.frame, end, _("set loop start from EP"));
6621 Editor::set_loop_end_from_edit_point ()
6625 framepos_t start = 0;
6626 MusicFrame end (max_framepos, 0);
6628 //use the existing loop start, if any
6629 Location* tpl = transport_loop_location();
6631 start = tpl->start();
6634 if ((_edit_point == EditAtPlayhead) && _session->transport_rolling()) {
6635 end.frame = _session->audible_frame();
6637 end.frame = get_preferred_edit_position();
6640 //snap the selection start/end
6643 set_loop_range (start, end.frame, _("set loop end from EP"));
6648 Editor::set_punch_from_region ()
6650 framepos_t start, end;
6651 if (!get_selection_extents ( start, end))
6654 set_punch_range (start, end, _("set punch range from region"));
6658 Editor::pitch_shift_region ()
6660 RegionSelection rs = get_regions_from_selection_and_entered ();
6662 RegionSelection audio_rs;
6663 for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
6664 if (dynamic_cast<AudioRegionView*> (*i)) {
6665 audio_rs.push_back (*i);
6669 if (audio_rs.empty()) {
6673 pitch_shift (audio_rs, 1.2);
6677 Editor::set_tempo_from_region ()
6679 RegionSelection rs = get_regions_from_selection_and_entered ();
6681 if (!_session || rs.empty()) {
6685 RegionView* rv = rs.front();
6687 define_one_bar (rv->region()->position(), rv->region()->last_frame() + 1);
6691 Editor::use_range_as_bar ()
6693 framepos_t start, end;
6694 if (get_edit_op_range (start, end)) {
6695 define_one_bar (start, end);
6700 Editor::define_one_bar (framepos_t start, framepos_t end)
6702 framepos_t length = end - start;
6704 const Meter& m (_session->tempo_map().meter_at_frame (start));
6706 /* length = 1 bar */
6708 /* We're going to deliver a constant tempo here,
6709 so we can use frames per beat to determine length.
6710 now we want frames per beat.
6711 we have frames per bar, and beats per bar, so ...
6714 /* XXXX METER MATH */
6716 double frames_per_beat = length / m.divisions_per_bar();
6718 /* beats per minute = */
6720 double beats_per_minute = (_session->frame_rate() * 60.0) / frames_per_beat;
6722 /* now decide whether to:
6724 (a) set global tempo
6725 (b) add a new tempo marker
6729 const TempoSection& t (_session->tempo_map().tempo_section_at_frame (start));
6731 bool do_global = false;
6733 if ((_session->tempo_map().n_tempos() == 1) && (_session->tempo_map().n_meters() == 1)) {
6735 /* only 1 tempo & 1 meter: ask if the user wants to set the tempo
6736 at the start, or create a new marker
6739 vector<string> options;
6740 options.push_back (_("Cancel"));
6741 options.push_back (_("Add new marker"));
6742 options.push_back (_("Set global tempo"));
6745 _("Define one bar"),
6746 _("Do you want to set the global tempo or add a new tempo marker?"),
6750 c.set_default_response (2);
6766 /* more than 1 tempo and/or meter section already, go ahead do the "usual":
6767 if the marker is at the region starter, change it, otherwise add
6772 begin_reversible_command (_("set tempo from region"));
6773 XMLNode& before (_session->tempo_map().get_state());
6776 _session->tempo_map().change_initial_tempo (beats_per_minute, t.note_type(), t.end_note_types_per_minute());
6777 } else if (t.frame() == start) {
6778 _session->tempo_map().change_existing_tempo_at (start, beats_per_minute, t.note_type(), t.end_note_types_per_minute());
6780 /* constant tempo */
6781 const Tempo tempo (beats_per_minute, t.note_type());
6782 _session->tempo_map().add_tempo (tempo, 0.0, start, AudioTime);
6785 XMLNode& after (_session->tempo_map().get_state());
6787 _session->add_command (new MementoCommand<TempoMap>(_session->tempo_map(), &before, &after));
6788 commit_reversible_command ();
6792 Editor::split_region_at_transients ()
6794 AnalysisFeatureList positions;
6796 RegionSelection rs = get_regions_from_selection_and_entered ();
6798 if (!_session || rs.empty()) {
6802 begin_reversible_command (_("split regions"));
6804 for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ) {
6806 RegionSelection::iterator tmp;
6811 boost::shared_ptr<AudioRegion> ar = boost::dynamic_pointer_cast<AudioRegion> ((*i)->region());
6814 ar->transients (positions);
6815 split_region_at_points ((*i)->region(), positions, true);
6822 commit_reversible_command ();
6827 Editor::split_region_at_points (boost::shared_ptr<Region> r, AnalysisFeatureList& positions, bool can_ferret, bool select_new)
6829 bool use_rhythmic_rodent = false;
6831 boost::shared_ptr<Playlist> pl = r->playlist();
6833 list<boost::shared_ptr<Region> > new_regions;
6839 if (positions.empty()) {
6843 if (positions.size() > 20 && can_ferret) {
6844 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);
6845 MessageDialog msg (msgstr,
6848 Gtk::BUTTONS_OK_CANCEL);
6851 msg.add_button (_("Call for the Ferret!"), RESPONSE_APPLY);
6852 msg.set_secondary_text (_("Press OK to continue with this split operation\nor ask the Ferret dialog to tune the analysis"));
6854 msg.set_secondary_text (_("Press OK to continue with this split operation"));
6857 msg.set_title (_("Excessive split?"));
6860 int response = msg.run();
6866 case RESPONSE_APPLY:
6867 use_rhythmic_rodent = true;
6874 if (use_rhythmic_rodent) {
6875 show_rhythm_ferret ();
6879 AnalysisFeatureList::const_iterator x;
6881 pl->clear_changes ();
6882 pl->clear_owned_changes ();
6884 x = positions.begin();
6886 if (x == positions.end()) {
6891 pl->remove_region (r);
6895 framepos_t rstart = r->first_frame ();
6896 framepos_t rend = r->last_frame ();
6898 while (x != positions.end()) {
6900 /* deal with positons that are out of scope of present region bounds */
6901 if (*x <= rstart || *x > rend) {
6906 /* file start = original start + how far we from the initial position ? */
6908 framepos_t file_start = r->start() + pos;
6910 /* length = next position - current position */
6912 framepos_t len = (*x) - pos - rstart;
6914 /* XXX we do we really want to allow even single-sample regions?
6915 * shouldn't we have some kind of lower limit on region size?
6924 if (RegionFactory::region_name (new_name, r->name())) {
6928 /* do NOT announce new regions 1 by one, just wait till they are all done */
6932 plist.add (ARDOUR::Properties::start, file_start);
6933 plist.add (ARDOUR::Properties::length, len);
6934 plist.add (ARDOUR::Properties::name, new_name);
6935 plist.add (ARDOUR::Properties::layer, 0);
6936 // TODO set transients_offset
6938 boost::shared_ptr<Region> nr = RegionFactory::create (r->sources(), plist, false);
6939 /* because we set annouce to false, manually add the new region to the
6942 RegionFactory::map_add (nr);
6944 pl->add_region (nr, rstart + pos);
6947 new_regions.push_front(nr);
6956 RegionFactory::region_name (new_name, r->name());
6958 /* Add the final region */
6961 plist.add (ARDOUR::Properties::start, r->start() + pos);
6962 plist.add (ARDOUR::Properties::length, r->last_frame() - (r->position() + pos) + 1);
6963 plist.add (ARDOUR::Properties::name, new_name);
6964 plist.add (ARDOUR::Properties::layer, 0);
6966 boost::shared_ptr<Region> nr = RegionFactory::create (r->sources(), plist, false);
6967 /* because we set annouce to false, manually add the new region to the
6970 RegionFactory::map_add (nr);
6971 pl->add_region (nr, r->position() + pos);
6974 new_regions.push_front(nr);
6979 /* We might have removed regions, which alters other regions' layering_index,
6980 so we need to do a recursive diff here.
6982 vector<Command*> cmds;
6984 _session->add_commands (cmds);
6986 _session->add_command (new StatefulDiffCommand (pl));
6990 for (list<boost::shared_ptr<Region> >::iterator i = new_regions.begin(); i != new_regions.end(); ++i){
6991 set_selected_regionview_from_region_list ((*i), Selection::Add);
6997 Editor::place_transient()
7003 RegionSelection rs = get_regions_from_selection_and_edit_point ();
7009 framepos_t where = get_preferred_edit_position();
7011 begin_reversible_command (_("place transient"));
7013 for (RegionSelection::iterator r = rs.begin(); r != rs.end(); ++r) {
7014 (*r)->region()->add_transient(where);
7017 commit_reversible_command ();
7021 Editor::remove_transient(ArdourCanvas::Item* item)
7027 ArdourCanvas::Line* _line = reinterpret_cast<ArdourCanvas::Line*> (item);
7030 AudioRegionView* _arv = reinterpret_cast<AudioRegionView*> (item->get_data ("regionview"));
7031 _arv->remove_transient (*(float*) _line->get_data ("position"));
7035 Editor::snap_regions_to_grid ()
7037 list <boost::shared_ptr<Playlist > > used_playlists;
7039 RegionSelection rs = get_regions_from_selection_and_entered ();
7041 if (!_session || rs.empty()) {
7045 begin_reversible_command (_("snap regions to grid"));
7047 for (RegionSelection::iterator r = rs.begin(); r != rs.end(); ++r) {
7049 boost::shared_ptr<Playlist> pl = (*r)->region()->playlist();
7051 if (!pl->frozen()) {
7052 /* we haven't seen this playlist before */
7054 /* remember used playlists so we can thaw them later */
7055 used_playlists.push_back(pl);
7058 (*r)->region()->clear_changes ();
7060 MusicFrame start ((*r)->region()->first_frame (), 0);
7062 (*r)->region()->set_position (start.frame, start.division);
7063 _session->add_command(new StatefulDiffCommand ((*r)->region()));
7066 while (used_playlists.size() > 0) {
7067 list <boost::shared_ptr<Playlist > >::iterator i = used_playlists.begin();
7069 used_playlists.pop_front();
7072 commit_reversible_command ();
7076 Editor::close_region_gaps ()
7078 list <boost::shared_ptr<Playlist > > used_playlists;
7080 RegionSelection rs = get_regions_from_selection_and_entered ();
7082 if (!_session || rs.empty()) {
7086 Dialog dialog (_("Close Region Gaps"));
7089 table.set_spacings (12);
7090 table.set_border_width (12);
7091 Label* l = manage (left_aligned_label (_("Crossfade length")));
7092 table.attach (*l, 0, 1, 0, 1);
7094 SpinButton spin_crossfade (1, 0);
7095 spin_crossfade.set_range (0, 15);
7096 spin_crossfade.set_increments (1, 1);
7097 spin_crossfade.set_value (5);
7098 table.attach (spin_crossfade, 1, 2, 0, 1);
7100 table.attach (*manage (new Label (_("ms"))), 2, 3, 0, 1);
7102 l = manage (left_aligned_label (_("Pull-back length")));
7103 table.attach (*l, 0, 1, 1, 2);
7105 SpinButton spin_pullback (1, 0);
7106 spin_pullback.set_range (0, 100);
7107 spin_pullback.set_increments (1, 1);
7108 spin_pullback.set_value(30);
7109 table.attach (spin_pullback, 1, 2, 1, 2);
7111 table.attach (*manage (new Label (_("ms"))), 2, 3, 1, 2);
7113 dialog.get_vbox()->pack_start (table);
7114 dialog.add_button (Stock::CANCEL, RESPONSE_CANCEL);
7115 dialog.add_button (_("Ok"), RESPONSE_ACCEPT);
7118 if (dialog.run () == RESPONSE_CANCEL) {
7122 framepos_t crossfade_len = spin_crossfade.get_value();
7123 framepos_t pull_back_frames = spin_pullback.get_value();
7125 crossfade_len = lrintf (crossfade_len * _session->frame_rate()/1000);
7126 pull_back_frames = lrintf (pull_back_frames * _session->frame_rate()/1000);
7128 /* Iterate over the region list and make adjacent regions overlap by crossfade_len_ms */
7130 begin_reversible_command (_("close region gaps"));
7133 boost::shared_ptr<Region> last_region;
7135 rs.sort_by_position_and_track();
7137 for (RegionSelection::iterator r = rs.begin(); r != rs.end(); ++r) {
7139 boost::shared_ptr<Playlist> pl = (*r)->region()->playlist();
7141 if (!pl->frozen()) {
7142 /* we haven't seen this playlist before */
7144 /* remember used playlists so we can thaw them later */
7145 used_playlists.push_back(pl);
7149 framepos_t position = (*r)->region()->position();
7151 if (idx == 0 || position < last_region->position()){
7152 last_region = (*r)->region();
7157 (*r)->region()->clear_changes ();
7158 (*r)->region()->trim_front( (position - pull_back_frames));
7160 last_region->clear_changes ();
7161 last_region->trim_end( (position - pull_back_frames + crossfade_len));
7163 _session->add_command (new StatefulDiffCommand ((*r)->region()));
7164 _session->add_command (new StatefulDiffCommand (last_region));
7166 last_region = (*r)->region();
7170 while (used_playlists.size() > 0) {
7171 list <boost::shared_ptr<Playlist > >::iterator i = used_playlists.begin();
7173 used_playlists.pop_front();
7176 commit_reversible_command ();
7180 Editor::tab_to_transient (bool forward)
7182 AnalysisFeatureList positions;
7184 RegionSelection rs = get_regions_from_selection_and_entered ();
7190 framepos_t pos = _session->audible_frame ();
7192 if (!selection->tracks.empty()) {
7194 /* don't waste time searching for transients in duplicate playlists.
7197 TrackViewList ts = selection->tracks.filter_to_unique_playlists ();
7199 for (TrackViewList::iterator t = ts.begin(); t != ts.end(); ++t) {
7201 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (*t);
7204 boost::shared_ptr<Track> tr = rtv->track();
7206 boost::shared_ptr<Playlist> pl = tr->playlist ();
7208 framepos_t result = pl->find_next_transient (pos, forward ? 1 : -1);
7211 positions.push_back (result);
7224 for (RegionSelection::iterator r = rs.begin(); r != rs.end(); ++r) {
7225 (*r)->region()->get_transients (positions);
7229 TransientDetector::cleanup_transients (positions, _session->frame_rate(), 3.0);
7232 AnalysisFeatureList::iterator x;
7234 for (x = positions.begin(); x != positions.end(); ++x) {
7240 if (x != positions.end ()) {
7241 _session->request_locate (*x);
7245 AnalysisFeatureList::reverse_iterator x;
7247 for (x = positions.rbegin(); x != positions.rend(); ++x) {
7253 if (x != positions.rend ()) {
7254 _session->request_locate (*x);
7260 Editor::playhead_forward_to_grid ()
7266 MusicFrame pos (playhead_cursor->current_frame (), 0);
7268 if (pos.frame < max_framepos - 1) {
7270 snap_to_internal (pos, RoundUpAlways, false, true);
7271 _session->request_locate (pos.frame);
7277 Editor::playhead_backward_to_grid ()
7283 MusicFrame pos (playhead_cursor->current_frame (), 0);
7285 if (pos.frame > 2) {
7287 snap_to_internal (pos, RoundDownAlways, false, true);
7288 _session->request_locate (pos.frame);
7293 Editor::set_track_height (Height h)
7295 TrackSelection& ts (selection->tracks);
7297 for (TrackSelection::iterator x = ts.begin(); x != ts.end(); ++x) {
7298 (*x)->set_height_enum (h);
7303 Editor::toggle_tracks_active ()
7305 TrackSelection& ts (selection->tracks);
7307 bool target = false;
7313 for (TrackSelection::iterator x = ts.begin(); x != ts.end(); ++x) {
7314 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*>(*x);
7318 target = !rtv->_route->active();
7321 rtv->_route->set_active (target, this);
7327 Editor::remove_tracks ()
7329 /* this will delete GUI objects that may be the subject of an event
7330 handler in which this method is called. Defer actual deletion to the
7331 next idle callback, when all event handling is finished.
7333 Glib::signal_idle().connect (sigc::mem_fun (*this, &Editor::idle_remove_tracks));
7337 Editor::idle_remove_tracks ()
7339 Session::StateProtector sp (_session);
7341 return false; /* do not call again */
7345 Editor::_remove_tracks ()
7347 TrackSelection& ts (selection->tracks);
7353 vector<string> choices;
7358 const char* trackstr;
7361 vector<boost::shared_ptr<Route> > routes;
7362 vector<boost::shared_ptr<VCA> > vcas;
7363 bool special_bus = false;
7365 for (TrackSelection::iterator x = ts.begin(); x != ts.end(); ++x) {
7366 VCATimeAxisView* vtv = dynamic_cast<VCATimeAxisView*> (*x);
7368 vcas.push_back (vtv->vca());
7372 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (*x);
7376 if (rtv->is_track()) {
7381 routes.push_back (rtv->_route);
7383 if (rtv->route()->is_master() || rtv->route()->is_monitor()) {
7388 if (special_bus && !Config->get_allow_special_bus_removal()) {
7389 MessageDialog msg (_("That would be bad news ...."),
7393 msg.set_secondary_text (string_compose (_(
7394 "Removing the master or monitor bus is such a bad idea\n\
7395 that %1 is not going to allow it.\n\
7397 If you really want to do this sort of thing\n\
7398 edit your ardour.rc file to set the\n\
7399 \"allow-special-bus-removal\" option to be \"yes\""), PROGRAM_NAME));
7406 if (ntracks + nbusses + nvcas == 0) {
7412 trackstr = P_("track", "tracks", ntracks);
7413 busstr = P_("bus", "busses", nbusses);
7414 vcastr = P_("VCA", "VCAs", nvcas);
7416 if (ntracks > 0 && nbusses > 0 && nvcas > 0) {
7417 title = _("Remove various strips");
7418 prompt = string_compose (_("Do you really want to remove %1 %2, %3 %4 and %5 %6?"),
7419 ntracks, trackstr, nbusses, busstr, nvcas, vcastr);
7421 else if (ntracks > 0 && nbusses > 0) {
7422 title = string_compose (_("Remove %1 and %2"), trackstr, busstr);
7423 prompt = string_compose (_("Do you really want to remove %1 %2 and %3 %4?"),
7424 ntracks, trackstr, nbusses, busstr);
7426 else if (ntracks > 0 && nvcas > 0) {
7427 title = string_compose (_("Remove %1 and %2"), trackstr, vcastr);
7428 prompt = string_compose (_("Do you really want to remove %1 %2 and %3 %4?"),
7429 ntracks, trackstr, nvcas, vcastr);
7431 else if (nbusses > 0 && nvcas > 0) {
7432 title = string_compose (_("Remove %1 and %2"), busstr, vcastr);
7433 prompt = string_compose (_("Do you really want to remove %1 %2 and %3 %4?"),
7434 nbusses, busstr, nvcas, vcastr);
7436 else if (ntracks > 0) {
7437 title = string_compose (_("Remove %1"), trackstr);
7438 prompt = string_compose (_("Do you really want to remove %1 %2?"),
7441 else if (nbusses > 0) {
7442 title = string_compose (_("Remove %1"), busstr);
7443 prompt = string_compose (_("Do you really want to remove %1 %2?"),
7446 else if (nvcas > 0) {
7447 title = string_compose (_("Remove %1"), vcastr);
7448 prompt = string_compose (_("Do you really want to remove %1 %2?"),
7456 prompt += "\n" + string_compose ("(You may also lose the playlists associated with the %1)", trackstr) + "\n";
7459 prompt += "\n" + string(_("This action cannot be undone, and the session file will be overwritten!"));
7461 choices.push_back (_("No, do nothing."));
7462 if (ntracks + nbusses + nvcas > 1) {
7463 choices.push_back (_("Yes, remove them."));
7465 choices.push_back (_("Yes, remove it."));
7468 Choice prompter (title, prompt, choices);
7470 if (prompter.run () != 1) {
7474 if (current_mixer_strip && routes.size () > 1 && std::find (routes.begin(), routes.end(), current_mixer_strip->route()) != routes.end ()) {
7475 /* Route deletion calls Editor::timeaxisview_deleted() iteratively (for each deleted
7476 * route). If the deleted route is currently displayed in the Editor-Mixer (highly
7477 * likely because deletion requires selection) this will call
7478 * Editor::set_selected_mixer_strip () which is expensive ( MixerStrip::set_route() ).
7479 * It's likewise likely that the route that has just been displayed in the
7480 * Editor-Mixer will be next in line for deletion.
7482 * So simply switch to the master-bus (if present)
7484 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
7485 if ((*i)->stripable ()->is_master ()) {
7486 set_selected_mixer_strip (*(*i));
7493 PresentationInfo::ChangeSuspender cs;
7494 DisplaySuspender ds;
7496 boost::shared_ptr<RouteList> rl (new RouteList);
7497 for (vector<boost::shared_ptr<Route> >::iterator x = routes.begin(); x != routes.end(); ++x) {
7500 _session->remove_routes (rl);
7502 for (vector<boost::shared_ptr<VCA> >::iterator x = vcas.begin(); x != vcas.end(); ++x) {
7503 _session->vca_manager().remove_vca (*x);
7507 /* TrackSelection and RouteList leave scope,
7508 * destructors are called,
7509 * diskstream drops references, save_state is called (again for every track)
7514 Editor::do_insert_time ()
7516 if (selection->tracks.empty()) {
7520 InsertRemoveTimeDialog d (*this);
7521 int response = d.run ();
7523 if (response != RESPONSE_OK) {
7527 if (d.distance() == 0) {
7534 d.intersected_region_action (),
7538 d.move_glued_markers(),
7539 d.move_locked_markers(),
7545 Editor::insert_time (
7546 framepos_t pos, framecnt_t frames, InsertTimeOption opt,
7547 bool all_playlists, bool ignore_music_glue, bool markers_too, bool glued_markers_too, bool locked_markers_too, bool tempo_too
7551 if (Config->get_edit_mode() == Lock) {
7554 bool in_command = false;
7556 TrackViewList ts = selection->tracks.filter_to_unique_playlists ();
7558 for (TrackViewList::iterator x = ts.begin(); x != ts.end(); ++x) {
7562 /* don't operate on any playlist more than once, which could
7563 * happen if "all playlists" is enabled, but there is more
7564 * than 1 track using playlists "from" a given track.
7567 set<boost::shared_ptr<Playlist> > pl;
7569 if (all_playlists) {
7570 RouteTimeAxisView* rtav = dynamic_cast<RouteTimeAxisView*> (*x);
7571 if (rtav && rtav->track ()) {
7572 vector<boost::shared_ptr<Playlist> > all = _session->playlists->playlists_for_track (rtav->track ());
7573 for (vector<boost::shared_ptr<Playlist> >::iterator p = all.begin(); p != all.end(); ++p) {
7578 if ((*x)->playlist ()) {
7579 pl.insert ((*x)->playlist ());
7583 for (set<boost::shared_ptr<Playlist> >::iterator i = pl.begin(); i != pl.end(); ++i) {
7585 (*i)->clear_changes ();
7586 (*i)->clear_owned_changes ();
7589 begin_reversible_command (_("insert time"));
7593 if (opt == SplitIntersected) {
7594 /* non musical split */
7595 (*i)->split (MusicFrame (pos, 0));
7598 (*i)->shift (pos, frames, (opt == MoveIntersected), ignore_music_glue);
7600 vector<Command*> cmds;
7602 _session->add_commands (cmds);
7604 _session->add_command (new StatefulDiffCommand (*i));
7608 RouteTimeAxisView* rtav = dynamic_cast<RouteTimeAxisView*> (*x);
7611 begin_reversible_command (_("insert time"));
7614 rtav->route ()->shift (pos, frames);
7621 const int32_t divisions = get_grid_music_divisions (0);
7622 XMLNode& before (_session->locations()->get_state());
7623 Locations::LocationList copy (_session->locations()->list());
7625 for (Locations::LocationList::iterator i = copy.begin(); i != copy.end(); ++i) {
7627 Locations::LocationList::const_iterator tmp;
7629 if ((*i)->position_lock_style() == AudioTime || glued_markers_too) {
7630 bool const was_locked = (*i)->locked ();
7631 if (locked_markers_too) {
7635 if ((*i)->start() >= pos) {
7636 // move end first, in case we're moving by more than the length of the range
7637 if (!(*i)->is_mark()) {
7638 (*i)->set_end ((*i)->end() + frames, false, true, divisions);
7640 (*i)->set_start ((*i)->start() + frames, false, true, divisions);
7652 begin_reversible_command (_("insert time"));
7655 XMLNode& after (_session->locations()->get_state());
7656 _session->add_command (new MementoCommand<Locations>(*_session->locations(), &before, &after));
7662 begin_reversible_command (_("insert time"));
7665 XMLNode& before (_session->tempo_map().get_state());
7666 _session->tempo_map().insert_time (pos, frames);
7667 XMLNode& after (_session->tempo_map().get_state());
7668 _session->add_command (new MementoCommand<TempoMap>(_session->tempo_map(), &before, &after));
7672 commit_reversible_command ();
7677 Editor::do_remove_time ()
7679 if (selection->tracks.empty()) {
7683 InsertRemoveTimeDialog d (*this, true);
7685 int response = d.run ();
7687 if (response != RESPONSE_OK) {
7691 framecnt_t distance = d.distance();
7693 if (distance == 0) {
7703 d.move_glued_markers(),
7704 d.move_locked_markers(),
7710 Editor::remove_time (framepos_t pos, framecnt_t frames, InsertTimeOption opt,
7711 bool ignore_music_glue, bool markers_too, bool glued_markers_too, bool locked_markers_too, bool tempo_too)
7713 if (Config->get_edit_mode() == Lock) {
7714 error << (_("Cannot insert or delete time when in Lock edit.")) << endmsg;
7717 bool in_command = false;
7719 for (TrackSelection::iterator x = selection->tracks.begin(); x != selection->tracks.end(); ++x) {
7721 boost::shared_ptr<Playlist> pl = (*x)->playlist();
7725 XMLNode &before = pl->get_state();
7728 begin_reversible_command (_("remove time"));
7732 std::list<AudioRange> rl;
7733 AudioRange ar(pos, pos+frames, 0);
7736 pl->shift (pos, -frames, true, ignore_music_glue);
7738 XMLNode &after = pl->get_state();
7740 _session->add_command (new MementoCommand<Playlist> (*pl, &before, &after));
7744 RouteTimeAxisView* rtav = dynamic_cast<RouteTimeAxisView*> (*x);
7747 begin_reversible_command (_("remove time"));
7750 rtav->route ()->shift (pos, -frames);
7754 const int32_t divisions = get_grid_music_divisions (0);
7755 std::list<Location*> loc_kill_list;
7760 XMLNode& before (_session->locations()->get_state());
7761 Locations::LocationList copy (_session->locations()->list());
7763 for (Locations::LocationList::iterator i = copy.begin(); i != copy.end(); ++i) {
7764 if ((*i)->position_lock_style() == AudioTime || glued_markers_too) {
7766 bool const was_locked = (*i)->locked ();
7767 if (locked_markers_too) {
7771 if (!(*i)->is_mark()) { // it's a range; have to handle both start and end
7772 if ((*i)->end() >= pos
7773 && (*i)->end() < pos+frames
7774 && (*i)->start() >= pos
7775 && (*i)->end() < pos+frames) { // range is completely enclosed; kill it
7777 loc_kill_list.push_back(*i);
7778 } else { // only start or end is included, try to do the right thing
7779 // move start before moving end, to avoid trying to move the end to before the start
7780 // if we're removing more time than the length of the range
7781 if ((*i)->start() >= pos && (*i)->start() < pos+frames) {
7782 // start is within cut
7783 (*i)->set_start (pos, false, true,divisions); // bring the start marker to the beginning of the cut
7785 } else if ((*i)->start() >= pos+frames) {
7786 // start (and thus entire range) lies beyond end of cut
7787 (*i)->set_start ((*i)->start() - frames, false, true, divisions); // slip the start marker back
7790 if ((*i)->end() >= pos && (*i)->end() < pos+frames) {
7791 // end is inside cut
7792 (*i)->set_end (pos, false, true, divisions); // bring the end to the cut
7794 } else if ((*i)->end() >= pos+frames) {
7795 // end is beyond end of cut
7796 (*i)->set_end ((*i)->end() - frames, false, true, divisions); // slip the end marker back
7801 } else if ((*i)->start() >= pos && (*i)->start() < pos+frames ) {
7802 loc_kill_list.push_back(*i);
7804 } else if ((*i)->start() >= pos) {
7805 (*i)->set_start ((*i)->start() -frames, false, true, divisions);
7815 for (list<Location*>::iterator i = loc_kill_list.begin(); i != loc_kill_list.end(); ++i) {
7816 _session->locations()->remove( *i );
7821 begin_reversible_command (_("remove time"));
7824 XMLNode& after (_session->locations()->get_state());
7825 _session->add_command (new MementoCommand<Locations>(*_session->locations(), &before, &after));
7830 XMLNode& before (_session->tempo_map().get_state());
7832 if (_session->tempo_map().remove_time (pos, frames) ) {
7834 begin_reversible_command (_("remove time"));
7837 XMLNode& after (_session->tempo_map().get_state());
7838 _session->add_command (new MementoCommand<TempoMap>(_session->tempo_map(), &before, &after));
7843 commit_reversible_command ();
7848 Editor::fit_selection ()
7850 if (!selection->tracks.empty()) {
7851 fit_tracks (selection->tracks);
7855 /* no selected tracks - use tracks with selected regions */
7857 if (!selection->regions.empty()) {
7858 for (RegionSelection::iterator r = selection->regions.begin(); r != selection->regions.end(); ++r) {
7859 tvl.push_back (&(*r)->get_time_axis_view ());
7865 } else if (internal_editing()) {
7866 /* no selected tracks, or regions, but in internal edit mode, so follow the mouse and use
7869 if (entered_track) {
7870 tvl.push_back (entered_track);
7878 Editor::fit_tracks (TrackViewList & tracks)
7880 if (tracks.empty()) {
7884 uint32_t child_heights = 0;
7885 int visible_tracks = 0;
7887 for (TrackSelection::iterator t = tracks.begin(); t != tracks.end(); ++t) {
7889 if (!(*t)->marked_for_display()) {
7893 child_heights += (*t)->effective_height() - (*t)->current_height();
7897 /* compute the per-track height from:
7899 * total canvas visible height
7900 * - height that will be taken by visible children of selected tracks
7901 * - height of the ruler/hscroll area
7903 uint32_t h = (uint32_t) floor ((trackviews_height() - child_heights) / visible_tracks);
7904 double first_y_pos = DBL_MAX;
7906 if (h < TimeAxisView::preset_height (HeightSmall)) {
7907 MessageDialog msg (_("There are too many tracks to fit in the current window"));
7908 /* too small to be displayed */
7912 undo_visual_stack.push_back (current_visual_state (true));
7913 PBD::Unwinder<bool> nsv (no_save_visual, true);
7915 /* build a list of all tracks, including children */
7918 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
7920 TimeAxisView::Children c = (*i)->get_child_list ();
7921 for (TimeAxisView::Children::iterator j = c.begin(); j != c.end(); ++j) {
7922 all.push_back (j->get());
7927 // find selection range.
7928 // if someone knows how to user TrackViewList::iterator for this
7930 int selected_top = -1;
7931 int selected_bottom = -1;
7933 for (TrackViewList::iterator t = all.begin(); t != all.end(); ++t, ++i) {
7934 if ((*t)->marked_for_display ()) {
7935 if (tracks.contains(*t)) {
7936 if (selected_top == -1) {
7939 selected_bottom = i;
7945 for (TrackViewList::iterator t = all.begin(); t != all.end(); ++t, ++i) {
7946 if ((*t)->marked_for_display ()) {
7947 if (tracks.contains(*t)) {
7948 (*t)->set_height (h);
7949 first_y_pos = std::min ((*t)->y_position (), first_y_pos);
7951 if (i > selected_top && i < selected_bottom) {
7952 hide_track_in_display (*t);
7959 set the controls_layout height now, because waiting for its size
7960 request signal handler will cause the vertical adjustment setting to fail
7963 controls_layout.property_height () = _full_canvas_height;
7964 vertical_adjustment.set_value (first_y_pos);
7966 redo_visual_stack.push_back (current_visual_state (true));
7968 visible_tracks_selector.set_text (_("Sel"));
7972 Editor::save_visual_state (uint32_t n)
7974 while (visual_states.size() <= n) {
7975 visual_states.push_back (0);
7978 if (visual_states[n] != 0) {
7979 delete visual_states[n];
7982 visual_states[n] = current_visual_state (true);
7987 Editor::goto_visual_state (uint32_t n)
7989 if (visual_states.size() <= n) {
7993 if (visual_states[n] == 0) {
7997 use_visual_state (*visual_states[n]);
8001 Editor::start_visual_state_op (uint32_t n)
8003 save_visual_state (n);
8005 PopUp* pup = new PopUp (WIN_POS_MOUSE, 1000, true);
8007 snprintf (buf, sizeof (buf), _("Saved view %u"), n+1);
8008 pup->set_text (buf);
8013 Editor::cancel_visual_state_op (uint32_t n)
8015 goto_visual_state (n);
8019 Editor::toggle_region_mute ()
8021 if (_ignore_region_action) {
8025 RegionSelection rs = get_regions_from_selection_and_entered ();
8031 if (rs.size() > 1) {
8032 begin_reversible_command (_("mute regions"));
8034 begin_reversible_command (_("mute region"));
8037 for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
8039 (*i)->region()->playlist()->clear_changes ();
8040 (*i)->region()->set_muted (!(*i)->region()->muted ());
8041 _session->add_command (new StatefulDiffCommand ((*i)->region()));
8045 commit_reversible_command ();
8049 Editor::combine_regions ()
8051 /* foreach track with selected regions, take all selected regions
8052 and join them into a new region containing the subregions (as a
8056 typedef set<RouteTimeAxisView*> RTVS;
8059 if (selection->regions.empty()) {
8063 for (RegionSelection::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
8064 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*>(&(*i)->get_time_axis_view());
8067 tracks.insert (rtv);
8071 begin_reversible_command (_("combine regions"));
8073 vector<RegionView*> new_selection;
8075 for (RTVS::iterator i = tracks.begin(); i != tracks.end(); ++i) {
8078 if ((rv = (*i)->combine_regions ()) != 0) {
8079 new_selection.push_back (rv);
8083 selection->clear_regions ();
8084 for (vector<RegionView*>::iterator i = new_selection.begin(); i != new_selection.end(); ++i) {
8085 selection->add (*i);
8088 commit_reversible_command ();
8092 Editor::uncombine_regions ()
8094 typedef set<RouteTimeAxisView*> RTVS;
8097 if (selection->regions.empty()) {
8101 for (RegionSelection::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
8102 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*>(&(*i)->get_time_axis_view());
8105 tracks.insert (rtv);
8109 begin_reversible_command (_("uncombine regions"));
8111 for (RTVS::iterator i = tracks.begin(); i != tracks.end(); ++i) {
8112 (*i)->uncombine_regions ();
8115 commit_reversible_command ();
8119 Editor::toggle_midi_input_active (bool flip_others)
8122 boost::shared_ptr<RouteList> rl (new RouteList);
8124 for (TrackSelection::iterator i = selection->tracks.begin(); i != selection->tracks.end(); ++i) {
8125 RouteTimeAxisView *rtav = dynamic_cast<RouteTimeAxisView *>(*i);
8131 boost::shared_ptr<MidiTrack> mt = rtav->midi_track();
8134 rl->push_back (rtav->route());
8135 onoff = !mt->input_active();
8139 _session->set_exclusive_input_active (rl, onoff, flip_others);
8142 static bool ok_fine (GdkEventAny*) { return true; }
8148 lock_dialog = new Gtk::Dialog (string_compose (_("%1: Locked"), PROGRAM_NAME), true);
8150 Gtk::Image* padlock = manage (new Gtk::Image (ARDOUR_UI_UTILS::get_icon ("padlock_closed")));
8151 lock_dialog->get_vbox()->pack_start (*padlock);
8152 lock_dialog->signal_delete_event ().connect (sigc::ptr_fun (ok_fine));
8154 ArdourButton* b = manage (new ArdourButton);
8155 b->set_name ("lock button");
8156 b->set_text (_("Click to unlock"));
8157 b->signal_clicked.connect (sigc::mem_fun (*this, &Editor::unlock));
8158 lock_dialog->get_vbox()->pack_start (*b);
8160 lock_dialog->get_vbox()->show_all ();
8161 lock_dialog->set_size_request (200, 200);
8164 delete _main_menu_disabler;
8165 _main_menu_disabler = new MainMenuDisabler;
8167 lock_dialog->present ();
8169 lock_dialog->get_window()->set_decorations (Gdk::WMDecoration (0));
8175 lock_dialog->hide ();
8177 delete _main_menu_disabler;
8178 _main_menu_disabler = 0;
8180 if (UIConfiguration::instance().get_lock_gui_after_seconds()) {
8181 start_lock_event_timing ();
8186 Editor::bring_in_callback (Gtk::Label* label, uint32_t n, uint32_t total, string name)
8188 Gtkmm2ext::UI::instance()->call_slot (invalidator (*this), boost::bind (&Editor::update_bring_in_message, this, label, n, total, name));
8192 Editor::update_bring_in_message (Gtk::Label* label, uint32_t n, uint32_t total, string name)
8194 Timers::TimerSuspender t;
8195 label->set_text (string_compose ("Copying %1, %2 of %3", name, n, total));
8196 Gtkmm2ext::UI::instance()->flush_pending (1);
8200 Editor::bring_all_sources_into_session ()
8207 ArdourDialog w (_("Moving embedded files into session folder"));
8208 w.get_vbox()->pack_start (msg);
8211 /* flush all pending GUI events because we're about to start copying
8215 Timers::TimerSuspender t;
8216 Gtkmm2ext::UI::instance()->flush_pending (3);
8220 _session->bring_all_sources_into_session (boost::bind (&Editor::bring_in_callback, this, &msg, _1, _2, _3));