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"
67 #include "canvas/canvas.h"
70 #include "audio_region_view.h"
71 #include "audio_streamview.h"
72 #include "audio_time_axis.h"
73 #include "automation_region_view.h"
74 #include "automation_time_axis.h"
75 #include "control_point.h"
79 #include "editor_cursors.h"
80 #include "editor_drag.h"
81 #include "editor_regions.h"
82 #include "editor_routes.h"
83 #include "gui_thread.h"
84 #include "insert_remove_time_dialog.h"
85 #include "interthread_progress_window.h"
86 #include "item_counts.h"
88 #include "midi_region_view.h"
90 #include "mixer_strip.h"
91 #include "mouse_cursors.h"
92 #include "normalize_dialog.h"
94 #include "paste_context.h"
95 #include "patch_change_dialog.h"
96 #include "quantize_dialog.h"
97 #include "region_gain_line.h"
98 #include "rgb_macros.h"
99 #include "route_time_axis.h"
100 #include "selection.h"
101 #include "selection_templates.h"
102 #include "streamview.h"
103 #include "strip_silence_dialog.h"
104 #include "time_axis_view.h"
106 #include "transpose_dialog.h"
107 #include "transform_dialog.h"
108 #include "ui_config.h"
110 #include "pbd/i18n.h"
113 using namespace ARDOUR;
116 using namespace Gtkmm2ext;
117 using namespace ArdourWidgets;
118 using namespace Editing;
119 using Gtkmm2ext::Keyboard;
121 /***********************************************************************
123 ***********************************************************************/
126 Editor::undo (uint32_t n)
128 if (_session && _session->actively_recording()) {
129 /* no undo allowed while recording. Session will check also,
130 but we don't even want to get to that.
135 if (_drags->active ()) {
141 if (_session->undo_depth() == 0) {
142 undo_action->set_sensitive(false);
144 redo_action->set_sensitive(true);
145 begin_selection_op_history ();
150 Editor::redo (uint32_t n)
152 if (_session && _session->actively_recording()) {
153 /* no redo allowed while recording. Session will check also,
154 but we don't even want to get to that.
159 if (_drags->active ()) {
165 if (_session->redo_depth() == 0) {
166 redo_action->set_sensitive(false);
168 undo_action->set_sensitive(true);
169 begin_selection_op_history ();
174 Editor::split_regions_at (MusicFrame where, RegionSelection& regions, bool snap_frame)
178 RegionSelection pre_selected_regions = selection->regions;
179 bool working_on_selection = !pre_selected_regions.empty();
181 list<boost::shared_ptr<Playlist> > used_playlists;
182 list<RouteTimeAxisView*> used_trackviews;
184 if (regions.empty()) {
188 begin_reversible_command (_("split"));
190 // if splitting a single region, and snap-to is using
191 // region boundaries, don't pay attention to them
193 if (regions.size() == 1) {
194 switch (_snap_type) {
195 case SnapToRegionStart:
196 case SnapToRegionSync:
197 case SnapToRegionEnd:
210 EditorFreeze(); /* Emit Signal */
213 for (RegionSelection::iterator a = regions.begin(); a != regions.end(); ) {
215 RegionSelection::iterator tmp;
217 /* XXX this test needs to be more complicated, to make sure we really
218 have something to split.
221 if (!(*a)->region()->covers (where.frame)) {
229 boost::shared_ptr<Playlist> pl = (*a)->region()->playlist();
237 /* we haven't seen this playlist before */
239 /* remember used playlists so we can thaw them later */
240 used_playlists.push_back(pl);
242 TimeAxisView& tv = (*a)->get_time_axis_view();
243 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (&tv);
245 used_trackviews.push_back (rtv);
252 pl->clear_changes ();
253 pl->split_region ((*a)->region(), where);
254 _session->add_command (new StatefulDiffCommand (pl));
260 latest_regionviews.clear ();
262 vector<sigc::connection> region_added_connections;
264 for (list<RouteTimeAxisView*>::iterator i = used_trackviews.begin(); i != used_trackviews.end(); ++i) {
265 region_added_connections.push_back ((*i)->view()->RegionViewAdded.connect (sigc::mem_fun(*this, &Editor::collect_new_region_view)));
268 while (used_playlists.size() > 0) {
269 list <boost::shared_ptr<Playlist > >::iterator i = used_playlists.begin();
271 used_playlists.pop_front();
274 for (vector<sigc::connection>::iterator c = region_added_connections.begin(); c != region_added_connections.end(); ++c) {
279 EditorThaw(); /* Emit Signal */
282 if (working_on_selection) {
283 // IFF we were working on selected regions, try to reinstate the other region selections that existed before the freeze/thaw.
285 RegionSelectionAfterSplit rsas = Config->get_region_selection_after_split();
286 /* There are three classes of regions that we might want selected after
287 splitting selected regions:
288 - regions selected before the split operation, and unaffected by it
289 - newly-created regions before the split
290 - newly-created regions after the split
293 if (rsas & Existing) {
294 // region selections that existed before the split.
295 selection->add ( pre_selected_regions );
298 for (RegionSelection::iterator ri = latest_regionviews.begin(); ri != latest_regionviews.end(); ri++) {
299 if ((*ri)->region()->position() < where.frame) {
300 // new regions created before the split
301 if (rsas & NewlyCreatedLeft) {
302 selection->add (*ri);
305 // new regions created after the split
306 if (rsas & NewlyCreatedRight) {
307 selection->add (*ri);
312 if( working_on_selection ) {
313 selection->add (latest_regionviews); //these are the new regions created after the split
317 commit_reversible_command ();
320 /** Move one extreme of the current range selection. If more than one range is selected,
321 * the start of the earliest range or the end of the latest range is moved.
323 * @param move_end true to move the end of the current range selection, false to move
325 * @param next true to move the extreme to the next region boundary, false to move to
329 Editor::move_range_selection_start_or_end_to_region_boundary (bool move_end, bool next)
331 if (selection->time.start() == selection->time.end_frame()) {
335 framepos_t start = selection->time.start ();
336 framepos_t end = selection->time.end_frame ();
338 /* the position of the thing we may move */
339 framepos_t pos = move_end ? end : start;
340 int dir = next ? 1 : -1;
342 /* so we don't find the current region again */
343 if (dir > 0 || pos > 0) {
347 framepos_t const target = get_region_boundary (pos, dir, true, false);
362 begin_reversible_selection_op (_("alter selection"));
363 selection->set_preserving_all_ranges (start, end);
364 commit_reversible_selection_op ();
368 Editor::nudge_forward_release (GdkEventButton* ev)
370 if (ev->state & Keyboard::PrimaryModifier) {
371 nudge_forward (false, true);
373 nudge_forward (false, false);
379 Editor::nudge_backward_release (GdkEventButton* ev)
381 if (ev->state & Keyboard::PrimaryModifier) {
382 nudge_backward (false, true);
384 nudge_backward (false, false);
391 Editor::nudge_forward (bool next, bool force_playhead)
394 framepos_t next_distance;
400 RegionSelection rs = get_regions_from_selection_and_entered ();
402 if (!force_playhead && !rs.empty()) {
404 begin_reversible_command (_("nudge regions forward"));
406 for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
407 boost::shared_ptr<Region> r ((*i)->region());
409 distance = get_nudge_distance (r->position(), next_distance);
412 distance = next_distance;
416 r->set_position (r->position() + distance);
417 _session->add_command (new StatefulDiffCommand (r));
420 commit_reversible_command ();
423 } else if (!force_playhead && !selection->markers.empty()) {
426 bool in_command = false;
427 const int32_t divisions = get_grid_music_divisions (0);
429 for (MarkerSelection::iterator i = selection->markers.begin(); i != selection->markers.end(); ++i) {
431 Location* loc = find_location_from_marker ((*i), is_start);
435 XMLNode& before (loc->get_state());
438 distance = get_nudge_distance (loc->start(), next_distance);
440 distance = next_distance;
442 if (max_framepos - distance > loc->start() + loc->length()) {
443 loc->set_start (loc->start() + distance, false, true, divisions);
445 loc->set_start (max_framepos - loc->length(), false, true, divisions);
448 distance = get_nudge_distance (loc->end(), next_distance);
450 distance = next_distance;
452 if (max_framepos - distance > loc->end()) {
453 loc->set_end (loc->end() + distance, false, true, divisions);
455 loc->set_end (max_framepos, false, true, divisions);
457 if (loc->is_session_range()) {
458 _session->set_end_is_free (false);
462 begin_reversible_command (_("nudge location forward"));
465 XMLNode& after (loc->get_state());
466 _session->add_command (new MementoCommand<Location>(*loc, &before, &after));
471 commit_reversible_command ();
474 distance = get_nudge_distance (playhead_cursor->current_frame (), next_distance);
475 _session->request_locate (playhead_cursor->current_frame () + distance);
480 Editor::nudge_backward (bool next, bool force_playhead)
483 framepos_t next_distance;
489 RegionSelection rs = get_regions_from_selection_and_entered ();
491 if (!force_playhead && !rs.empty()) {
493 begin_reversible_command (_("nudge regions backward"));
495 for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
496 boost::shared_ptr<Region> r ((*i)->region());
498 distance = get_nudge_distance (r->position(), next_distance);
501 distance = next_distance;
506 if (r->position() > distance) {
507 r->set_position (r->position() - distance);
511 _session->add_command (new StatefulDiffCommand (r));
514 commit_reversible_command ();
516 } else if (!force_playhead && !selection->markers.empty()) {
519 bool in_command = false;
521 for (MarkerSelection::iterator i = selection->markers.begin(); i != selection->markers.end(); ++i) {
523 Location* loc = find_location_from_marker ((*i), is_start);
527 XMLNode& before (loc->get_state());
530 distance = get_nudge_distance (loc->start(), next_distance);
532 distance = next_distance;
534 if (distance < loc->start()) {
535 loc->set_start (loc->start() - distance, false, true, get_grid_music_divisions(0));
537 loc->set_start (0, false, true, get_grid_music_divisions(0));
540 distance = get_nudge_distance (loc->end(), next_distance);
543 distance = next_distance;
546 if (distance < loc->end() - loc->length()) {
547 loc->set_end (loc->end() - distance, false, true, get_grid_music_divisions(0));
549 loc->set_end (loc->length(), false, true, get_grid_music_divisions(0));
551 if (loc->is_session_range()) {
552 _session->set_end_is_free (false);
556 begin_reversible_command (_("nudge location forward"));
559 XMLNode& after (loc->get_state());
560 _session->add_command (new MementoCommand<Location>(*loc, &before, &after));
564 commit_reversible_command ();
569 distance = get_nudge_distance (playhead_cursor->current_frame (), next_distance);
571 if (playhead_cursor->current_frame () > distance) {
572 _session->request_locate (playhead_cursor->current_frame () - distance);
574 _session->goto_start();
580 Editor::nudge_forward_capture_offset ()
582 RegionSelection rs = get_regions_from_selection_and_entered ();
584 if (!_session || rs.empty()) {
588 begin_reversible_command (_("nudge forward"));
590 framepos_t const distance = _session->worst_output_latency();
592 for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
593 boost::shared_ptr<Region> r ((*i)->region());
596 r->set_position (r->position() + distance);
597 _session->add_command(new StatefulDiffCommand (r));
600 commit_reversible_command ();
604 Editor::nudge_backward_capture_offset ()
606 RegionSelection rs = get_regions_from_selection_and_entered ();
608 if (!_session || rs.empty()) {
612 begin_reversible_command (_("nudge backward"));
614 framepos_t const distance = _session->worst_output_latency();
616 for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
617 boost::shared_ptr<Region> r ((*i)->region());
621 if (r->position() > distance) {
622 r->set_position (r->position() - distance);
626 _session->add_command(new StatefulDiffCommand (r));
629 commit_reversible_command ();
632 struct RegionSelectionPositionSorter {
633 bool operator() (RegionView* a, RegionView* b) {
634 return a->region()->position() < b->region()->position();
639 Editor::sequence_regions ()
642 framepos_t r_end_prev;
650 RegionSelection rs = get_regions_from_selection_and_entered ();
651 rs.sort(RegionSelectionPositionSorter());
655 bool in_command = false;
657 for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
658 boost::shared_ptr<Region> r ((*i)->region());
666 if(r->position_locked())
673 r->set_position(r_end_prev);
677 begin_reversible_command (_("sequence regions"));
680 _session->add_command (new StatefulDiffCommand (r));
682 r_end=r->position() + r->length();
688 commit_reversible_command ();
697 Editor::move_to_start ()
699 _session->goto_start ();
703 Editor::move_to_end ()
706 _session->request_locate (_session->current_end_frame());
710 Editor::build_region_boundary_cache ()
713 vector<RegionPoint> interesting_points;
714 boost::shared_ptr<Region> r;
715 TrackViewList tracks;
718 region_boundary_cache.clear ();
724 switch (_snap_type) {
725 case SnapToRegionStart:
726 interesting_points.push_back (Start);
728 case SnapToRegionEnd:
729 interesting_points.push_back (End);
731 case SnapToRegionSync:
732 interesting_points.push_back (SyncPoint);
734 case SnapToRegionBoundary:
735 interesting_points.push_back (Start);
736 interesting_points.push_back (End);
739 fatal << string_compose (_("build_region_boundary_cache called with snap_type = %1"), _snap_type) << endmsg;
740 abort(); /*NOTREACHED*/
744 TimeAxisView *ontrack = 0;
747 if (!selection->tracks.empty()) {
748 tlist = selection->tracks.filter_to_unique_playlists ();
750 tlist = track_views.filter_to_unique_playlists ();
753 while (pos < _session->current_end_frame() && !at_end) {
756 framepos_t lpos = max_framepos;
758 for (vector<RegionPoint>::iterator p = interesting_points.begin(); p != interesting_points.end(); ++p) {
760 if ((r = find_next_region (pos, *p, 1, tlist, &ontrack)) == 0) {
761 if (*p == interesting_points.back()) {
764 /* move to next point type */
770 rpos = r->first_frame();
774 rpos = r->last_frame();
778 rpos = r->sync_position ();
786 RouteTimeAxisView *rtav;
788 if (ontrack != 0 && (rtav = dynamic_cast<RouteTimeAxisView*>(ontrack)) != 0 ) {
789 if (rtav->track() != 0) {
790 speed = rtav->track()->speed();
794 rpos = track_frame_to_session_frame (rpos, speed);
800 /* prevent duplicates, but we don't use set<> because we want to be able
804 vector<framepos_t>::iterator ri;
806 for (ri = region_boundary_cache.begin(); ri != region_boundary_cache.end(); ++ri) {
812 if (ri == region_boundary_cache.end()) {
813 region_boundary_cache.push_back (rpos);
820 /* finally sort to be sure that the order is correct */
822 sort (region_boundary_cache.begin(), region_boundary_cache.end());
825 boost::shared_ptr<Region>
826 Editor::find_next_region (framepos_t frame, RegionPoint point, int32_t dir, TrackViewList& tracks, TimeAxisView **ontrack)
828 TrackViewList::iterator i;
829 framepos_t closest = max_framepos;
830 boost::shared_ptr<Region> ret;
834 framepos_t track_frame;
835 RouteTimeAxisView *rtav;
837 for (i = tracks.begin(); i != tracks.end(); ++i) {
840 boost::shared_ptr<Region> r;
843 if ( (rtav = dynamic_cast<RouteTimeAxisView*>(*i)) != 0 ) {
844 if (rtav->track()!=0)
845 track_speed = rtav->track()->speed();
848 track_frame = session_frame_to_track_frame(frame, track_speed);
850 if ((r = (*i)->find_next_region (track_frame, point, dir)) == 0) {
856 rpos = r->first_frame ();
860 rpos = r->last_frame ();
864 rpos = r->sync_position ();
868 // rpos is a "track frame", converting it to "_session frame"
869 rpos = track_frame_to_session_frame(rpos, track_speed);
872 distance = rpos - frame;
874 distance = frame - rpos;
877 if (distance < closest) {
889 Editor::find_next_region_boundary (framepos_t pos, int32_t dir, const TrackViewList& tracks)
891 framecnt_t distance = max_framepos;
892 framepos_t current_nearest = -1;
894 for (TrackViewList::const_iterator i = tracks.begin(); i != tracks.end(); ++i) {
895 framepos_t contender;
898 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (*i);
904 if ((contender = rtv->find_next_region_boundary (pos, dir)) < 0) {
908 d = ::llabs (pos - contender);
911 current_nearest = contender;
916 return current_nearest;
920 Editor::get_region_boundary (framepos_t pos, int32_t dir, bool with_selection, bool only_onscreen)
925 if (with_selection && Config->get_region_boundaries_from_selected_tracks()) {
927 if (!selection->tracks.empty()) {
929 target = find_next_region_boundary (pos, dir, selection->tracks);
933 if (only_onscreen || Config->get_region_boundaries_from_onscreen_tracks()) {
934 get_onscreen_tracks (tvl);
935 target = find_next_region_boundary (pos, dir, tvl);
937 target = find_next_region_boundary (pos, dir, track_views);
943 if (only_onscreen || Config->get_region_boundaries_from_onscreen_tracks()) {
944 get_onscreen_tracks (tvl);
945 target = find_next_region_boundary (pos, dir, tvl);
947 target = find_next_region_boundary (pos, dir, track_views);
955 Editor::cursor_to_region_boundary (bool with_selection, int32_t dir)
957 framepos_t pos = playhead_cursor->current_frame ();
964 // so we don't find the current region again..
965 if (dir > 0 || pos > 0) {
969 if ((target = get_region_boundary (pos, dir, with_selection, false)) < 0) {
973 _session->request_locate (target);
977 Editor::cursor_to_next_region_boundary (bool with_selection)
979 cursor_to_region_boundary (with_selection, 1);
983 Editor::cursor_to_previous_region_boundary (bool with_selection)
985 cursor_to_region_boundary (with_selection, -1);
989 Editor::cursor_to_region_point (EditorCursor* cursor, RegionPoint point, int32_t dir)
991 boost::shared_ptr<Region> r;
992 framepos_t pos = cursor->current_frame ();
998 TimeAxisView *ontrack = 0;
1000 // so we don't find the current region again..
1004 if (!selection->tracks.empty()) {
1006 r = find_next_region (pos, point, dir, selection->tracks, &ontrack);
1008 } else if (clicked_axisview) {
1011 t.push_back (clicked_axisview);
1013 r = find_next_region (pos, point, dir, t, &ontrack);
1017 r = find_next_region (pos, point, dir, track_views, &ontrack);
1026 pos = r->first_frame ();
1030 pos = r->last_frame ();
1034 pos = r->sync_position ();
1039 RouteTimeAxisView *rtav;
1041 if ( ontrack != 0 && (rtav = dynamic_cast<RouteTimeAxisView*>(ontrack)) != 0 ) {
1042 if (rtav->track() != 0) {
1043 speed = rtav->track()->speed();
1047 pos = track_frame_to_session_frame(pos, speed);
1049 if (cursor == playhead_cursor) {
1050 _session->request_locate (pos);
1052 cursor->set_position (pos);
1057 Editor::cursor_to_next_region_point (EditorCursor* cursor, RegionPoint point)
1059 cursor_to_region_point (cursor, point, 1);
1063 Editor::cursor_to_previous_region_point (EditorCursor* cursor, RegionPoint point)
1065 cursor_to_region_point (cursor, point, -1);
1069 Editor::cursor_to_selection_start (EditorCursor *cursor)
1073 switch (mouse_mode) {
1075 if (!selection->regions.empty()) {
1076 pos = selection->regions.start();
1081 if (!selection->time.empty()) {
1082 pos = selection->time.start ();
1090 if (cursor == playhead_cursor) {
1091 _session->request_locate (pos);
1093 cursor->set_position (pos);
1098 Editor::cursor_to_selection_end (EditorCursor *cursor)
1102 switch (mouse_mode) {
1104 if (!selection->regions.empty()) {
1105 pos = selection->regions.end_frame();
1110 if (!selection->time.empty()) {
1111 pos = selection->time.end_frame ();
1119 if (cursor == playhead_cursor) {
1120 _session->request_locate (pos);
1122 cursor->set_position (pos);
1127 Editor::selected_marker_to_region_boundary (bool with_selection, int32_t dir)
1137 if (selection->markers.empty()) {
1141 if (!mouse_frame (mouse, ignored)) {
1145 add_location_mark (mouse);
1148 if ((loc = find_location_from_marker (selection->markers.front(), ignored)) == 0) {
1152 framepos_t pos = loc->start();
1154 // so we don't find the current region again..
1155 if (dir > 0 || pos > 0) {
1159 if ((target = get_region_boundary (pos, dir, with_selection, false)) < 0) {
1163 loc->move_to (target, 0);
1167 Editor::selected_marker_to_next_region_boundary (bool with_selection)
1169 selected_marker_to_region_boundary (with_selection, 1);
1173 Editor::selected_marker_to_previous_region_boundary (bool with_selection)
1175 selected_marker_to_region_boundary (with_selection, -1);
1179 Editor::selected_marker_to_region_point (RegionPoint point, int32_t dir)
1181 boost::shared_ptr<Region> r;
1186 if (!_session || selection->markers.empty()) {
1190 if ((loc = find_location_from_marker (selection->markers.front(), ignored)) == 0) {
1194 TimeAxisView *ontrack = 0;
1198 // so we don't find the current region again..
1202 if (!selection->tracks.empty()) {
1204 r = find_next_region (pos, point, dir, selection->tracks, &ontrack);
1208 r = find_next_region (pos, point, dir, track_views, &ontrack);
1217 pos = r->first_frame ();
1221 pos = r->last_frame ();
1225 pos = r->adjust_to_sync (r->first_frame());
1230 RouteTimeAxisView *rtav;
1232 if (ontrack != 0 && (rtav = dynamic_cast<RouteTimeAxisView*>(ontrack)) != 0) {
1233 if (rtav->track() != 0) {
1234 speed = rtav->track()->speed();
1238 pos = track_frame_to_session_frame(pos, speed);
1240 loc->move_to (pos, 0);
1244 Editor::selected_marker_to_next_region_point (RegionPoint point)
1246 selected_marker_to_region_point (point, 1);
1250 Editor::selected_marker_to_previous_region_point (RegionPoint point)
1252 selected_marker_to_region_point (point, -1);
1256 Editor::selected_marker_to_selection_start ()
1262 if (!_session || selection->markers.empty()) {
1266 if ((loc = find_location_from_marker (selection->markers.front(), ignored)) == 0) {
1270 switch (mouse_mode) {
1272 if (!selection->regions.empty()) {
1273 pos = selection->regions.start();
1278 if (!selection->time.empty()) {
1279 pos = selection->time.start ();
1287 loc->move_to (pos, 0);
1291 Editor::selected_marker_to_selection_end ()
1297 if (!_session || selection->markers.empty()) {
1301 if ((loc = find_location_from_marker (selection->markers.front(), ignored)) == 0) {
1305 switch (mouse_mode) {
1307 if (!selection->regions.empty()) {
1308 pos = selection->regions.end_frame();
1313 if (!selection->time.empty()) {
1314 pos = selection->time.end_frame ();
1322 loc->move_to (pos, 0);
1326 Editor::scroll_playhead (bool forward)
1328 framepos_t pos = playhead_cursor->current_frame ();
1329 framecnt_t delta = (framecnt_t) floor (current_page_samples() / 0.8);
1332 if (pos == max_framepos) {
1336 if (pos < max_framepos - delta) {
1355 _session->request_locate (pos);
1359 Editor::cursor_align (bool playhead_to_edit)
1365 if (playhead_to_edit) {
1367 if (selection->markers.empty()) {
1371 _session->request_locate (selection->markers.front()->position(), _session->transport_rolling());
1374 const int32_t divisions = get_grid_music_divisions (0);
1375 /* move selected markers to playhead */
1377 for (MarkerSelection::iterator i = selection->markers.begin(); i != selection->markers.end(); ++i) {
1380 Location* loc = find_location_from_marker (*i, ignored);
1382 if (loc->is_mark()) {
1383 loc->set_start (playhead_cursor->current_frame (), false, true, divisions);
1385 loc->set (playhead_cursor->current_frame (),
1386 playhead_cursor->current_frame () + loc->length(), true, divisions);
1393 Editor::scroll_backward (float pages)
1395 framepos_t const one_page = (framepos_t) rint (_visible_canvas_width * samples_per_pixel);
1396 framepos_t const cnt = (framepos_t) floor (pages * one_page);
1399 if (leftmost_frame < cnt) {
1402 frame = leftmost_frame - cnt;
1405 reset_x_origin (frame);
1409 Editor::scroll_forward (float pages)
1411 framepos_t const one_page = (framepos_t) rint (_visible_canvas_width * samples_per_pixel);
1412 framepos_t const cnt = (framepos_t) floor (pages * one_page);
1415 if (max_framepos - cnt < leftmost_frame) {
1416 frame = max_framepos - cnt;
1418 frame = leftmost_frame + cnt;
1421 reset_x_origin (frame);
1425 Editor::scroll_tracks_down ()
1427 double vert_value = vertical_adjustment.get_value() + vertical_adjustment.get_page_size();
1428 if (vert_value > vertical_adjustment.get_upper() - _visible_canvas_height) {
1429 vert_value = vertical_adjustment.get_upper() - _visible_canvas_height;
1432 vertical_adjustment.set_value (vert_value);
1436 Editor::scroll_tracks_up ()
1438 vertical_adjustment.set_value (vertical_adjustment.get_value() - vertical_adjustment.get_page_size());
1442 Editor::scroll_tracks_down_line ()
1444 double vert_value = vertical_adjustment.get_value() + 60;
1446 if (vert_value > vertical_adjustment.get_upper() - _visible_canvas_height) {
1447 vert_value = vertical_adjustment.get_upper() - _visible_canvas_height;
1450 vertical_adjustment.set_value (vert_value);
1454 Editor::scroll_tracks_up_line ()
1456 reset_y_origin (vertical_adjustment.get_value() - 60);
1460 Editor::select_topmost_track ()
1462 const double top_of_trackviews = vertical_adjustment.get_value();
1463 for (TrackViewList::iterator t = track_views.begin(); t != track_views.end(); ++t) {
1464 if ((*t)->hidden()) {
1467 std::pair<TimeAxisView*,double> res = (*t)->covers_y_position (top_of_trackviews);
1469 selection->set (*t);
1476 Editor::scroll_down_one_track (bool skip_child_views)
1478 TrackViewList::reverse_iterator next = track_views.rend();
1479 const double top_of_trackviews = vertical_adjustment.get_value();
1481 for (TrackViewList::reverse_iterator t = track_views.rbegin(); t != track_views.rend(); ++t) {
1482 if ((*t)->hidden()) {
1486 /* If this is the upper-most visible trackview, we want to display
1487 * the one above it (next)
1489 * Note that covers_y_position() is recursive and includes child views
1491 std::pair<TimeAxisView*,double> res = (*t)->covers_y_position (top_of_trackviews);
1494 if (skip_child_views) {
1497 /* automation lane (one level, non-recursive)
1499 * - if no automation lane exists -> move to next tack
1500 * - if the first (here: bottom-most) matches -> move to next tack
1501 * - if no y-axis match is found -> the current track is at the top
1502 * -> move to last (here: top-most) automation lane
1504 TimeAxisView::Children kids = (*t)->get_child_list();
1505 TimeAxisView::Children::reverse_iterator nkid = kids.rend();
1507 for (TimeAxisView::Children::reverse_iterator ci = kids.rbegin(); ci != kids.rend(); ++ci) {
1508 if ((*ci)->hidden()) {
1512 std::pair<TimeAxisView*,double> dev;
1513 dev = (*ci)->covers_y_position (top_of_trackviews);
1515 /* some automation lane is currently at the top */
1516 if (ci == kids.rbegin()) {
1517 /* first (bottom-most) autmation lane is at the top.
1518 * -> move to next track
1527 if (nkid != kids.rend()) {
1528 ensure_time_axis_view_is_visible (**nkid, true);
1536 /* move to the track below the first one that covers the */
1538 if (next != track_views.rend()) {
1539 ensure_time_axis_view_is_visible (**next, true);
1547 Editor::scroll_up_one_track (bool skip_child_views)
1549 TrackViewList::iterator prev = track_views.end();
1550 double top_of_trackviews = vertical_adjustment.get_value ();
1552 for (TrackViewList::iterator t = track_views.begin(); t != track_views.end(); ++t) {
1554 if ((*t)->hidden()) {
1558 /* find the trackview at the top of the trackview group
1560 * Note that covers_y_position() is recursive and includes child views
1562 std::pair<TimeAxisView*,double> res = (*t)->covers_y_position (top_of_trackviews);
1565 if (skip_child_views) {
1568 /* automation lane (one level, non-recursive)
1570 * - if no automation lane exists -> move to prev tack
1571 * - if no y-axis match is found -> the current track is at the top -> move to prev track
1572 * (actually last automation lane of previous track, see below)
1573 * - if first (top-most) lane is at the top -> move to this track
1574 * - else move up one lane
1576 TimeAxisView::Children kids = (*t)->get_child_list();
1577 TimeAxisView::Children::iterator pkid = kids.end();
1579 for (TimeAxisView::Children::iterator ci = kids.begin(); ci != kids.end(); ++ci) {
1580 if ((*ci)->hidden()) {
1584 std::pair<TimeAxisView*,double> dev;
1585 dev = (*ci)->covers_y_position (top_of_trackviews);
1587 /* some automation lane is currently at the top */
1588 if (ci == kids.begin()) {
1589 /* first (top-most) autmation lane is at the top.
1590 * jump directly to this track's top
1592 ensure_time_axis_view_is_visible (**t, true);
1595 else if (pkid != kids.end()) {
1596 /* some other automation lane is at the top.
1597 * move up to prev automation lane.
1599 ensure_time_axis_view_is_visible (**pkid, true);
1602 assert(0); // not reached
1613 if (prev != track_views.end()) {
1614 // move to bottom-most automation-lane of the previous track
1615 TimeAxisView::Children kids = (*prev)->get_child_list();
1616 TimeAxisView::Children::reverse_iterator pkid = kids.rend();
1617 if (!skip_child_views) {
1618 // find the last visible lane
1619 for (TimeAxisView::Children::reverse_iterator ci = kids.rbegin(); ci != kids.rend(); ++ci) {
1620 if (!(*ci)->hidden()) {
1626 if (pkid != kids.rend()) {
1627 ensure_time_axis_view_is_visible (**pkid, true);
1629 ensure_time_axis_view_is_visible (**prev, true);
1638 Editor::scroll_left_step ()
1640 framepos_t xdelta = (current_page_samples() / 8);
1642 if (leftmost_frame > xdelta) {
1643 reset_x_origin (leftmost_frame - xdelta);
1651 Editor::scroll_right_step ()
1653 framepos_t xdelta = (current_page_samples() / 8);
1655 if (max_framepos - xdelta > leftmost_frame) {
1656 reset_x_origin (leftmost_frame + xdelta);
1658 reset_x_origin (max_framepos - current_page_samples());
1663 Editor::scroll_left_half_page ()
1665 framepos_t xdelta = (current_page_samples() / 2);
1666 if (leftmost_frame > xdelta) {
1667 reset_x_origin (leftmost_frame - xdelta);
1674 Editor::scroll_right_half_page ()
1676 framepos_t xdelta = (current_page_samples() / 2);
1677 if (max_framepos - xdelta > leftmost_frame) {
1678 reset_x_origin (leftmost_frame + xdelta);
1680 reset_x_origin (max_framepos - current_page_samples());
1687 Editor::tav_zoom_step (bool coarser)
1689 DisplaySuspender ds;
1693 if (selection->tracks.empty()) {
1696 ts = &selection->tracks;
1699 for (TrackViewList::iterator i = ts->begin(); i != ts->end(); ++i) {
1700 TimeAxisView *tv = (static_cast<TimeAxisView*>(*i));
1701 tv->step_height (coarser);
1706 Editor::tav_zoom_smooth (bool coarser, bool force_all)
1708 DisplaySuspender ds;
1712 if (selection->tracks.empty() || force_all) {
1715 ts = &selection->tracks;
1718 for (TrackViewList::iterator i = ts->begin(); i != ts->end(); ++i) {
1719 TimeAxisView *tv = (static_cast<TimeAxisView*>(*i));
1720 uint32_t h = tv->current_height ();
1725 if (h >= TimeAxisView::preset_height (HeightSmall)) {
1730 tv->set_height (h + 5);
1736 Editor::temporal_zoom_step_mouse_focus_scale (bool zoom_out, double scale)
1738 Editing::ZoomFocus temp_focus = zoom_focus;
1739 zoom_focus = Editing::ZoomFocusMouse;
1740 temporal_zoom_step_scale (zoom_out, scale);
1741 zoom_focus = temp_focus;
1745 Editor::temporal_zoom_step_mouse_focus (bool zoom_out)
1747 temporal_zoom_step_mouse_focus_scale (zoom_out, 2.0);
1751 Editor::temporal_zoom_step (bool zoom_out)
1753 temporal_zoom_step_scale (zoom_out, 2.0);
1757 Editor::temporal_zoom_step_scale (bool zoom_out, double scale)
1759 ENSURE_GUI_THREAD (*this, &Editor::temporal_zoom_step, zoom_out, scale)
1761 framecnt_t nspp = samples_per_pixel;
1765 if (nspp == samples_per_pixel) {
1770 if (nspp == samples_per_pixel) {
1775 temporal_zoom (nspp);
1779 Editor::temporal_zoom (framecnt_t fpp)
1785 framepos_t current_page = current_page_samples();
1786 framepos_t current_leftmost = leftmost_frame;
1787 framepos_t current_rightmost;
1788 framepos_t current_center;
1789 framepos_t new_page_size;
1790 framepos_t half_page_size;
1791 framepos_t leftmost_after_zoom = 0;
1793 bool in_track_canvas;
1794 bool use_mouse_frame = true;
1798 if (fpp == samples_per_pixel) {
1802 // Imposing an arbitrary limit to zoom out as too much zoom out produces
1803 // segfaults for lack of memory. If somebody decides this is not high enough I
1804 // believe it can be raisen to higher values but some limit must be in place.
1806 // This constant represents 1 day @ 48kHz on a 1600 pixel wide display
1807 // all of which is used for the editor track displays. The whole day
1808 // would be 4147200000 samples, so 2592000 samples per pixel.
1810 nfpp = min (fpp, (framecnt_t) 2592000);
1811 nfpp = max ((framecnt_t) 1, nfpp);
1813 new_page_size = (framepos_t) floor (_visible_canvas_width * nfpp);
1814 half_page_size = new_page_size / 2;
1816 switch (zoom_focus) {
1818 leftmost_after_zoom = current_leftmost;
1821 case ZoomFocusRight:
1822 current_rightmost = leftmost_frame + current_page;
1823 if (current_rightmost < new_page_size) {
1824 leftmost_after_zoom = 0;
1826 leftmost_after_zoom = current_rightmost - new_page_size;
1830 case ZoomFocusCenter:
1831 current_center = current_leftmost + (current_page/2);
1832 if (current_center < half_page_size) {
1833 leftmost_after_zoom = 0;
1835 leftmost_after_zoom = current_center - half_page_size;
1839 case ZoomFocusPlayhead:
1840 /* centre playhead */
1841 l = playhead_cursor->current_frame () - (new_page_size * 0.5);
1844 leftmost_after_zoom = 0;
1845 } else if (l > max_framepos) {
1846 leftmost_after_zoom = max_framepos - new_page_size;
1848 leftmost_after_zoom = (framepos_t) l;
1852 case ZoomFocusMouse:
1853 /* try to keep the mouse over the same point in the display */
1855 if (_drags->active()) {
1856 where = _drags->current_pointer_frame ();
1857 } else if (!mouse_frame (where, in_track_canvas)) {
1858 use_mouse_frame = false;
1861 if (use_mouse_frame) {
1862 l = - ((new_page_size * ((where - current_leftmost)/(double)current_page)) - where);
1865 leftmost_after_zoom = 0;
1866 } else if (l > max_framepos) {
1867 leftmost_after_zoom = max_framepos - new_page_size;
1869 leftmost_after_zoom = (framepos_t) l;
1872 /* use playhead instead */
1873 where = playhead_cursor->current_frame ();
1875 if (where < half_page_size) {
1876 leftmost_after_zoom = 0;
1878 leftmost_after_zoom = where - half_page_size;
1884 /* try to keep the edit point in the same place */
1885 where = get_preferred_edit_position ();
1889 double l = - ((new_page_size * ((where - current_leftmost)/(double)current_page)) - where);
1892 leftmost_after_zoom = 0;
1893 } else if (l > max_framepos) {
1894 leftmost_after_zoom = max_framepos - new_page_size;
1896 leftmost_after_zoom = (framepos_t) l;
1900 /* edit point not defined */
1907 // leftmost_after_zoom = min (leftmost_after_zoom, _session->current_end_frame());
1909 reposition_and_zoom (leftmost_after_zoom, nfpp);
1913 Editor::calc_extra_zoom_edges(framepos_t &start, framepos_t &end)
1915 /* this func helps make sure we leave a little space
1916 at each end of the editor so that the zoom doesn't fit the region
1917 precisely to the screen.
1920 GdkScreen* screen = gdk_screen_get_default ();
1921 const gint pixwidth = gdk_screen_get_width (screen);
1922 const gint mmwidth = gdk_screen_get_width_mm (screen);
1923 const double pix_per_mm = (double) pixwidth/ (double) mmwidth;
1924 const double one_centimeter_in_pixels = pix_per_mm * 10.0;
1926 const framepos_t range = end - start;
1927 const framecnt_t new_fpp = (framecnt_t) ceil ((double) range / (double) _visible_canvas_width);
1928 const framepos_t extra_samples = (framepos_t) floor (one_centimeter_in_pixels * new_fpp);
1930 if (start > extra_samples) {
1931 start -= extra_samples;
1936 if (max_framepos - extra_samples > end) {
1937 end += extra_samples;
1944 Editor::get_selection_extents (framepos_t &start, framepos_t &end) const
1946 start = max_framepos;
1950 //ToDo: if notes are selected, set extents to that selection
1952 //ToDo: if control points are selected, set extents to that selection
1954 if ( !selection->regions.empty() ) {
1955 RegionSelection rs = get_regions_from_selection_and_entered ();
1957 for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
1959 if ((*i)->region()->position() < start) {
1960 start = (*i)->region()->position();
1963 if ((*i)->region()->last_frame() + 1 > end) {
1964 end = (*i)->region()->last_frame() + 1;
1968 } else if (!selection->time.empty()) {
1969 start = selection->time.start();
1970 end = selection->time.end_frame();
1972 ret = false; //no selection found
1975 if ((start == 0 && end == 0) || end < start) {
1984 Editor::temporal_zoom_selection (Editing::ZoomAxis axes)
1986 if (!selection) return;
1988 //ToDo: if notes are selected, zoom to that
1990 //ToDo: if control points are selected, zoom to that
1992 if (axes == Horizontal || axes == Both) {
1994 framepos_t start, end;
1995 if (get_selection_extents (start, end)) {
1996 calc_extra_zoom_edges (start, end);
1997 temporal_zoom_by_frame (start, end);
2001 if (axes == Vertical || axes == Both) {
2007 Editor::temporal_zoom_session ()
2009 ENSURE_GUI_THREAD (*this, &Editor::temporal_zoom_session)
2012 framecnt_t start = _session->current_start_frame();
2013 framecnt_t end = _session->current_end_frame();
2015 if (_session->actively_recording () ) {
2016 framepos_t cur = playhead_cursor->current_frame ();
2018 /* recording beyond the end marker; zoom out
2019 * by 5 seconds more so that if 'follow
2020 * playhead' is active we don't immediately
2023 end = cur + _session->frame_rate() * 5;
2027 if ((start == 0 && end == 0) || end < start) {
2031 calc_extra_zoom_edges(start, end);
2033 temporal_zoom_by_frame (start, end);
2038 Editor::temporal_zoom_by_frame (framepos_t start, framepos_t end)
2040 if (!_session) return;
2042 if ((start == 0 && end == 0) || end < start) {
2046 framepos_t range = end - start;
2048 const framecnt_t new_fpp = (framecnt_t) ceil ((double) range / (double) _visible_canvas_width);
2050 framepos_t new_page = range;
2051 framepos_t middle = (framepos_t) floor ((double) start + ((double) range / 2.0f));
2052 framepos_t new_leftmost = (framepos_t) floor ((double) middle - ((double) new_page / 2.0f));
2054 if (new_leftmost > middle) {
2058 if (new_leftmost < 0) {
2062 reposition_and_zoom (new_leftmost, new_fpp);
2066 Editor::temporal_zoom_to_frame (bool coarser, framepos_t frame)
2072 framecnt_t range_before = frame - leftmost_frame;
2076 if (samples_per_pixel <= 1) {
2079 new_spp = samples_per_pixel + (samples_per_pixel/2);
2081 range_before += range_before/2;
2083 if (samples_per_pixel >= 1) {
2084 new_spp = samples_per_pixel - (samples_per_pixel/2);
2086 /* could bail out here since we cannot zoom any finer,
2087 but leave that to the equality test below
2089 new_spp = samples_per_pixel;
2092 range_before -= range_before/2;
2095 if (new_spp == samples_per_pixel) {
2099 /* zoom focus is automatically taken as @param frame when this
2103 framepos_t new_leftmost = frame - (framepos_t)range_before;
2105 if (new_leftmost > frame) {
2109 if (new_leftmost < 0) {
2113 reposition_and_zoom (new_leftmost, new_spp);
2118 Editor::choose_new_marker_name(string &name) {
2120 if (!UIConfiguration::instance().get_name_new_markers()) {
2121 /* don't prompt user for a new name */
2125 Prompter dialog (true);
2127 dialog.set_prompt (_("New Name:"));
2129 dialog.set_title (_("New Location Marker"));
2131 dialog.set_name ("MarkNameWindow");
2132 dialog.set_size_request (250, -1);
2133 dialog.set_position (Gtk::WIN_POS_MOUSE);
2135 dialog.add_button (Stock::OK, RESPONSE_ACCEPT);
2136 dialog.set_initial_text (name);
2140 switch (dialog.run ()) {
2141 case RESPONSE_ACCEPT:
2147 dialog.get_result(name);
2154 Editor::add_location_from_selection ()
2158 if (selection->time.empty()) {
2162 if (_session == 0 || clicked_axisview == 0) {
2166 framepos_t start = selection->time[clicked_selection].start;
2167 framepos_t end = selection->time[clicked_selection].end;
2169 _session->locations()->next_available_name(rangename,"selection");
2170 Location *location = new Location (*_session, start, end, rangename, Location::IsRangeMarker, get_grid_music_divisions(0));
2172 begin_reversible_command (_("add marker"));
2174 XMLNode &before = _session->locations()->get_state();
2175 _session->locations()->add (location, true);
2176 XMLNode &after = _session->locations()->get_state();
2177 _session->add_command(new MementoCommand<Locations>(*(_session->locations()), &before, &after));
2179 commit_reversible_command ();
2183 Editor::add_location_mark (framepos_t where)
2187 select_new_marker = true;
2189 _session->locations()->next_available_name(markername,"mark");
2190 if (!choose_new_marker_name(markername)) {
2193 Location *location = new Location (*_session, where, where, markername, Location::IsMark, get_grid_music_divisions (0));
2194 begin_reversible_command (_("add marker"));
2196 XMLNode &before = _session->locations()->get_state();
2197 _session->locations()->add (location, true);
2198 XMLNode &after = _session->locations()->get_state();
2199 _session->add_command(new MementoCommand<Locations>(*(_session->locations()), &before, &after));
2201 commit_reversible_command ();
2205 Editor::set_session_start_from_playhead ()
2211 if ((loc = _session->locations()->session_range_location()) == 0) { //should never happen
2212 _session->set_session_extents ( _session->audible_frame(), _session->audible_frame() );
2214 XMLNode &before = loc->get_state();
2216 _session->set_session_extents ( _session->audible_frame(), loc->end() );
2218 XMLNode &after = loc->get_state();
2220 begin_reversible_command (_("Set session start"));
2222 _session->add_command (new MementoCommand<Location>(*loc, &before, &after));
2224 commit_reversible_command ();
2229 Editor::set_session_end_from_playhead ()
2235 if ((loc = _session->locations()->session_range_location()) == 0) { //should never happen
2236 _session->set_session_extents ( _session->audible_frame(), _session->audible_frame() );
2238 XMLNode &before = loc->get_state();
2240 _session->set_session_extents ( loc->start(), _session->audible_frame() );
2242 XMLNode &after = loc->get_state();
2244 begin_reversible_command (_("Set session start"));
2246 _session->add_command (new MementoCommand<Location>(*loc, &before, &after));
2248 commit_reversible_command ();
2251 _session->set_end_is_free (false);
2256 Editor::toggle_location_at_playhead_cursor ()
2258 if (!do_remove_location_at_playhead_cursor())
2260 add_location_from_playhead_cursor();
2265 Editor::add_location_from_playhead_cursor ()
2267 add_location_mark (_session->audible_frame());
2271 Editor::do_remove_location_at_playhead_cursor ()
2273 bool removed = false;
2276 XMLNode &before = _session->locations()->get_state();
2278 //find location(s) at this time
2279 Locations::LocationList locs;
2280 _session->locations()->find_all_between (_session->audible_frame(), _session->audible_frame()+1, locs, Location::Flags(0));
2281 for (Locations::LocationList::iterator i = locs.begin(); i != locs.end(); ++i) {
2282 if ((*i)->is_mark()) {
2283 _session->locations()->remove (*i);
2290 begin_reversible_command (_("remove marker"));
2291 XMLNode &after = _session->locations()->get_state();
2292 _session->add_command(new MementoCommand<Locations>(*(_session->locations()), &before, &after));
2293 commit_reversible_command ();
2300 Editor::remove_location_at_playhead_cursor ()
2302 do_remove_location_at_playhead_cursor ();
2305 /** Add a range marker around each selected region */
2307 Editor::add_locations_from_region ()
2309 RegionSelection rs = get_regions_from_selection_and_entered ();
2314 bool commit = false;
2316 XMLNode &before = _session->locations()->get_state();
2318 for (RegionSelection::iterator i = rs.begin (); i != rs.end (); ++i) {
2320 boost::shared_ptr<Region> region = (*i)->region ();
2322 Location *location = new Location (*_session, region->position(), region->last_frame(), region->name(), Location::IsRangeMarker, 0);
2324 _session->locations()->add (location, true);
2329 begin_reversible_command (selection->regions.size () > 1 ? _("add markers") : _("add marker"));
2330 XMLNode &after = _session->locations()->get_state();
2331 _session->add_command (new MementoCommand<Locations>(*(_session->locations()), &before, &after));
2332 commit_reversible_command ();
2336 /** Add a single range marker around all selected regions */
2338 Editor::add_location_from_region ()
2340 RegionSelection rs = get_regions_from_selection_and_entered ();
2346 XMLNode &before = _session->locations()->get_state();
2350 if (rs.size() > 1) {
2351 _session->locations()->next_available_name(markername, "regions");
2353 RegionView* rv = *(rs.begin());
2354 boost::shared_ptr<Region> region = rv->region();
2355 markername = region->name();
2358 if (!choose_new_marker_name(markername)) {
2362 // single range spanning all selected
2363 Location *location = new Location (*_session, selection->regions.start(), selection->regions.end_frame(), markername, Location::IsRangeMarker, 0);
2364 _session->locations()->add (location, true);
2366 begin_reversible_command (_("add marker"));
2367 XMLNode &after = _session->locations()->get_state();
2368 _session->add_command (new MementoCommand<Locations>(*(_session->locations()), &before, &after));
2369 commit_reversible_command ();
2375 Editor::jump_forward_to_mark ()
2381 framepos_t pos = _session->locations()->first_mark_after (playhead_cursor->current_frame());
2387 _session->request_locate (pos, _session->transport_rolling());
2391 Editor::jump_backward_to_mark ()
2397 framepos_t pos = _session->locations()->first_mark_before (playhead_cursor->current_frame());
2399 //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...
2400 if ( _session->transport_rolling() ) {
2401 if ( (playhead_cursor->current_frame() - pos) < _session->frame_rate()/2 ) {
2402 framepos_t prior = _session->locations()->first_mark_before ( pos );
2411 _session->request_locate (pos, _session->transport_rolling());
2417 framepos_t const pos = _session->audible_frame ();
2420 _session->locations()->next_available_name (markername, "mark");
2422 if (!choose_new_marker_name (markername)) {
2426 _session->locations()->add (new Location (*_session, pos, 0, markername, Location::IsMark, 0), true);
2430 Editor::clear_markers ()
2433 begin_reversible_command (_("clear markers"));
2435 XMLNode &before = _session->locations()->get_state();
2436 _session->locations()->clear_markers ();
2437 XMLNode &after = _session->locations()->get_state();
2438 _session->add_command(new MementoCommand<Locations>(*(_session->locations()), &before, &after));
2440 commit_reversible_command ();
2445 Editor::clear_ranges ()
2448 begin_reversible_command (_("clear ranges"));
2450 XMLNode &before = _session->locations()->get_state();
2452 _session->locations()->clear_ranges ();
2454 XMLNode &after = _session->locations()->get_state();
2455 _session->add_command(new MementoCommand<Locations>(*(_session->locations()), &before, &after));
2457 commit_reversible_command ();
2462 Editor::clear_locations ()
2464 begin_reversible_command (_("clear locations"));
2466 XMLNode &before = _session->locations()->get_state();
2467 _session->locations()->clear ();
2468 XMLNode &after = _session->locations()->get_state();
2469 _session->add_command(new MementoCommand<Locations>(*(_session->locations()), &before, &after));
2471 commit_reversible_command ();
2475 Editor::unhide_markers ()
2477 for (LocationMarkerMap::iterator i = location_markers.begin(); i != location_markers.end(); ++i) {
2478 Location *l = (*i).first;
2479 if (l->is_hidden() && l->is_mark()) {
2480 l->set_hidden(false, this);
2486 Editor::unhide_ranges ()
2488 for (LocationMarkerMap::iterator i = location_markers.begin(); i != location_markers.end(); ++i) {
2489 Location *l = (*i).first;
2490 if (l->is_hidden() && l->is_range_marker()) {
2491 l->set_hidden(false, this);
2496 /* INSERT/REPLACE */
2499 Editor::insert_region_list_selection (float times)
2501 RouteTimeAxisView *tv = 0;
2502 boost::shared_ptr<Playlist> playlist;
2504 if (clicked_routeview != 0) {
2505 tv = clicked_routeview;
2506 } else if (!selection->tracks.empty()) {
2507 if ((tv = dynamic_cast<RouteTimeAxisView*>(selection->tracks.front())) == 0) {
2510 } else if (entered_track != 0) {
2511 if ((tv = dynamic_cast<RouteTimeAxisView*>(entered_track)) == 0) {
2518 if ((playlist = tv->playlist()) == 0) {
2522 boost::shared_ptr<Region> region = _regions->get_single_selection ();
2527 begin_reversible_command (_("insert region"));
2528 playlist->clear_changes ();
2529 playlist->add_region ((RegionFactory::create (region, true)), get_preferred_edit_position(), times);
2530 if (Config->get_edit_mode() == Ripple)
2531 playlist->ripple (get_preferred_edit_position(), region->length() * times, boost::shared_ptr<Region>());
2533 _session->add_command(new StatefulDiffCommand (playlist));
2534 commit_reversible_command ();
2537 /* BUILT-IN EFFECTS */
2540 Editor::reverse_selection ()
2545 /* GAIN ENVELOPE EDITING */
2548 Editor::edit_envelope ()
2555 Editor::transition_to_rolling (bool fwd)
2561 if (_session->config.get_external_sync()) {
2562 switch (Config->get_sync_source()) {
2566 /* transport controlled by the master */
2571 if (_session->is_auditioning()) {
2572 _session->cancel_audition ();
2576 _session->request_transport_speed (fwd ? 1.0f : -1.0f);
2580 Editor::play_from_start ()
2582 _session->request_locate (_session->current_start_frame(), true);
2586 Editor::play_from_edit_point ()
2588 _session->request_locate (get_preferred_edit_position(), true);
2592 Editor::play_from_edit_point_and_return ()
2594 framepos_t start_frame;
2595 framepos_t return_frame;
2597 start_frame = get_preferred_edit_position ( EDIT_IGNORE_PHEAD );
2599 if (_session->transport_rolling()) {
2600 _session->request_locate (start_frame, false);
2604 /* don't reset the return frame if its already set */
2606 if ((return_frame = _session->requested_return_frame()) < 0) {
2607 return_frame = _session->audible_frame();
2610 if (start_frame >= 0) {
2611 _session->request_roll_at_and_return (start_frame, return_frame);
2616 Editor::play_selection ()
2618 framepos_t start, end;
2619 if (!get_selection_extents ( start, end))
2622 AudioRange ar (start, end, 0);
2623 list<AudioRange> lar;
2626 _session->request_play_range (&lar, true);
2631 Editor::maybe_locate_with_edit_preroll (framepos_t location)
2633 if ( _session->transport_rolling() || !UIConfiguration::instance().get_follow_edits() || _session->config.get_external_sync() )
2636 location -= _session->preroll_samples (location);
2638 //don't try to locate before the beginning of time
2643 //if follow_playhead is on, keep the playhead on the screen
2644 if ( _follow_playhead )
2645 if ( location < leftmost_frame )
2646 location = leftmost_frame;
2648 _session->request_locate( location );
2652 Editor::play_with_preroll ()
2654 framepos_t start, end;
2655 if ( UIConfiguration::instance().get_follow_edits() && get_selection_extents ( start, end) ) {
2656 const framepos_t preroll = _session->preroll_samples (start);
2658 framepos_t ret = start;
2660 if (start > preroll) {
2661 start = start - preroll;
2664 end = end + preroll; //"post-roll"
2666 AudioRange ar (start, end, 0);
2667 list<AudioRange> lar;
2670 _session->request_play_range (&lar, true);
2671 _session->set_requested_return_frame (ret); //force auto-return to return to range start, without the preroll
2673 framepos_t ph = playhead_cursor->current_frame ();
2674 const framepos_t preroll = _session->preroll_samples (ph);
2677 start = ph - preroll;
2681 _session->request_locate (start, true);
2682 _session->set_requested_return_frame (ph); //force auto-return to return to playhead location, without the preroll
2687 Editor::rec_with_preroll ()
2689 framepos_t ph = playhead_cursor->current_frame ();
2690 framepos_t preroll = _session->preroll_samples (ph);
2691 _session->request_preroll_record_trim (ph, preroll);
2695 Editor::rec_with_count_in ()
2697 _session->request_count_in_record ();
2701 Editor::play_location (Location& location)
2703 if (location.start() <= location.end()) {
2707 _session->request_bounded_roll (location.start(), location.end());
2711 Editor::loop_location (Location& location)
2713 if (location.start() <= location.end()) {
2719 if ((tll = transport_loop_location()) != 0) {
2720 tll->set (location.start(), location.end());
2722 // enable looping, reposition and start rolling
2723 _session->request_locate (tll->start(), true);
2724 _session->request_play_loop (true);
2729 Editor::do_layer_operation (LayerOperation op)
2731 if (selection->regions.empty ()) {
2735 bool const multiple = selection->regions.size() > 1;
2739 begin_reversible_command (_("raise regions"));
2741 begin_reversible_command (_("raise region"));
2747 begin_reversible_command (_("raise regions to top"));
2749 begin_reversible_command (_("raise region to top"));
2755 begin_reversible_command (_("lower regions"));
2757 begin_reversible_command (_("lower region"));
2763 begin_reversible_command (_("lower regions to bottom"));
2765 begin_reversible_command (_("lower region"));
2770 set<boost::shared_ptr<Playlist> > playlists = selection->regions.playlists ();
2771 for (set<boost::shared_ptr<Playlist> >::iterator i = playlists.begin(); i != playlists.end(); ++i) {
2772 (*i)->clear_owned_changes ();
2775 for (RegionSelection::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
2776 boost::shared_ptr<Region> r = (*i)->region ();
2788 r->lower_to_bottom ();
2792 for (set<boost::shared_ptr<Playlist> >::iterator i = playlists.begin(); i != playlists.end(); ++i) {
2793 vector<Command*> cmds;
2795 _session->add_commands (cmds);
2798 commit_reversible_command ();
2802 Editor::raise_region ()
2804 do_layer_operation (Raise);
2808 Editor::raise_region_to_top ()
2810 do_layer_operation (RaiseToTop);
2814 Editor::lower_region ()
2816 do_layer_operation (Lower);
2820 Editor::lower_region_to_bottom ()
2822 do_layer_operation (LowerToBottom);
2825 /** Show the region editor for the selected regions */
2827 Editor::show_region_properties ()
2829 selection->foreach_regionview (&RegionView::show_region_editor);
2832 /** Show the midi list editor for the selected MIDI regions */
2834 Editor::show_midi_list_editor ()
2836 selection->foreach_midi_regionview (&MidiRegionView::show_list_editor);
2840 Editor::rename_region ()
2842 RegionSelection rs = get_regions_from_selection_and_entered ();
2848 ArdourDialog d (_("Rename Region"), true, false);
2850 Label label (_("New name:"));
2853 hbox.set_spacing (6);
2854 hbox.pack_start (label, false, false);
2855 hbox.pack_start (entry, true, true);
2857 d.get_vbox()->set_border_width (12);
2858 d.get_vbox()->pack_start (hbox, false, false);
2860 d.add_button(Gtk::Stock::CANCEL, Gtk::RESPONSE_CANCEL);
2861 d.add_button(Gtk::Stock::OK, Gtk::RESPONSE_OK);
2863 d.set_size_request (300, -1);
2865 entry.set_text (rs.front()->region()->name());
2866 entry.select_region (0, -1);
2868 entry.signal_activate().connect (sigc::bind (sigc::mem_fun (d, &Dialog::response), RESPONSE_OK));
2874 int const ret = d.run();
2878 if (ret != RESPONSE_OK) {
2882 std::string str = entry.get_text();
2883 strip_whitespace_edges (str);
2885 rs.front()->region()->set_name (str);
2886 _regions->redisplay ();
2890 /** Start an audition of the first selected region */
2892 Editor::play_edit_range ()
2894 framepos_t start, end;
2896 if (get_edit_op_range (start, end)) {
2897 _session->request_bounded_roll (start, end);
2902 Editor::play_selected_region ()
2904 framepos_t start = max_framepos;
2907 RegionSelection rs = get_regions_from_selection_and_entered ();
2913 for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
2914 if ((*i)->region()->position() < start) {
2915 start = (*i)->region()->position();
2917 if ((*i)->region()->last_frame() + 1 > end) {
2918 end = (*i)->region()->last_frame() + 1;
2922 _session->request_bounded_roll (start, end);
2926 Editor::audition_playlist_region_standalone (boost::shared_ptr<Region> region)
2928 _session->audition_region (region);
2932 Editor::region_from_selection ()
2934 if (clicked_axisview == 0) {
2938 if (selection->time.empty()) {
2942 framepos_t start = selection->time[clicked_selection].start;
2943 framepos_t end = selection->time[clicked_selection].end;
2945 TrackViewList tracks = get_tracks_for_range_action ();
2947 framepos_t selection_cnt = end - start + 1;
2949 for (TrackSelection::iterator i = tracks.begin(); i != tracks.end(); ++i) {
2950 boost::shared_ptr<Region> current;
2951 boost::shared_ptr<Playlist> pl;
2952 framepos_t internal_start;
2955 if ((pl = (*i)->playlist()) == 0) {
2959 if ((current = pl->top_region_at (start)) == 0) {
2963 internal_start = start - current->position();
2964 RegionFactory::region_name (new_name, current->name(), true);
2968 plist.add (ARDOUR::Properties::start, current->start() + internal_start);
2969 plist.add (ARDOUR::Properties::length, selection_cnt);
2970 plist.add (ARDOUR::Properties::name, new_name);
2971 plist.add (ARDOUR::Properties::layer, 0);
2973 boost::shared_ptr<Region> region (RegionFactory::create (current, plist));
2978 Editor::create_region_from_selection (vector<boost::shared_ptr<Region> >& new_regions)
2980 if (selection->time.empty() || selection->tracks.empty()) {
2984 framepos_t start, end;
2985 if (clicked_selection) {
2986 start = selection->time[clicked_selection].start;
2987 end = selection->time[clicked_selection].end;
2989 start = selection->time.start();
2990 end = selection->time.end_frame();
2993 TrackViewList ts = selection->tracks.filter_to_unique_playlists ();
2994 sort_track_selection (ts);
2996 for (TrackSelection::iterator i = ts.begin(); i != ts.end(); ++i) {
2997 boost::shared_ptr<Region> current;
2998 boost::shared_ptr<Playlist> playlist;
2999 framepos_t internal_start;
3002 if ((playlist = (*i)->playlist()) == 0) {
3006 if ((current = playlist->top_region_at(start)) == 0) {
3010 internal_start = start - current->position();
3011 RegionFactory::region_name (new_name, current->name(), true);
3015 plist.add (ARDOUR::Properties::start, current->start() + internal_start);
3016 plist.add (ARDOUR::Properties::length, end - start + 1);
3017 plist.add (ARDOUR::Properties::name, new_name);
3019 new_regions.push_back (RegionFactory::create (current, plist));
3024 Editor::split_multichannel_region ()
3026 RegionSelection rs = get_regions_from_selection_and_entered ();
3032 vector< boost::shared_ptr<Region> > v;
3034 for (list<RegionView*>::iterator x = rs.begin(); x != rs.end(); ++x) {
3035 (*x)->region()->separate_by_channel (*_session, v);
3040 Editor::new_region_from_selection ()
3042 region_from_selection ();
3043 cancel_selection ();
3047 add_if_covered (RegionView* rv, const AudioRange* ar, RegionSelection* rs)
3049 switch (rv->region()->coverage (ar->start, ar->end - 1)) {
3050 // n.b. -1 because AudioRange::end is one past the end, but coverage expects inclusive ranges
3051 case Evoral::OverlapNone:
3059 * - selected tracks, or if there are none...
3060 * - tracks containing selected regions, or if there are none...
3065 Editor::get_tracks_for_range_action () const
3069 if (selection->tracks.empty()) {
3071 /* use tracks with selected regions */
3073 RegionSelection rs = selection->regions;
3075 for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
3076 TimeAxisView* tv = &(*i)->get_time_axis_view();
3078 if (!t.contains (tv)) {
3084 /* no regions and no tracks: use all tracks */
3090 t = selection->tracks;
3093 return t.filter_to_unique_playlists();
3097 Editor::separate_regions_between (const TimeSelection& ts)
3099 bool in_command = false;
3100 boost::shared_ptr<Playlist> playlist;
3101 RegionSelection new_selection;
3103 TrackViewList tmptracks = get_tracks_for_range_action ();
3104 sort_track_selection (tmptracks);
3106 for (TrackSelection::iterator i = tmptracks.begin(); i != tmptracks.end(); ++i) {
3108 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> ((*i));
3114 if (!rtv->is_track()) {
3118 /* no edits to destructive tracks */
3120 if (rtv->track()->destructive()) {
3124 if ((playlist = rtv->playlist()) != 0) {
3126 playlist->clear_changes ();
3128 /* XXX need to consider musical time selections here at some point */
3130 double speed = rtv->track()->speed();
3132 for (list<AudioRange>::const_iterator t = ts.begin(); t != ts.end(); ++t) {
3134 sigc::connection c = rtv->view()->RegionViewAdded.connect (
3135 sigc::mem_fun(*this, &Editor::collect_new_region_view));
3137 latest_regionviews.clear ();
3139 playlist->partition ((framepos_t)((*t).start * speed),
3140 (framepos_t)((*t).end * speed), false);
3144 if (!latest_regionviews.empty()) {
3146 rtv->view()->foreach_regionview (sigc::bind (
3147 sigc::ptr_fun (add_if_covered),
3148 &(*t), &new_selection));
3151 begin_reversible_command (_("separate"));
3155 /* pick up changes to existing regions */
3157 vector<Command*> cmds;
3158 playlist->rdiff (cmds);
3159 _session->add_commands (cmds);
3161 /* pick up changes to the playlist itself (adds/removes)
3164 _session->add_command(new StatefulDiffCommand (playlist));
3171 // selection->set (new_selection);
3173 commit_reversible_command ();
3177 struct PlaylistState {
3178 boost::shared_ptr<Playlist> playlist;
3182 /** Take tracks from get_tracks_for_range_action and cut any regions
3183 * on those tracks so that the tracks are empty over the time
3187 Editor::separate_region_from_selection ()
3189 /* preferentially use *all* ranges in the time selection if we're in range mode
3190 to allow discontiguous operation, since get_edit_op_range() currently
3191 returns a single range.
3194 if (!selection->time.empty()) {
3196 separate_regions_between (selection->time);
3203 if (get_edit_op_range (start, end)) {
3205 AudioRange ar (start, end, 1);
3209 separate_regions_between (ts);
3215 Editor::separate_region_from_punch ()
3217 Location* loc = _session->locations()->auto_punch_location();
3219 separate_regions_using_location (*loc);
3224 Editor::separate_region_from_loop ()
3226 Location* loc = _session->locations()->auto_loop_location();
3228 separate_regions_using_location (*loc);
3233 Editor::separate_regions_using_location (Location& loc)
3235 if (loc.is_mark()) {
3239 AudioRange ar (loc.start(), loc.end(), 1);
3244 separate_regions_between (ts);
3247 /** Separate regions under the selected region */
3249 Editor::separate_under_selected_regions ()
3251 vector<PlaylistState> playlists;
3255 rs = get_regions_from_selection_and_entered();
3257 if (!_session || rs.empty()) {
3261 begin_reversible_command (_("separate region under"));
3263 list<boost::shared_ptr<Region> > regions_to_remove;
3265 for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
3266 // we can't just remove the region(s) in this loop because
3267 // this removes them from the RegionSelection, and they thus
3268 // disappear from underneath the iterator, and the ++i above
3269 // SEGVs in a puzzling fashion.
3271 // so, first iterate over the regions to be removed from rs and
3272 // add them to the regions_to_remove list, and then
3273 // iterate over the list to actually remove them.
3275 regions_to_remove.push_back ((*i)->region());
3278 for (list<boost::shared_ptr<Region> >::iterator rl = regions_to_remove.begin(); rl != regions_to_remove.end(); ++rl) {
3280 boost::shared_ptr<Playlist> playlist = (*rl)->playlist();
3283 // is this check necessary?
3287 vector<PlaylistState>::iterator i;
3289 //only take state if this is a new playlist.
3290 for (i = playlists.begin(); i != playlists.end(); ++i) {
3291 if ((*i).playlist == playlist) {
3296 if (i == playlists.end()) {
3298 PlaylistState before;
3299 before.playlist = playlist;
3300 before.before = &playlist->get_state();
3302 playlist->freeze ();
3303 playlists.push_back(before);
3306 //Partition on the region bounds
3307 playlist->partition ((*rl)->first_frame() - 1, (*rl)->last_frame() + 1, true);
3309 //Re-add region that was just removed due to the partition operation
3310 playlist->add_region( (*rl), (*rl)->first_frame() );
3313 vector<PlaylistState>::iterator pl;
3315 for (pl = playlists.begin(); pl != playlists.end(); ++pl) {
3316 (*pl).playlist->thaw ();
3317 _session->add_command(new MementoCommand<Playlist>(*(*pl).playlist, (*pl).before, &(*pl).playlist->get_state()));
3320 commit_reversible_command ();
3324 Editor::crop_region_to_selection ()
3326 if (!selection->time.empty()) {
3328 crop_region_to (selection->time.start(), selection->time.end_frame());
3335 if (get_edit_op_range (start, end)) {
3336 crop_region_to (start, end);
3343 Editor::crop_region_to (framepos_t start, framepos_t end)
3345 vector<boost::shared_ptr<Playlist> > playlists;
3346 boost::shared_ptr<Playlist> playlist;
3349 if (selection->tracks.empty()) {
3350 ts = track_views.filter_to_unique_playlists();
3352 ts = selection->tracks.filter_to_unique_playlists ();
3355 sort_track_selection (ts);
3357 for (TrackSelection::iterator i = ts.begin(); i != ts.end(); ++i) {
3359 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> ((*i));
3365 boost::shared_ptr<Track> t = rtv->track();
3367 if (t != 0 && ! t->destructive()) {
3369 if ((playlist = rtv->playlist()) != 0) {
3370 playlists.push_back (playlist);
3375 if (playlists.empty()) {
3380 framepos_t new_start;
3382 framecnt_t new_length;
3383 bool in_command = false;
3385 for (vector<boost::shared_ptr<Playlist> >::iterator i = playlists.begin(); i != playlists.end(); ++i) {
3387 /* Only the top regions at start and end have to be cropped */
3388 boost::shared_ptr<Region> region_at_start = (*i)->top_region_at(start);
3389 boost::shared_ptr<Region> region_at_end = (*i)->top_region_at(end);
3391 vector<boost::shared_ptr<Region> > regions;
3393 if (region_at_start != 0) {
3394 regions.push_back (region_at_start);
3396 if (region_at_end != 0) {
3397 regions.push_back (region_at_end);
3400 /* now adjust lengths */
3401 for (vector<boost::shared_ptr<Region> >::iterator i = regions.begin(); i != regions.end(); ++i) {
3403 pos = (*i)->position();
3404 new_start = max (start, pos);
3405 if (max_framepos - pos > (*i)->length()) {
3406 new_end = pos + (*i)->length() - 1;
3408 new_end = max_framepos;
3410 new_end = min (end, new_end);
3411 new_length = new_end - new_start + 1;
3414 begin_reversible_command (_("trim to selection"));
3417 (*i)->clear_changes ();
3418 (*i)->trim_to (new_start, new_length);
3419 _session->add_command (new StatefulDiffCommand (*i));
3424 commit_reversible_command ();
3429 Editor::region_fill_track ()
3431 boost::shared_ptr<Playlist> playlist;
3432 RegionSelection regions = get_regions_from_selection_and_entered ();
3433 RegionSelection foo;
3435 framepos_t const end = _session->current_end_frame ();
3437 if (regions.empty () || regions.end_frame () + 1 >= end) {
3441 framepos_t const start_frame = regions.start ();
3442 framepos_t const end_frame = regions.end_frame ();
3443 framecnt_t const gap = end_frame - start_frame + 1;
3445 begin_reversible_command (Operations::region_fill);
3447 selection->clear_regions ();
3449 for (RegionSelection::iterator i = regions.begin(); i != regions.end(); ++i) {
3451 boost::shared_ptr<Region> r ((*i)->region());
3453 TimeAxisView& tv = (*i)->get_time_axis_view();
3454 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (&tv);
3455 latest_regionviews.clear ();
3456 sigc::connection c = rtv->view()->RegionViewAdded.connect (sigc::mem_fun(*this, &Editor::collect_new_region_view));
3458 framepos_t const position = end_frame + (r->first_frame() - start_frame + 1);
3459 playlist = (*i)->region()->playlist();
3460 playlist->clear_changes ();
3461 playlist->duplicate_until (r, position, gap, end);
3462 _session->add_command(new StatefulDiffCommand (playlist));
3466 foo.insert (foo.end(), latest_regionviews.begin(), latest_regionviews.end());
3470 selection->set (foo);
3473 commit_reversible_command ();
3477 Editor::set_region_sync_position ()
3479 set_sync_point (get_preferred_edit_position (), get_regions_from_selection_and_edit_point ());
3483 Editor::set_sync_point (framepos_t where, const RegionSelection& rs)
3485 bool in_command = false;
3487 for (RegionSelection::const_iterator r = rs.begin(); r != rs.end(); ++r) {
3489 if (!(*r)->region()->covers (where)) {
3493 boost::shared_ptr<Region> region ((*r)->region());
3496 begin_reversible_command (_("set sync point"));
3500 region->clear_changes ();
3501 region->set_sync_position (where);
3502 _session->add_command(new StatefulDiffCommand (region));
3506 commit_reversible_command ();
3510 /** Remove the sync positions of the selection */
3512 Editor::remove_region_sync ()
3514 RegionSelection rs = get_regions_from_selection_and_entered ();
3520 begin_reversible_command (_("remove region sync"));
3522 for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
3524 (*i)->region()->clear_changes ();
3525 (*i)->region()->clear_sync_position ();
3526 _session->add_command(new StatefulDiffCommand ((*i)->region()));
3529 commit_reversible_command ();
3533 Editor::naturalize_region ()
3535 RegionSelection rs = get_regions_from_selection_and_entered ();
3541 if (rs.size() > 1) {
3542 begin_reversible_command (_("move regions to original position"));
3544 begin_reversible_command (_("move region to original position"));
3547 for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
3548 (*i)->region()->clear_changes ();
3549 (*i)->region()->move_to_natural_position ();
3550 _session->add_command (new StatefulDiffCommand ((*i)->region()));
3553 commit_reversible_command ();
3557 Editor::align_regions (RegionPoint what)
3559 RegionSelection const rs = get_regions_from_selection_and_edit_point ();
3565 begin_reversible_command (_("align selection"));
3567 framepos_t const position = get_preferred_edit_position ();
3569 for (RegionSelection::const_iterator i = rs.begin(); i != rs.end(); ++i) {
3570 align_region_internal ((*i)->region(), what, position);
3573 commit_reversible_command ();
3576 struct RegionSortByTime {
3577 bool operator() (const RegionView* a, const RegionView* b) {
3578 return a->region()->position() < b->region()->position();
3583 Editor::align_regions_relative (RegionPoint point)
3585 RegionSelection const rs = get_regions_from_selection_and_edit_point ();
3591 framepos_t const position = get_preferred_edit_position ();
3593 framepos_t distance = 0;
3597 list<RegionView*> sorted;
3598 rs.by_position (sorted);
3600 boost::shared_ptr<Region> r ((*sorted.begin())->region());
3605 if (position > r->position()) {
3606 distance = position - r->position();
3608 distance = r->position() - position;
3614 if (position > r->last_frame()) {
3615 distance = position - r->last_frame();
3616 pos = r->position() + distance;
3618 distance = r->last_frame() - position;
3619 pos = r->position() - distance;
3625 pos = r->adjust_to_sync (position);
3626 if (pos > r->position()) {
3627 distance = pos - r->position();
3629 distance = r->position() - pos;
3635 if (pos == r->position()) {
3639 begin_reversible_command (_("align selection (relative)"));
3641 /* move first one specially */
3643 r->clear_changes ();
3644 r->set_position (pos);
3645 _session->add_command(new StatefulDiffCommand (r));
3647 /* move rest by the same amount */
3651 for (list<RegionView*>::iterator i = sorted.begin(); i != sorted.end(); ++i) {
3653 boost::shared_ptr<Region> region ((*i)->region());
3655 region->clear_changes ();
3658 region->set_position (region->position() + distance);
3660 region->set_position (region->position() - distance);
3663 _session->add_command(new StatefulDiffCommand (region));
3667 commit_reversible_command ();
3671 Editor::align_region (boost::shared_ptr<Region> region, RegionPoint point, framepos_t position)
3673 begin_reversible_command (_("align region"));
3674 align_region_internal (region, point, position);
3675 commit_reversible_command ();
3679 Editor::align_region_internal (boost::shared_ptr<Region> region, RegionPoint point, framepos_t position)
3681 region->clear_changes ();
3685 region->set_position (region->adjust_to_sync (position));
3689 if (position > region->length()) {
3690 region->set_position (position - region->length());
3695 region->set_position (position);
3699 _session->add_command(new StatefulDiffCommand (region));
3703 Editor::trim_region_front ()
3709 Editor::trim_region_back ()
3711 trim_region (false);
3715 Editor::trim_region (bool front)
3717 framepos_t where = get_preferred_edit_position();
3718 RegionSelection rs = get_regions_from_selection_and_edit_point ();
3724 begin_reversible_command (front ? _("trim front") : _("trim back"));
3726 for (list<RegionView*>::const_iterator i = rs.by_layer().begin(); i != rs.by_layer().end(); ++i) {
3727 if (!(*i)->region()->locked()) {
3729 (*i)->region()->clear_changes ();
3732 (*i)->region()->trim_front (where);
3734 (*i)->region()->trim_end (where);
3737 _session->add_command (new StatefulDiffCommand ((*i)->region()));
3741 commit_reversible_command ();
3744 /** Trim the end of the selected regions to the position of the edit cursor */
3746 Editor::trim_region_to_loop ()
3748 Location* loc = _session->locations()->auto_loop_location();
3752 trim_region_to_location (*loc, _("trim to loop"));
3756 Editor::trim_region_to_punch ()
3758 Location* loc = _session->locations()->auto_punch_location();
3762 trim_region_to_location (*loc, _("trim to punch"));
3766 Editor::trim_region_to_location (const Location& loc, const char* str)
3768 RegionSelection rs = get_regions_from_selection_and_entered ();
3769 bool in_command = false;
3771 for (RegionSelection::iterator x = rs.begin(); x != rs.end(); ++x) {
3772 RegionView* rv = (*x);
3774 /* require region to span proposed trim */
3775 switch (rv->region()->coverage (loc.start(), loc.end())) {
3776 case Evoral::OverlapInternal:
3782 RouteTimeAxisView* tav = dynamic_cast<RouteTimeAxisView*> (&rv->get_time_axis_view());
3791 if (tav->track() != 0) {
3792 speed = tav->track()->speed();
3795 start = session_frame_to_track_frame (loc.start(), speed);
3796 end = session_frame_to_track_frame (loc.end(), speed);
3798 rv->region()->clear_changes ();
3799 rv->region()->trim_to (start, (end - start));
3802 begin_reversible_command (str);
3805 _session->add_command(new StatefulDiffCommand (rv->region()));
3809 commit_reversible_command ();
3814 Editor::trim_region_to_previous_region_end ()
3816 return trim_to_region(false);
3820 Editor::trim_region_to_next_region_start ()
3822 return trim_to_region(true);
3826 Editor::trim_to_region(bool forward)
3828 RegionSelection rs = get_regions_from_selection_and_entered ();
3829 bool in_command = false;
3831 boost::shared_ptr<Region> next_region;
3833 for (RegionSelection::iterator x = rs.begin(); x != rs.end(); ++x) {
3835 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (*x);
3841 AudioTimeAxisView* atav = dynamic_cast<AudioTimeAxisView*> (&arv->get_time_axis_view());
3849 if (atav->track() != 0) {
3850 speed = atav->track()->speed();
3854 boost::shared_ptr<Region> region = arv->region();
3855 boost::shared_ptr<Playlist> playlist (region->playlist());
3857 region->clear_changes ();
3861 next_region = playlist->find_next_region (region->first_frame(), Start, 1);
3867 region->trim_end((framepos_t) ( (next_region->first_frame() - 1) * speed));
3868 arv->region_changed (PropertyChange (ARDOUR::Properties::length));
3872 next_region = playlist->find_next_region (region->first_frame(), Start, 0);
3878 region->trim_front((framepos_t) ((next_region->last_frame() + 1) * speed));
3880 arv->region_changed (ARDOUR::bounds_change);
3884 begin_reversible_command (_("trim to region"));
3887 _session->add_command(new StatefulDiffCommand (region));
3891 commit_reversible_command ();
3896 Editor::unfreeze_route ()
3898 if (clicked_routeview == 0 || !clicked_routeview->is_track()) {
3902 clicked_routeview->track()->unfreeze ();
3906 Editor::_freeze_thread (void* arg)
3908 return static_cast<Editor*>(arg)->freeze_thread ();
3912 Editor::freeze_thread ()
3914 /* create event pool because we may need to talk to the session */
3915 SessionEvent::create_per_thread_pool ("freeze events", 64);
3916 /* create per-thread buffers for process() tree to use */
3917 clicked_routeview->audio_track()->freeze_me (*current_interthread_info);
3918 current_interthread_info->done = true;
3923 Editor::freeze_route ()
3929 /* stop transport before we start. this is important */
3931 _session->request_transport_speed (0.0);
3933 /* wait for just a little while, because the above call is asynchronous */
3935 Glib::usleep (250000);
3937 if (clicked_routeview == 0 || !clicked_routeview->is_audio_track()) {
3941 if (!clicked_routeview->track()->bounceable (clicked_routeview->track()->main_outs(), true)) {
3943 _("This track/bus cannot be frozen because the signal adds or loses channels before reaching the outputs.\n"
3944 "This is typically caused by plugins that generate stereo output from mono input or vice versa.")
3946 d.set_title (_("Cannot freeze"));
3951 if (clicked_routeview->track()->has_external_redirects()) {
3952 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"
3953 "Freezing will only process the signal as far as the first send/insert/return."),
3954 clicked_routeview->track()->name()), true, MESSAGE_INFO, BUTTONS_NONE, true);
3956 d.add_button (_("Freeze anyway"), Gtk::RESPONSE_OK);
3957 d.add_button (_("Don't freeze"), Gtk::RESPONSE_CANCEL);
3958 d.set_title (_("Freeze Limits"));
3960 int response = d.run ();
3963 case Gtk::RESPONSE_CANCEL:
3970 InterThreadInfo itt;
3971 current_interthread_info = &itt;
3973 InterthreadProgressWindow ipw (current_interthread_info, _("Freeze"), _("Cancel Freeze"));
3975 pthread_create_and_store (X_("freezer"), &itt.thread, _freeze_thread, this);
3977 CursorContext::Handle cursor_ctx = CursorContext::create(*this, _cursors->wait);
3979 while (!itt.done && !itt.cancel) {
3980 gtk_main_iteration ();
3983 pthread_join (itt.thread, 0);
3984 current_interthread_info = 0;
3988 Editor::bounce_range_selection (bool replace, bool enable_processing)
3990 if (selection->time.empty()) {
3994 TrackSelection views = selection->tracks;
3996 for (TrackViewList::iterator i = views.begin(); i != views.end(); ++i) {
3998 if (enable_processing) {
4000 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (*i);
4002 if (rtv && rtv->track() && replace && enable_processing && !rtv->track()->bounceable (rtv->track()->main_outs(), false)) {
4004 _("You can't perform this operation because the processing of the signal "
4005 "will cause one or more of the tracks to end up with a region with more channels than this track has inputs.\n\n"
4006 "You can do this without processing, which is a different operation.")
4008 d.set_title (_("Cannot bounce"));
4015 framepos_t start = selection->time[clicked_selection].start;
4016 framepos_t end = selection->time[clicked_selection].end;
4017 framepos_t cnt = end - start + 1;
4018 bool in_command = false;
4020 for (TrackViewList::iterator i = views.begin(); i != views.end(); ++i) {
4022 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (*i);
4028 boost::shared_ptr<Playlist> playlist;
4030 if ((playlist = rtv->playlist()) == 0) {
4034 InterThreadInfo itt;
4036 playlist->clear_changes ();
4037 playlist->clear_owned_changes ();
4039 boost::shared_ptr<Region> r;
4041 if (enable_processing) {
4042 r = rtv->track()->bounce_range (start, start+cnt, itt, rtv->track()->main_outs(), false);
4044 r = rtv->track()->bounce_range (start, start+cnt, itt, boost::shared_ptr<Processor>(), false);
4052 list<AudioRange> ranges;
4053 ranges.push_back (AudioRange (start, start+cnt, 0));
4054 playlist->cut (ranges); // discard result
4055 playlist->add_region (r, start);
4059 begin_reversible_command (_("bounce range"));
4062 vector<Command*> cmds;
4063 playlist->rdiff (cmds);
4064 _session->add_commands (cmds);
4066 _session->add_command (new StatefulDiffCommand (playlist));
4070 commit_reversible_command ();
4074 /** Delete selected regions, automation points or a time range */
4078 //special case: if the user is pointing in the editor/mixer strip, they may be trying to delete a plugin.
4079 //we need this because the editor-mixer strip is in the editor window, so it doesn't get the bindings from the mix window
4080 bool deleted = false;
4081 if ( current_mixer_strip && current_mixer_strip == MixerStrip::entered_mixer_strip() )
4082 deleted = current_mixer_strip->delete_processors ();
4088 /** Cut selected regions, automation points or a time range */
4095 /** Copy selected regions, automation points or a time range */
4103 /** @return true if a Cut, Copy or Clear is possible */
4105 Editor::can_cut_copy () const
4107 if (!selection->time.empty() || !selection->regions.empty() || !selection->points.empty())
4114 /** Cut, copy or clear selected regions, automation points or a time range.
4115 * @param op Operation (Delete, Cut, Copy or Clear)
4118 Editor::cut_copy (CutCopyOp op)
4120 /* only cancel selection if cut/copy is successful.*/
4126 opname = _("delete");
4135 opname = _("clear");
4139 /* if we're deleting something, and the mouse is still pressed,
4140 the thing we started a drag for will be gone when we release
4141 the mouse button(s). avoid this. see part 2 at the end of
4145 if (op == Delete || op == Cut || op == Clear) {
4146 if (_drags->active ()) {
4151 if ( op != Delete ) //"Delete" doesn't change copy/paste buf
4152 cut_buffer->clear ();
4154 if (entered_marker) {
4156 /* cut/delete op while pointing at a marker */
4159 Location* loc = find_location_from_marker (entered_marker, ignored);
4161 if (_session && loc) {
4162 entered_marker = NULL;
4163 Glib::signal_idle().connect (sigc::bind (sigc::mem_fun(*this, &Editor::really_remove_marker), loc));
4170 switch (mouse_mode) {
4173 begin_reversible_command (opname + ' ' + X_("MIDI"));
4175 commit_reversible_command ();
4181 bool did_edit = false;
4183 if (!selection->regions.empty() || !selection->points.empty()) {
4184 begin_reversible_command (opname + ' ' + _("objects"));
4187 if (!selection->regions.empty()) {
4188 cut_copy_regions (op, selection->regions);
4190 if (op == Cut || op == Delete) {
4191 selection->clear_regions ();
4195 if (!selection->points.empty()) {
4196 cut_copy_points (op);
4198 if (op == Cut || op == Delete) {
4199 selection->clear_points ();
4202 } else if (selection->time.empty()) {
4203 framepos_t start, end;
4204 /* no time selection, see if we can get an edit range
4207 if (get_edit_op_range (start, end)) {
4208 selection->set (start, end);
4210 } else if (!selection->time.empty()) {
4211 begin_reversible_command (opname + ' ' + _("range"));
4214 cut_copy_ranges (op);
4216 if (op == Cut || op == Delete) {
4217 selection->clear_time ();
4222 /* reset repeated paste state */
4225 commit_reversible_command ();
4228 if (op == Delete || op == Cut || op == Clear) {
4234 struct AutomationRecord {
4235 AutomationRecord () : state (0) , line(NULL) {}
4236 AutomationRecord (XMLNode* s, const AutomationLine* l) : state (s) , line (l) {}
4238 XMLNode* state; ///< state before any operation
4239 const AutomationLine* line; ///< line this came from
4240 boost::shared_ptr<Evoral::ControlList> copy; ///< copied events for the cut buffer
4243 struct PointsSelectionPositionSorter {
4244 bool operator() (ControlPoint* a, ControlPoint* b) {
4245 return (*(a->model()))->when < (*(b->model()))->when;
4249 /** Cut, copy or clear selected automation points.
4250 * @param op Operation (Cut, Copy or Clear)
4253 Editor::cut_copy_points (Editing::CutCopyOp op, Evoral::Beats earliest, bool midi)
4255 if (selection->points.empty ()) {
4259 /* XXX: not ideal, as there may be more than one track involved in the point selection */
4260 _last_cut_copy_source_track = &selection->points.front()->line().trackview;
4262 /* Keep a record of the AutomationLists that we end up using in this operation */
4263 typedef std::map<boost::shared_ptr<AutomationList>, AutomationRecord> Lists;
4266 /* user could select points in any order */
4267 selection->points.sort(PointsSelectionPositionSorter ());
4269 /* Go through all selected points, making an AutomationRecord for each distinct AutomationList */
4270 for (PointSelection::iterator sel_point = selection->points.begin(); sel_point != selection->points.end(); ++sel_point) {
4271 const AutomationLine& line = (*sel_point)->line();
4272 const boost::shared_ptr<AutomationList> al = line.the_list();
4273 if (lists.find (al) == lists.end ()) {
4274 /* We haven't seen this list yet, so make a record for it. This includes
4275 taking a copy of its current state, in case this is needed for undo later.
4277 lists[al] = AutomationRecord (&al->get_state (), &line);
4281 if (op == Cut || op == Copy) {
4282 /* This operation will involve putting things in the cut buffer, so create an empty
4283 ControlList for each of our source lists to put the cut buffer data in.
4285 for (Lists::iterator i = lists.begin(); i != lists.end(); ++i) {
4286 i->second.copy = i->first->create (i->first->parameter (), i->first->descriptor());
4289 /* Add all selected points to the relevant copy ControlLists */
4290 MusicFrame start (std::numeric_limits<framepos_t>::max(), 0);
4291 for (PointSelection::iterator sel_point = selection->points.begin(); sel_point != selection->points.end(); ++sel_point) {
4292 boost::shared_ptr<AutomationList> al = (*sel_point)->line().the_list();
4293 AutomationList::const_iterator ctrl_evt = (*sel_point)->model ();
4295 lists[al].copy->fast_simple_add ((*ctrl_evt)->when, (*ctrl_evt)->value);
4297 /* Update earliest MIDI start time in beats */
4298 earliest = std::min(earliest, Evoral::Beats((*ctrl_evt)->when));
4300 /* Update earliest session start time in frames */
4301 start.frame = std::min(start.frame, (*sel_point)->line().session_position(ctrl_evt));
4305 /* Snap start time backwards, so copy/paste is snap aligned. */
4307 if (earliest == Evoral::Beats::max()) {
4308 earliest = Evoral::Beats(); // Weird... don't offset
4310 earliest.round_down_to_beat();
4312 if (start.frame == std::numeric_limits<double>::max()) {
4313 start.frame = 0; // Weird... don't offset
4315 snap_to(start, RoundDownMaybe);
4318 const double line_offset = midi ? earliest.to_double() : start.frame;
4319 for (Lists::iterator i = lists.begin(); i != lists.end(); ++i) {
4320 /* Correct this copy list so that it is relative to the earliest
4321 start time, so relative ordering between points is preserved
4322 when copying from several lists and the paste starts at the
4323 earliest copied piece of data. */
4324 boost::shared_ptr<Evoral::ControlList> &al_cpy = i->second.copy;
4325 for (AutomationList::iterator ctrl_evt = al_cpy->begin(); ctrl_evt != al_cpy->end(); ++ctrl_evt) {
4326 (*ctrl_evt)->when -= line_offset;
4329 /* And add it to the cut buffer */
4330 cut_buffer->add (al_cpy);
4334 if (op == Delete || op == Cut) {
4335 /* This operation needs to remove things from the main AutomationList, so do that now */
4337 for (Lists::iterator i = lists.begin(); i != lists.end(); ++i) {
4338 i->first->freeze ();
4341 /* Remove each selected point from its AutomationList */
4342 for (PointSelection::iterator sel_point = selection->points.begin(); sel_point != selection->points.end(); ++sel_point) {
4343 AutomationLine& line = (*sel_point)->line ();
4344 boost::shared_ptr<AutomationList> al = line.the_list();
4348 if (dynamic_cast<AudioRegionGainLine*> (&line)) {
4349 /* removing of first and last gain point in region gain lines is prohibited*/
4350 if (line.is_last_point (*(*sel_point)) || line.is_first_point (*(*sel_point))) {
4356 al->erase ((*sel_point)->model ());
4360 /* Thaw the lists and add undo records for them */
4361 for (Lists::iterator i = lists.begin(); i != lists.end(); ++i) {
4362 boost::shared_ptr<AutomationList> al = i->first;
4364 _session->add_command (new MementoCommand<AutomationList> (*al.get(), i->second.state, &(al->get_state ())));
4369 /** Cut, copy or clear selected automation points.
4370 * @param op Operation (Cut, Copy or Clear)
4373 Editor::cut_copy_midi (CutCopyOp op)
4375 Evoral::Beats earliest = Evoral::Beats::max();
4376 for (MidiRegionSelection::iterator i = selection->midi_regions.begin(); i != selection->midi_regions.end(); ++i) {
4377 MidiRegionView* mrv = dynamic_cast<MidiRegionView*>(*i);
4379 if (!mrv->selection().empty()) {
4380 earliest = std::min(earliest, (*mrv->selection().begin())->note()->time());
4382 mrv->cut_copy_clear (op);
4384 /* XXX: not ideal, as there may be more than one track involved in the selection */
4385 _last_cut_copy_source_track = &mrv->get_time_axis_view();
4389 if (!selection->points.empty()) {
4390 cut_copy_points (op, earliest, true);
4391 if (op == Cut || op == Delete) {
4392 selection->clear_points ();
4397 struct lt_playlist {
4398 bool operator () (const PlaylistState& a, const PlaylistState& b) {
4399 return a.playlist < b.playlist;
4403 struct PlaylistMapping {
4405 boost::shared_ptr<Playlist> pl;
4407 PlaylistMapping (TimeAxisView* tvp) : tv (tvp) {}
4410 /** Remove `clicked_regionview' */
4412 Editor::remove_clicked_region ()
4414 if (clicked_routeview == 0 || clicked_regionview == 0) {
4418 begin_reversible_command (_("remove region"));
4420 boost::shared_ptr<Playlist> playlist = clicked_routeview->playlist();
4422 playlist->clear_changes ();
4423 playlist->clear_owned_changes ();
4424 playlist->remove_region (clicked_regionview->region());
4425 if (Config->get_edit_mode() == Ripple)
4426 playlist->ripple (clicked_regionview->region()->position(), -clicked_regionview->region()->length(), boost::shared_ptr<Region>());
4428 /* We might have removed regions, which alters other regions' layering_index,
4429 so we need to do a recursive diff here.
4431 vector<Command*> cmds;
4432 playlist->rdiff (cmds);
4433 _session->add_commands (cmds);
4435 _session->add_command(new StatefulDiffCommand (playlist));
4436 commit_reversible_command ();
4440 /** Remove the selected regions */
4442 Editor::remove_selected_regions ()
4444 RegionSelection rs = get_regions_from_selection_and_entered ();
4446 if (!_session || rs.empty()) {
4450 list<boost::shared_ptr<Region> > regions_to_remove;
4452 for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
4453 // we can't just remove the region(s) in this loop because
4454 // this removes them from the RegionSelection, and they thus
4455 // disappear from underneath the iterator, and the ++i above
4456 // SEGVs in a puzzling fashion.
4458 // so, first iterate over the regions to be removed from rs and
4459 // add them to the regions_to_remove list, and then
4460 // iterate over the list to actually remove them.
4462 regions_to_remove.push_back ((*i)->region());
4465 vector<boost::shared_ptr<Playlist> > playlists;
4467 for (list<boost::shared_ptr<Region> >::iterator rl = regions_to_remove.begin(); rl != regions_to_remove.end(); ++rl) {
4469 boost::shared_ptr<Playlist> playlist = (*rl)->playlist();
4472 // is this check necessary?
4476 /* get_regions_from_selection_and_entered() guarantees that
4477 the playlists involved are unique, so there is no need
4481 playlists.push_back (playlist);
4483 playlist->clear_changes ();
4484 playlist->clear_owned_changes ();
4485 playlist->freeze ();
4486 playlist->remove_region (*rl);
4487 if (Config->get_edit_mode() == Ripple)
4488 playlist->ripple ((*rl)->position(), -(*rl)->length(), boost::shared_ptr<Region>());
4492 vector<boost::shared_ptr<Playlist> >::iterator pl;
4493 bool in_command = false;
4495 for (pl = playlists.begin(); pl != playlists.end(); ++pl) {
4498 /* We might have removed regions, which alters other regions' layering_index,
4499 so we need to do a recursive diff here.
4503 begin_reversible_command (_("remove region"));
4506 vector<Command*> cmds;
4507 (*pl)->rdiff (cmds);
4508 _session->add_commands (cmds);
4510 _session->add_command(new StatefulDiffCommand (*pl));
4514 commit_reversible_command ();
4518 /** Cut, copy or clear selected regions.
4519 * @param op Operation (Cut, Copy or Clear)
4522 Editor::cut_copy_regions (CutCopyOp op, RegionSelection& rs)
4524 /* we can't use a std::map here because the ordering is important, and we can't trivially sort
4525 a map when we want ordered access to both elements. i think.
4528 vector<PlaylistMapping> pmap;
4530 framepos_t first_position = max_framepos;
4532 typedef set<boost::shared_ptr<Playlist> > FreezeList;
4533 FreezeList freezelist;
4535 /* get ordering correct before we cut/copy */
4537 rs.sort_by_position_and_track ();
4539 for (RegionSelection::iterator x = rs.begin(); x != rs.end(); ++x) {
4541 first_position = min ((framepos_t) (*x)->region()->position(), first_position);
4543 if (op == Cut || op == Clear || op == Delete) {
4544 boost::shared_ptr<Playlist> pl = (*x)->region()->playlist();
4547 FreezeList::iterator fl;
4549 // only take state if this is a new playlist.
4550 for (fl = freezelist.begin(); fl != freezelist.end(); ++fl) {
4556 if (fl == freezelist.end()) {
4557 pl->clear_changes();
4558 pl->clear_owned_changes ();
4560 freezelist.insert (pl);
4565 TimeAxisView* tv = &(*x)->get_time_axis_view();
4566 vector<PlaylistMapping>::iterator z;
4568 for (z = pmap.begin(); z != pmap.end(); ++z) {
4569 if ((*z).tv == tv) {
4574 if (z == pmap.end()) {
4575 pmap.push_back (PlaylistMapping (tv));
4579 for (RegionSelection::iterator x = rs.begin(); x != rs.end(); ) {
4581 boost::shared_ptr<Playlist> pl = (*x)->region()->playlist();
4584 /* region not yet associated with a playlist (e.g. unfinished
4591 TimeAxisView& tv = (*x)->get_time_axis_view();
4592 boost::shared_ptr<Playlist> npl;
4593 RegionSelection::iterator tmp;
4600 vector<PlaylistMapping>::iterator z;
4602 for (z = pmap.begin(); z != pmap.end(); ++z) {
4603 if ((*z).tv == &tv) {
4608 assert (z != pmap.end());
4611 npl = PlaylistFactory::create (pl->data_type(), *_session, "cutlist", true);
4619 boost::shared_ptr<Region> r = (*x)->region();
4620 boost::shared_ptr<Region> _xx;
4626 pl->remove_region (r);
4627 if (Config->get_edit_mode() == Ripple)
4628 pl->ripple (r->position(), -r->length(), boost::shared_ptr<Region>());
4632 _xx = RegionFactory::create (r);
4633 npl->add_region (_xx, r->position() - first_position);
4634 pl->remove_region (r);
4635 if (Config->get_edit_mode() == Ripple)
4636 pl->ripple (r->position(), -r->length(), boost::shared_ptr<Region>());
4640 /* copy region before adding, so we're not putting same object into two different playlists */
4641 npl->add_region (RegionFactory::create (r), r->position() - first_position);
4645 pl->remove_region (r);
4646 if (Config->get_edit_mode() == Ripple)
4647 pl->ripple (r->position(), -r->length(), boost::shared_ptr<Region>());
4656 list<boost::shared_ptr<Playlist> > foo;
4658 /* the pmap is in the same order as the tracks in which selected regions occurred */
4660 for (vector<PlaylistMapping>::iterator i = pmap.begin(); i != pmap.end(); ++i) {
4663 foo.push_back ((*i).pl);
4668 cut_buffer->set (foo);
4672 _last_cut_copy_source_track = 0;
4674 _last_cut_copy_source_track = pmap.front().tv;
4678 for (FreezeList::iterator pl = freezelist.begin(); pl != freezelist.end(); ++pl) {
4681 /* We might have removed regions, which alters other regions' layering_index,
4682 so we need to do a recursive diff here.
4684 vector<Command*> cmds;
4685 (*pl)->rdiff (cmds);
4686 _session->add_commands (cmds);
4688 _session->add_command (new StatefulDiffCommand (*pl));
4693 Editor::cut_copy_ranges (CutCopyOp op)
4695 TrackViewList ts = selection->tracks.filter_to_unique_playlists ();
4697 /* Sort the track selection now, so that it if is used, the playlists
4698 selected by the calls below to cut_copy_clear are in the order that
4699 their tracks appear in the editor. This makes things like paste
4700 of ranges work properly.
4703 sort_track_selection (ts);
4706 if (!entered_track) {
4709 ts.push_back (entered_track);
4712 for (TrackViewList::iterator i = ts.begin(); i != ts.end(); ++i) {
4713 (*i)->cut_copy_clear (*selection, op);
4718 Editor::paste (float times, bool from_context)
4720 DEBUG_TRACE (DEBUG::CutNPaste, "paste to preferred edit pos\n");
4721 MusicFrame where (get_preferred_edit_position (EDIT_IGNORE_NONE, from_context), 0);
4722 paste_internal (where.frame, times, 0);
4726 Editor::mouse_paste ()
4728 MusicFrame where (0, 0);
4730 if (!mouse_frame (where.frame, ignored)) {
4735 paste_internal (where.frame, 1, where.division);
4739 Editor::paste_internal (framepos_t position, float times, const int32_t sub_num)
4741 DEBUG_TRACE (DEBUG::CutNPaste, string_compose ("apparent paste position is %1\n", position));
4743 if (cut_buffer->empty(internal_editing())) {
4747 if (position == max_framepos) {
4748 position = get_preferred_edit_position();
4749 DEBUG_TRACE (DEBUG::CutNPaste, string_compose ("preferred edit position is %1\n", position));
4752 if (position == last_paste_pos) {
4753 /* repeated paste in the same position */
4756 /* paste in new location, reset repeated paste state */
4758 last_paste_pos = position;
4761 /* get everything in the correct order */
4764 if (!selection->tracks.empty()) {
4765 /* If there is a track selection, paste into exactly those tracks and
4766 * only those tracks. This allows the user to be explicit and override
4767 * the below "do the reasonable thing" logic. */
4768 ts = selection->tracks.filter_to_unique_playlists ();
4769 sort_track_selection (ts);
4771 /* Figure out which track to base the paste at. */
4772 TimeAxisView* base_track = NULL;
4773 if (_edit_point == Editing::EditAtMouse && entered_track) {
4774 /* With the mouse edit point, paste onto the track under the mouse. */
4775 base_track = entered_track;
4776 } else if (_edit_point == Editing::EditAtMouse && entered_regionview) {
4777 /* With the mouse edit point, paste onto the track of the region under the mouse. */
4778 base_track = &entered_regionview->get_time_axis_view();
4779 } else if (_last_cut_copy_source_track) {
4780 /* Paste to the track that the cut/copy came from (see mantis #333). */
4781 base_track = _last_cut_copy_source_track;
4783 /* This is "impossible" since we've copied... well, do nothing. */
4787 /* Walk up to parent if necessary, so base track is a route. */
4788 while (base_track->get_parent()) {
4789 base_track = base_track->get_parent();
4792 /* Add base track and all tracks below it. The paste logic will select
4793 the appropriate object types from the cut buffer in relative order. */
4794 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
4795 if ((*i)->order() >= base_track->order()) {
4800 /* Sort tracks so the nth track of type T will pick the nth object of type T. */
4801 sort_track_selection (ts);
4803 /* Add automation children of each track in order, for pasting several lines. */
4804 for (TrackViewList::iterator i = ts.begin(); i != ts.end();) {
4805 /* Add any automation children for pasting several lines */
4806 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*>(*i++);
4811 typedef RouteTimeAxisView::AutomationTracks ATracks;
4812 const ATracks& atracks = rtv->automation_tracks();
4813 for (ATracks::const_iterator a = atracks.begin(); a != atracks.end(); ++a) {
4814 i = ts.insert(i, a->second.get());
4819 /* We now have a list of trackviews starting at base_track, including
4820 automation children, in the order shown in the editor, e.g. R1,
4821 R1.A1, R1.A2, R2, R2.A1, ... */
4824 begin_reversible_command (Operations::paste);
4826 if (ts.size() == 1 && cut_buffer->lines.size() == 1 &&
4827 dynamic_cast<AutomationTimeAxisView*>(ts.front())) {
4828 /* Only one line copied, and one automation track selected. Do a
4829 "greedy" paste from one automation type to another. */
4831 PasteContext ctx(paste_count, times, ItemCounts(), true);
4832 ts.front()->paste (position, *cut_buffer, ctx, sub_num);
4836 /* Paste into tracks */
4838 PasteContext ctx(paste_count, times, ItemCounts(), false);
4839 for (TrackViewList::iterator i = ts.begin(); i != ts.end(); ++i) {
4840 (*i)->paste (position, *cut_buffer, ctx, sub_num);
4844 commit_reversible_command ();
4848 Editor::duplicate_regions (float times)
4850 RegionSelection rs (get_regions_from_selection_and_entered());
4851 duplicate_some_regions (rs, times);
4855 Editor::duplicate_some_regions (RegionSelection& regions, float times)
4857 if (regions.empty ()) {
4861 boost::shared_ptr<Playlist> playlist;
4862 RegionSelection sel = regions; // clear (below) may clear the argument list if its the current region selection
4863 RegionSelection foo;
4865 framepos_t const start_frame = regions.start ();
4866 framepos_t const end_frame = regions.end_frame ();
4867 framecnt_t const gap = end_frame - start_frame + 1;
4869 begin_reversible_command (Operations::duplicate_region);
4871 selection->clear_regions ();
4873 for (RegionSelection::iterator i = sel.begin(); i != sel.end(); ++i) {
4875 boost::shared_ptr<Region> r ((*i)->region());
4877 TimeAxisView& tv = (*i)->get_time_axis_view();
4878 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (&tv);
4879 latest_regionviews.clear ();
4880 sigc::connection c = rtv->view()->RegionViewAdded.connect (sigc::mem_fun(*this, &Editor::collect_new_region_view));
4882 framepos_t const position = end_frame + (r->first_frame() - start_frame + 1);
4883 playlist = (*i)->region()->playlist();
4884 playlist->clear_changes ();
4885 playlist->duplicate (r, position, gap, times);
4886 _session->add_command(new StatefulDiffCommand (playlist));
4890 foo.insert (foo.end(), latest_regionviews.begin(), latest_regionviews.end());
4894 selection->set (foo);
4897 commit_reversible_command ();
4901 Editor::duplicate_selection (float times)
4903 if (selection->time.empty() || selection->tracks.empty()) {
4907 boost::shared_ptr<Playlist> playlist;
4909 TrackViewList ts = selection->tracks.filter_to_unique_playlists ();
4911 bool in_command = false;
4913 for (TrackViewList::iterator i = ts.begin(); i != ts.end(); ++i) {
4914 if ((playlist = (*i)->playlist()) == 0) {
4917 playlist->clear_changes ();
4919 if (clicked_selection) {
4920 playlist->duplicate_range (selection->time[clicked_selection], times);
4922 playlist->duplicate_ranges (selection->time, times);
4926 begin_reversible_command (_("duplicate range selection"));
4929 _session->add_command (new StatefulDiffCommand (playlist));
4934 if (times == 1.0f) {
4935 // now "move" range selection to after the current range selection
4936 framecnt_t distance = 0;
4938 if (clicked_selection) {
4940 selection->time[clicked_selection].end - selection->time[clicked_selection].start;
4942 distance = selection->time.end_frame () - selection->time.start ();
4945 selection->move_time (distance);
4947 commit_reversible_command ();
4951 /** Reset all selected points to the relevant default value */
4953 Editor::reset_point_selection ()
4955 for (PointSelection::iterator i = selection->points.begin(); i != selection->points.end(); ++i) {
4956 ARDOUR::AutomationList::iterator j = (*i)->model ();
4957 (*j)->value = (*i)->line().the_list()->descriptor ().normal;
4962 Editor::center_playhead ()
4964 float const page = _visible_canvas_width * samples_per_pixel;
4965 center_screen_internal (playhead_cursor->current_frame (), page);
4969 Editor::center_edit_point ()
4971 float const page = _visible_canvas_width * samples_per_pixel;
4972 center_screen_internal (get_preferred_edit_position(), page);
4975 /** Caller must begin and commit a reversible command */
4977 Editor::clear_playlist (boost::shared_ptr<Playlist> playlist)
4979 playlist->clear_changes ();
4981 _session->add_command (new StatefulDiffCommand (playlist));
4985 Editor::nudge_track (bool use_edit, bool forwards)
4987 boost::shared_ptr<Playlist> playlist;
4988 framepos_t distance;
4989 framepos_t next_distance;
4993 start = get_preferred_edit_position();
4998 if ((distance = get_nudge_distance (start, next_distance)) == 0) {
5002 if (selection->tracks.empty()) {
5006 TrackViewList ts = selection->tracks.filter_to_unique_playlists ();
5007 bool in_command = false;
5009 for (TrackViewList::iterator i = ts.begin(); i != ts.end(); ++i) {
5011 if ((playlist = (*i)->playlist()) == 0) {
5015 playlist->clear_changes ();
5016 playlist->clear_owned_changes ();
5018 playlist->nudge_after (start, distance, forwards);
5021 begin_reversible_command (_("nudge track"));
5024 vector<Command*> cmds;
5026 playlist->rdiff (cmds);
5027 _session->add_commands (cmds);
5029 _session->add_command (new StatefulDiffCommand (playlist));
5033 commit_reversible_command ();
5038 Editor::remove_last_capture ()
5040 vector<string> choices;
5047 if (Config->get_verify_remove_last_capture()) {
5048 prompt = _("Do you really want to destroy the last capture?"
5049 "\n(This is destructive and cannot be undone)");
5051 choices.push_back (_("No, do nothing."));
5052 choices.push_back (_("Yes, destroy it."));
5054 Choice prompter (_("Destroy last capture"), prompt, choices);
5056 if (prompter.run () == 1) {
5057 _session->remove_last_capture ();
5058 _regions->redisplay ();
5062 _session->remove_last_capture();
5063 _regions->redisplay ();
5068 Editor::normalize_region ()
5074 RegionSelection rs = get_regions_from_selection_and_entered ();
5080 NormalizeDialog dialog (rs.size() > 1);
5082 if (dialog.run () != RESPONSE_ACCEPT) {
5086 CursorContext::Handle cursor_ctx = CursorContext::create(*this, _cursors->wait);
5089 /* XXX: should really only count audio regions here */
5090 int const regions = rs.size ();
5092 /* Make a list of the selected audio regions' maximum amplitudes, and also
5093 obtain the maximum amplitude of them all.
5095 list<double> max_amps;
5096 list<double> rms_vals;
5099 bool use_rms = dialog.constrain_rms ();
5101 for (RegionSelection::const_iterator i = rs.begin(); i != rs.end(); ++i) {
5102 AudioRegionView const * arv = dynamic_cast<AudioRegionView const *> (*i);
5106 dialog.descend (1.0 / regions);
5107 double const a = arv->audio_region()->maximum_amplitude (&dialog);
5109 double r = arv->audio_region()->rms (&dialog);
5110 max_rms = max (max_rms, r);
5111 rms_vals.push_back (r);
5115 /* the user cancelled the operation */
5119 max_amps.push_back (a);
5120 max_amp = max (max_amp, a);
5124 list<double>::const_iterator a = max_amps.begin ();
5125 list<double>::const_iterator l = rms_vals.begin ();
5126 bool in_command = false;
5128 for (RegionSelection::iterator r = rs.begin(); r != rs.end(); ++r) {
5129 AudioRegionView* const arv = dynamic_cast<AudioRegionView*> (*r);
5134 arv->region()->clear_changes ();
5136 double amp = dialog.normalize_individually() ? *a : max_amp;
5137 double target = dialog.target_peak (); // dB
5140 double const amp_rms = dialog.normalize_individually() ? *l : max_rms;
5141 const double t_rms = dialog.target_rms ();
5142 const gain_t c_peak = dB_to_coefficient (target);
5143 const gain_t c_rms = dB_to_coefficient (t_rms);
5144 if ((amp_rms / c_rms) > (amp / c_peak)) {
5150 arv->audio_region()->normalize (amp, target);
5153 begin_reversible_command (_("normalize"));
5156 _session->add_command (new StatefulDiffCommand (arv->region()));
5163 commit_reversible_command ();
5169 Editor::reset_region_scale_amplitude ()
5175 RegionSelection rs = get_regions_from_selection_and_entered ();
5181 bool in_command = false;
5183 for (RegionSelection::iterator r = rs.begin(); r != rs.end(); ++r) {
5184 AudioRegionView* const arv = dynamic_cast<AudioRegionView*>(*r);
5187 arv->region()->clear_changes ();
5188 arv->audio_region()->set_scale_amplitude (1.0f);
5191 begin_reversible_command ("reset gain");
5194 _session->add_command (new StatefulDiffCommand (arv->region()));
5198 commit_reversible_command ();
5203 Editor::adjust_region_gain (bool up)
5205 RegionSelection rs = get_regions_from_selection_and_entered ();
5207 if (!_session || rs.empty()) {
5211 bool in_command = false;
5213 for (RegionSelection::iterator r = rs.begin(); r != rs.end(); ++r) {
5214 AudioRegionView* const arv = dynamic_cast<AudioRegionView*>(*r);
5219 arv->region()->clear_changes ();
5221 double dB = accurate_coefficient_to_dB (arv->audio_region()->scale_amplitude ());
5229 arv->audio_region()->set_scale_amplitude (dB_to_coefficient (dB));
5232 begin_reversible_command ("adjust region gain");
5235 _session->add_command (new StatefulDiffCommand (arv->region()));
5239 commit_reversible_command ();
5244 Editor::reset_region_gain ()
5246 RegionSelection rs = get_regions_from_selection_and_entered ();
5248 if (!_session || rs.empty()) {
5252 bool in_command = false;
5254 for (RegionSelection::iterator r = rs.begin(); r != rs.end(); ++r) {
5255 AudioRegionView* const arv = dynamic_cast<AudioRegionView*>(*r);
5260 arv->region()->clear_changes ();
5262 arv->audio_region()->set_scale_amplitude (1.0f);
5265 begin_reversible_command ("reset region gain");
5268 _session->add_command (new StatefulDiffCommand (arv->region()));
5272 commit_reversible_command ();
5277 Editor::reverse_region ()
5283 Reverse rev (*_session);
5284 apply_filter (rev, _("reverse regions"));
5288 Editor::strip_region_silence ()
5294 RegionSelection rs = get_regions_from_selection_and_entered ();
5300 std::list<RegionView*> audio_only;
5302 for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
5303 AudioRegionView* const arv = dynamic_cast<AudioRegionView*> (*i);
5305 audio_only.push_back (arv);
5309 assert (!audio_only.empty());
5311 StripSilenceDialog d (_session, audio_only);
5312 int const r = d.run ();
5316 if (r == Gtk::RESPONSE_OK) {
5317 ARDOUR::AudioIntervalMap silences;
5318 d.silences (silences);
5319 StripSilence s (*_session, silences, d.fade_length());
5321 apply_filter (s, _("strip silence"), &d);
5326 Editor::apply_midi_note_edit_op_to_region (MidiOperator& op, MidiRegionView& mrv)
5328 Evoral::Sequence<Evoral::Beats>::Notes selected;
5329 mrv.selection_as_notelist (selected, true);
5331 vector<Evoral::Sequence<Evoral::Beats>::Notes> v;
5332 v.push_back (selected);
5334 Evoral::Beats pos_beats = Evoral::Beats (mrv.midi_region()->beat()) - mrv.midi_region()->start_beats();
5336 return op (mrv.midi_region()->model(), pos_beats, v);
5340 Editor::apply_midi_note_edit_op (MidiOperator& op, const RegionSelection& rs)
5346 bool in_command = false;
5348 for (RegionSelection::const_iterator r = rs.begin(); r != rs.end(); ) {
5349 RegionSelection::const_iterator tmp = r;
5352 MidiRegionView* const mrv = dynamic_cast<MidiRegionView*> (*r);
5355 Command* cmd = apply_midi_note_edit_op_to_region (op, *mrv);
5358 begin_reversible_command (op.name ());
5362 _session->add_command (cmd);
5370 commit_reversible_command ();
5375 Editor::fork_region ()
5377 RegionSelection rs = get_regions_from_selection_and_entered ();
5383 CursorContext::Handle cursor_ctx = CursorContext::create(*this, _cursors->wait);
5384 bool in_command = false;
5388 for (RegionSelection::iterator r = rs.begin(); r != rs.end(); ) {
5389 RegionSelection::iterator tmp = r;
5392 MidiRegionView* const mrv = dynamic_cast<MidiRegionView*>(*r);
5396 boost::shared_ptr<Playlist> playlist = mrv->region()->playlist();
5397 boost::shared_ptr<MidiSource> new_source = _session->create_midi_source_by_stealing_name (mrv->midi_view()->track());
5398 boost::shared_ptr<MidiRegion> newregion = mrv->midi_region()->clone (new_source);
5401 begin_reversible_command (_("Fork Region(s)"));
5404 playlist->clear_changes ();
5405 playlist->replace_region (mrv->region(), newregion, mrv->region()->position());
5406 _session->add_command(new StatefulDiffCommand (playlist));
5408 error << string_compose (_("Could not unlink %1"), mrv->region()->name()) << endmsg;
5416 commit_reversible_command ();
5421 Editor::quantize_region ()
5424 quantize_regions(get_regions_from_selection_and_entered ());
5429 Editor::quantize_regions (const RegionSelection& rs)
5431 if (rs.n_midi_regions() == 0) {
5435 if (!quantize_dialog) {
5436 quantize_dialog = new QuantizeDialog (*this);
5439 if (quantize_dialog->is_mapped()) {
5440 /* in progress already */
5444 quantize_dialog->present ();
5445 const int r = quantize_dialog->run ();
5446 quantize_dialog->hide ();
5448 if (r == Gtk::RESPONSE_OK) {
5449 Quantize quant (quantize_dialog->snap_start(),
5450 quantize_dialog->snap_end(),
5451 quantize_dialog->start_grid_size(),
5452 quantize_dialog->end_grid_size(),
5453 quantize_dialog->strength(),
5454 quantize_dialog->swing(),
5455 quantize_dialog->threshold());
5457 apply_midi_note_edit_op (quant, rs);
5462 Editor::legatize_region (bool shrink_only)
5465 legatize_regions(get_regions_from_selection_and_entered (), shrink_only);
5470 Editor::legatize_regions (const RegionSelection& rs, bool shrink_only)
5472 if (rs.n_midi_regions() == 0) {
5476 Legatize legatize(shrink_only);
5477 apply_midi_note_edit_op (legatize, rs);
5481 Editor::transform_region ()
5484 transform_regions(get_regions_from_selection_and_entered ());
5489 Editor::transform_regions (const RegionSelection& rs)
5491 if (rs.n_midi_regions() == 0) {
5498 const int r = td.run();
5501 if (r == Gtk::RESPONSE_OK) {
5502 Transform transform(td.get());
5503 apply_midi_note_edit_op(transform, rs);
5508 Editor::transpose_region ()
5511 transpose_regions(get_regions_from_selection_and_entered ());
5516 Editor::transpose_regions (const RegionSelection& rs)
5518 if (rs.n_midi_regions() == 0) {
5523 int const r = d.run ();
5525 if (r == RESPONSE_ACCEPT) {
5526 Transpose transpose(d.semitones ());
5527 apply_midi_note_edit_op (transpose, rs);
5532 Editor::insert_patch_change (bool from_context)
5534 RegionSelection rs = get_regions_from_selection_and_entered ();
5540 const framepos_t p = get_preferred_edit_position (EDIT_IGNORE_NONE, from_context);
5542 /* XXX: bit of a hack; use the MIDNAM from the first selected region;
5543 there may be more than one, but the PatchChangeDialog can only offer
5544 one set of patch menus.
5546 MidiRegionView* first = dynamic_cast<MidiRegionView*> (rs.front ());
5548 Evoral::PatchChange<Evoral::Beats> empty (Evoral::Beats(), 0, 0, 0);
5549 PatchChangeDialog d (0, _session, empty, first->instrument_info(), Gtk::Stock::ADD);
5551 if (d.run() == RESPONSE_CANCEL) {
5555 for (RegionSelection::iterator i = rs.begin (); i != rs.end(); ++i) {
5556 MidiRegionView* const mrv = dynamic_cast<MidiRegionView*> (*i);
5558 if (p >= mrv->region()->first_frame() && p <= mrv->region()->last_frame()) {
5559 mrv->add_patch_change (p - mrv->region()->position(), d.patch ());
5566 Editor::apply_filter (Filter& filter, string command, ProgressReporter* progress)
5568 RegionSelection rs = get_regions_from_selection_and_entered ();
5574 CursorContext::Handle cursor_ctx = CursorContext::create(*this, _cursors->wait);
5575 bool in_command = false;
5580 int const N = rs.size ();
5582 for (RegionSelection::iterator r = rs.begin(); r != rs.end(); ) {
5583 RegionSelection::iterator tmp = r;
5586 AudioRegionView* const arv = dynamic_cast<AudioRegionView*>(*r);
5588 boost::shared_ptr<Playlist> playlist = arv->region()->playlist();
5591 progress->descend (1.0 / N);
5594 if (arv->audio_region()->apply (filter, progress) == 0) {
5596 playlist->clear_changes ();
5597 playlist->clear_owned_changes ();
5600 begin_reversible_command (command);
5604 if (filter.results.empty ()) {
5606 /* no regions returned; remove the old one */
5607 playlist->remove_region (arv->region ());
5611 std::vector<boost::shared_ptr<Region> >::iterator res = filter.results.begin ();
5613 /* first region replaces the old one */
5614 playlist->replace_region (arv->region(), *res, (*res)->position());
5618 while (res != filter.results.end()) {
5619 playlist->add_region (*res, (*res)->position());
5625 /* We might have removed regions, which alters other regions' layering_index,
5626 so we need to do a recursive diff here.
5628 vector<Command*> cmds;
5629 playlist->rdiff (cmds);
5630 _session->add_commands (cmds);
5632 _session->add_command(new StatefulDiffCommand (playlist));
5636 progress->ascend ();
5645 commit_reversible_command ();
5650 Editor::external_edit_region ()
5656 Editor::reset_region_gain_envelopes ()
5658 RegionSelection rs = get_regions_from_selection_and_entered ();
5660 if (!_session || rs.empty()) {
5664 bool in_command = false;
5666 for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
5667 AudioRegionView* const arv = dynamic_cast<AudioRegionView*>(*i);
5669 boost::shared_ptr<AutomationList> alist (arv->audio_region()->envelope());
5670 XMLNode& before (alist->get_state());
5672 arv->audio_region()->set_default_envelope ();
5675 begin_reversible_command (_("reset region gain"));
5678 _session->add_command (new MementoCommand<AutomationList>(*arv->audio_region()->envelope().get(), &before, &alist->get_state()));
5683 commit_reversible_command ();
5688 Editor::set_region_gain_visibility (RegionView* rv)
5690 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (rv);
5692 arv->update_envelope_visibility();
5697 Editor::set_gain_envelope_visibility ()
5703 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
5704 AudioTimeAxisView* v = dynamic_cast<AudioTimeAxisView*>(*i);
5706 v->audio_view()->foreach_regionview (sigc::mem_fun (this, &Editor::set_region_gain_visibility));
5712 Editor::toggle_gain_envelope_active ()
5714 if (_ignore_region_action) {
5718 RegionSelection rs = get_regions_from_selection_and_entered ();
5720 if (!_session || rs.empty()) {
5724 bool in_command = false;
5726 for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
5727 AudioRegionView* const arv = dynamic_cast<AudioRegionView*>(*i);
5729 arv->region()->clear_changes ();
5730 arv->audio_region()->set_envelope_active (!arv->audio_region()->envelope_active());
5733 begin_reversible_command (_("region gain envelope active"));
5736 _session->add_command (new StatefulDiffCommand (arv->region()));
5741 commit_reversible_command ();
5746 Editor::toggle_region_lock ()
5748 if (_ignore_region_action) {
5752 RegionSelection rs = get_regions_from_selection_and_entered ();
5754 if (!_session || rs.empty()) {
5758 begin_reversible_command (_("toggle region lock"));
5760 for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
5761 (*i)->region()->clear_changes ();
5762 (*i)->region()->set_locked (!(*i)->region()->locked());
5763 _session->add_command (new StatefulDiffCommand ((*i)->region()));
5766 commit_reversible_command ();
5770 Editor::toggle_region_video_lock ()
5772 if (_ignore_region_action) {
5776 RegionSelection rs = get_regions_from_selection_and_entered ();
5778 if (!_session || rs.empty()) {
5782 begin_reversible_command (_("Toggle Video Lock"));
5784 for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
5785 (*i)->region()->clear_changes ();
5786 (*i)->region()->set_video_locked (!(*i)->region()->video_locked());
5787 _session->add_command (new StatefulDiffCommand ((*i)->region()));
5790 commit_reversible_command ();
5794 Editor::toggle_region_lock_style ()
5796 if (_ignore_region_action) {
5800 RegionSelection rs = get_regions_from_selection_and_entered ();
5802 if (!_session || rs.empty()) {
5806 Glib::RefPtr<ToggleAction> a = Glib::RefPtr<ToggleAction>::cast_dynamic (_region_actions->get_action("toggle-region-lock-style"));
5807 vector<Widget*> proxies = a->get_proxies();
5808 Gtk::CheckMenuItem* cmi = dynamic_cast<Gtk::CheckMenuItem*> (proxies.front());
5812 begin_reversible_command (_("toggle region lock style"));
5814 for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
5815 (*i)->region()->clear_changes ();
5816 PositionLockStyle const ns = ((*i)->region()->position_lock_style() == AudioTime && !cmi->get_inconsistent()) ? MusicTime : AudioTime;
5817 (*i)->region()->set_position_lock_style (ns);
5818 _session->add_command (new StatefulDiffCommand ((*i)->region()));
5821 commit_reversible_command ();
5825 Editor::toggle_opaque_region ()
5827 if (_ignore_region_action) {
5831 RegionSelection rs = get_regions_from_selection_and_entered ();
5833 if (!_session || rs.empty()) {
5837 begin_reversible_command (_("change region opacity"));
5839 for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
5840 (*i)->region()->clear_changes ();
5841 (*i)->region()->set_opaque (!(*i)->region()->opaque());
5842 _session->add_command (new StatefulDiffCommand ((*i)->region()));
5845 commit_reversible_command ();
5849 Editor::toggle_record_enable ()
5851 bool new_state = false;
5853 for (TrackSelection::iterator i = selection->tracks.begin(); i != selection->tracks.end(); ++i) {
5854 RouteTimeAxisView *rtav = dynamic_cast<RouteTimeAxisView *>(*i);
5857 if (!rtav->is_track())
5861 new_state = !rtav->track()->rec_enable_control()->get_value();
5865 rtav->track()->rec_enable_control()->set_value (new_state, Controllable::UseGroup);
5870 Editor::toggle_solo ()
5872 bool new_state = false;
5874 boost::shared_ptr<ControlList> cl (new ControlList);
5876 for (TrackSelection::iterator i = selection->tracks.begin(); i != selection->tracks.end(); ++i) {
5877 RouteTimeAxisView *rtav = dynamic_cast<RouteTimeAxisView *>(*i);
5884 new_state = !rtav->route()->soloed ();
5888 cl->push_back (rtav->route()->solo_control());
5891 _session->set_controls (cl, new_state ? 1.0 : 0.0, Controllable::UseGroup);
5895 Editor::toggle_mute ()
5897 bool new_state = false;
5899 boost::shared_ptr<RouteList> rl (new RouteList);
5901 for (TrackSelection::iterator i = selection->tracks.begin(); i != selection->tracks.end(); ++i) {
5902 RouteTimeAxisView *rtav = dynamic_cast<RouteTimeAxisView *>(*i);
5909 new_state = !rtav->route()->muted();
5913 rl->push_back (rtav->route());
5916 _session->set_controls (route_list_to_control_list (rl, &Stripable::mute_control), new_state, Controllable::UseGroup);
5920 Editor::toggle_solo_isolate ()
5926 Editor::fade_range ()
5928 TrackViewList ts = selection->tracks.filter_to_unique_playlists ();
5930 begin_reversible_command (_("fade range"));
5932 for (TrackViewList::iterator i = ts.begin(); i != ts.end(); ++i) {
5933 (*i)->fade_range (selection->time);
5936 commit_reversible_command ();
5941 Editor::set_fade_length (bool in)
5943 RegionSelection rs = get_regions_from_selection_and_entered ();
5949 /* we need a region to measure the offset from the start */
5951 RegionView* rv = rs.front ();
5953 framepos_t pos = get_preferred_edit_position();
5957 if (pos > rv->region()->last_frame() || pos < rv->region()->first_frame()) {
5958 /* edit point is outside the relevant region */
5963 if (pos <= rv->region()->position()) {
5967 len = pos - rv->region()->position();
5968 cmd = _("set fade in length");
5970 if (pos >= rv->region()->last_frame()) {
5974 len = rv->region()->last_frame() - pos;
5975 cmd = _("set fade out length");
5978 bool in_command = false;
5980 for (RegionSelection::iterator x = rs.begin(); x != rs.end(); ++x) {
5981 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (*x);
5987 boost::shared_ptr<AutomationList> alist;
5989 alist = tmp->audio_region()->fade_in();
5991 alist = tmp->audio_region()->fade_out();
5994 XMLNode &before = alist->get_state();
5997 tmp->audio_region()->set_fade_in_length (len);
5998 tmp->audio_region()->set_fade_in_active (true);
6000 tmp->audio_region()->set_fade_out_length (len);
6001 tmp->audio_region()->set_fade_out_active (true);
6005 begin_reversible_command (cmd);
6008 XMLNode &after = alist->get_state();
6009 _session->add_command(new MementoCommand<AutomationList>(*alist, &before, &after));
6013 commit_reversible_command ();
6018 Editor::set_fade_in_shape (FadeShape shape)
6020 RegionSelection rs = get_regions_from_selection_and_entered ();
6025 bool in_command = false;
6027 for (RegionSelection::iterator x = rs.begin(); x != rs.end(); ++x) {
6028 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (*x);
6034 boost::shared_ptr<AutomationList> alist = tmp->audio_region()->fade_in();
6035 XMLNode &before = alist->get_state();
6037 tmp->audio_region()->set_fade_in_shape (shape);
6040 begin_reversible_command (_("set fade in shape"));
6043 XMLNode &after = alist->get_state();
6044 _session->add_command(new MementoCommand<AutomationList>(*alist.get(), &before, &after));
6048 commit_reversible_command ();
6053 Editor::set_fade_out_shape (FadeShape shape)
6055 RegionSelection rs = get_regions_from_selection_and_entered ();
6060 bool in_command = false;
6062 for (RegionSelection::iterator x = rs.begin(); x != rs.end(); ++x) {
6063 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (*x);
6069 boost::shared_ptr<AutomationList> alist = tmp->audio_region()->fade_out();
6070 XMLNode &before = alist->get_state();
6072 tmp->audio_region()->set_fade_out_shape (shape);
6075 begin_reversible_command (_("set fade out shape"));
6078 XMLNode &after = alist->get_state();
6079 _session->add_command(new MementoCommand<AutomationList>(*alist.get(), &before, &after));
6083 commit_reversible_command ();
6088 Editor::set_fade_in_active (bool yn)
6090 RegionSelection rs = get_regions_from_selection_and_entered ();
6095 bool in_command = false;
6097 for (RegionSelection::iterator x = rs.begin(); x != rs.end(); ++x) {
6098 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (*x);
6105 boost::shared_ptr<AudioRegion> ar (tmp->audio_region());
6107 ar->clear_changes ();
6108 ar->set_fade_in_active (yn);
6111 begin_reversible_command (_("set fade in active"));
6114 _session->add_command (new StatefulDiffCommand (ar));
6118 commit_reversible_command ();
6123 Editor::set_fade_out_active (bool yn)
6125 RegionSelection rs = get_regions_from_selection_and_entered ();
6130 bool in_command = false;
6132 for (RegionSelection::iterator x = rs.begin(); x != rs.end(); ++x) {
6133 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (*x);
6139 boost::shared_ptr<AudioRegion> ar (tmp->audio_region());
6141 ar->clear_changes ();
6142 ar->set_fade_out_active (yn);
6145 begin_reversible_command (_("set fade out active"));
6148 _session->add_command(new StatefulDiffCommand (ar));
6152 commit_reversible_command ();
6157 Editor::toggle_region_fades (int dir)
6159 if (_ignore_region_action) {
6163 boost::shared_ptr<AudioRegion> ar;
6166 RegionSelection rs = get_regions_from_selection_and_entered ();
6172 RegionSelection::iterator i;
6173 for (i = rs.begin(); i != rs.end(); ++i) {
6174 if ((ar = boost::dynamic_pointer_cast<AudioRegion>((*i)->region())) != 0) {
6176 yn = ar->fade_out_active ();
6178 yn = ar->fade_in_active ();
6184 if (i == rs.end()) {
6188 /* XXX should this undo-able? */
6189 bool in_command = false;
6191 for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
6192 if ((ar = boost::dynamic_pointer_cast<AudioRegion>((*i)->region())) == 0) {
6195 ar->clear_changes ();
6197 if (dir == 1 || dir == 0) {
6198 ar->set_fade_in_active (!yn);
6201 if (dir == -1 || dir == 0) {
6202 ar->set_fade_out_active (!yn);
6205 begin_reversible_command (_("toggle fade active"));
6208 _session->add_command(new StatefulDiffCommand (ar));
6212 commit_reversible_command ();
6217 /** Update region fade visibility after its configuration has been changed */
6219 Editor::update_region_fade_visibility ()
6221 bool _fade_visibility = _session->config.get_show_region_fades ();
6223 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
6224 AudioTimeAxisView* v = dynamic_cast<AudioTimeAxisView*>(*i);
6226 if (_fade_visibility) {
6227 v->audio_view()->show_all_fades ();
6229 v->audio_view()->hide_all_fades ();
6236 Editor::set_edit_point ()
6239 MusicFrame where (0, 0);
6241 if (!mouse_frame (where.frame, ignored)) {
6247 if (selection->markers.empty()) {
6249 mouse_add_new_marker (where.frame);
6254 Location* loc = find_location_from_marker (selection->markers.front(), ignored);
6257 loc->move_to (where.frame, where.division);
6263 Editor::set_playhead_cursor ()
6265 if (entered_marker) {
6266 _session->request_locate (entered_marker->position(), _session->transport_rolling());
6268 MusicFrame where (0, 0);
6271 if (!mouse_frame (where.frame, ignored)) {
6278 _session->request_locate (where.frame, _session->transport_rolling());
6282 //not sure what this was for; remove it for now.
6283 // if (UIConfiguration::instance().get_follow_edits() && (!_session || !_session->config.get_external_sync())) {
6284 // cancel_time_selection();
6290 Editor::split_region ()
6292 if (_drags->active ()) {
6296 //if a range is selected, separate it
6297 if ( !selection->time.empty()) {
6298 separate_regions_between (selection->time);
6302 //if no range was selected, try to find some regions to split
6303 if (current_mouse_mode() == MouseObject) { //don't try this for Internal Edit, Stretch, Draw, etc.
6305 RegionSelection rs = get_regions_from_selection_and_edit_point ();
6306 const framepos_t pos = get_preferred_edit_position();
6307 const int32_t division = get_grid_music_divisions (0);
6308 MusicFrame where (pos, division);
6314 split_regions_at (where, rs);
6320 Editor::select_next_route()
6322 if (selection->tracks.empty()) {
6323 selection->set (track_views.front());
6327 TimeAxisView* current = selection->tracks.front();
6331 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
6333 if (*i == current) {
6335 if (i != track_views.end()) {
6338 current = (*(track_views.begin()));
6339 //selection->set (*(track_views.begin()));
6345 rui = dynamic_cast<RouteUI *>(current);
6347 } while (current->hidden() || (rui == NULL) || !rui->route()->active());
6349 selection->set (current);
6351 ensure_time_axis_view_is_visible (*current, false);
6355 Editor::select_prev_route()
6357 if (selection->tracks.empty()) {
6358 selection->set (track_views.front());
6362 TimeAxisView* current = selection->tracks.front();
6366 for (TrackViewList::reverse_iterator i = track_views.rbegin(); i != track_views.rend(); ++i) {
6368 if (*i == current) {
6370 if (i != track_views.rend()) {
6373 current = *(track_views.rbegin());
6378 rui = dynamic_cast<RouteUI *>(current);
6380 } while (current->hidden() || (rui == NULL) || !rui->route()->active());
6382 selection->set (current);
6384 ensure_time_axis_view_is_visible (*current, false);
6388 Editor::set_loop_from_selection (bool play)
6390 if (_session == 0) {
6394 framepos_t start, end;
6395 if (!get_selection_extents ( start, end))
6398 set_loop_range (start, end, _("set loop range from selection"));
6401 _session->request_play_loop (true, true);
6406 Editor::set_loop_from_region (bool play)
6408 framepos_t start, end;
6409 if (!get_selection_extents ( start, end))
6412 set_loop_range (start, end, _("set loop range from region"));
6415 _session->request_locate (start, true);
6416 _session->request_play_loop (true);
6421 Editor::set_punch_from_selection ()
6423 if (_session == 0) {
6427 framepos_t start, end;
6428 if (!get_selection_extents ( start, end))
6431 set_punch_range (start, end, _("set punch range from selection"));
6435 Editor::set_auto_punch_range ()
6437 // auto punch in/out button from a single button
6438 // If Punch In is unset, set punch range from playhead to end, enable punch in
6439 // If Punch In is set, the next punch sets Punch Out, unless the playhead has been
6440 // rewound beyond the Punch In marker, in which case that marker will be moved back
6441 // to the current playhead position.
6442 // If punch out is set, it clears the punch range and Punch In/Out buttons
6444 if (_session == 0) {
6448 Location* tpl = transport_punch_location();
6449 framepos_t now = playhead_cursor->current_frame();
6450 framepos_t begin = now;
6451 framepos_t end = _session->current_end_frame();
6453 if (!_session->config.get_punch_in()) {
6454 // First Press - set punch in and create range from here to eternity
6455 set_punch_range (begin, end, _("Auto Punch In"));
6456 _session->config.set_punch_in(true);
6457 } else if (tpl && !_session->config.get_punch_out()) {
6458 // Second press - update end range marker and set punch_out
6459 if (now < tpl->start()) {
6460 // playhead has been rewound - move start back and pretend nothing happened
6462 set_punch_range (begin, end, _("Auto Punch In/Out"));
6464 // normal case for 2nd press - set the punch out
6465 end = playhead_cursor->current_frame ();
6466 set_punch_range (tpl->start(), now, _("Auto Punch In/Out"));
6467 _session->config.set_punch_out(true);
6470 if (_session->config.get_punch_out()) {
6471 _session->config.set_punch_out(false);
6474 if (_session->config.get_punch_in()) {
6475 _session->config.set_punch_in(false);
6480 // third press - unset punch in/out and remove range
6481 _session->locations()->remove(tpl);
6488 Editor::set_session_extents_from_selection ()
6490 if (_session == 0) {
6494 framepos_t start, end;
6495 if (!get_selection_extents ( start, end))
6499 if ((loc = _session->locations()->session_range_location()) == 0) {
6500 _session->set_session_extents (start, end); // this will create a new session range; no need for UNDO
6502 XMLNode &before = loc->get_state();
6504 _session->set_session_extents (start, end);
6506 XMLNode &after = loc->get_state();
6508 begin_reversible_command (_("set session start/end from selection"));
6510 _session->add_command (new MementoCommand<Location>(*loc, &before, &after));
6512 commit_reversible_command ();
6515 _session->set_end_is_free (false);
6519 Editor::set_punch_start_from_edit_point ()
6523 MusicFrame start (0, 0);
6524 framepos_t end = max_framepos;
6526 //use the existing punch end, if any
6527 Location* tpl = transport_punch_location();
6532 if ((_edit_point == EditAtPlayhead) && _session->transport_rolling()) {
6533 start.frame = _session->audible_frame();
6535 start.frame = get_preferred_edit_position();
6538 //snap the selection start/end
6541 //if there's not already a sensible selection endpoint, go "forever"
6542 if (start.frame > end ) {
6546 set_punch_range (start.frame, end, _("set punch start from EP"));
6552 Editor::set_punch_end_from_edit_point ()
6556 framepos_t start = 0;
6557 MusicFrame end (max_framepos, 0);
6559 //use the existing punch start, if any
6560 Location* tpl = transport_punch_location();
6562 start = tpl->start();
6565 if ((_edit_point == EditAtPlayhead) && _session->transport_rolling()) {
6566 end.frame = _session->audible_frame();
6568 end.frame = get_preferred_edit_position();
6571 //snap the selection start/end
6574 set_punch_range (start, end.frame, _("set punch end from EP"));
6580 Editor::set_loop_start_from_edit_point ()
6584 MusicFrame start (0, 0);
6585 framepos_t end = max_framepos;
6587 //use the existing loop end, if any
6588 Location* tpl = transport_loop_location();
6593 if ((_edit_point == EditAtPlayhead) && _session->transport_rolling()) {
6594 start.frame = _session->audible_frame();
6596 start.frame = get_preferred_edit_position();
6599 //snap the selection start/end
6602 //if there's not already a sensible selection endpoint, go "forever"
6603 if (start.frame > end ) {
6607 set_loop_range (start.frame, end, _("set loop start from EP"));
6613 Editor::set_loop_end_from_edit_point ()
6617 framepos_t start = 0;
6618 MusicFrame end (max_framepos, 0);
6620 //use the existing loop start, if any
6621 Location* tpl = transport_loop_location();
6623 start = tpl->start();
6626 if ((_edit_point == EditAtPlayhead) && _session->transport_rolling()) {
6627 end.frame = _session->audible_frame();
6629 end.frame = get_preferred_edit_position();
6632 //snap the selection start/end
6635 set_loop_range (start, end.frame, _("set loop end from EP"));
6640 Editor::set_punch_from_region ()
6642 framepos_t start, end;
6643 if (!get_selection_extents ( start, end))
6646 set_punch_range (start, end, _("set punch range from region"));
6650 Editor::pitch_shift_region ()
6652 RegionSelection rs = get_regions_from_selection_and_entered ();
6654 RegionSelection audio_rs;
6655 for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
6656 if (dynamic_cast<AudioRegionView*> (*i)) {
6657 audio_rs.push_back (*i);
6661 if (audio_rs.empty()) {
6665 pitch_shift (audio_rs, 1.2);
6669 Editor::set_tempo_from_region ()
6671 RegionSelection rs = get_regions_from_selection_and_entered ();
6673 if (!_session || rs.empty()) {
6677 RegionView* rv = rs.front();
6679 define_one_bar (rv->region()->position(), rv->region()->last_frame() + 1);
6683 Editor::use_range_as_bar ()
6685 framepos_t start, end;
6686 if (get_edit_op_range (start, end)) {
6687 define_one_bar (start, end);
6692 Editor::define_one_bar (framepos_t start, framepos_t end)
6694 framepos_t length = end - start;
6696 const Meter& m (_session->tempo_map().meter_at_frame (start));
6698 /* length = 1 bar */
6700 /* We're going to deliver a constant tempo here,
6701 so we can use frames per beat to determine length.
6702 now we want frames per beat.
6703 we have frames per bar, and beats per bar, so ...
6706 /* XXXX METER MATH */
6708 double frames_per_beat = length / m.divisions_per_bar();
6710 /* beats per minute = */
6712 double beats_per_minute = (_session->frame_rate() * 60.0) / frames_per_beat;
6714 /* now decide whether to:
6716 (a) set global tempo
6717 (b) add a new tempo marker
6721 const TempoSection& t (_session->tempo_map().tempo_section_at_frame (start));
6723 bool do_global = false;
6725 if ((_session->tempo_map().n_tempos() == 1) && (_session->tempo_map().n_meters() == 1)) {
6727 /* only 1 tempo & 1 meter: ask if the user wants to set the tempo
6728 at the start, or create a new marker
6731 vector<string> options;
6732 options.push_back (_("Cancel"));
6733 options.push_back (_("Add new marker"));
6734 options.push_back (_("Set global tempo"));
6737 _("Define one bar"),
6738 _("Do you want to set the global tempo or add a new tempo marker?"),
6742 c.set_default_response (2);
6758 /* more than 1 tempo and/or meter section already, go ahead do the "usual":
6759 if the marker is at the region starter, change it, otherwise add
6764 begin_reversible_command (_("set tempo from region"));
6765 XMLNode& before (_session->tempo_map().get_state());
6768 _session->tempo_map().change_initial_tempo (beats_per_minute, t.note_type(), t.end_note_types_per_minute());
6769 } else if (t.frame() == start) {
6770 _session->tempo_map().change_existing_tempo_at (start, beats_per_minute, t.note_type(), t.end_note_types_per_minute());
6772 /* constant tempo */
6773 const Tempo tempo (beats_per_minute, t.note_type());
6774 _session->tempo_map().add_tempo (tempo, 0.0, start, AudioTime);
6777 XMLNode& after (_session->tempo_map().get_state());
6779 _session->add_command (new MementoCommand<TempoMap>(_session->tempo_map(), &before, &after));
6780 commit_reversible_command ();
6784 Editor::split_region_at_transients ()
6786 AnalysisFeatureList positions;
6788 RegionSelection rs = get_regions_from_selection_and_entered ();
6790 if (!_session || rs.empty()) {
6794 begin_reversible_command (_("split regions"));
6796 for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ) {
6798 RegionSelection::iterator tmp;
6803 boost::shared_ptr<AudioRegion> ar = boost::dynamic_pointer_cast<AudioRegion> ((*i)->region());
6806 ar->transients (positions);
6807 split_region_at_points ((*i)->region(), positions, true);
6814 commit_reversible_command ();
6819 Editor::split_region_at_points (boost::shared_ptr<Region> r, AnalysisFeatureList& positions, bool can_ferret, bool select_new)
6821 bool use_rhythmic_rodent = false;
6823 boost::shared_ptr<Playlist> pl = r->playlist();
6825 list<boost::shared_ptr<Region> > new_regions;
6831 if (positions.empty()) {
6835 if (positions.size() > 20 && can_ferret) {
6836 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);
6837 MessageDialog msg (msgstr,
6840 Gtk::BUTTONS_OK_CANCEL);
6843 msg.add_button (_("Call for the Ferret!"), RESPONSE_APPLY);
6844 msg.set_secondary_text (_("Press OK to continue with this split operation\nor ask the Ferret dialog to tune the analysis"));
6846 msg.set_secondary_text (_("Press OK to continue with this split operation"));
6849 msg.set_title (_("Excessive split?"));
6852 int response = msg.run();
6858 case RESPONSE_APPLY:
6859 use_rhythmic_rodent = true;
6866 if (use_rhythmic_rodent) {
6867 show_rhythm_ferret ();
6871 AnalysisFeatureList::const_iterator x;
6873 pl->clear_changes ();
6874 pl->clear_owned_changes ();
6876 x = positions.begin();
6878 if (x == positions.end()) {
6883 pl->remove_region (r);
6887 framepos_t rstart = r->first_frame ();
6888 framepos_t rend = r->last_frame ();
6890 while (x != positions.end()) {
6892 /* deal with positons that are out of scope of present region bounds */
6893 if (*x <= rstart || *x > rend) {
6898 /* file start = original start + how far we from the initial position ? */
6900 framepos_t file_start = r->start() + pos;
6902 /* length = next position - current position */
6904 framepos_t len = (*x) - pos - rstart;
6906 /* XXX we do we really want to allow even single-sample regions?
6907 * shouldn't we have some kind of lower limit on region size?
6916 if (RegionFactory::region_name (new_name, r->name())) {
6920 /* do NOT announce new regions 1 by one, just wait till they are all done */
6924 plist.add (ARDOUR::Properties::start, file_start);
6925 plist.add (ARDOUR::Properties::length, len);
6926 plist.add (ARDOUR::Properties::name, new_name);
6927 plist.add (ARDOUR::Properties::layer, 0);
6928 // TODO set transients_offset
6930 boost::shared_ptr<Region> nr = RegionFactory::create (r->sources(), plist, false);
6931 /* because we set annouce to false, manually add the new region to the
6934 RegionFactory::map_add (nr);
6936 pl->add_region (nr, rstart + pos);
6939 new_regions.push_front(nr);
6948 RegionFactory::region_name (new_name, r->name());
6950 /* Add the final region */
6953 plist.add (ARDOUR::Properties::start, r->start() + pos);
6954 plist.add (ARDOUR::Properties::length, r->last_frame() - (r->position() + pos) + 1);
6955 plist.add (ARDOUR::Properties::name, new_name);
6956 plist.add (ARDOUR::Properties::layer, 0);
6958 boost::shared_ptr<Region> nr = RegionFactory::create (r->sources(), plist, false);
6959 /* because we set annouce to false, manually add the new region to the
6962 RegionFactory::map_add (nr);
6963 pl->add_region (nr, r->position() + pos);
6966 new_regions.push_front(nr);
6971 /* We might have removed regions, which alters other regions' layering_index,
6972 so we need to do a recursive diff here.
6974 vector<Command*> cmds;
6976 _session->add_commands (cmds);
6978 _session->add_command (new StatefulDiffCommand (pl));
6982 for (list<boost::shared_ptr<Region> >::iterator i = new_regions.begin(); i != new_regions.end(); ++i){
6983 set_selected_regionview_from_region_list ((*i), Selection::Add);
6989 Editor::place_transient()
6995 RegionSelection rs = get_regions_from_selection_and_edit_point ();
7001 framepos_t where = get_preferred_edit_position();
7003 begin_reversible_command (_("place transient"));
7005 for (RegionSelection::iterator r = rs.begin(); r != rs.end(); ++r) {
7006 (*r)->region()->add_transient(where);
7009 commit_reversible_command ();
7013 Editor::remove_transient(ArdourCanvas::Item* item)
7019 ArdourCanvas::Line* _line = reinterpret_cast<ArdourCanvas::Line*> (item);
7022 AudioRegionView* _arv = reinterpret_cast<AudioRegionView*> (item->get_data ("regionview"));
7023 _arv->remove_transient (*(float*) _line->get_data ("position"));
7027 Editor::snap_regions_to_grid ()
7029 list <boost::shared_ptr<Playlist > > used_playlists;
7031 RegionSelection rs = get_regions_from_selection_and_entered ();
7033 if (!_session || rs.empty()) {
7037 begin_reversible_command (_("snap regions to grid"));
7039 for (RegionSelection::iterator r = rs.begin(); r != rs.end(); ++r) {
7041 boost::shared_ptr<Playlist> pl = (*r)->region()->playlist();
7043 if (!pl->frozen()) {
7044 /* we haven't seen this playlist before */
7046 /* remember used playlists so we can thaw them later */
7047 used_playlists.push_back(pl);
7050 (*r)->region()->clear_changes ();
7052 MusicFrame start ((*r)->region()->first_frame (), 0);
7054 (*r)->region()->set_position (start.frame, start.division);
7055 _session->add_command(new StatefulDiffCommand ((*r)->region()));
7058 while (used_playlists.size() > 0) {
7059 list <boost::shared_ptr<Playlist > >::iterator i = used_playlists.begin();
7061 used_playlists.pop_front();
7064 commit_reversible_command ();
7068 Editor::close_region_gaps ()
7070 list <boost::shared_ptr<Playlist > > used_playlists;
7072 RegionSelection rs = get_regions_from_selection_and_entered ();
7074 if (!_session || rs.empty()) {
7078 Dialog dialog (_("Close Region Gaps"));
7081 table.set_spacings (12);
7082 table.set_border_width (12);
7083 Label* l = manage (left_aligned_label (_("Crossfade length")));
7084 table.attach (*l, 0, 1, 0, 1);
7086 SpinButton spin_crossfade (1, 0);
7087 spin_crossfade.set_range (0, 15);
7088 spin_crossfade.set_increments (1, 1);
7089 spin_crossfade.set_value (5);
7090 table.attach (spin_crossfade, 1, 2, 0, 1);
7092 table.attach (*manage (new Label (_("ms"))), 2, 3, 0, 1);
7094 l = manage (left_aligned_label (_("Pull-back length")));
7095 table.attach (*l, 0, 1, 1, 2);
7097 SpinButton spin_pullback (1, 0);
7098 spin_pullback.set_range (0, 100);
7099 spin_pullback.set_increments (1, 1);
7100 spin_pullback.set_value(30);
7101 table.attach (spin_pullback, 1, 2, 1, 2);
7103 table.attach (*manage (new Label (_("ms"))), 2, 3, 1, 2);
7105 dialog.get_vbox()->pack_start (table);
7106 dialog.add_button (Stock::CANCEL, RESPONSE_CANCEL);
7107 dialog.add_button (_("Ok"), RESPONSE_ACCEPT);
7110 if (dialog.run () == RESPONSE_CANCEL) {
7114 framepos_t crossfade_len = spin_crossfade.get_value();
7115 framepos_t pull_back_frames = spin_pullback.get_value();
7117 crossfade_len = lrintf (crossfade_len * _session->frame_rate()/1000);
7118 pull_back_frames = lrintf (pull_back_frames * _session->frame_rate()/1000);
7120 /* Iterate over the region list and make adjacent regions overlap by crossfade_len_ms */
7122 begin_reversible_command (_("close region gaps"));
7125 boost::shared_ptr<Region> last_region;
7127 rs.sort_by_position_and_track();
7129 for (RegionSelection::iterator r = rs.begin(); r != rs.end(); ++r) {
7131 boost::shared_ptr<Playlist> pl = (*r)->region()->playlist();
7133 if (!pl->frozen()) {
7134 /* we haven't seen this playlist before */
7136 /* remember used playlists so we can thaw them later */
7137 used_playlists.push_back(pl);
7141 framepos_t position = (*r)->region()->position();
7143 if (idx == 0 || position < last_region->position()){
7144 last_region = (*r)->region();
7149 (*r)->region()->clear_changes ();
7150 (*r)->region()->trim_front( (position - pull_back_frames));
7152 last_region->clear_changes ();
7153 last_region->trim_end( (position - pull_back_frames + crossfade_len));
7155 _session->add_command (new StatefulDiffCommand ((*r)->region()));
7156 _session->add_command (new StatefulDiffCommand (last_region));
7158 last_region = (*r)->region();
7162 while (used_playlists.size() > 0) {
7163 list <boost::shared_ptr<Playlist > >::iterator i = used_playlists.begin();
7165 used_playlists.pop_front();
7168 commit_reversible_command ();
7172 Editor::tab_to_transient (bool forward)
7174 AnalysisFeatureList positions;
7176 RegionSelection rs = get_regions_from_selection_and_entered ();
7182 framepos_t pos = _session->audible_frame ();
7184 if (!selection->tracks.empty()) {
7186 /* don't waste time searching for transients in duplicate playlists.
7189 TrackViewList ts = selection->tracks.filter_to_unique_playlists ();
7191 for (TrackViewList::iterator t = ts.begin(); t != ts.end(); ++t) {
7193 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (*t);
7196 boost::shared_ptr<Track> tr = rtv->track();
7198 boost::shared_ptr<Playlist> pl = tr->playlist ();
7200 framepos_t result = pl->find_next_transient (pos, forward ? 1 : -1);
7203 positions.push_back (result);
7216 for (RegionSelection::iterator r = rs.begin(); r != rs.end(); ++r) {
7217 (*r)->region()->get_transients (positions);
7221 TransientDetector::cleanup_transients (positions, _session->frame_rate(), 3.0);
7224 AnalysisFeatureList::iterator x;
7226 for (x = positions.begin(); x != positions.end(); ++x) {
7232 if (x != positions.end ()) {
7233 _session->request_locate (*x);
7237 AnalysisFeatureList::reverse_iterator x;
7239 for (x = positions.rbegin(); x != positions.rend(); ++x) {
7245 if (x != positions.rend ()) {
7246 _session->request_locate (*x);
7252 Editor::playhead_forward_to_grid ()
7258 MusicFrame pos (playhead_cursor->current_frame (), 0);
7260 if (pos.frame < max_framepos - 1) {
7262 snap_to_internal (pos, RoundUpAlways, false);
7263 _session->request_locate (pos.frame);
7269 Editor::playhead_backward_to_grid ()
7275 MusicFrame pos (playhead_cursor->current_frame (), 0);
7277 if (pos.frame > 2) {
7279 snap_to_internal (pos, RoundDownAlways, false);
7280 _session->request_locate (pos.frame);
7285 Editor::set_track_height (Height h)
7287 TrackSelection& ts (selection->tracks);
7289 for (TrackSelection::iterator x = ts.begin(); x != ts.end(); ++x) {
7290 (*x)->set_height_enum (h);
7295 Editor::toggle_tracks_active ()
7297 TrackSelection& ts (selection->tracks);
7299 bool target = false;
7305 for (TrackSelection::iterator x = ts.begin(); x != ts.end(); ++x) {
7306 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*>(*x);
7310 target = !rtv->_route->active();
7313 rtv->_route->set_active (target, this);
7319 Editor::remove_tracks ()
7321 /* this will delete GUI objects that may be the subject of an event
7322 handler in which this method is called. Defer actual deletion to the
7323 next idle callback, when all event handling is finished.
7325 Glib::signal_idle().connect (sigc::mem_fun (*this, &Editor::idle_remove_tracks));
7329 Editor::idle_remove_tracks ()
7331 Session::StateProtector sp (_session);
7333 return false; /* do not call again */
7337 Editor::_remove_tracks ()
7339 TrackSelection& ts (selection->tracks);
7345 vector<string> choices;
7349 const char* trackstr;
7351 vector<boost::shared_ptr<Route> > routes;
7352 bool special_bus = false;
7354 for (TrackSelection::iterator x = ts.begin(); x != ts.end(); ++x) {
7355 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (*x);
7359 if (rtv->is_track()) {
7364 routes.push_back (rtv->_route);
7366 if (rtv->route()->is_master() || rtv->route()->is_monitor()) {
7371 if (special_bus && !Config->get_allow_special_bus_removal()) {
7372 MessageDialog msg (_("That would be bad news ...."),
7376 msg.set_secondary_text (string_compose (_(
7377 "Removing the master or monitor bus is such a bad idea\n\
7378 that %1 is not going to allow it.\n\
7380 If you really want to do this sort of thing\n\
7381 edit your ardour.rc file to set the\n\
7382 \"allow-special-bus-removal\" option to be \"yes\""), PROGRAM_NAME));
7389 if (ntracks + nbusses == 0) {
7393 trackstr = P_("track", "tracks", ntracks);
7394 busstr = P_("bus", "busses", nbusses);
7398 prompt = string_compose (_("Do you really want to remove %1 %2 and %3 %4?\n"
7399 "(You may also lose the playlists associated with the %2)\n\n"
7400 "This action cannot be undone, and the session file will be overwritten!"),
7401 ntracks, trackstr, nbusses, busstr);
7403 prompt = string_compose (_("Do you really want to remove %1 %2?\n"
7404 "(You may also lose the playlists associated with the %2)\n\n"
7405 "This action cannot be undone, and the session file will be overwritten!"),
7408 } else if (nbusses) {
7409 prompt = string_compose (_("Do you really want to remove %1 %2?\n\n"
7410 "This action cannot be undone, and the session file will be overwritten"),
7414 choices.push_back (_("No, do nothing."));
7415 if (ntracks + nbusses > 1) {
7416 choices.push_back (_("Yes, remove them."));
7418 choices.push_back (_("Yes, remove it."));
7423 title = string_compose (_("Remove %1"), trackstr);
7425 title = string_compose (_("Remove %1"), busstr);
7428 Choice prompter (title, prompt, choices);
7430 if (prompter.run () != 1) {
7434 if (current_mixer_strip && routes.size () > 1 && std::find (routes.begin(), routes.end(), current_mixer_strip->route()) != routes.end ()) {
7435 /* Route deletion calls Editor::timeaxisview_deleted() iteratively (for each deleted
7436 * route). If the deleted route is currently displayed in the Editor-Mixer (highly
7437 * likely because deletion requires selection) this will call
7438 * Editor::set_selected_mixer_strip () which is expensive ( MixerStrip::set_route() ).
7439 * It's likewise likely that the route that has just been displayed in the
7440 * Editor-Mixer will be next in line for deletion.
7442 * So simply switch to the master-bus (if present)
7444 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
7445 if ((*i)->stripable ()->is_master ()) {
7446 set_selected_mixer_strip (*(*i));
7453 PresentationInfo::ChangeSuspender cs;
7454 DisplaySuspender ds;
7456 boost::shared_ptr<RouteList> rl (new RouteList);
7457 for (vector<boost::shared_ptr<Route> >::iterator x = routes.begin(); x != routes.end(); ++x) {
7460 _session->remove_routes (rl);
7462 /* TrackSelection and RouteList leave scope,
7463 * destructors are called,
7464 * diskstream drops references, save_state is called (again for every track)
7469 Editor::do_insert_time ()
7471 if (selection->tracks.empty()) {
7475 InsertRemoveTimeDialog d (*this);
7476 int response = d.run ();
7478 if (response != RESPONSE_OK) {
7482 if (d.distance() == 0) {
7489 d.intersected_region_action (),
7493 d.move_glued_markers(),
7494 d.move_locked_markers(),
7500 Editor::insert_time (
7501 framepos_t pos, framecnt_t frames, InsertTimeOption opt,
7502 bool all_playlists, bool ignore_music_glue, bool markers_too, bool glued_markers_too, bool locked_markers_too, bool tempo_too
7506 if (Config->get_edit_mode() == Lock) {
7509 bool in_command = false;
7511 TrackViewList ts = selection->tracks.filter_to_unique_playlists ();
7513 for (TrackViewList::iterator x = ts.begin(); x != ts.end(); ++x) {
7517 /* don't operate on any playlist more than once, which could
7518 * happen if "all playlists" is enabled, but there is more
7519 * than 1 track using playlists "from" a given track.
7522 set<boost::shared_ptr<Playlist> > pl;
7524 if (all_playlists) {
7525 RouteTimeAxisView* rtav = dynamic_cast<RouteTimeAxisView*> (*x);
7526 if (rtav && rtav->track ()) {
7527 vector<boost::shared_ptr<Playlist> > all = _session->playlists->playlists_for_track (rtav->track ());
7528 for (vector<boost::shared_ptr<Playlist> >::iterator p = all.begin(); p != all.end(); ++p) {
7533 if ((*x)->playlist ()) {
7534 pl.insert ((*x)->playlist ());
7538 for (set<boost::shared_ptr<Playlist> >::iterator i = pl.begin(); i != pl.end(); ++i) {
7540 (*i)->clear_changes ();
7541 (*i)->clear_owned_changes ();
7544 begin_reversible_command (_("insert time"));
7548 if (opt == SplitIntersected) {
7549 /* non musical split */
7550 (*i)->split (MusicFrame (pos, 0));
7553 (*i)->shift (pos, frames, (opt == MoveIntersected), ignore_music_glue);
7555 vector<Command*> cmds;
7557 _session->add_commands (cmds);
7559 _session->add_command (new StatefulDiffCommand (*i));
7563 RouteTimeAxisView* rtav = dynamic_cast<RouteTimeAxisView*> (*x);
7566 begin_reversible_command (_("insert time"));
7569 rtav->route ()->shift (pos, frames);
7576 const int32_t divisions = get_grid_music_divisions (0);
7577 XMLNode& before (_session->locations()->get_state());
7578 Locations::LocationList copy (_session->locations()->list());
7580 for (Locations::LocationList::iterator i = copy.begin(); i != copy.end(); ++i) {
7582 Locations::LocationList::const_iterator tmp;
7584 if ((*i)->position_lock_style() == AudioTime || glued_markers_too) {
7585 bool const was_locked = (*i)->locked ();
7586 if (locked_markers_too) {
7590 if ((*i)->start() >= pos) {
7591 // move end first, in case we're moving by more than the length of the range
7592 if (!(*i)->is_mark()) {
7593 (*i)->set_end ((*i)->end() + frames, false, true, divisions);
7595 (*i)->set_start ((*i)->start() + frames, false, true, divisions);
7607 begin_reversible_command (_("insert time"));
7610 XMLNode& after (_session->locations()->get_state());
7611 _session->add_command (new MementoCommand<Locations>(*_session->locations(), &before, &after));
7617 begin_reversible_command (_("insert time"));
7620 XMLNode& before (_session->tempo_map().get_state());
7621 _session->tempo_map().insert_time (pos, frames);
7622 XMLNode& after (_session->tempo_map().get_state());
7623 _session->add_command (new MementoCommand<TempoMap>(_session->tempo_map(), &before, &after));
7627 commit_reversible_command ();
7632 Editor::do_remove_time ()
7634 if (selection->tracks.empty()) {
7638 InsertRemoveTimeDialog d (*this, true);
7640 int response = d.run ();
7642 if (response != RESPONSE_OK) {
7646 framecnt_t distance = d.distance();
7648 if (distance == 0) {
7658 d.move_glued_markers(),
7659 d.move_locked_markers(),
7665 Editor::remove_time (framepos_t pos, framecnt_t frames, InsertTimeOption opt,
7666 bool ignore_music_glue, bool markers_too, bool glued_markers_too, bool locked_markers_too, bool tempo_too)
7668 if (Config->get_edit_mode() == Lock) {
7669 error << (_("Cannot insert or delete time when in Lock edit.")) << endmsg;
7672 bool in_command = false;
7674 for (TrackSelection::iterator x = selection->tracks.begin(); x != selection->tracks.end(); ++x) {
7676 boost::shared_ptr<Playlist> pl = (*x)->playlist();
7680 XMLNode &before = pl->get_state();
7683 begin_reversible_command (_("remove time"));
7687 std::list<AudioRange> rl;
7688 AudioRange ar(pos, pos+frames, 0);
7691 pl->shift (pos, -frames, true, ignore_music_glue);
7693 XMLNode &after = pl->get_state();
7695 _session->add_command (new MementoCommand<Playlist> (*pl, &before, &after));
7699 RouteTimeAxisView* rtav = dynamic_cast<RouteTimeAxisView*> (*x);
7702 begin_reversible_command (_("remove time"));
7705 rtav->route ()->shift (pos, -frames);
7709 const int32_t divisions = get_grid_music_divisions (0);
7710 std::list<Location*> loc_kill_list;
7715 XMLNode& before (_session->locations()->get_state());
7716 Locations::LocationList copy (_session->locations()->list());
7718 for (Locations::LocationList::iterator i = copy.begin(); i != copy.end(); ++i) {
7719 if ((*i)->position_lock_style() == AudioTime || glued_markers_too) {
7721 bool const was_locked = (*i)->locked ();
7722 if (locked_markers_too) {
7726 if (!(*i)->is_mark()) { // it's a range; have to handle both start and end
7727 if ((*i)->end() >= pos
7728 && (*i)->end() < pos+frames
7729 && (*i)->start() >= pos
7730 && (*i)->end() < pos+frames) { // range is completely enclosed; kill it
7732 loc_kill_list.push_back(*i);
7733 } else { // only start or end is included, try to do the right thing
7734 // move start before moving end, to avoid trying to move the end to before the start
7735 // if we're removing more time than the length of the range
7736 if ((*i)->start() >= pos && (*i)->start() < pos+frames) {
7737 // start is within cut
7738 (*i)->set_start (pos, false, true,divisions); // bring the start marker to the beginning of the cut
7740 } else if ((*i)->start() >= pos+frames) {
7741 // start (and thus entire range) lies beyond end of cut
7742 (*i)->set_start ((*i)->start() - frames, false, true, divisions); // slip the start marker back
7745 if ((*i)->end() >= pos && (*i)->end() < pos+frames) {
7746 // end is inside cut
7747 (*i)->set_end (pos, false, true, divisions); // bring the end to the cut
7749 } else if ((*i)->end() >= pos+frames) {
7750 // end is beyond end of cut
7751 (*i)->set_end ((*i)->end() - frames, false, true, divisions); // slip the end marker back
7756 } else if ((*i)->start() >= pos && (*i)->start() < pos+frames ) {
7757 loc_kill_list.push_back(*i);
7759 } else if ((*i)->start() >= pos) {
7760 (*i)->set_start ((*i)->start() -frames, false, true, divisions);
7770 for (list<Location*>::iterator i = loc_kill_list.begin(); i != loc_kill_list.end(); ++i) {
7771 _session->locations()->remove( *i );
7776 begin_reversible_command (_("remove time"));
7779 XMLNode& after (_session->locations()->get_state());
7780 _session->add_command (new MementoCommand<Locations>(*_session->locations(), &before, &after));
7785 XMLNode& before (_session->tempo_map().get_state());
7787 if (_session->tempo_map().remove_time (pos, frames) ) {
7789 begin_reversible_command (_("remove time"));
7792 XMLNode& after (_session->tempo_map().get_state());
7793 _session->add_command (new MementoCommand<TempoMap>(_session->tempo_map(), &before, &after));
7798 commit_reversible_command ();
7803 Editor::fit_selection ()
7805 if (!selection->tracks.empty()) {
7806 fit_tracks (selection->tracks);
7810 /* no selected tracks - use tracks with selected regions */
7812 if (!selection->regions.empty()) {
7813 for (RegionSelection::iterator r = selection->regions.begin(); r != selection->regions.end(); ++r) {
7814 tvl.push_back (&(*r)->get_time_axis_view ());
7820 } else if (internal_editing()) {
7821 /* no selected tracks, or regions, but in internal edit mode, so follow the mouse and use
7824 if (entered_track) {
7825 tvl.push_back (entered_track);
7833 Editor::fit_tracks (TrackViewList & tracks)
7835 if (tracks.empty()) {
7839 uint32_t child_heights = 0;
7840 int visible_tracks = 0;
7842 for (TrackSelection::iterator t = tracks.begin(); t != tracks.end(); ++t) {
7844 if (!(*t)->marked_for_display()) {
7848 child_heights += (*t)->effective_height() - (*t)->current_height();
7852 /* compute the per-track height from:
7854 * total canvas visible height
7855 * - height that will be taken by visible children of selected tracks
7856 * - height of the ruler/hscroll area
7858 uint32_t h = (uint32_t) floor ((trackviews_height() - child_heights) / visible_tracks);
7859 double first_y_pos = DBL_MAX;
7861 if (h < TimeAxisView::preset_height (HeightSmall)) {
7862 MessageDialog msg (_("There are too many tracks to fit in the current window"));
7863 /* too small to be displayed */
7867 undo_visual_stack.push_back (current_visual_state (true));
7868 PBD::Unwinder<bool> nsv (no_save_visual, true);
7870 /* build a list of all tracks, including children */
7873 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
7875 TimeAxisView::Children c = (*i)->get_child_list ();
7876 for (TimeAxisView::Children::iterator j = c.begin(); j != c.end(); ++j) {
7877 all.push_back (j->get());
7882 // find selection range.
7883 // if someone knows how to user TrackViewList::iterator for this
7885 int selected_top = -1;
7886 int selected_bottom = -1;
7888 for (TrackViewList::iterator t = all.begin(); t != all.end(); ++t, ++i) {
7889 if ((*t)->marked_for_display ()) {
7890 if (tracks.contains(*t)) {
7891 if (selected_top == -1) {
7894 selected_bottom = i;
7900 for (TrackViewList::iterator t = all.begin(); t != all.end(); ++t, ++i) {
7901 if ((*t)->marked_for_display ()) {
7902 if (tracks.contains(*t)) {
7903 (*t)->set_height (h);
7904 first_y_pos = std::min ((*t)->y_position (), first_y_pos);
7906 if (i > selected_top && i < selected_bottom) {
7907 hide_track_in_display (*t);
7914 set the controls_layout height now, because waiting for its size
7915 request signal handler will cause the vertical adjustment setting to fail
7918 controls_layout.property_height () = _full_canvas_height;
7919 vertical_adjustment.set_value (first_y_pos);
7921 redo_visual_stack.push_back (current_visual_state (true));
7923 visible_tracks_selector.set_text (_("Sel"));
7927 Editor::save_visual_state (uint32_t n)
7929 while (visual_states.size() <= n) {
7930 visual_states.push_back (0);
7933 if (visual_states[n] != 0) {
7934 delete visual_states[n];
7937 visual_states[n] = current_visual_state (true);
7942 Editor::goto_visual_state (uint32_t n)
7944 if (visual_states.size() <= n) {
7948 if (visual_states[n] == 0) {
7952 use_visual_state (*visual_states[n]);
7956 Editor::start_visual_state_op (uint32_t n)
7958 save_visual_state (n);
7960 PopUp* pup = new PopUp (WIN_POS_MOUSE, 1000, true);
7962 snprintf (buf, sizeof (buf), _("Saved view %u"), n+1);
7963 pup->set_text (buf);
7968 Editor::cancel_visual_state_op (uint32_t n)
7970 goto_visual_state (n);
7974 Editor::toggle_region_mute ()
7976 if (_ignore_region_action) {
7980 RegionSelection rs = get_regions_from_selection_and_entered ();
7986 if (rs.size() > 1) {
7987 begin_reversible_command (_("mute regions"));
7989 begin_reversible_command (_("mute region"));
7992 for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
7994 (*i)->region()->playlist()->clear_changes ();
7995 (*i)->region()->set_muted (!(*i)->region()->muted ());
7996 _session->add_command (new StatefulDiffCommand ((*i)->region()));
8000 commit_reversible_command ();
8004 Editor::combine_regions ()
8006 /* foreach track with selected regions, take all selected regions
8007 and join them into a new region containing the subregions (as a
8011 typedef set<RouteTimeAxisView*> RTVS;
8014 if (selection->regions.empty()) {
8018 for (RegionSelection::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
8019 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*>(&(*i)->get_time_axis_view());
8022 tracks.insert (rtv);
8026 begin_reversible_command (_("combine regions"));
8028 vector<RegionView*> new_selection;
8030 for (RTVS::iterator i = tracks.begin(); i != tracks.end(); ++i) {
8033 if ((rv = (*i)->combine_regions ()) != 0) {
8034 new_selection.push_back (rv);
8038 selection->clear_regions ();
8039 for (vector<RegionView*>::iterator i = new_selection.begin(); i != new_selection.end(); ++i) {
8040 selection->add (*i);
8043 commit_reversible_command ();
8047 Editor::uncombine_regions ()
8049 typedef set<RouteTimeAxisView*> RTVS;
8052 if (selection->regions.empty()) {
8056 for (RegionSelection::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
8057 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*>(&(*i)->get_time_axis_view());
8060 tracks.insert (rtv);
8064 begin_reversible_command (_("uncombine regions"));
8066 for (RTVS::iterator i = tracks.begin(); i != tracks.end(); ++i) {
8067 (*i)->uncombine_regions ();
8070 commit_reversible_command ();
8074 Editor::toggle_midi_input_active (bool flip_others)
8077 boost::shared_ptr<RouteList> rl (new RouteList);
8079 for (TrackSelection::iterator i = selection->tracks.begin(); i != selection->tracks.end(); ++i) {
8080 RouteTimeAxisView *rtav = dynamic_cast<RouteTimeAxisView *>(*i);
8086 boost::shared_ptr<MidiTrack> mt = rtav->midi_track();
8089 rl->push_back (rtav->route());
8090 onoff = !mt->input_active();
8094 _session->set_exclusive_input_active (rl, onoff, flip_others);
8097 static bool ok_fine (GdkEventAny*) { return true; }
8103 lock_dialog = new Gtk::Dialog (string_compose (_("%1: Locked"), PROGRAM_NAME), true);
8105 Gtk::Image* padlock = manage (new Gtk::Image (ARDOUR_UI_UTILS::get_icon ("padlock_closed")));
8106 lock_dialog->get_vbox()->pack_start (*padlock);
8107 lock_dialog->signal_delete_event ().connect (sigc::ptr_fun (ok_fine));
8109 ArdourButton* b = manage (new ArdourButton);
8110 b->set_name ("lock button");
8111 b->set_text (_("Click to unlock"));
8112 b->signal_clicked.connect (sigc::mem_fun (*this, &Editor::unlock));
8113 lock_dialog->get_vbox()->pack_start (*b);
8115 lock_dialog->get_vbox()->show_all ();
8116 lock_dialog->set_size_request (200, 200);
8119 delete _main_menu_disabler;
8120 _main_menu_disabler = new MainMenuDisabler;
8122 lock_dialog->present ();
8124 lock_dialog->get_window()->set_decorations (Gdk::WMDecoration (0));
8130 lock_dialog->hide ();
8132 delete _main_menu_disabler;
8133 _main_menu_disabler = 0;
8135 if (UIConfiguration::instance().get_lock_gui_after_seconds()) {
8136 start_lock_event_timing ();
8141 Editor::bring_in_callback (Gtk::Label* label, uint32_t n, uint32_t total, string name)
8143 Gtkmm2ext::UI::instance()->call_slot (invalidator (*this), boost::bind (&Editor::update_bring_in_message, this, label, n, total, name));
8147 Editor::update_bring_in_message (Gtk::Label* label, uint32_t n, uint32_t total, string name)
8149 Timers::TimerSuspender t;
8150 label->set_text (string_compose ("Copying %1, %2 of %3", name, n, total));
8151 Gtkmm2ext::UI::instance()->flush_pending (1);
8155 Editor::bring_all_sources_into_session ()
8162 ArdourDialog w (_("Moving embedded files into session folder"));
8163 w.get_vbox()->pack_start (msg);
8166 /* flush all pending GUI events because we're about to start copying
8170 Timers::TimerSuspender t;
8171 Gtkmm2ext::UI::instance()->flush_pending (3);
8175 _session->bring_all_sources_into_session (boost::bind (&Editor::bring_in_callback, this, &msg, _1, _2, _3));