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 */
30 #include "pbd/error.h"
31 #include "pbd/basename.h"
32 #include "pbd/pthread_utils.h"
33 #include "pbd/memento_command.h"
34 #include "pbd/unwind.h"
35 #include "pbd/whitespace.h"
36 #include "pbd/stateful_diff_command.h"
38 #include <gtkmm2ext/utils.h>
39 #include <gtkmm2ext/choice.h>
40 #include <gtkmm2ext/popup.h>
42 #include "ardour/audio_track.h"
43 #include "ardour/audioregion.h"
44 #include "ardour/dB.h"
45 #include "ardour/location.h"
46 #include "ardour/midi_region.h"
47 #include "ardour/midi_track.h"
48 #include "ardour/operations.h"
49 #include "ardour/playlist_factory.h"
50 #include "ardour/quantize.h"
51 #include "ardour/region_factory.h"
52 #include "ardour/reverse.h"
53 #include "ardour/session.h"
54 #include "ardour/session_playlists.h"
55 #include "ardour/strip_silence.h"
56 #include "ardour/transient_detector.h"
58 #include "canvas/canvas.h"
60 #include "ardour_ui.h"
61 #include "audio_region_view.h"
62 #include "audio_streamview.h"
63 #include "audio_time_axis.h"
64 #include "automation_time_axis.h"
65 #include "control_point.h"
69 #include "editor_cursors.h"
70 #include "editor_drag.h"
71 #include "editor_regions.h"
72 #include "editor_routes.h"
73 #include "gtk-custom-hruler.h"
74 #include "gui_thread.h"
75 #include "insert_time_dialog.h"
76 #include "interthread_progress_window.h"
78 #include "midi_region_view.h"
79 #include "mouse_cursors.h"
80 #include "normalize_dialog.h"
81 #include "patch_change_dialog.h"
82 #include "quantize_dialog.h"
83 #include "region_gain_line.h"
84 #include "rgb_macros.h"
85 #include "route_time_axis.h"
86 #include "selection.h"
87 #include "selection_templates.h"
88 #include "streamview.h"
89 #include "strip_silence_dialog.h"
90 #include "time_axis_view.h"
91 #include "transpose_dialog.h"
97 using namespace ARDOUR;
100 using namespace Gtkmm2ext;
101 using namespace Editing;
102 using Gtkmm2ext::Keyboard;
104 /***********************************************************************
106 ***********************************************************************/
109 Editor::undo (uint32_t n)
111 if (_drags->active ()) {
121 Editor::redo (uint32_t n)
123 if (_drags->active ()) {
133 Editor::split_regions_at (framepos_t where, RegionSelection& regions)
137 list <boost::shared_ptr<Playlist > > used_playlists;
139 if (regions.empty()) {
143 begin_reversible_command (_("split"));
145 // if splitting a single region, and snap-to is using
146 // region boundaries, don't pay attention to them
148 if (regions.size() == 1) {
149 switch (_snap_type) {
150 case SnapToRegionStart:
151 case SnapToRegionSync:
152 case SnapToRegionEnd:
161 EditorFreeze(); /* Emit Signal */
164 for (RegionSelection::iterator a = regions.begin(); a != regions.end(); ) {
166 RegionSelection::iterator tmp;
168 /* XXX this test needs to be more complicated, to make sure we really
169 have something to split.
172 if (!(*a)->region()->covers (where)) {
180 boost::shared_ptr<Playlist> pl = (*a)->region()->playlist();
188 /* we haven't seen this playlist before */
190 /* remember used playlists so we can thaw them later */
191 used_playlists.push_back(pl);
196 pl->clear_changes ();
197 pl->split_region ((*a)->region(), where);
198 _session->add_command (new StatefulDiffCommand (pl));
204 while (used_playlists.size() > 0) {
205 list <boost::shared_ptr<Playlist > >::iterator i = used_playlists.begin();
207 used_playlists.pop_front();
210 commit_reversible_command ();
213 EditorThaw(); /* Emit Signal */
217 /** Move one extreme of the current range selection. If more than one range is selected,
218 * the start of the earliest range or the end of the latest range is moved.
220 * @param move_end true to move the end of the current range selection, false to move
222 * @param next true to move the extreme to the next region boundary, false to move to
226 Editor::move_range_selection_start_or_end_to_region_boundary (bool move_end, bool next)
228 if (selection->time.start() == selection->time.end_frame()) {
232 framepos_t start = selection->time.start ();
233 framepos_t end = selection->time.end_frame ();
235 /* the position of the thing we may move */
236 framepos_t pos = move_end ? end : start;
237 int dir = next ? 1 : -1;
239 /* so we don't find the current region again */
240 if (dir > 0 || pos > 0) {
244 framepos_t const target = get_region_boundary (pos, dir, true, false);
259 begin_reversible_command (_("alter selection"));
260 selection->set_preserving_all_ranges (start, end);
261 commit_reversible_command ();
265 Editor::nudge_forward_release (GdkEventButton* ev)
267 if (ev->state & Keyboard::PrimaryModifier) {
268 nudge_forward (false, true);
270 nudge_forward (false, false);
276 Editor::nudge_backward_release (GdkEventButton* ev)
278 if (ev->state & Keyboard::PrimaryModifier) {
279 nudge_backward (false, true);
281 nudge_backward (false, false);
288 Editor::nudge_forward (bool next, bool force_playhead)
291 framepos_t next_distance;
297 RegionSelection rs = get_regions_from_selection_and_entered ();
299 if (!force_playhead && !rs.empty()) {
301 begin_reversible_command (_("nudge regions forward"));
303 for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
304 boost::shared_ptr<Region> r ((*i)->region());
306 distance = get_nudge_distance (r->position(), next_distance);
309 distance = next_distance;
313 r->set_position (r->position() + distance);
314 _session->add_command (new StatefulDiffCommand (r));
317 commit_reversible_command ();
320 } else if (!force_playhead && !selection->markers.empty()) {
324 begin_reversible_command (_("nudge location forward"));
326 for (MarkerSelection::iterator i = selection->markers.begin(); i != selection->markers.end(); ++i) {
328 Location* loc = find_location_from_marker ((*i), is_start);
332 XMLNode& before (loc->get_state());
335 distance = get_nudge_distance (loc->start(), next_distance);
337 distance = next_distance;
339 if (max_framepos - distance > loc->start() + loc->length()) {
340 loc->set_start (loc->start() + distance);
342 loc->set_start (max_framepos - loc->length());
345 distance = get_nudge_distance (loc->end(), next_distance);
347 distance = next_distance;
349 if (max_framepos - distance > loc->end()) {
350 loc->set_end (loc->end() + distance);
352 loc->set_end (max_framepos);
355 XMLNode& after (loc->get_state());
356 _session->add_command (new MementoCommand<Location>(*loc, &before, &after));
360 commit_reversible_command ();
363 distance = get_nudge_distance (playhead_cursor->current_frame (), next_distance);
364 _session->request_locate (playhead_cursor->current_frame () + distance);
369 Editor::nudge_backward (bool next, bool force_playhead)
372 framepos_t next_distance;
378 RegionSelection rs = get_regions_from_selection_and_entered ();
380 if (!force_playhead && !rs.empty()) {
382 begin_reversible_command (_("nudge regions backward"));
384 for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
385 boost::shared_ptr<Region> r ((*i)->region());
387 distance = get_nudge_distance (r->position(), next_distance);
390 distance = next_distance;
395 if (r->position() > distance) {
396 r->set_position (r->position() - distance);
400 _session->add_command (new StatefulDiffCommand (r));
403 commit_reversible_command ();
405 } else if (!force_playhead && !selection->markers.empty()) {
409 begin_reversible_command (_("nudge location forward"));
411 for (MarkerSelection::iterator i = selection->markers.begin(); i != selection->markers.end(); ++i) {
413 Location* loc = find_location_from_marker ((*i), is_start);
417 XMLNode& before (loc->get_state());
420 distance = get_nudge_distance (loc->start(), next_distance);
422 distance = next_distance;
424 if (distance < loc->start()) {
425 loc->set_start (loc->start() - distance);
430 distance = get_nudge_distance (loc->end(), next_distance);
433 distance = next_distance;
436 if (distance < loc->end() - loc->length()) {
437 loc->set_end (loc->end() - distance);
439 loc->set_end (loc->length());
443 XMLNode& after (loc->get_state());
444 _session->add_command (new MementoCommand<Location>(*loc, &before, &after));
448 commit_reversible_command ();
452 distance = get_nudge_distance (playhead_cursor->current_frame (), next_distance);
454 if (playhead_cursor->current_frame () > distance) {
455 _session->request_locate (playhead_cursor->current_frame () - distance);
457 _session->goto_start();
463 Editor::nudge_forward_capture_offset ()
465 RegionSelection rs = get_regions_from_selection_and_entered ();
467 if (!_session || rs.empty()) {
471 begin_reversible_command (_("nudge forward"));
473 framepos_t const distance = _session->worst_output_latency();
475 for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
476 boost::shared_ptr<Region> r ((*i)->region());
479 r->set_position (r->position() + distance);
480 _session->add_command(new StatefulDiffCommand (r));
483 commit_reversible_command ();
487 Editor::nudge_backward_capture_offset ()
489 RegionSelection rs = get_regions_from_selection_and_entered ();
491 if (!_session || rs.empty()) {
495 begin_reversible_command (_("nudge backward"));
497 framepos_t const distance = _session->worst_output_latency();
499 for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
500 boost::shared_ptr<Region> r ((*i)->region());
504 if (r->position() > distance) {
505 r->set_position (r->position() - distance);
509 _session->add_command(new StatefulDiffCommand (r));
512 commit_reversible_command ();
518 Editor::move_to_start ()
520 _session->goto_start ();
524 Editor::move_to_end ()
527 _session->request_locate (_session->current_end_frame());
531 Editor::build_region_boundary_cache ()
534 vector<RegionPoint> interesting_points;
535 boost::shared_ptr<Region> r;
536 TrackViewList tracks;
539 region_boundary_cache.clear ();
545 switch (_snap_type) {
546 case SnapToRegionStart:
547 interesting_points.push_back (Start);
549 case SnapToRegionEnd:
550 interesting_points.push_back (End);
552 case SnapToRegionSync:
553 interesting_points.push_back (SyncPoint);
555 case SnapToRegionBoundary:
556 interesting_points.push_back (Start);
557 interesting_points.push_back (End);
560 fatal << string_compose (_("build_region_boundary_cache called with snap_type = %1"), _snap_type) << endmsg;
565 TimeAxisView *ontrack = 0;
568 if (!selection->tracks.empty()) {
569 tlist = selection->tracks.filter_to_unique_playlists ();
571 tlist = track_views.filter_to_unique_playlists ();
574 while (pos < _session->current_end_frame() && !at_end) {
577 framepos_t lpos = max_framepos;
579 for (vector<RegionPoint>::iterator p = interesting_points.begin(); p != interesting_points.end(); ++p) {
581 if ((r = find_next_region (pos, *p, 1, tlist, &ontrack)) == 0) {
582 if (*p == interesting_points.back()) {
585 /* move to next point type */
591 rpos = r->first_frame();
595 rpos = r->last_frame();
599 rpos = r->sync_position ();
607 RouteTimeAxisView *rtav;
609 if (ontrack != 0 && (rtav = dynamic_cast<RouteTimeAxisView*>(ontrack)) != 0 ) {
610 if (rtav->track() != 0) {
611 speed = rtav->track()->speed();
615 rpos = track_frame_to_session_frame (rpos, speed);
621 /* prevent duplicates, but we don't use set<> because we want to be able
625 vector<framepos_t>::iterator ri;
627 for (ri = region_boundary_cache.begin(); ri != region_boundary_cache.end(); ++ri) {
633 if (ri == region_boundary_cache.end()) {
634 region_boundary_cache.push_back (rpos);
641 /* finally sort to be sure that the order is correct */
643 sort (region_boundary_cache.begin(), region_boundary_cache.end());
646 boost::shared_ptr<Region>
647 Editor::find_next_region (framepos_t frame, RegionPoint point, int32_t dir, TrackViewList& tracks, TimeAxisView **ontrack)
649 TrackViewList::iterator i;
650 framepos_t closest = max_framepos;
651 boost::shared_ptr<Region> ret;
655 framepos_t track_frame;
656 RouteTimeAxisView *rtav;
658 for (i = tracks.begin(); i != tracks.end(); ++i) {
661 boost::shared_ptr<Region> r;
664 if ( (rtav = dynamic_cast<RouteTimeAxisView*>(*i)) != 0 ) {
665 if (rtav->track()!=0)
666 track_speed = rtav->track()->speed();
669 track_frame = session_frame_to_track_frame(frame, track_speed);
671 if ((r = (*i)->find_next_region (track_frame, point, dir)) == 0) {
677 rpos = r->first_frame ();
681 rpos = r->last_frame ();
685 rpos = r->sync_position ();
689 // rpos is a "track frame", converting it to "_session frame"
690 rpos = track_frame_to_session_frame(rpos, track_speed);
693 distance = rpos - frame;
695 distance = frame - rpos;
698 if (distance < closest) {
710 Editor::find_next_region_boundary (framepos_t pos, int32_t dir, const TrackViewList& tracks)
712 framecnt_t distance = max_framepos;
713 framepos_t current_nearest = -1;
715 for (TrackViewList::const_iterator i = tracks.begin(); i != tracks.end(); ++i) {
716 framepos_t contender;
719 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (*i);
725 if ((contender = rtv->find_next_region_boundary (pos, dir)) < 0) {
729 d = ::llabs (pos - contender);
732 current_nearest = contender;
737 return current_nearest;
741 Editor::get_region_boundary (framepos_t pos, int32_t dir, bool with_selection, bool only_onscreen)
746 if (with_selection && Config->get_region_boundaries_from_selected_tracks()) {
748 if (!selection->tracks.empty()) {
750 target = find_next_region_boundary (pos, dir, selection->tracks);
754 if (only_onscreen || Config->get_region_boundaries_from_onscreen_tracks()) {
755 get_onscreen_tracks (tvl);
756 target = find_next_region_boundary (pos, dir, tvl);
758 target = find_next_region_boundary (pos, dir, track_views);
764 if (only_onscreen || Config->get_region_boundaries_from_onscreen_tracks()) {
765 get_onscreen_tracks (tvl);
766 target = find_next_region_boundary (pos, dir, tvl);
768 target = find_next_region_boundary (pos, dir, track_views);
776 Editor::cursor_to_region_boundary (bool with_selection, int32_t dir)
778 framepos_t pos = playhead_cursor->current_frame ();
785 // so we don't find the current region again..
786 if (dir > 0 || pos > 0) {
790 if ((target = get_region_boundary (pos, dir, with_selection, false)) < 0) {
794 _session->request_locate (target);
798 Editor::cursor_to_next_region_boundary (bool with_selection)
800 cursor_to_region_boundary (with_selection, 1);
804 Editor::cursor_to_previous_region_boundary (bool with_selection)
806 cursor_to_region_boundary (with_selection, -1);
810 Editor::cursor_to_region_point (EditorCursor* cursor, RegionPoint point, int32_t dir)
812 boost::shared_ptr<Region> r;
813 framepos_t pos = cursor->current_frame ();
819 TimeAxisView *ontrack = 0;
821 // so we don't find the current region again..
825 if (!selection->tracks.empty()) {
827 r = find_next_region (pos, point, dir, selection->tracks, &ontrack);
829 } else if (clicked_axisview) {
832 t.push_back (clicked_axisview);
834 r = find_next_region (pos, point, dir, t, &ontrack);
838 r = find_next_region (pos, point, dir, track_views, &ontrack);
847 pos = r->first_frame ();
851 pos = r->last_frame ();
855 pos = r->sync_position ();
860 RouteTimeAxisView *rtav;
862 if ( ontrack != 0 && (rtav = dynamic_cast<RouteTimeAxisView*>(ontrack)) != 0 ) {
863 if (rtav->track() != 0) {
864 speed = rtav->track()->speed();
868 pos = track_frame_to_session_frame(pos, speed);
870 if (cursor == playhead_cursor) {
871 _session->request_locate (pos);
873 cursor->set_position (pos);
878 Editor::cursor_to_next_region_point (EditorCursor* cursor, RegionPoint point)
880 cursor_to_region_point (cursor, point, 1);
884 Editor::cursor_to_previous_region_point (EditorCursor* cursor, RegionPoint point)
886 cursor_to_region_point (cursor, point, -1);
890 Editor::cursor_to_selection_start (EditorCursor *cursor)
894 switch (mouse_mode) {
896 if (!selection->regions.empty()) {
897 pos = selection->regions.start();
902 if (!selection->time.empty()) {
903 pos = selection->time.start ();
911 if (cursor == playhead_cursor) {
912 _session->request_locate (pos);
914 cursor->set_position (pos);
919 Editor::cursor_to_selection_end (EditorCursor *cursor)
923 switch (mouse_mode) {
925 if (!selection->regions.empty()) {
926 pos = selection->regions.end_frame();
931 if (!selection->time.empty()) {
932 pos = selection->time.end_frame ();
940 if (cursor == playhead_cursor) {
941 _session->request_locate (pos);
943 cursor->set_position (pos);
948 Editor::selected_marker_to_region_boundary (bool with_selection, int32_t dir)
958 if (selection->markers.empty()) {
962 if (!mouse_frame (mouse, ignored)) {
966 add_location_mark (mouse);
969 if ((loc = find_location_from_marker (selection->markers.front(), ignored)) == 0) {
973 framepos_t pos = loc->start();
975 // so we don't find the current region again..
976 if (dir > 0 || pos > 0) {
980 if ((target = get_region_boundary (pos, dir, with_selection, false)) < 0) {
984 loc->move_to (target);
988 Editor::selected_marker_to_next_region_boundary (bool with_selection)
990 selected_marker_to_region_boundary (with_selection, 1);
994 Editor::selected_marker_to_previous_region_boundary (bool with_selection)
996 selected_marker_to_region_boundary (with_selection, -1);
1000 Editor::selected_marker_to_region_point (RegionPoint point, int32_t dir)
1002 boost::shared_ptr<Region> r;
1007 if (!_session || selection->markers.empty()) {
1011 if ((loc = find_location_from_marker (selection->markers.front(), ignored)) == 0) {
1015 TimeAxisView *ontrack = 0;
1019 // so we don't find the current region again..
1023 if (!selection->tracks.empty()) {
1025 r = find_next_region (pos, point, dir, selection->tracks, &ontrack);
1029 r = find_next_region (pos, point, dir, track_views, &ontrack);
1038 pos = r->first_frame ();
1042 pos = r->last_frame ();
1046 pos = r->adjust_to_sync (r->first_frame());
1051 RouteTimeAxisView *rtav;
1053 if (ontrack != 0 && (rtav = dynamic_cast<RouteTimeAxisView*>(ontrack)) != 0) {
1054 if (rtav->track() != 0) {
1055 speed = rtav->track()->speed();
1059 pos = track_frame_to_session_frame(pos, speed);
1065 Editor::selected_marker_to_next_region_point (RegionPoint point)
1067 selected_marker_to_region_point (point, 1);
1071 Editor::selected_marker_to_previous_region_point (RegionPoint point)
1073 selected_marker_to_region_point (point, -1);
1077 Editor::selected_marker_to_selection_start ()
1083 if (!_session || selection->markers.empty()) {
1087 if ((loc = find_location_from_marker (selection->markers.front(), ignored)) == 0) {
1091 switch (mouse_mode) {
1093 if (!selection->regions.empty()) {
1094 pos = selection->regions.start();
1099 if (!selection->time.empty()) {
1100 pos = selection->time.start ();
1112 Editor::selected_marker_to_selection_end ()
1118 if (!_session || selection->markers.empty()) {
1122 if ((loc = find_location_from_marker (selection->markers.front(), ignored)) == 0) {
1126 switch (mouse_mode) {
1128 if (!selection->regions.empty()) {
1129 pos = selection->regions.end_frame();
1134 if (!selection->time.empty()) {
1135 pos = selection->time.end_frame ();
1147 Editor::scroll_playhead (bool forward)
1149 framepos_t pos = playhead_cursor->current_frame ();
1150 framecnt_t delta = (framecnt_t) floor (current_page_samples() / 0.8);
1153 if (pos == max_framepos) {
1157 if (pos < max_framepos - delta) {
1176 _session->request_locate (pos);
1180 Editor::cursor_align (bool playhead_to_edit)
1186 if (playhead_to_edit) {
1188 if (selection->markers.empty()) {
1192 _session->request_locate (selection->markers.front()->position(), _session->transport_rolling());
1195 /* move selected markers to playhead */
1197 for (MarkerSelection::iterator i = selection->markers.begin(); i != selection->markers.end(); ++i) {
1200 Location* loc = find_location_from_marker (*i, ignored);
1202 if (loc->is_mark()) {
1203 loc->set_start (playhead_cursor->current_frame ());
1205 loc->set (playhead_cursor->current_frame (),
1206 playhead_cursor->current_frame () + loc->length());
1213 Editor::scroll_backward (float pages)
1215 framepos_t const one_page = (framepos_t) rint (_visible_canvas_width * samples_per_pixel);
1216 framepos_t const cnt = (framepos_t) floor (pages * one_page);
1219 if (leftmost_frame < cnt) {
1222 frame = leftmost_frame - cnt;
1225 reset_x_origin (frame);
1229 Editor::scroll_forward (float pages)
1231 framepos_t const one_page = (framepos_t) rint (_visible_canvas_width * samples_per_pixel);
1232 framepos_t const cnt = (framepos_t) floor (pages * one_page);
1235 if (max_framepos - cnt < leftmost_frame) {
1236 frame = max_framepos - cnt;
1238 frame = leftmost_frame + cnt;
1241 reset_x_origin (frame);
1245 Editor::scroll_tracks_down ()
1247 double vert_value = vertical_adjustment.get_value() + vertical_adjustment.get_page_size();
1248 if (vert_value > vertical_adjustment.get_upper() - _visible_canvas_height) {
1249 vert_value = vertical_adjustment.get_upper() - _visible_canvas_height;
1252 vertical_adjustment.set_value (vert_value);
1256 Editor::scroll_tracks_up ()
1258 vertical_adjustment.set_value (vertical_adjustment.get_value() - vertical_adjustment.get_page_size());
1262 Editor::scroll_tracks_down_line ()
1264 double vert_value = vertical_adjustment.get_value() + 60;
1266 if (vert_value > vertical_adjustment.get_upper() - _visible_canvas_height) {
1267 vert_value = vertical_adjustment.get_upper() - _visible_canvas_height;
1270 vertical_adjustment.set_value (vert_value);
1274 Editor::scroll_tracks_up_line ()
1276 reset_y_origin (vertical_adjustment.get_value() - 60);
1282 Editor::tav_zoom_step (bool coarser)
1284 _routes->suspend_redisplay ();
1288 if (selection->tracks.empty()) {
1291 ts = &selection->tracks;
1294 for (TrackViewList::iterator i = ts->begin(); i != ts->end(); ++i) {
1295 TimeAxisView *tv = (static_cast<TimeAxisView*>(*i));
1296 tv->step_height (coarser);
1299 _routes->resume_redisplay ();
1303 Editor::tav_zoom_smooth (bool coarser, bool force_all)
1305 _routes->suspend_redisplay ();
1309 if (selection->tracks.empty() || force_all) {
1312 ts = &selection->tracks;
1315 for (TrackViewList::iterator i = ts->begin(); i != ts->end(); ++i) {
1316 TimeAxisView *tv = (static_cast<TimeAxisView*>(*i));
1317 uint32_t h = tv->current_height ();
1322 if (h >= TimeAxisView::preset_height (HeightSmall)) {
1327 tv->set_height (h + 5);
1331 _routes->resume_redisplay ();
1335 Editor::clamp_samples_per_pixel (framecnt_t& fpp) const
1337 bool clamped = false;
1344 if (max_framepos / fpp < 800) {
1345 fpp = max_framepos / 800;
1353 Editor::temporal_zoom_step (bool coarser)
1355 ENSURE_GUI_THREAD (*this, &Editor::temporal_zoom_step, coarser)
1357 framecnt_t nspp = samples_per_pixel;
1365 temporal_zoom (nspp);
1369 Editor::temporal_zoom (framecnt_t fpp)
1375 framepos_t current_page = current_page_samples();
1376 framepos_t current_leftmost = leftmost_frame;
1377 framepos_t current_rightmost;
1378 framepos_t current_center;
1379 framepos_t new_page_size;
1380 framepos_t half_page_size;
1381 framepos_t leftmost_after_zoom = 0;
1383 bool in_track_canvas;
1387 clamp_samples_per_pixel (fpp);
1388 if (fpp == samples_per_pixel) {
1392 // Imposing an arbitrary limit to zoom out as too much zoom out produces
1393 // segfaults for lack of memory. If somebody decides this is not high enough I
1394 // believe it can be raisen to higher values but some limit must be in place.
1396 // This constant represents 1 day @ 48kHz on a 1600 pixel wide display
1397 // all of which is used for the editor track displays. The whole day
1398 // would be 4147200000 samples, so 2592000 samples per pixel.
1400 nfpp = min (fpp, (framecnt_t) 2592000);
1401 nfpp = max ((framecnt_t) 1, fpp);
1403 new_page_size = (framepos_t) floor (_visible_canvas_width * nfpp);
1404 half_page_size = new_page_size / 2;
1406 switch (zoom_focus) {
1408 leftmost_after_zoom = current_leftmost;
1411 case ZoomFocusRight:
1412 current_rightmost = leftmost_frame + current_page;
1413 if (current_rightmost < new_page_size) {
1414 leftmost_after_zoom = 0;
1416 leftmost_after_zoom = current_rightmost - new_page_size;
1420 case ZoomFocusCenter:
1421 current_center = current_leftmost + (current_page/2);
1422 if (current_center < half_page_size) {
1423 leftmost_after_zoom = 0;
1425 leftmost_after_zoom = current_center - half_page_size;
1429 case ZoomFocusPlayhead:
1430 /* centre playhead */
1431 l = playhead_cursor->current_frame () - (new_page_size * 0.5);
1434 leftmost_after_zoom = 0;
1435 } else if (l > max_framepos) {
1436 leftmost_after_zoom = max_framepos - new_page_size;
1438 leftmost_after_zoom = (framepos_t) l;
1442 case ZoomFocusMouse:
1443 /* try to keep the mouse over the same point in the display */
1445 if (!mouse_frame (where, in_track_canvas)) {
1446 /* use playhead instead */
1447 where = playhead_cursor->current_frame ();
1449 if (where < half_page_size) {
1450 leftmost_after_zoom = 0;
1452 leftmost_after_zoom = where - half_page_size;
1457 l = - ((new_page_size * ((where - current_leftmost)/(double)current_page)) - where);
1460 leftmost_after_zoom = 0;
1461 } else if (l > max_framepos) {
1462 leftmost_after_zoom = max_framepos - new_page_size;
1464 leftmost_after_zoom = (framepos_t) l;
1471 /* try to keep the edit point in the same place */
1472 where = get_preferred_edit_position ();
1476 double l = - ((new_page_size * ((where - current_leftmost)/(double)current_page)) - where);
1479 leftmost_after_zoom = 0;
1480 } else if (l > max_framepos) {
1481 leftmost_after_zoom = max_framepos - new_page_size;
1483 leftmost_after_zoom = (framepos_t) l;
1487 /* edit point not defined */
1494 // leftmost_after_zoom = min (leftmost_after_zoom, _session->current_end_frame());
1496 reposition_and_zoom (leftmost_after_zoom, nfpp);
1500 Editor::temporal_zoom_region (bool both_axes)
1502 framepos_t start = max_framepos;
1504 set<TimeAxisView*> tracks;
1506 RegionSelection rs = get_regions_from_selection_and_entered ();
1512 for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
1514 if ((*i)->region()->position() < start) {
1515 start = (*i)->region()->position();
1518 if ((*i)->region()->last_frame() + 1 > end) {
1519 end = (*i)->region()->last_frame() + 1;
1522 tracks.insert (&((*i)->get_time_axis_view()));
1525 /* now comes an "interesting" hack ... make sure we leave a little space
1526 at each end of the editor so that the zoom doesn't fit the region
1527 precisely to the screen.
1530 GdkScreen* screen = gdk_screen_get_default ();
1531 gint pixwidth = gdk_screen_get_width (screen);
1532 gint mmwidth = gdk_screen_get_width_mm (screen);
1533 double pix_per_mm = (double) pixwidth/ (double) mmwidth;
1534 double one_centimeter_in_pixels = pix_per_mm * 10.0;
1536 if ((start == 0 && end == 0) || end < start) {
1540 framepos_t range = end - start;
1541 double new_fpp = (double) range / (double) _visible_canvas_width;
1542 framepos_t extra_samples = (framepos_t) floor (one_centimeter_in_pixels * new_fpp);
1544 if (start > extra_samples) {
1545 start -= extra_samples;
1550 if (max_framepos - extra_samples > end) {
1551 end += extra_samples;
1556 /* if we're zooming on both axes we need to save track heights etc.
1559 undo_visual_stack.push_back (current_visual_state (both_axes));
1561 PBD::Unwinder<bool> nsv (no_save_visual, true);
1563 temporal_zoom_by_frame (start, end);
1566 uint32_t per_track_height = (uint32_t) floor ((_visible_canvas_height - 10.0) / tracks.size());
1568 /* set visible track heights appropriately */
1570 for (set<TimeAxisView*>::iterator t = tracks.begin(); t != tracks.end(); ++t) {
1571 (*t)->set_height (per_track_height);
1574 /* hide irrelevant tracks */
1576 _routes->suspend_redisplay ();
1578 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
1579 if (find (tracks.begin(), tracks.end(), (*i)) == tracks.end()) {
1580 hide_track_in_display (*i);
1584 _routes->resume_redisplay ();
1586 vertical_adjustment.set_value (0.0);
1589 redo_visual_stack.push_back (current_visual_state (both_axes));
1593 Editor::zoom_to_region (bool both_axes)
1595 temporal_zoom_region (both_axes);
1599 Editor::temporal_zoom_selection ()
1601 if (!selection) return;
1603 if (selection->time.empty()) {
1607 framepos_t start = selection->time[clicked_selection].start;
1608 framepos_t end = selection->time[clicked_selection].end;
1610 temporal_zoom_by_frame (start, end);
1614 Editor::temporal_zoom_session ()
1616 ENSURE_GUI_THREAD (*this, &Editor::temporal_zoom_session)
1619 framecnt_t const l = _session->current_end_frame() - _session->current_start_frame();
1620 double s = _session->current_start_frame() - l * 0.01;
1624 framecnt_t const e = _session->current_end_frame() + l * 0.01;
1625 temporal_zoom_by_frame (framecnt_t (s), e);
1630 Editor::temporal_zoom_by_frame (framepos_t start, framepos_t end)
1632 if (!_session) return;
1634 if ((start == 0 && end == 0) || end < start) {
1638 framepos_t range = end - start;
1640 double const new_fpp = (double) range / (double) _visible_canvas_width;
1642 framepos_t new_page = (framepos_t) floor (_visible_canvas_width * new_fpp);
1643 framepos_t middle = (framepos_t) floor ((double) start + ((double) range / 2.0f));
1644 framepos_t new_leftmost = (framepos_t) floor ((double) middle - ((double) new_page / 2.0f));
1646 if (new_leftmost > middle) {
1650 if (new_leftmost < 0) {
1654 reposition_and_zoom (new_leftmost, new_fpp);
1658 Editor::temporal_zoom_to_frame (bool coarser, framepos_t frame)
1663 double range_before = frame - leftmost_frame;
1666 new_fpp = samples_per_pixel;
1669 new_fpp *= 1.61803399;
1670 range_before *= 1.61803399;
1672 new_fpp = max(1.0,(new_fpp/1.61803399));
1673 range_before /= 1.61803399;
1676 if (new_fpp == samples_per_pixel) {
1680 framepos_t new_leftmost = frame - (framepos_t)range_before;
1682 if (new_leftmost > frame) {
1686 if (new_leftmost < 0) {
1690 reposition_and_zoom (new_leftmost, new_fpp);
1695 Editor::choose_new_marker_name(string &name) {
1697 if (!Config->get_name_new_markers()) {
1698 /* don't prompt user for a new name */
1702 ArdourPrompter dialog (true);
1704 dialog.set_prompt (_("New Name:"));
1706 dialog.set_title (_("New Location Marker"));
1708 dialog.set_name ("MarkNameWindow");
1709 dialog.set_size_request (250, -1);
1710 dialog.set_position (Gtk::WIN_POS_MOUSE);
1712 dialog.add_button (Stock::OK, RESPONSE_ACCEPT);
1713 dialog.set_initial_text (name);
1717 switch (dialog.run ()) {
1718 case RESPONSE_ACCEPT:
1724 dialog.get_result(name);
1731 Editor::add_location_from_selection ()
1735 if (selection->time.empty()) {
1739 if (_session == 0 || clicked_axisview == 0) {
1743 framepos_t start = selection->time[clicked_selection].start;
1744 framepos_t end = selection->time[clicked_selection].end;
1746 _session->locations()->next_available_name(rangename,"selection");
1747 Location *location = new Location (*_session, start, end, rangename, Location::IsRangeMarker);
1749 _session->begin_reversible_command (_("add marker"));
1750 XMLNode &before = _session->locations()->get_state();
1751 _session->locations()->add (location, true);
1752 XMLNode &after = _session->locations()->get_state();
1753 _session->add_command(new MementoCommand<Locations>(*(_session->locations()), &before, &after));
1754 _session->commit_reversible_command ();
1758 Editor::add_location_mark (framepos_t where)
1762 select_new_marker = true;
1764 _session->locations()->next_available_name(markername,"mark");
1765 if (!choose_new_marker_name(markername)) {
1768 Location *location = new Location (*_session, where, where, markername, Location::IsMark);
1769 _session->begin_reversible_command (_("add marker"));
1770 XMLNode &before = _session->locations()->get_state();
1771 _session->locations()->add (location, true);
1772 XMLNode &after = _session->locations()->get_state();
1773 _session->add_command(new MementoCommand<Locations>(*(_session->locations()), &before, &after));
1774 _session->commit_reversible_command ();
1778 Editor::add_location_from_playhead_cursor ()
1780 add_location_mark (_session->audible_frame());
1783 /** Add a range marker around each selected region */
1785 Editor::add_locations_from_region ()
1787 RegionSelection rs = get_regions_from_selection_and_entered ();
1793 _session->begin_reversible_command (selection->regions.size () > 1 ? _("add markers") : _("add marker"));
1794 XMLNode &before = _session->locations()->get_state();
1796 for (RegionSelection::iterator i = rs.begin (); i != rs.end (); ++i) {
1798 boost::shared_ptr<Region> region = (*i)->region ();
1800 Location *location = new Location (*_session, region->position(), region->last_frame(), region->name(), Location::IsRangeMarker);
1802 _session->locations()->add (location, true);
1805 XMLNode &after = _session->locations()->get_state();
1806 _session->add_command (new MementoCommand<Locations>(*(_session->locations()), &before, &after));
1807 _session->commit_reversible_command ();
1810 /** Add a single range marker around all selected regions */
1812 Editor::add_location_from_region ()
1814 RegionSelection rs = get_regions_from_selection_and_entered ();
1820 _session->begin_reversible_command (_("add marker"));
1821 XMLNode &before = _session->locations()->get_state();
1825 if (rs.size() > 1) {
1826 _session->locations()->next_available_name(markername, "regions");
1828 RegionView* rv = *(rs.begin());
1829 boost::shared_ptr<Region> region = rv->region();
1830 markername = region->name();
1833 if (!choose_new_marker_name(markername)) {
1837 // single range spanning all selected
1838 Location *location = new Location (*_session, selection->regions.start(), selection->regions.end_frame(), markername, Location::IsRangeMarker);
1839 _session->locations()->add (location, true);
1841 XMLNode &after = _session->locations()->get_state();
1842 _session->add_command (new MementoCommand<Locations>(*(_session->locations()), &before, &after));
1843 _session->commit_reversible_command ();
1849 Editor::jump_forward_to_mark ()
1855 framepos_t pos = _session->locations()->first_mark_after (playhead_cursor->current_frame());
1861 _session->request_locate (pos, _session->transport_rolling());
1865 Editor::jump_backward_to_mark ()
1871 framepos_t pos = _session->locations()->first_mark_before (playhead_cursor->current_frame());
1877 _session->request_locate (pos, _session->transport_rolling());
1883 framepos_t const pos = _session->audible_frame ();
1886 _session->locations()->next_available_name (markername, "mark");
1888 if (!choose_new_marker_name (markername)) {
1892 _session->locations()->add (new Location (*_session, pos, 0, markername, Location::IsMark), true);
1896 Editor::clear_markers ()
1899 _session->begin_reversible_command (_("clear markers"));
1900 XMLNode &before = _session->locations()->get_state();
1901 _session->locations()->clear_markers ();
1902 XMLNode &after = _session->locations()->get_state();
1903 _session->add_command(new MementoCommand<Locations>(*(_session->locations()), &before, &after));
1904 _session->commit_reversible_command ();
1909 Editor::clear_ranges ()
1912 _session->begin_reversible_command (_("clear ranges"));
1913 XMLNode &before = _session->locations()->get_state();
1915 Location * looploc = _session->locations()->auto_loop_location();
1916 Location * punchloc = _session->locations()->auto_punch_location();
1917 Location * sessionloc = _session->locations()->session_range_location();
1919 _session->locations()->clear_ranges ();
1921 if (looploc) _session->locations()->add (looploc);
1922 if (punchloc) _session->locations()->add (punchloc);
1923 if (sessionloc) _session->locations()->add (sessionloc);
1925 XMLNode &after = _session->locations()->get_state();
1926 _session->add_command(new MementoCommand<Locations>(*(_session->locations()), &before, &after));
1927 _session->commit_reversible_command ();
1932 Editor::clear_locations ()
1934 _session->begin_reversible_command (_("clear locations"));
1935 XMLNode &before = _session->locations()->get_state();
1936 _session->locations()->clear ();
1937 XMLNode &after = _session->locations()->get_state();
1938 _session->add_command(new MementoCommand<Locations>(*(_session->locations()), &before, &after));
1939 _session->commit_reversible_command ();
1940 _session->locations()->clear ();
1944 Editor::unhide_markers ()
1946 for (LocationMarkerMap::iterator i = location_markers.begin(); i != location_markers.end(); ++i) {
1947 Location *l = (*i).first;
1948 if (l->is_hidden() && l->is_mark()) {
1949 l->set_hidden(false, this);
1955 Editor::unhide_ranges ()
1957 for (LocationMarkerMap::iterator i = location_markers.begin(); i != location_markers.end(); ++i) {
1958 Location *l = (*i).first;
1959 if (l->is_hidden() && l->is_range_marker()) {
1960 l->set_hidden(false, this);
1965 /* INSERT/REPLACE */
1968 Editor::insert_region_list_drag (boost::shared_ptr<Region> region, int x, int y)
1972 RouteTimeAxisView *rtv = 0;
1973 boost::shared_ptr<Playlist> playlist;
1976 event.type = GDK_BUTTON_RELEASE;
1980 where = window_event_sample (&event, &cx, &cy);
1982 if (where < leftmost_frame || where > leftmost_frame + current_page_samples()) {
1983 /* clearly outside canvas area */
1987 std::pair<TimeAxisView*, int> tv = trackview_by_y_position (cy);
1988 if (tv.first == 0) {
1992 if ((rtv = dynamic_cast<RouteTimeAxisView*> (tv.first)) == 0) {
1996 if ((playlist = rtv->playlist()) == 0) {
2002 begin_reversible_command (_("insert dragged region"));
2003 playlist->clear_changes ();
2004 playlist->add_region (RegionFactory::create (region, true), where, 1.0);
2005 _session->add_command(new StatefulDiffCommand (playlist));
2006 commit_reversible_command ();
2010 Editor::insert_route_list_drag (boost::shared_ptr<Route> route, int x, int y)
2013 RouteTimeAxisView *dest_rtv = 0;
2014 RouteTimeAxisView *source_rtv = 0;
2017 event.type = GDK_BUTTON_RELEASE;
2021 window_event_sample (&event, &cx, &cy);
2023 std::pair<TimeAxisView*, int> const tv = trackview_by_y_position (cy);
2024 if (tv.first == 0) {
2028 if ((dest_rtv = dynamic_cast<RouteTimeAxisView*> (tv.first)) == 0) {
2032 /* use this drag source to add underlay to a track. But we really don't care
2033 about the Route, only the view of the route, so find it first */
2034 for(TrackViewList::iterator it = track_views.begin(); it != track_views.end(); ++it) {
2035 if((source_rtv = dynamic_cast<RouteTimeAxisView*>(*it)) == 0) {
2039 if(source_rtv->route() == route && source_rtv != dest_rtv) {
2040 dest_rtv->add_underlay(source_rtv->view());
2047 Editor::insert_region_list_selection (float times)
2049 RouteTimeAxisView *tv = 0;
2050 boost::shared_ptr<Playlist> playlist;
2052 if (clicked_routeview != 0) {
2053 tv = clicked_routeview;
2054 } else if (!selection->tracks.empty()) {
2055 if ((tv = dynamic_cast<RouteTimeAxisView*>(selection->tracks.front())) == 0) {
2058 } else if (entered_track != 0) {
2059 if ((tv = dynamic_cast<RouteTimeAxisView*>(entered_track)) == 0) {
2066 if ((playlist = tv->playlist()) == 0) {
2070 boost::shared_ptr<Region> region = _regions->get_single_selection ();
2075 begin_reversible_command (_("insert region"));
2076 playlist->clear_changes ();
2077 playlist->add_region ((RegionFactory::create (region, true)), get_preferred_edit_position(), times);
2078 _session->add_command(new StatefulDiffCommand (playlist));
2079 commit_reversible_command ();
2082 /* BUILT-IN EFFECTS */
2085 Editor::reverse_selection ()
2090 /* GAIN ENVELOPE EDITING */
2093 Editor::edit_envelope ()
2100 Editor::transition_to_rolling (bool fwd)
2106 if (_session->config.get_external_sync()) {
2107 switch (Config->get_sync_source()) {
2111 /* transport controlled by the master */
2116 if (_session->is_auditioning()) {
2117 _session->cancel_audition ();
2121 _session->request_transport_speed (fwd ? 1.0f : -1.0f);
2125 Editor::play_from_start ()
2127 _session->request_locate (_session->current_start_frame(), true);
2131 Editor::play_from_edit_point ()
2133 _session->request_locate (get_preferred_edit_position(), true);
2137 Editor::play_from_edit_point_and_return ()
2139 framepos_t start_frame;
2140 framepos_t return_frame;
2142 start_frame = get_preferred_edit_position (true);
2144 if (_session->transport_rolling()) {
2145 _session->request_locate (start_frame, false);
2149 /* don't reset the return frame if its already set */
2151 if ((return_frame = _session->requested_return_frame()) < 0) {
2152 return_frame = _session->audible_frame();
2155 if (start_frame >= 0) {
2156 _session->request_roll_at_and_return (start_frame, return_frame);
2161 Editor::play_selection ()
2163 if (selection->time.empty()) {
2167 _session->request_play_range (&selection->time, true);
2171 Editor::get_preroll ()
2173 return 1.0 /*Config->get_edit_preroll_seconds()*/ * _session->frame_rate();
2178 Editor::maybe_locate_with_edit_preroll ( framepos_t location )
2180 if ( _session->transport_rolling() || !Config->get_always_play_range() )
2183 location -= get_preroll();
2185 //don't try to locate before the beginning of time
2189 //if follow_playhead is on, keep the playhead on the screen
2190 if ( _follow_playhead )
2191 if ( location < leftmost_frame )
2192 location = leftmost_frame;
2194 _session->request_locate( location );
2198 Editor::play_with_preroll ()
2200 if (selection->time.empty()) {
2203 framepos_t preroll = get_preroll();
2205 framepos_t start = 0;
2206 if (selection->time[clicked_selection].start > preroll)
2207 start = selection->time[clicked_selection].start - preroll;
2209 framepos_t end = selection->time[clicked_selection].end + preroll;
2211 AudioRange ar (start, end, 0);
2212 list<AudioRange> lar;
2215 _session->request_play_range (&lar, true);
2220 Editor::play_location (Location& location)
2222 if (location.start() <= location.end()) {
2226 _session->request_bounded_roll (location.start(), location.end());
2230 Editor::loop_location (Location& location)
2232 if (location.start() <= location.end()) {
2238 if ((tll = transport_loop_location()) != 0) {
2239 tll->set (location.start(), location.end());
2241 // enable looping, reposition and start rolling
2242 _session->request_play_loop (true);
2243 _session->request_locate (tll->start(), true);
2248 Editor::do_layer_operation (LayerOperation op)
2250 if (selection->regions.empty ()) {
2254 bool const multiple = selection->regions.size() > 1;
2258 begin_reversible_command (_("raise regions"));
2260 begin_reversible_command (_("raise region"));
2266 begin_reversible_command (_("raise regions to top"));
2268 begin_reversible_command (_("raise region to top"));
2274 begin_reversible_command (_("lower regions"));
2276 begin_reversible_command (_("lower region"));
2282 begin_reversible_command (_("lower regions to bottom"));
2284 begin_reversible_command (_("lower region"));
2289 set<boost::shared_ptr<Playlist> > playlists = selection->regions.playlists ();
2290 for (set<boost::shared_ptr<Playlist> >::iterator i = playlists.begin(); i != playlists.end(); ++i) {
2291 (*i)->clear_owned_changes ();
2294 for (RegionSelection::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
2295 boost::shared_ptr<Region> r = (*i)->region ();
2307 r->lower_to_bottom ();
2311 for (set<boost::shared_ptr<Playlist> >::iterator i = playlists.begin(); i != playlists.end(); ++i) {
2312 vector<Command*> cmds;
2314 _session->add_commands (cmds);
2317 commit_reversible_command ();
2321 Editor::raise_region ()
2323 do_layer_operation (Raise);
2327 Editor::raise_region_to_top ()
2329 do_layer_operation (RaiseToTop);
2333 Editor::lower_region ()
2335 do_layer_operation (Lower);
2339 Editor::lower_region_to_bottom ()
2341 do_layer_operation (LowerToBottom);
2344 /** Show the region editor for the selected regions */
2346 Editor::show_region_properties ()
2348 selection->foreach_regionview (&RegionView::show_region_editor);
2351 /** Show the midi list editor for the selected MIDI regions */
2353 Editor::show_midi_list_editor ()
2355 selection->foreach_midi_regionview (&MidiRegionView::show_list_editor);
2359 Editor::rename_region ()
2361 RegionSelection rs = get_regions_from_selection_and_entered ();
2367 ArdourDialog d (*this, _("Rename Region"), true, false);
2369 Label label (_("New name:"));
2372 hbox.set_spacing (6);
2373 hbox.pack_start (label, false, false);
2374 hbox.pack_start (entry, true, true);
2376 d.get_vbox()->set_border_width (12);
2377 d.get_vbox()->pack_start (hbox, false, false);
2379 d.add_button(Gtk::Stock::CANCEL, Gtk::RESPONSE_CANCEL);
2380 d.add_button(Gtk::Stock::OK, Gtk::RESPONSE_OK);
2382 d.set_size_request (300, -1);
2384 entry.set_text (rs.front()->region()->name());
2385 entry.select_region (0, -1);
2387 entry.signal_activate().connect (sigc::bind (sigc::mem_fun (d, &Dialog::response), RESPONSE_OK));
2393 int const ret = d.run();
2397 if (ret != RESPONSE_OK) {
2401 std::string str = entry.get_text();
2402 strip_whitespace_edges (str);
2404 rs.front()->region()->set_name (str);
2405 _regions->redisplay ();
2410 Editor::audition_playlist_region_via_route (boost::shared_ptr<Region> region, Route& route)
2412 if (_session->is_auditioning()) {
2413 _session->cancel_audition ();
2416 // note: some potential for creativity here, because region doesn't
2417 // have to belong to the playlist that Route is handling
2419 // bool was_soloed = route.soloed();
2421 route.set_solo (true, this);
2423 _session->request_bounded_roll (region->position(), region->position() + region->length());
2425 /* XXX how to unset the solo state ? */
2428 /** Start an audition of the first selected region */
2430 Editor::play_edit_range ()
2432 framepos_t start, end;
2434 if (get_edit_op_range (start, end)) {
2435 _session->request_bounded_roll (start, end);
2440 Editor::play_selected_region ()
2442 framepos_t start = max_framepos;
2445 RegionSelection rs = get_regions_from_selection_and_entered ();
2451 for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
2452 if ((*i)->region()->position() < start) {
2453 start = (*i)->region()->position();
2455 if ((*i)->region()->last_frame() + 1 > end) {
2456 end = (*i)->region()->last_frame() + 1;
2460 _session->request_bounded_roll (start, end);
2464 Editor::audition_playlist_region_standalone (boost::shared_ptr<Region> region)
2466 _session->audition_region (region);
2470 Editor::region_from_selection ()
2472 if (clicked_axisview == 0) {
2476 if (selection->time.empty()) {
2480 framepos_t start = selection->time[clicked_selection].start;
2481 framepos_t end = selection->time[clicked_selection].end;
2483 TrackViewList tracks = get_tracks_for_range_action ();
2485 framepos_t selection_cnt = end - start + 1;
2487 for (TrackSelection::iterator i = tracks.begin(); i != tracks.end(); ++i) {
2488 boost::shared_ptr<Region> current;
2489 boost::shared_ptr<Playlist> pl;
2490 framepos_t internal_start;
2493 if ((pl = (*i)->playlist()) == 0) {
2497 if ((current = pl->top_region_at (start)) == 0) {
2501 internal_start = start - current->position();
2502 RegionFactory::region_name (new_name, current->name(), true);
2506 plist.add (ARDOUR::Properties::start, current->start() + internal_start);
2507 plist.add (ARDOUR::Properties::length, selection_cnt);
2508 plist.add (ARDOUR::Properties::name, new_name);
2509 plist.add (ARDOUR::Properties::layer, 0);
2511 boost::shared_ptr<Region> region (RegionFactory::create (current, plist));
2516 Editor::create_region_from_selection (vector<boost::shared_ptr<Region> >& new_regions)
2518 if (selection->time.empty() || selection->tracks.empty()) {
2522 framepos_t start = selection->time[clicked_selection].start;
2523 framepos_t end = selection->time[clicked_selection].end;
2525 TrackViewList ts = selection->tracks.filter_to_unique_playlists ();
2526 sort_track_selection (ts);
2528 for (TrackSelection::iterator i = ts.begin(); i != ts.end(); ++i) {
2529 boost::shared_ptr<Region> current;
2530 boost::shared_ptr<Playlist> playlist;
2531 framepos_t internal_start;
2534 if ((playlist = (*i)->playlist()) == 0) {
2538 if ((current = playlist->top_region_at(start)) == 0) {
2542 internal_start = start - current->position();
2543 RegionFactory::region_name (new_name, current->name(), true);
2547 plist.add (ARDOUR::Properties::start, current->start() + internal_start);
2548 plist.add (ARDOUR::Properties::length, end - start + 1);
2549 plist.add (ARDOUR::Properties::name, new_name);
2551 new_regions.push_back (RegionFactory::create (current, plist));
2556 Editor::split_multichannel_region ()
2558 RegionSelection rs = get_regions_from_selection_and_entered ();
2564 vector< boost::shared_ptr<Region> > v;
2566 for (list<RegionView*>::iterator x = rs.begin(); x != rs.end(); ++x) {
2567 (*x)->region()->separate_by_channel (*_session, v);
2572 Editor::new_region_from_selection ()
2574 region_from_selection ();
2575 cancel_selection ();
2579 add_if_covered (RegionView* rv, const AudioRange* ar, RegionSelection* rs)
2581 switch (rv->region()->coverage (ar->start, ar->end - 1)) {
2582 case Evoral::OverlapNone:
2590 * - selected tracks, or if there are none...
2591 * - tracks containing selected regions, or if there are none...
2596 Editor::get_tracks_for_range_action () const
2600 if (selection->tracks.empty()) {
2602 /* use tracks with selected regions */
2604 RegionSelection rs = selection->regions;
2606 for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
2607 TimeAxisView* tv = &(*i)->get_time_axis_view();
2609 if (!t.contains (tv)) {
2615 /* no regions and no tracks: use all tracks */
2621 t = selection->tracks;
2624 return t.filter_to_unique_playlists();
2628 Editor::separate_regions_between (const TimeSelection& ts)
2630 bool in_command = false;
2631 boost::shared_ptr<Playlist> playlist;
2632 RegionSelection new_selection;
2634 TrackViewList tmptracks = get_tracks_for_range_action ();
2635 sort_track_selection (tmptracks);
2637 for (TrackSelection::iterator i = tmptracks.begin(); i != tmptracks.end(); ++i) {
2639 RouteTimeAxisView* rtv;
2641 if ((rtv = dynamic_cast<RouteTimeAxisView*> ((*i))) != 0) {
2643 if (rtv->is_track()) {
2645 /* no edits to destructive tracks */
2647 if (rtv->track()->destructive()) {
2651 if ((playlist = rtv->playlist()) != 0) {
2653 playlist->clear_changes ();
2655 /* XXX need to consider musical time selections here at some point */
2657 double speed = rtv->track()->speed();
2660 for (list<AudioRange>::const_iterator t = ts.begin(); t != ts.end(); ++t) {
2662 sigc::connection c = rtv->view()->RegionViewAdded.connect (
2663 sigc::mem_fun(*this, &Editor::collect_new_region_view));
2665 latest_regionviews.clear ();
2667 playlist->partition ((framepos_t)((*t).start * speed),
2668 (framepos_t)((*t).end * speed), false);
2672 if (!latest_regionviews.empty()) {
2674 rtv->view()->foreach_regionview (sigc::bind (
2675 sigc::ptr_fun (add_if_covered),
2676 &(*t), &new_selection));
2679 begin_reversible_command (_("separate"));
2683 /* pick up changes to existing regions */
2685 vector<Command*> cmds;
2686 playlist->rdiff (cmds);
2687 _session->add_commands (cmds);
2689 /* pick up changes to the playlist itself (adds/removes)
2692 _session->add_command(new StatefulDiffCommand (playlist));
2701 selection->set (new_selection);
2702 set_mouse_mode (MouseObject);
2704 commit_reversible_command ();
2708 struct PlaylistState {
2709 boost::shared_ptr<Playlist> playlist;
2713 /** Take tracks from get_tracks_for_range_action and cut any regions
2714 * on those tracks so that the tracks are empty over the time
2718 Editor::separate_region_from_selection ()
2720 /* preferentially use *all* ranges in the time selection if we're in range mode
2721 to allow discontiguous operation, since get_edit_op_range() currently
2722 returns a single range.
2725 if (!selection->time.empty()) {
2727 separate_regions_between (selection->time);
2734 if (get_edit_op_range (start, end)) {
2736 AudioRange ar (start, end, 1);
2740 separate_regions_between (ts);
2746 Editor::separate_region_from_punch ()
2748 Location* loc = _session->locations()->auto_punch_location();
2750 separate_regions_using_location (*loc);
2755 Editor::separate_region_from_loop ()
2757 Location* loc = _session->locations()->auto_loop_location();
2759 separate_regions_using_location (*loc);
2764 Editor::separate_regions_using_location (Location& loc)
2766 if (loc.is_mark()) {
2770 AudioRange ar (loc.start(), loc.end(), 1);
2775 separate_regions_between (ts);
2778 /** Separate regions under the selected region */
2780 Editor::separate_under_selected_regions ()
2782 vector<PlaylistState> playlists;
2786 rs = get_regions_from_selection_and_entered();
2788 if (!_session || rs.empty()) {
2792 begin_reversible_command (_("separate region under"));
2794 list<boost::shared_ptr<Region> > regions_to_remove;
2796 for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
2797 // we can't just remove the region(s) in this loop because
2798 // this removes them from the RegionSelection, and they thus
2799 // disappear from underneath the iterator, and the ++i above
2800 // SEGVs in a puzzling fashion.
2802 // so, first iterate over the regions to be removed from rs and
2803 // add them to the regions_to_remove list, and then
2804 // iterate over the list to actually remove them.
2806 regions_to_remove.push_back ((*i)->region());
2809 for (list<boost::shared_ptr<Region> >::iterator rl = regions_to_remove.begin(); rl != regions_to_remove.end(); ++rl) {
2811 boost::shared_ptr<Playlist> playlist = (*rl)->playlist();
2814 // is this check necessary?
2818 vector<PlaylistState>::iterator i;
2820 //only take state if this is a new playlist.
2821 for (i = playlists.begin(); i != playlists.end(); ++i) {
2822 if ((*i).playlist == playlist) {
2827 if (i == playlists.end()) {
2829 PlaylistState before;
2830 before.playlist = playlist;
2831 before.before = &playlist->get_state();
2833 playlist->freeze ();
2834 playlists.push_back(before);
2837 //Partition on the region bounds
2838 playlist->partition ((*rl)->first_frame() - 1, (*rl)->last_frame() + 1, true);
2840 //Re-add region that was just removed due to the partition operation
2841 playlist->add_region( (*rl), (*rl)->first_frame() );
2844 vector<PlaylistState>::iterator pl;
2846 for (pl = playlists.begin(); pl != playlists.end(); ++pl) {
2847 (*pl).playlist->thaw ();
2848 _session->add_command(new MementoCommand<Playlist>(*(*pl).playlist, (*pl).before, &(*pl).playlist->get_state()));
2851 commit_reversible_command ();
2855 Editor::crop_region_to_selection ()
2857 if (!selection->time.empty()) {
2859 crop_region_to (selection->time.start(), selection->time.end_frame());
2866 if (get_edit_op_range (start, end)) {
2867 crop_region_to (start, end);
2874 Editor::crop_region_to (framepos_t start, framepos_t end)
2876 vector<boost::shared_ptr<Playlist> > playlists;
2877 boost::shared_ptr<Playlist> playlist;
2880 if (selection->tracks.empty()) {
2881 ts = track_views.filter_to_unique_playlists();
2883 ts = selection->tracks.filter_to_unique_playlists ();
2886 sort_track_selection (ts);
2888 for (TrackSelection::iterator i = ts.begin(); i != ts.end(); ++i) {
2890 RouteTimeAxisView* rtv;
2892 if ((rtv = dynamic_cast<RouteTimeAxisView*> ((*i))) != 0) {
2894 boost::shared_ptr<Track> t = rtv->track();
2896 if (t != 0 && ! t->destructive()) {
2898 if ((playlist = rtv->playlist()) != 0) {
2899 playlists.push_back (playlist);
2905 if (playlists.empty()) {
2909 framepos_t the_start;
2913 begin_reversible_command (_("trim to selection"));
2915 for (vector<boost::shared_ptr<Playlist> >::iterator i = playlists.begin(); i != playlists.end(); ++i) {
2917 boost::shared_ptr<Region> region;
2921 if ((region = (*i)->top_region_at(the_start)) == 0) {
2925 /* now adjust lengths to that we do the right thing
2926 if the selection extends beyond the region
2929 the_start = max (the_start, (framepos_t) region->position());
2930 if (max_framepos - the_start < region->length()) {
2931 the_end = the_start + region->length() - 1;
2933 the_end = max_framepos;
2935 the_end = min (end, the_end);
2936 cnt = the_end - the_start + 1;
2938 region->clear_changes ();
2939 region->trim_to (the_start, cnt);
2940 _session->add_command (new StatefulDiffCommand (region));
2943 commit_reversible_command ();
2947 Editor::region_fill_track ()
2949 RegionSelection rs = get_regions_from_selection_and_entered ();
2951 if (!_session || rs.empty()) {
2955 framepos_t const end = _session->current_end_frame ();
2957 begin_reversible_command (Operations::region_fill);
2959 for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
2961 boost::shared_ptr<Region> region ((*i)->region());
2963 boost::shared_ptr<Playlist> pl = region->playlist();
2965 if (end <= region->last_frame()) {
2969 double times = (double) (end - region->last_frame()) / (double) region->length();
2975 pl->clear_changes ();
2976 pl->add_region (RegionFactory::create (region, true), region->last_frame(), times);
2977 _session->add_command (new StatefulDiffCommand (pl));
2980 commit_reversible_command ();
2984 Editor::region_fill_selection ()
2986 if (clicked_routeview == 0 || !clicked_routeview->is_audio_track()) {
2990 if (selection->time.empty()) {
2994 boost::shared_ptr<Region> region = _regions->get_single_selection ();
2999 framepos_t start = selection->time[clicked_selection].start;
3000 framepos_t end = selection->time[clicked_selection].end;
3002 boost::shared_ptr<Playlist> playlist;
3004 if (selection->tracks.empty()) {
3008 framepos_t selection_length = end - start;
3009 float times = (float)selection_length / region->length();
3011 begin_reversible_command (Operations::fill_selection);
3013 TrackViewList ts = selection->tracks.filter_to_unique_playlists ();
3015 for (TrackViewList::iterator i = ts.begin(); i != ts.end(); ++i) {
3017 if ((playlist = (*i)->playlist()) == 0) {
3021 playlist->clear_changes ();
3022 playlist->add_region (RegionFactory::create (region, true), start, times);
3023 _session->add_command (new StatefulDiffCommand (playlist));
3026 commit_reversible_command ();
3030 Editor::set_region_sync_position ()
3032 set_sync_point (get_preferred_edit_position (), get_regions_from_selection_and_edit_point ());
3036 Editor::set_sync_point (framepos_t where, const RegionSelection& rs)
3038 bool in_command = false;
3040 for (RegionSelection::const_iterator r = rs.begin(); r != rs.end(); ++r) {
3042 if (!(*r)->region()->covers (where)) {
3046 boost::shared_ptr<Region> region ((*r)->region());
3049 begin_reversible_command (_("set sync point"));
3053 region->clear_changes ();
3054 region->set_sync_position (where);
3055 _session->add_command(new StatefulDiffCommand (region));
3059 commit_reversible_command ();
3063 /** Remove the sync positions of the selection */
3065 Editor::remove_region_sync ()
3067 RegionSelection rs = get_regions_from_selection_and_entered ();
3073 begin_reversible_command (_("remove region sync"));
3075 for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
3077 (*i)->region()->clear_changes ();
3078 (*i)->region()->clear_sync_position ();
3079 _session->add_command(new StatefulDiffCommand ((*i)->region()));
3082 commit_reversible_command ();
3086 Editor::naturalize_region ()
3088 RegionSelection rs = get_regions_from_selection_and_entered ();
3094 if (rs.size() > 1) {
3095 begin_reversible_command (_("move regions to original position"));
3097 begin_reversible_command (_("move region to original position"));
3100 for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
3101 (*i)->region()->clear_changes ();
3102 (*i)->region()->move_to_natural_position ();
3103 _session->add_command (new StatefulDiffCommand ((*i)->region()));
3106 commit_reversible_command ();
3110 Editor::align_regions (RegionPoint what)
3112 RegionSelection const rs = get_regions_from_selection_and_edit_point ();
3118 begin_reversible_command (_("align selection"));
3120 framepos_t const position = get_preferred_edit_position ();
3122 for (RegionSelection::const_iterator i = rs.begin(); i != rs.end(); ++i) {
3123 align_region_internal ((*i)->region(), what, position);
3126 commit_reversible_command ();
3129 struct RegionSortByTime {
3130 bool operator() (const RegionView* a, const RegionView* b) {
3131 return a->region()->position() < b->region()->position();
3136 Editor::align_regions_relative (RegionPoint point)
3138 RegionSelection const rs = get_regions_from_selection_and_edit_point ();
3144 framepos_t const position = get_preferred_edit_position ();
3146 framepos_t distance = 0;
3150 list<RegionView*> sorted;
3151 rs.by_position (sorted);
3153 boost::shared_ptr<Region> r ((*sorted.begin())->region());
3158 if (position > r->position()) {
3159 distance = position - r->position();
3161 distance = r->position() - position;
3167 if (position > r->last_frame()) {
3168 distance = position - r->last_frame();
3169 pos = r->position() + distance;
3171 distance = r->last_frame() - position;
3172 pos = r->position() - distance;
3178 pos = r->adjust_to_sync (position);
3179 if (pos > r->position()) {
3180 distance = pos - r->position();
3182 distance = r->position() - pos;
3188 if (pos == r->position()) {
3192 begin_reversible_command (_("align selection (relative)"));
3194 /* move first one specially */
3196 r->clear_changes ();
3197 r->set_position (pos);
3198 _session->add_command(new StatefulDiffCommand (r));
3200 /* move rest by the same amount */
3204 for (list<RegionView*>::iterator i = sorted.begin(); i != sorted.end(); ++i) {
3206 boost::shared_ptr<Region> region ((*i)->region());
3208 region->clear_changes ();
3211 region->set_position (region->position() + distance);
3213 region->set_position (region->position() - distance);
3216 _session->add_command(new StatefulDiffCommand (region));
3220 commit_reversible_command ();
3224 Editor::align_region (boost::shared_ptr<Region> region, RegionPoint point, framepos_t position)
3226 begin_reversible_command (_("align region"));
3227 align_region_internal (region, point, position);
3228 commit_reversible_command ();
3232 Editor::align_region_internal (boost::shared_ptr<Region> region, RegionPoint point, framepos_t position)
3234 region->clear_changes ();
3238 region->set_position (region->adjust_to_sync (position));
3242 if (position > region->length()) {
3243 region->set_position (position - region->length());
3248 region->set_position (position);
3252 _session->add_command(new StatefulDiffCommand (region));
3256 Editor::trim_region_front ()
3262 Editor::trim_region_back ()
3264 trim_region (false);
3268 Editor::trim_region (bool front)
3270 framepos_t where = get_preferred_edit_position();
3271 RegionSelection rs = get_regions_from_selection_and_edit_point ();
3277 begin_reversible_command (front ? _("trim front") : _("trim back"));
3279 for (list<RegionView*>::const_iterator i = rs.by_layer().begin(); i != rs.by_layer().end(); ++i) {
3280 if (!(*i)->region()->locked()) {
3282 (*i)->region()->clear_changes ();
3285 (*i)->region()->trim_front (where);
3286 maybe_locate_with_edit_preroll ( where );
3288 (*i)->region()->trim_end (where);
3289 maybe_locate_with_edit_preroll ( where );
3292 _session->add_command (new StatefulDiffCommand ((*i)->region()));
3296 commit_reversible_command ();
3299 /** Trim the end of the selected regions to the position of the edit cursor */
3301 Editor::trim_region_to_loop ()
3303 Location* loc = _session->locations()->auto_loop_location();
3307 trim_region_to_location (*loc, _("trim to loop"));
3311 Editor::trim_region_to_punch ()
3313 Location* loc = _session->locations()->auto_punch_location();
3317 trim_region_to_location (*loc, _("trim to punch"));
3321 Editor::trim_region_to_location (const Location& loc, const char* str)
3323 RegionSelection rs = get_regions_from_selection_and_entered ();
3325 begin_reversible_command (str);
3327 for (RegionSelection::iterator x = rs.begin(); x != rs.end(); ++x) {
3328 RegionView* rv = (*x);
3330 /* require region to span proposed trim */
3331 switch (rv->region()->coverage (loc.start(), loc.end())) {
3332 case Evoral::OverlapInternal:
3338 RouteTimeAxisView* tav = dynamic_cast<RouteTimeAxisView*> (&rv->get_time_axis_view());
3347 if (tav->track() != 0) {
3348 speed = tav->track()->speed();
3351 start = session_frame_to_track_frame (loc.start(), speed);
3352 end = session_frame_to_track_frame (loc.end(), speed);
3354 rv->region()->clear_changes ();
3355 rv->region()->trim_to (start, (end - start));
3356 _session->add_command(new StatefulDiffCommand (rv->region()));
3359 commit_reversible_command ();
3363 Editor::trim_region_to_previous_region_end ()
3365 return trim_to_region(false);
3369 Editor::trim_region_to_next_region_start ()
3371 return trim_to_region(true);
3375 Editor::trim_to_region(bool forward)
3377 RegionSelection rs = get_regions_from_selection_and_entered ();
3379 begin_reversible_command (_("trim to region"));
3381 boost::shared_ptr<Region> next_region;
3383 for (RegionSelection::iterator x = rs.begin(); x != rs.end(); ++x) {
3385 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (*x);
3391 AudioTimeAxisView* atav = dynamic_cast<AudioTimeAxisView*> (&arv->get_time_axis_view());
3399 if (atav->track() != 0) {
3400 speed = atav->track()->speed();
3404 boost::shared_ptr<Region> region = arv->region();
3405 boost::shared_ptr<Playlist> playlist (region->playlist());
3407 region->clear_changes ();
3411 next_region = playlist->find_next_region (region->first_frame(), Start, 1);
3417 region->trim_end((framepos_t) ( (next_region->first_frame() - 1) * speed));
3418 arv->region_changed (PropertyChange (ARDOUR::Properties::length));
3422 next_region = playlist->find_next_region (region->first_frame(), Start, 0);
3428 region->trim_front((framepos_t) ((next_region->last_frame() + 1) * speed));
3430 arv->region_changed (ARDOUR::bounds_change);
3433 _session->add_command(new StatefulDiffCommand (region));
3436 commit_reversible_command ();
3440 Editor::unfreeze_route ()
3442 if (clicked_routeview == 0 || !clicked_routeview->is_track()) {
3446 clicked_routeview->track()->unfreeze ();
3450 Editor::_freeze_thread (void* arg)
3452 return static_cast<Editor*>(arg)->freeze_thread ();
3456 Editor::freeze_thread ()
3458 /* create event pool because we may need to talk to the session */
3459 SessionEvent::create_per_thread_pool ("freeze events", 64);
3460 /* create per-thread buffers for process() tree to use */
3461 current_interthread_info->process_thread.get_buffers ();
3462 clicked_routeview->audio_track()->freeze_me (*current_interthread_info);
3463 current_interthread_info->done = true;
3464 current_interthread_info->process_thread.drop_buffers();
3469 Editor::freeze_route ()
3475 /* stop transport before we start. this is important */
3477 _session->request_transport_speed (0.0);
3479 /* wait for just a little while, because the above call is asynchronous */
3481 Glib::usleep (250000);
3483 if (clicked_routeview == 0 || !clicked_routeview->is_audio_track()) {
3487 if (!clicked_routeview->track()->bounceable (clicked_routeview->track()->main_outs(), true)) {
3489 _("This track/bus cannot be frozen because the signal adds or loses channels before reaching the outputs.\n"
3490 "This is typically caused by plugins that generate stereo output from mono input or vice versa.")
3492 d.set_title (_("Cannot freeze"));
3497 if (clicked_routeview->track()->has_external_redirects()) {
3498 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"
3499 "Freezing will only process the signal as far as the first send/insert/return."),
3500 clicked_routeview->track()->name()), true, MESSAGE_INFO, BUTTONS_NONE, true);
3502 d.add_button (_("Freeze anyway"), Gtk::RESPONSE_OK);
3503 d.add_button (_("Don't freeze"), Gtk::RESPONSE_CANCEL);
3504 d.set_title (_("Freeze Limits"));
3506 int response = d.run ();
3509 case Gtk::RESPONSE_CANCEL:
3516 InterThreadInfo itt;
3517 current_interthread_info = &itt;
3519 InterthreadProgressWindow ipw (current_interthread_info, _("Freeze"), _("Cancel Freeze"));
3521 pthread_create_and_store (X_("freezer"), &itt.thread, _freeze_thread, this);
3523 set_canvas_cursor (_cursors->wait);
3525 while (!itt.done && !itt.cancel) {
3526 gtk_main_iteration ();
3529 current_interthread_info = 0;
3530 set_canvas_cursor (current_canvas_cursor);
3534 Editor::bounce_range_selection (bool replace, bool enable_processing)
3536 if (selection->time.empty()) {
3540 TrackSelection views = selection->tracks;
3542 for (TrackViewList::iterator i = views.begin(); i != views.end(); ++i) {
3544 if (enable_processing) {
3546 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (*i);
3548 if (rtv && rtv->track() && replace && enable_processing && !rtv->track()->bounceable (rtv->track()->main_outs(), false)) {
3550 _("You can't perform this operation because the processing of the signal "
3551 "will cause one or more of the tracks to end up with a region with more channels than this track has inputs.\n\n"
3552 "You can do this without processing, which is a different operation.")
3554 d.set_title (_("Cannot bounce"));
3561 framepos_t start = selection->time[clicked_selection].start;
3562 framepos_t end = selection->time[clicked_selection].end;
3563 framepos_t cnt = end - start + 1;
3565 begin_reversible_command (_("bounce range"));
3567 for (TrackViewList::iterator i = views.begin(); i != views.end(); ++i) {
3569 RouteTimeAxisView* rtv;
3571 if ((rtv = dynamic_cast<RouteTimeAxisView*> (*i)) == 0) {
3575 boost::shared_ptr<Playlist> playlist;
3577 if ((playlist = rtv->playlist()) == 0) {
3581 InterThreadInfo itt;
3583 playlist->clear_changes ();
3584 playlist->clear_owned_changes ();
3586 boost::shared_ptr<Region> r;
3588 if (enable_processing) {
3589 r = rtv->track()->bounce_range (start, start+cnt, itt, rtv->track()->main_outs(), false);
3591 r = rtv->track()->bounce_range (start, start+cnt, itt, boost::shared_ptr<Processor>(), false);
3599 list<AudioRange> ranges;
3600 ranges.push_back (AudioRange (start, start+cnt, 0));
3601 playlist->cut (ranges); // discard result
3602 playlist->add_region (r, start);
3605 vector<Command*> cmds;
3606 playlist->rdiff (cmds);
3607 _session->add_commands (cmds);
3609 _session->add_command (new StatefulDiffCommand (playlist));
3612 commit_reversible_command ();
3615 /** Delete selected regions, automation points or a time range */
3622 /** Cut selected regions, automation points or a time range */
3629 /** Copy selected regions, automation points or a time range */
3637 /** @return true if a Cut, Copy or Clear is possible */
3639 Editor::can_cut_copy () const
3641 switch (effective_mouse_mode()) {
3644 if (!selection->regions.empty() || !selection->points.empty()) {
3650 if (!selection->time.empty()) {
3663 /** Cut, copy or clear selected regions, automation points or a time range.
3664 * @param op Operation (Cut, Copy or Clear)
3667 Editor::cut_copy (CutCopyOp op)
3669 /* only cancel selection if cut/copy is successful.*/
3675 opname = _("delete");
3684 opname = _("clear");
3688 /* if we're deleting something, and the mouse is still pressed,
3689 the thing we started a drag for will be gone when we release
3690 the mouse button(s). avoid this. see part 2 at the end of
3694 if (op == Delete || op == Cut || op == Clear) {
3695 if (_drags->active ()) {
3700 if ( op != Clear ) //"Delete" doesn't change copy/paste buf
3701 cut_buffer->clear ();
3703 if (entered_marker) {
3705 /* cut/delete op while pointing at a marker */
3708 Location* loc = find_location_from_marker (entered_marker, ignored);
3710 if (_session && loc) {
3711 Glib::signal_idle().connect (sigc::bind (sigc::mem_fun(*this, &Editor::really_remove_marker), loc));
3718 if (internal_editing()) {
3720 switch (effective_mouse_mode()) {
3723 begin_reversible_command (opname + ' ' + X_("MIDI"));
3725 commit_reversible_command ();
3734 bool did_edit = false;
3736 switch (effective_mouse_mode()) {
3738 if (!selection->points.empty()) {
3739 begin_reversible_command (opname + _(" points"));
3741 cut_copy_points (op);
3742 if (op == Cut || op == Delete) {
3743 selection->clear_points ();
3750 if (!selection->regions.empty() || !selection->points.empty()) {
3754 if (selection->regions.empty()) {
3755 thing_name = _("points");
3756 } else if (selection->points.empty()) {
3757 thing_name = _("regions");
3759 thing_name = _("objects");
3762 begin_reversible_command (opname + ' ' + thing_name);
3765 if (!selection->regions.empty()) {
3766 cut_copy_regions (op, selection->regions);
3768 if (op == Cut || op == Delete) {
3769 selection->clear_regions ();
3773 if (!selection->points.empty()) {
3774 cut_copy_points (op);
3776 if (op == Cut || op == Delete) {
3777 selection->clear_points ();
3784 if (selection->time.empty()) {
3785 framepos_t start, end;
3786 /* no time selection, see if we can get an edit range
3789 if (get_edit_op_range (start, end)) {
3790 selection->set (start, end);
3793 if (!selection->time.empty()) {
3794 begin_reversible_command (opname + _(" range"));
3797 cut_copy_ranges (op);
3799 if (op == Cut || op == Delete) {
3800 selection->clear_time ();
3810 commit_reversible_command ();
3813 if (op == Delete || op == Cut || op == Clear) {
3818 struct AutomationRecord {
3819 AutomationRecord () : state (0) {}
3820 AutomationRecord (XMLNode* s) : state (s) {}
3822 XMLNode* state; ///< state before any operation
3823 boost::shared_ptr<Evoral::ControlList> copy; ///< copied events for the cut buffer
3826 /** Cut, copy or clear selected automation points.
3827 * @param op Operation (Cut, Copy or Clear)
3830 Editor::cut_copy_points (CutCopyOp op)
3832 if (selection->points.empty ()) {
3836 /* XXX: not ideal, as there may be more than one track involved in the point selection */
3837 _last_cut_copy_source_track = &selection->points.front()->line().trackview;
3839 /* Keep a record of the AutomationLists that we end up using in this operation */
3840 typedef std::map<boost::shared_ptr<AutomationList>, AutomationRecord> Lists;
3843 /* Go through all selected points, making an AutomationRecord for each distinct AutomationList */
3844 for (PointSelection::iterator i = selection->points.begin(); i != selection->points.end(); ++i) {
3845 boost::shared_ptr<AutomationList> al = (*i)->line().the_list();
3846 if (lists.find (al) == lists.end ()) {
3847 /* We haven't seen this list yet, so make a record for it. This includes
3848 taking a copy of its current state, in case this is needed for undo later.
3850 lists[al] = AutomationRecord (&al->get_state ());
3854 if (op == Cut || op == Copy) {
3855 /* This operation will involve putting things in the cut buffer, so create an empty
3856 ControlList for each of our source lists to put the cut buffer data in.
3858 for (Lists::iterator i = lists.begin(); i != lists.end(); ++i) {
3859 i->second.copy = i->first->create (i->first->parameter ());
3862 /* Add all selected points to the relevant copy ControlLists */
3863 for (PointSelection::iterator i = selection->points.begin(); i != selection->points.end(); ++i) {
3864 boost::shared_ptr<AutomationList> al = (*i)->line().the_list();
3865 AutomationList::const_iterator j = (*i)->model ();
3866 lists[al].copy->add ((*j)->when, (*j)->value);
3869 for (Lists::iterator i = lists.begin(); i != lists.end(); ++i) {
3870 /* Correct this copy list so that it starts at time 0 */
3871 double const start = i->second.copy->front()->when;
3872 for (AutomationList::iterator j = i->second.copy->begin(); j != i->second.copy->end(); ++j) {
3873 (*j)->when -= start;
3876 /* And add it to the cut buffer */
3877 cut_buffer->add (i->second.copy);
3881 if (op == Delete || op == Cut) {
3882 /* This operation needs to remove things from the main AutomationList, so do that now */
3884 for (Lists::iterator i = lists.begin(); i != lists.end(); ++i) {
3885 i->first->freeze ();
3888 /* Remove each selected point from its AutomationList */
3889 for (PointSelection::iterator i = selection->points.begin(); i != selection->points.end(); ++i) {
3890 boost::shared_ptr<AutomationList> al = (*i)->line().the_list();
3891 al->erase ((*i)->model ());
3894 /* Thaw the lists and add undo records for them */
3895 for (Lists::iterator i = lists.begin(); i != lists.end(); ++i) {
3896 boost::shared_ptr<AutomationList> al = i->first;
3898 _session->add_command (new MementoCommand<AutomationList> (*al.get(), i->second.state, &(al->get_state ())));
3903 /** Cut, copy or clear selected automation points.
3904 * @param op Operation (Cut, Copy or Clear)
3907 Editor::cut_copy_midi (CutCopyOp op)
3909 for (MidiRegionSelection::iterator i = selection->midi_regions.begin(); i != selection->midi_regions.end(); ++i) {
3910 MidiRegionView* mrv = *i;
3911 mrv->cut_copy_clear (op);
3917 struct lt_playlist {
3918 bool operator () (const PlaylistState& a, const PlaylistState& b) {
3919 return a.playlist < b.playlist;
3923 struct PlaylistMapping {
3925 boost::shared_ptr<Playlist> pl;
3927 PlaylistMapping (TimeAxisView* tvp) : tv (tvp) {}
3930 /** Remove `clicked_regionview' */
3932 Editor::remove_clicked_region ()
3934 if (clicked_routeview == 0 || clicked_regionview == 0) {
3938 boost::shared_ptr<Playlist> playlist = clicked_routeview->playlist();
3940 begin_reversible_command (_("remove region"));
3941 playlist->clear_changes ();
3942 playlist->clear_owned_changes ();
3943 playlist->remove_region (clicked_regionview->region());
3945 /* We might have removed regions, which alters other regions' layering_index,
3946 so we need to do a recursive diff here.
3948 vector<Command*> cmds;
3949 playlist->rdiff (cmds);
3950 _session->add_commands (cmds);
3952 _session->add_command(new StatefulDiffCommand (playlist));
3953 commit_reversible_command ();
3957 /** Remove the selected regions */
3959 Editor::remove_selected_regions ()
3961 RegionSelection rs = get_regions_from_selection_and_entered ();
3963 if (!_session || rs.empty()) {
3967 begin_reversible_command (_("remove region"));
3969 list<boost::shared_ptr<Region> > regions_to_remove;
3971 for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
3972 // we can't just remove the region(s) in this loop because
3973 // this removes them from the RegionSelection, and they thus
3974 // disappear from underneath the iterator, and the ++i above
3975 // SEGVs in a puzzling fashion.
3977 // so, first iterate over the regions to be removed from rs and
3978 // add them to the regions_to_remove list, and then
3979 // iterate over the list to actually remove them.
3981 regions_to_remove.push_back ((*i)->region());
3984 vector<boost::shared_ptr<Playlist> > playlists;
3986 for (list<boost::shared_ptr<Region> >::iterator rl = regions_to_remove.begin(); rl != regions_to_remove.end(); ++rl) {
3988 boost::shared_ptr<Playlist> playlist = (*rl)->playlist();
3991 // is this check necessary?
3995 /* get_regions_from_selection_and_entered() guarantees that
3996 the playlists involved are unique, so there is no need
4000 playlists.push_back (playlist);
4002 playlist->clear_changes ();
4003 playlist->clear_owned_changes ();
4004 playlist->freeze ();
4005 playlist->remove_region (*rl);
4008 vector<boost::shared_ptr<Playlist> >::iterator pl;
4010 for (pl = playlists.begin(); pl != playlists.end(); ++pl) {
4013 /* We might have removed regions, which alters other regions' layering_index,
4014 so we need to do a recursive diff here.
4016 vector<Command*> cmds;
4017 (*pl)->rdiff (cmds);
4018 _session->add_commands (cmds);
4020 _session->add_command(new StatefulDiffCommand (*pl));
4023 commit_reversible_command ();
4026 /** Cut, copy or clear selected regions.
4027 * @param op Operation (Cut, Copy or Clear)
4030 Editor::cut_copy_regions (CutCopyOp op, RegionSelection& rs)
4032 /* we can't use a std::map here because the ordering is important, and we can't trivially sort
4033 a map when we want ordered access to both elements. i think.
4036 vector<PlaylistMapping> pmap;
4038 framepos_t first_position = max_framepos;
4040 typedef set<boost::shared_ptr<Playlist> > FreezeList;
4041 FreezeList freezelist;
4043 /* get ordering correct before we cut/copy */
4045 rs.sort_by_position_and_track ();
4047 for (RegionSelection::iterator x = rs.begin(); x != rs.end(); ++x) {
4049 first_position = min ((framepos_t) (*x)->region()->position(), first_position);
4051 if (op == Cut || op == Clear || op == Delete) {
4052 boost::shared_ptr<Playlist> pl = (*x)->region()->playlist();
4055 FreezeList::iterator fl;
4057 // only take state if this is a new playlist.
4058 for (fl = freezelist.begin(); fl != freezelist.end(); ++fl) {
4064 if (fl == freezelist.end()) {
4065 pl->clear_changes();
4066 pl->clear_owned_changes ();
4068 freezelist.insert (pl);
4073 TimeAxisView* tv = &(*x)->get_time_axis_view();
4074 vector<PlaylistMapping>::iterator z;
4076 for (z = pmap.begin(); z != pmap.end(); ++z) {
4077 if ((*z).tv == tv) {
4082 if (z == pmap.end()) {
4083 pmap.push_back (PlaylistMapping (tv));
4087 for (RegionSelection::iterator x = rs.begin(); x != rs.end(); ) {
4089 boost::shared_ptr<Playlist> pl = (*x)->region()->playlist();
4092 /* region not yet associated with a playlist (e.g. unfinished
4099 TimeAxisView& tv = (*x)->get_time_axis_view();
4100 boost::shared_ptr<Playlist> npl;
4101 RegionSelection::iterator tmp;
4108 vector<PlaylistMapping>::iterator z;
4110 for (z = pmap.begin(); z != pmap.end(); ++z) {
4111 if ((*z).tv == &tv) {
4116 assert (z != pmap.end());
4119 npl = PlaylistFactory::create (pl->data_type(), *_session, "cutlist", true);
4127 boost::shared_ptr<Region> r = (*x)->region();
4128 boost::shared_ptr<Region> _xx;
4134 pl->remove_region (r);
4138 _xx = RegionFactory::create (r);
4139 npl->add_region (_xx, r->position() - first_position);
4140 pl->remove_region (r);
4144 /* copy region before adding, so we're not putting same object into two different playlists */
4145 npl->add_region (RegionFactory::create (r), r->position() - first_position);
4149 pl->remove_region (r);
4158 list<boost::shared_ptr<Playlist> > foo;
4160 /* the pmap is in the same order as the tracks in which selected regions occured */
4162 for (vector<PlaylistMapping>::iterator i = pmap.begin(); i != pmap.end(); ++i) {
4165 foo.push_back ((*i).pl);
4170 cut_buffer->set (foo);
4174 _last_cut_copy_source_track = 0;
4176 _last_cut_copy_source_track = pmap.front().tv;
4180 for (FreezeList::iterator pl = freezelist.begin(); pl != freezelist.end(); ++pl) {
4183 /* We might have removed regions, which alters other regions' layering_index,
4184 so we need to do a recursive diff here.
4186 vector<Command*> cmds;
4187 (*pl)->rdiff (cmds);
4188 _session->add_commands (cmds);
4190 _session->add_command (new StatefulDiffCommand (*pl));
4195 Editor::cut_copy_ranges (CutCopyOp op)
4197 TrackViewList ts = selection->tracks.filter_to_unique_playlists ();
4199 /* Sort the track selection now, so that it if is used, the playlists
4200 selected by the calls below to cut_copy_clear are in the order that
4201 their tracks appear in the editor. This makes things like paste
4202 of ranges work properly.
4205 sort_track_selection (ts);
4208 if (!entered_track) {
4211 ts.push_back (entered_track);
4214 for (TrackViewList::iterator i = ts.begin(); i != ts.end(); ++i) {
4215 (*i)->cut_copy_clear (*selection, op);
4220 Editor::paste (float times, bool from_context)
4222 DEBUG_TRACE (DEBUG::CutNPaste, "paste to preferred edit pos\n");
4224 paste_internal (get_preferred_edit_position (false, from_context), times);
4228 Editor::mouse_paste ()
4233 if (!mouse_frame (where, ignored)) {
4238 paste_internal (where, 1);
4242 Editor::paste_internal (framepos_t position, float times)
4244 DEBUG_TRACE (DEBUG::CutNPaste, string_compose ("apparent paste position is %1\n", position));
4246 if (internal_editing()) {
4247 if (cut_buffer->midi_notes.empty()) {
4251 if (cut_buffer->empty()) {
4256 if (position == max_framepos) {
4257 position = get_preferred_edit_position();
4258 DEBUG_TRACE (DEBUG::CutNPaste, string_compose ("preferred edit position is %1\n", position));
4262 TrackViewList::iterator i;
4265 /* get everything in the correct order */
4267 if (_edit_point == Editing::EditAtMouse && entered_track) {
4268 /* With the mouse edit point, paste onto the track under the mouse */
4269 ts.push_back (entered_track);
4270 } else if (!selection->tracks.empty()) {
4271 /* Otherwise, if there are some selected tracks, paste to them */
4272 ts = selection->tracks.filter_to_unique_playlists ();
4273 sort_track_selection (ts);
4274 } else if (_last_cut_copy_source_track) {
4275 /* Otherwise paste to the track that the cut/copy came from;
4276 see discussion in mantis #3333.
4278 ts.push_back (_last_cut_copy_source_track);
4281 if (internal_editing ()) {
4283 /* undo/redo is handled by individual tracks/regions */
4285 for (nth = 0, i = ts.begin(); i != ts.end(); ++i, ++nth) {
4288 RegionSelection::iterator r;
4289 MidiNoteSelection::iterator cb;
4291 get_regions_at (rs, position, ts);
4293 for (cb = cut_buffer->midi_notes.begin(), r = rs.begin();
4294 cb != cut_buffer->midi_notes.end() && r != rs.end(); ++r) {
4295 MidiRegionView* mrv = dynamic_cast<MidiRegionView*> (*r);
4297 mrv->paste (position, times, **cb);
4305 /* we do redo (do you do voodoo?) */
4307 begin_reversible_command (Operations::paste);
4309 for (nth = 0, i = ts.begin(); i != ts.end(); ++i, ++nth) {
4310 (*i)->paste (position, times, *cut_buffer, nth);
4313 commit_reversible_command ();
4318 Editor::duplicate_some_regions (RegionSelection& regions, float times)
4320 boost::shared_ptr<Playlist> playlist;
4321 RegionSelection sel = regions; // clear (below) may clear the argument list if its the current region selection
4322 RegionSelection foo;
4324 framepos_t const start_frame = regions.start ();
4325 framepos_t const end_frame = regions.end_frame ();
4327 begin_reversible_command (Operations::duplicate_region);
4329 selection->clear_regions ();
4331 for (RegionSelection::iterator i = sel.begin(); i != sel.end(); ++i) {
4333 boost::shared_ptr<Region> r ((*i)->region());
4335 TimeAxisView& tv = (*i)->get_time_axis_view();
4336 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (&tv);
4337 latest_regionviews.clear ();
4338 sigc::connection c = rtv->view()->RegionViewAdded.connect (sigc::mem_fun(*this, &Editor::collect_new_region_view));
4340 playlist = (*i)->region()->playlist();
4341 playlist->clear_changes ();
4342 playlist->duplicate (r, end_frame + (r->first_frame() - start_frame), times);
4343 _session->add_command(new StatefulDiffCommand (playlist));
4347 foo.insert (foo.end(), latest_regionviews.begin(), latest_regionviews.end());
4350 commit_reversible_command ();
4353 selection->set (foo);
4358 Editor::duplicate_selection (float times)
4360 if (selection->time.empty() || selection->tracks.empty()) {
4364 boost::shared_ptr<Playlist> playlist;
4365 vector<boost::shared_ptr<Region> > new_regions;
4366 vector<boost::shared_ptr<Region> >::iterator ri;
4368 create_region_from_selection (new_regions);
4370 if (new_regions.empty()) {
4374 begin_reversible_command (_("duplicate selection"));
4376 ri = new_regions.begin();
4378 TrackViewList ts = selection->tracks.filter_to_unique_playlists ();
4380 for (TrackViewList::iterator i = ts.begin(); i != ts.end(); ++i) {
4381 if ((playlist = (*i)->playlist()) == 0) {
4384 playlist->clear_changes ();
4385 playlist->duplicate (*ri, selection->time[clicked_selection].end, times);
4386 _session->add_command (new StatefulDiffCommand (playlist));
4389 if (ri == new_regions.end()) {
4394 commit_reversible_command ();
4397 /** Reset all selected points to the relevant default value */
4399 Editor::reset_point_selection ()
4401 for (PointSelection::iterator i = selection->points.begin(); i != selection->points.end(); ++i) {
4402 ARDOUR::AutomationList::iterator j = (*i)->model ();
4403 (*j)->value = (*i)->line().the_list()->default_value ();
4408 Editor::center_playhead ()
4410 float const page = _visible_canvas_width * samples_per_pixel;
4411 center_screen_internal (playhead_cursor->current_frame (), page);
4415 Editor::center_edit_point ()
4417 float const page = _visible_canvas_width * samples_per_pixel;
4418 center_screen_internal (get_preferred_edit_position(), page);
4421 /** Caller must begin and commit a reversible command */
4423 Editor::clear_playlist (boost::shared_ptr<Playlist> playlist)
4425 playlist->clear_changes ();
4427 _session->add_command (new StatefulDiffCommand (playlist));
4431 Editor::nudge_track (bool use_edit, bool forwards)
4433 boost::shared_ptr<Playlist> playlist;
4434 framepos_t distance;
4435 framepos_t next_distance;
4439 start = get_preferred_edit_position();
4444 if ((distance = get_nudge_distance (start, next_distance)) == 0) {
4448 if (selection->tracks.empty()) {
4452 begin_reversible_command (_("nudge track"));
4454 TrackViewList ts = selection->tracks.filter_to_unique_playlists ();
4456 for (TrackViewList::iterator i = ts.begin(); i != ts.end(); ++i) {
4458 if ((playlist = (*i)->playlist()) == 0) {
4462 playlist->clear_changes ();
4463 playlist->clear_owned_changes ();
4465 playlist->nudge_after (start, distance, forwards);
4467 vector<Command*> cmds;
4469 playlist->rdiff (cmds);
4470 _session->add_commands (cmds);
4472 _session->add_command (new StatefulDiffCommand (playlist));
4475 commit_reversible_command ();
4479 Editor::remove_last_capture ()
4481 vector<string> choices;
4488 if (Config->get_verify_remove_last_capture()) {
4489 prompt = _("Do you really want to destroy the last capture?"
4490 "\n(This is destructive and cannot be undone)");
4492 choices.push_back (_("No, do nothing."));
4493 choices.push_back (_("Yes, destroy it."));
4495 Gtkmm2ext::Choice prompter (_("Destroy last capture"), prompt, choices);
4497 if (prompter.run () == 1) {
4498 _session->remove_last_capture ();
4499 _regions->redisplay ();
4503 _session->remove_last_capture();
4504 _regions->redisplay ();
4509 Editor::normalize_region ()
4515 RegionSelection rs = get_regions_from_selection_and_entered ();
4521 NormalizeDialog dialog (rs.size() > 1);
4523 if (dialog.run () == RESPONSE_CANCEL) {
4527 set_canvas_cursor (_cursors->wait);
4530 /* XXX: should really only count audio regions here */
4531 int const regions = rs.size ();
4533 /* Make a list of the selected audio regions' maximum amplitudes, and also
4534 obtain the maximum amplitude of them all.
4536 list<double> max_amps;
4538 for (RegionSelection::const_iterator i = rs.begin(); i != rs.end(); ++i) {
4539 AudioRegionView const * arv = dynamic_cast<AudioRegionView const *> (*i);
4541 dialog.descend (1.0 / regions);
4542 double const a = arv->audio_region()->maximum_amplitude (&dialog);
4545 /* the user cancelled the operation */
4546 set_canvas_cursor (current_canvas_cursor);
4550 max_amps.push_back (a);
4551 max_amp = max (max_amp, a);
4556 begin_reversible_command (_("normalize"));
4558 list<double>::const_iterator a = max_amps.begin ();
4560 for (RegionSelection::iterator r = rs.begin(); r != rs.end(); ++r) {
4561 AudioRegionView* const arv = dynamic_cast<AudioRegionView*> (*r);
4566 arv->region()->clear_changes ();
4568 double const amp = dialog.normalize_individually() ? *a : max_amp;
4570 arv->audio_region()->normalize (amp, dialog.target ());
4571 _session->add_command (new StatefulDiffCommand (arv->region()));
4576 commit_reversible_command ();
4577 set_canvas_cursor (current_canvas_cursor);
4582 Editor::reset_region_scale_amplitude ()
4588 RegionSelection rs = get_regions_from_selection_and_entered ();
4594 begin_reversible_command ("reset gain");
4596 for (RegionSelection::iterator r = rs.begin(); r != rs.end(); ++r) {
4597 AudioRegionView* const arv = dynamic_cast<AudioRegionView*>(*r);
4600 arv->region()->clear_changes ();
4601 arv->audio_region()->set_scale_amplitude (1.0f);
4602 _session->add_command (new StatefulDiffCommand (arv->region()));
4605 commit_reversible_command ();
4609 Editor::adjust_region_gain (bool up)
4611 RegionSelection rs = get_regions_from_selection_and_entered ();
4613 if (!_session || rs.empty()) {
4617 begin_reversible_command ("adjust region gain");
4619 for (RegionSelection::iterator r = rs.begin(); r != rs.end(); ++r) {
4620 AudioRegionView* const arv = dynamic_cast<AudioRegionView*>(*r);
4625 arv->region()->clear_changes ();
4627 double dB = accurate_coefficient_to_dB (arv->audio_region()->scale_amplitude ());
4635 arv->audio_region()->set_scale_amplitude (dB_to_coefficient (dB));
4636 _session->add_command (new StatefulDiffCommand (arv->region()));
4639 commit_reversible_command ();
4644 Editor::reverse_region ()
4650 Reverse rev (*_session);
4651 apply_filter (rev, _("reverse regions"));
4655 Editor::strip_region_silence ()
4661 RegionSelection rs = get_regions_from_selection_and_entered ();
4667 std::list<RegionView*> audio_only;
4669 for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
4670 AudioRegionView* const arv = dynamic_cast<AudioRegionView*> (*i);
4672 audio_only.push_back (arv);
4676 StripSilenceDialog d (_session, audio_only);
4677 int const r = d.run ();
4681 if (r == Gtk::RESPONSE_OK) {
4682 ARDOUR::AudioIntervalMap silences;
4683 d.silences (silences);
4684 StripSilence s (*_session, silences, d.fade_length());
4685 apply_filter (s, _("strip silence"), &d);
4690 Editor::apply_midi_note_edit_op_to_region (MidiOperator& op, MidiRegionView& mrv)
4692 Evoral::Sequence<Evoral::MusicalTime>::Notes selected;
4693 mrv.selection_as_notelist (selected, true);
4695 vector<Evoral::Sequence<Evoral::MusicalTime>::Notes> v;
4696 v.push_back (selected);
4698 framepos_t pos_frames = mrv.midi_region()->position() - mrv.midi_region()->start();
4699 double pos_beats = _session->tempo_map().framewalk_to_beats(0, pos_frames);
4701 return op (mrv.midi_region()->model(), pos_beats, v);
4705 Editor::apply_midi_note_edit_op (MidiOperator& op)
4709 RegionSelection rs = get_regions_from_selection_and_entered ();
4715 begin_reversible_command (op.name ());
4717 for (RegionSelection::iterator r = rs.begin(); r != rs.end(); ) {
4718 RegionSelection::iterator tmp = r;
4721 MidiRegionView* const mrv = dynamic_cast<MidiRegionView*> (*r);
4724 cmd = apply_midi_note_edit_op_to_region (op, *mrv);
4727 _session->add_command (cmd);
4734 commit_reversible_command ();
4738 Editor::fork_region ()
4740 RegionSelection rs = get_regions_from_selection_and_entered ();
4746 begin_reversible_command (_("Fork Region(s)"));
4748 set_canvas_cursor (_cursors->wait);
4751 for (RegionSelection::iterator r = rs.begin(); r != rs.end(); ) {
4752 RegionSelection::iterator tmp = r;
4755 MidiRegionView* const mrv = dynamic_cast<MidiRegionView*>(*r);
4758 boost::shared_ptr<Playlist> playlist = mrv->region()->playlist();
4759 boost::shared_ptr<MidiRegion> newregion = mrv->midi_region()->clone ();
4761 playlist->clear_changes ();
4762 playlist->replace_region (mrv->region(), newregion, mrv->region()->position());
4763 _session->add_command(new StatefulDiffCommand (playlist));
4769 commit_reversible_command ();
4771 set_canvas_cursor (current_canvas_cursor);
4775 Editor::quantize_region ()
4777 int selected_midi_region_cnt = 0;
4783 RegionSelection rs = get_regions_from_selection_and_entered ();
4789 for (RegionSelection::iterator r = rs.begin(); r != rs.end(); ++r) {
4790 MidiRegionView* const mrv = dynamic_cast<MidiRegionView*> (*r);
4792 selected_midi_region_cnt++;
4796 if (selected_midi_region_cnt == 0) {
4800 QuantizeDialog* qd = new QuantizeDialog (*this);
4803 const int r = qd->run ();
4806 if (r == Gtk::RESPONSE_OK) {
4807 Quantize quant (*_session, qd->snap_start(), qd->snap_end(),
4808 qd->start_grid_size(), qd->end_grid_size(),
4809 qd->strength(), qd->swing(), qd->threshold());
4811 apply_midi_note_edit_op (quant);
4816 Editor::insert_patch_change (bool from_context)
4818 RegionSelection rs = get_regions_from_selection_and_entered ();
4824 const framepos_t p = get_preferred_edit_position (false, from_context);
4826 /* XXX: bit of a hack; use the MIDNAM from the first selected region;
4827 there may be more than one, but the PatchChangeDialog can only offer
4828 one set of patch menus.
4830 MidiRegionView* first = dynamic_cast<MidiRegionView*> (rs.front ());
4832 Evoral::PatchChange<Evoral::MusicalTime> empty (0, 0, 0, 0);
4833 PatchChangeDialog d (0, _session, empty, first->instrument_info(), Gtk::Stock::ADD);
4835 if (d.run() == RESPONSE_CANCEL) {
4839 for (RegionSelection::iterator i = rs.begin (); i != rs.end(); ++i) {
4840 MidiRegionView* const mrv = dynamic_cast<MidiRegionView*> (*i);
4842 if (p >= mrv->region()->first_frame() && p <= mrv->region()->last_frame()) {
4843 mrv->add_patch_change (p - mrv->region()->position(), d.patch ());
4850 Editor::apply_filter (Filter& filter, string command, ProgressReporter* progress)
4852 RegionSelection rs = get_regions_from_selection_and_entered ();
4858 begin_reversible_command (command);
4860 set_canvas_cursor (_cursors->wait);
4864 int const N = rs.size ();
4866 for (RegionSelection::iterator r = rs.begin(); r != rs.end(); ) {
4867 RegionSelection::iterator tmp = r;
4870 AudioRegionView* const arv = dynamic_cast<AudioRegionView*>(*r);
4872 boost::shared_ptr<Playlist> playlist = arv->region()->playlist();
4875 progress->descend (1.0 / N);
4878 if (arv->audio_region()->apply (filter, progress) == 0) {
4880 playlist->clear_changes ();
4881 playlist->clear_owned_changes ();
4883 if (filter.results.empty ()) {
4885 /* no regions returned; remove the old one */
4886 playlist->remove_region (arv->region ());
4890 std::vector<boost::shared_ptr<Region> >::iterator res = filter.results.begin ();
4892 /* first region replaces the old one */
4893 playlist->replace_region (arv->region(), *res, (*res)->position());
4897 while (res != filter.results.end()) {
4898 playlist->add_region (*res, (*res)->position());
4904 /* We might have removed regions, which alters other regions' layering_index,
4905 so we need to do a recursive diff here.
4907 vector<Command*> cmds;
4908 playlist->rdiff (cmds);
4909 _session->add_commands (cmds);
4911 _session->add_command(new StatefulDiffCommand (playlist));
4917 progress->ascend ();
4925 commit_reversible_command ();
4928 set_canvas_cursor (current_canvas_cursor);
4932 Editor::external_edit_region ()
4938 Editor::reset_region_gain_envelopes ()
4940 RegionSelection rs = get_regions_from_selection_and_entered ();
4942 if (!_session || rs.empty()) {
4946 _session->begin_reversible_command (_("reset region gain"));
4948 for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
4949 AudioRegionView* const arv = dynamic_cast<AudioRegionView*>(*i);
4951 boost::shared_ptr<AutomationList> alist (arv->audio_region()->envelope());
4952 XMLNode& before (alist->get_state());
4954 arv->audio_region()->set_default_envelope ();
4955 _session->add_command (new MementoCommand<AutomationList>(*arv->audio_region()->envelope().get(), &before, &alist->get_state()));
4959 _session->commit_reversible_command ();
4963 Editor::set_region_gain_visibility (RegionView* rv)
4965 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (rv);
4967 arv->update_envelope_visibility();
4972 Editor::set_gain_envelope_visibility ()
4978 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
4979 AudioTimeAxisView* v = dynamic_cast<AudioTimeAxisView*>(*i);
4981 v->audio_view()->foreach_regionview (sigc::mem_fun (this, &Editor::set_region_gain_visibility));
4987 Editor::toggle_gain_envelope_active ()
4989 if (_ignore_region_action) {
4993 RegionSelection rs = get_regions_from_selection_and_entered ();
4995 if (!_session || rs.empty()) {
4999 _session->begin_reversible_command (_("region gain envelope active"));
5001 for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
5002 AudioRegionView* const arv = dynamic_cast<AudioRegionView*>(*i);
5004 arv->region()->clear_changes ();
5005 arv->audio_region()->set_envelope_active (!arv->audio_region()->envelope_active());
5006 _session->add_command (new StatefulDiffCommand (arv->region()));
5010 _session->commit_reversible_command ();
5014 Editor::toggle_region_lock ()
5016 if (_ignore_region_action) {
5020 RegionSelection rs = get_regions_from_selection_and_entered ();
5022 if (!_session || rs.empty()) {
5026 _session->begin_reversible_command (_("toggle region lock"));
5028 for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
5029 (*i)->region()->clear_changes ();
5030 (*i)->region()->set_locked (!(*i)->region()->locked());
5031 _session->add_command (new StatefulDiffCommand ((*i)->region()));
5034 _session->commit_reversible_command ();
5038 Editor::toggle_region_video_lock ()
5040 if (_ignore_region_action) {
5044 RegionSelection rs = get_regions_from_selection_and_entered ();
5046 if (!_session || rs.empty()) {
5050 _session->begin_reversible_command (_("Toggle Video Lock"));
5052 for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
5053 (*i)->region()->clear_changes ();
5054 (*i)->region()->set_video_locked (!(*i)->region()->video_locked());
5055 _session->add_command (new StatefulDiffCommand ((*i)->region()));
5058 _session->commit_reversible_command ();
5062 Editor::toggle_region_lock_style ()
5064 if (_ignore_region_action) {
5068 RegionSelection rs = get_regions_from_selection_and_entered ();
5070 if (!_session || rs.empty()) {
5074 _session->begin_reversible_command (_("region lock style"));
5076 for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
5077 (*i)->region()->clear_changes ();
5078 PositionLockStyle const ns = (*i)->region()->position_lock_style() == AudioTime ? MusicTime : AudioTime;
5079 (*i)->region()->set_position_lock_style (ns);
5080 _session->add_command (new StatefulDiffCommand ((*i)->region()));
5083 _session->commit_reversible_command ();
5087 Editor::toggle_opaque_region ()
5089 if (_ignore_region_action) {
5093 RegionSelection rs = get_regions_from_selection_and_entered ();
5095 if (!_session || rs.empty()) {
5099 _session->begin_reversible_command (_("change region opacity"));
5101 for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
5102 (*i)->region()->clear_changes ();
5103 (*i)->region()->set_opaque (!(*i)->region()->opaque());
5104 _session->add_command (new StatefulDiffCommand ((*i)->region()));
5107 _session->commit_reversible_command ();
5111 Editor::toggle_record_enable ()
5113 bool new_state = false;
5115 for (TrackSelection::iterator i = selection->tracks.begin(); i != selection->tracks.end(); ++i) {
5116 RouteTimeAxisView *rtav = dynamic_cast<RouteTimeAxisView *>(*i);
5119 if (!rtav->is_track())
5123 new_state = !rtav->track()->record_enabled();
5127 rtav->track()->set_record_enabled (new_state, this);
5132 Editor::toggle_solo ()
5134 bool new_state = false;
5136 boost::shared_ptr<RouteList> rl (new RouteList);
5138 for (TrackSelection::iterator i = selection->tracks.begin(); i != selection->tracks.end(); ++i) {
5139 RouteTimeAxisView *rtav = dynamic_cast<RouteTimeAxisView *>(*i);
5146 new_state = !rtav->route()->soloed ();
5150 rl->push_back (rtav->route());
5153 _session->set_solo (rl, new_state, Session::rt_cleanup, true);
5157 Editor::toggle_mute ()
5159 bool new_state = false;
5161 boost::shared_ptr<RouteList> rl (new RouteList);
5163 for (TrackSelection::iterator i = selection->tracks.begin(); i != selection->tracks.end(); ++i) {
5164 RouteTimeAxisView *rtav = dynamic_cast<RouteTimeAxisView *>(*i);
5171 new_state = !rtav->route()->muted();
5175 rl->push_back (rtav->route());
5178 _session->set_mute (rl, new_state, Session::rt_cleanup, true);
5182 Editor::toggle_solo_isolate ()
5187 Editor::set_fade_length (bool in)
5189 RegionSelection rs = get_regions_from_selection_and_entered ();
5195 /* we need a region to measure the offset from the start */
5197 RegionView* rv = rs.front ();
5199 framepos_t pos = get_preferred_edit_position();
5203 if (pos > rv->region()->last_frame() || pos < rv->region()->first_frame()) {
5204 /* edit point is outside the relevant region */
5209 if (pos <= rv->region()->position()) {
5213 len = pos - rv->region()->position();
5214 cmd = _("set fade in length");
5216 if (pos >= rv->region()->last_frame()) {
5220 len = rv->region()->last_frame() - pos;
5221 cmd = _("set fade out length");
5224 begin_reversible_command (cmd);
5226 for (RegionSelection::iterator x = rs.begin(); x != rs.end(); ++x) {
5227 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (*x);
5233 boost::shared_ptr<AutomationList> alist;
5235 alist = tmp->audio_region()->fade_in();
5237 alist = tmp->audio_region()->fade_out();
5240 XMLNode &before = alist->get_state();
5243 tmp->audio_region()->set_fade_in_length (len);
5244 tmp->audio_region()->set_fade_in_active (true);
5246 tmp->audio_region()->set_fade_out_length (len);
5247 tmp->audio_region()->set_fade_out_active (true);
5250 XMLNode &after = alist->get_state();
5251 _session->add_command(new MementoCommand<AutomationList>(*alist, &before, &after));
5254 commit_reversible_command ();
5258 Editor::set_fade_in_shape (FadeShape shape)
5260 RegionSelection rs = get_regions_from_selection_and_entered ();
5266 begin_reversible_command (_("set fade in shape"));
5268 for (RegionSelection::iterator x = rs.begin(); x != rs.end(); ++x) {
5269 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (*x);
5275 boost::shared_ptr<AutomationList> alist = tmp->audio_region()->fade_in();
5276 XMLNode &before = alist->get_state();
5278 tmp->audio_region()->set_fade_in_shape (shape);
5280 XMLNode &after = alist->get_state();
5281 _session->add_command(new MementoCommand<AutomationList>(*alist.get(), &before, &after));
5284 commit_reversible_command ();
5289 Editor::set_fade_out_shape (FadeShape shape)
5291 RegionSelection rs = get_regions_from_selection_and_entered ();
5297 begin_reversible_command (_("set fade out shape"));
5299 for (RegionSelection::iterator x = rs.begin(); x != rs.end(); ++x) {
5300 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (*x);
5306 boost::shared_ptr<AutomationList> alist = tmp->audio_region()->fade_out();
5307 XMLNode &before = alist->get_state();
5309 tmp->audio_region()->set_fade_out_shape (shape);
5311 XMLNode &after = alist->get_state();
5312 _session->add_command(new MementoCommand<AutomationList>(*alist.get(), &before, &after));
5315 commit_reversible_command ();
5319 Editor::set_fade_in_active (bool yn)
5321 RegionSelection rs = get_regions_from_selection_and_entered ();
5327 begin_reversible_command (_("set fade in active"));
5329 for (RegionSelection::iterator x = rs.begin(); x != rs.end(); ++x) {
5330 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (*x);
5337 boost::shared_ptr<AudioRegion> ar (tmp->audio_region());
5339 ar->clear_changes ();
5340 ar->set_fade_in_active (yn);
5341 _session->add_command (new StatefulDiffCommand (ar));
5344 commit_reversible_command ();
5348 Editor::set_fade_out_active (bool yn)
5350 RegionSelection rs = get_regions_from_selection_and_entered ();
5356 begin_reversible_command (_("set fade out active"));
5358 for (RegionSelection::iterator x = rs.begin(); x != rs.end(); ++x) {
5359 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (*x);
5365 boost::shared_ptr<AudioRegion> ar (tmp->audio_region());
5367 ar->clear_changes ();
5368 ar->set_fade_out_active (yn);
5369 _session->add_command(new StatefulDiffCommand (ar));
5372 commit_reversible_command ();
5376 Editor::toggle_region_fades (int dir)
5378 if (_ignore_region_action) {
5382 boost::shared_ptr<AudioRegion> ar;
5385 RegionSelection rs = get_regions_from_selection_and_entered ();
5391 RegionSelection::iterator i;
5392 for (i = rs.begin(); i != rs.end(); ++i) {
5393 if ((ar = boost::dynamic_pointer_cast<AudioRegion>((*i)->region())) != 0) {
5395 yn = ar->fade_out_active ();
5397 yn = ar->fade_in_active ();
5403 if (i == rs.end()) {
5407 /* XXX should this undo-able? */
5409 for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
5410 if ((ar = boost::dynamic_pointer_cast<AudioRegion>((*i)->region())) == 0) {
5413 if (dir == 1 || dir == 0) {
5414 ar->set_fade_in_active (!yn);
5417 if (dir == -1 || dir == 0) {
5418 ar->set_fade_out_active (!yn);
5424 /** Update region fade visibility after its configuration has been changed */
5426 Editor::update_region_fade_visibility ()
5428 bool _fade_visibility = _session->config.get_show_region_fades ();
5430 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
5431 AudioTimeAxisView* v = dynamic_cast<AudioTimeAxisView*>(*i);
5433 if (_fade_visibility) {
5434 v->audio_view()->show_all_fades ();
5436 v->audio_view()->hide_all_fades ();
5443 Editor::set_edit_point ()
5448 if (!mouse_frame (where, ignored)) {
5454 if (selection->markers.empty()) {
5456 mouse_add_new_marker (where);
5461 Location* loc = find_location_from_marker (selection->markers.front(), ignored);
5464 loc->move_to (where);
5470 Editor::set_playhead_cursor ()
5472 if (entered_marker) {
5473 _session->request_locate (entered_marker->position(), _session->transport_rolling());
5478 if (!mouse_frame (where, ignored)) {
5485 _session->request_locate (where, _session->transport_rolling());
5489 if ( Config->get_always_play_range() )
5490 cancel_time_selection();
5494 Editor::split_region ()
5496 if ( !selection->time.empty()) {
5497 separate_regions_between (selection->time);
5501 RegionSelection rs = get_regions_from_selection_and_edit_point ();
5503 framepos_t where = get_preferred_edit_position ();
5509 split_regions_at (where, rs);
5512 struct EditorOrderRouteSorter {
5513 bool operator() (boost::shared_ptr<Route> a, boost::shared_ptr<Route> b) {
5514 return a->order_key () < b->order_key ();
5519 Editor::select_next_route()
5521 if (selection->tracks.empty()) {
5522 selection->set (track_views.front());
5526 TimeAxisView* current = selection->tracks.front();
5530 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
5531 if (*i == current) {
5533 if (i != track_views.end()) {
5536 current = (*(track_views.begin()));
5537 //selection->set (*(track_views.begin()));
5542 rui = dynamic_cast<RouteUI *>(current);
5543 } while ( current->hidden() || (rui != NULL && !rui->route()->active()));
5545 selection->set(current);
5547 ensure_track_visible(current);
5551 Editor::select_prev_route()
5553 if (selection->tracks.empty()) {
5554 selection->set (track_views.front());
5558 TimeAxisView* current = selection->tracks.front();
5562 for (TrackViewList::reverse_iterator i = track_views.rbegin(); i != track_views.rend(); ++i) {
5563 if (*i == current) {
5565 if (i != track_views.rend()) {
5568 current = *(track_views.rbegin());
5573 rui = dynamic_cast<RouteUI *>(current);
5574 } while ( current->hidden() || (rui != NULL && !rui->route()->active()));
5576 selection->set (current);
5578 ensure_track_visible(current);
5582 Editor::ensure_track_visible(TimeAxisView *track)
5584 if (track->hidden())
5587 double const current_view_min_y = vertical_adjustment.get_value();
5588 double const current_view_max_y = vertical_adjustment.get_value() + vertical_adjustment.get_page_size();
5590 double const track_min_y = track->y_position ();
5591 double const track_max_y = track->y_position () + track->effective_height ();
5593 if (track_min_y >= current_view_min_y &&
5594 track_max_y <= current_view_max_y) {
5600 if (track_min_y < current_view_min_y) {
5601 // Track is above the current view
5602 new_value = track_min_y;
5604 // Track is below the current view
5605 new_value = track->y_position () + track->effective_height() - vertical_adjustment.get_page_size();
5608 vertical_adjustment.set_value(new_value);
5612 Editor::set_loop_from_selection (bool play)
5614 if (_session == 0 || selection->time.empty()) {
5618 framepos_t start = selection->time[clicked_selection].start;
5619 framepos_t end = selection->time[clicked_selection].end;
5621 set_loop_range (start, end, _("set loop range from selection"));
5624 _session->request_play_loop (true);
5625 _session->request_locate (start, true);
5630 Editor::set_loop_from_edit_range (bool play)
5632 if (_session == 0) {
5639 if (!get_edit_op_range (start, end)) {
5643 set_loop_range (start, end, _("set loop range from edit range"));
5646 _session->request_play_loop (true);
5647 _session->request_locate (start, true);
5652 Editor::set_loop_from_region (bool play)
5654 framepos_t start = max_framepos;
5657 RegionSelection rs = get_regions_from_selection_and_entered ();
5663 for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
5664 if ((*i)->region()->position() < start) {
5665 start = (*i)->region()->position();
5667 if ((*i)->region()->last_frame() + 1 > end) {
5668 end = (*i)->region()->last_frame() + 1;
5672 set_loop_range (start, end, _("set loop range from region"));
5675 _session->request_play_loop (true);
5676 _session->request_locate (start, true);
5681 Editor::set_punch_from_selection ()
5683 if (_session == 0 || selection->time.empty()) {
5687 framepos_t start = selection->time[clicked_selection].start;
5688 framepos_t end = selection->time[clicked_selection].end;
5690 set_punch_range (start, end, _("set punch range from selection"));
5694 Editor::set_punch_from_edit_range ()
5696 if (_session == 0) {
5703 if (!get_edit_op_range (start, end)) {
5707 set_punch_range (start, end, _("set punch range from edit range"));
5711 Editor::set_punch_from_region ()
5713 framepos_t start = max_framepos;
5716 RegionSelection rs = get_regions_from_selection_and_entered ();
5722 for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
5723 if ((*i)->region()->position() < start) {
5724 start = (*i)->region()->position();
5726 if ((*i)->region()->last_frame() + 1 > end) {
5727 end = (*i)->region()->last_frame() + 1;
5731 set_punch_range (start, end, _("set punch range from region"));
5735 Editor::pitch_shift_region ()
5737 RegionSelection rs = get_regions_from_selection_and_entered ();
5739 RegionSelection audio_rs;
5740 for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
5741 if (dynamic_cast<AudioRegionView*> (*i)) {
5742 audio_rs.push_back (*i);
5746 if (audio_rs.empty()) {
5750 pitch_shift (audio_rs, 1.2);
5754 Editor::transpose_region ()
5756 RegionSelection rs = get_regions_from_selection_and_entered ();
5758 list<MidiRegionView*> midi_region_views;
5759 for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
5760 MidiRegionView* mrv = dynamic_cast<MidiRegionView*> (*i);
5762 midi_region_views.push_back (mrv);
5767 int const r = d.run ();
5768 if (r != RESPONSE_ACCEPT) {
5772 for (list<MidiRegionView*>::iterator i = midi_region_views.begin(); i != midi_region_views.end(); ++i) {
5773 (*i)->midi_region()->transpose (d.semitones ());
5778 Editor::set_tempo_from_region ()
5780 RegionSelection rs = get_regions_from_selection_and_entered ();
5782 if (!_session || rs.empty()) {
5786 RegionView* rv = rs.front();
5788 define_one_bar (rv->region()->position(), rv->region()->last_frame() + 1);
5792 Editor::use_range_as_bar ()
5794 framepos_t start, end;
5795 if (get_edit_op_range (start, end)) {
5796 define_one_bar (start, end);
5801 Editor::define_one_bar (framepos_t start, framepos_t end)
5803 framepos_t length = end - start;
5805 const Meter& m (_session->tempo_map().meter_at (start));
5807 /* length = 1 bar */
5809 /* now we want frames per beat.
5810 we have frames per bar, and beats per bar, so ...
5813 /* XXXX METER MATH */
5815 double frames_per_beat = length / m.divisions_per_bar();
5817 /* beats per minute = */
5819 double beats_per_minute = (_session->frame_rate() * 60.0) / frames_per_beat;
5821 /* now decide whether to:
5823 (a) set global tempo
5824 (b) add a new tempo marker
5828 const TempoSection& t (_session->tempo_map().tempo_section_at (start));
5830 bool do_global = false;
5832 if ((_session->tempo_map().n_tempos() == 1) && (_session->tempo_map().n_meters() == 1)) {
5834 /* only 1 tempo & 1 meter: ask if the user wants to set the tempo
5835 at the start, or create a new marker
5838 vector<string> options;
5839 options.push_back (_("Cancel"));
5840 options.push_back (_("Add new marker"));
5841 options.push_back (_("Set global tempo"));
5844 _("Define one bar"),
5845 _("Do you want to set the global tempo or add a new tempo marker?"),
5849 c.set_default_response (2);
5865 /* more than 1 tempo and/or meter section already, go ahead do the "usual":
5866 if the marker is at the region starter, change it, otherwise add
5871 begin_reversible_command (_("set tempo from region"));
5872 XMLNode& before (_session->tempo_map().get_state());
5875 _session->tempo_map().change_initial_tempo (beats_per_minute, t.note_type());
5876 } else if (t.frame() == start) {
5877 _session->tempo_map().change_existing_tempo_at (start, beats_per_minute, t.note_type());
5879 Timecode::BBT_Time bbt;
5880 _session->tempo_map().bbt_time (start, bbt);
5881 _session->tempo_map().add_tempo (Tempo (beats_per_minute, t.note_type()), bbt);
5884 XMLNode& after (_session->tempo_map().get_state());
5886 _session->add_command (new MementoCommand<TempoMap>(_session->tempo_map(), &before, &after));
5887 commit_reversible_command ();
5891 Editor::split_region_at_transients ()
5893 AnalysisFeatureList positions;
5895 RegionSelection rs = get_regions_from_selection_and_entered ();
5897 if (!_session || rs.empty()) {
5901 _session->begin_reversible_command (_("split regions"));
5903 for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ) {
5905 RegionSelection::iterator tmp;
5910 boost::shared_ptr<AudioRegion> ar = boost::dynamic_pointer_cast<AudioRegion> ((*i)->region());
5912 if (ar && (ar->get_transients (positions) == 0)) {
5913 split_region_at_points ((*i)->region(), positions, true);
5920 _session->commit_reversible_command ();
5925 Editor::split_region_at_points (boost::shared_ptr<Region> r, AnalysisFeatureList& positions, bool can_ferret, bool select_new)
5927 bool use_rhythmic_rodent = false;
5929 boost::shared_ptr<Playlist> pl = r->playlist();
5931 list<boost::shared_ptr<Region> > new_regions;
5937 if (positions.empty()) {
5942 if (positions.size() > 20 && can_ferret) {
5943 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);
5944 MessageDialog msg (msgstr,
5947 Gtk::BUTTONS_OK_CANCEL);
5950 msg.add_button (_("Call for the Ferret!"), RESPONSE_APPLY);
5951 msg.set_secondary_text (_("Press OK to continue with this split operation\nor ask the Ferret dialog to tune the analysis"));
5953 msg.set_secondary_text (_("Press OK to continue with this split operation"));
5956 msg.set_title (_("Excessive split?"));
5959 int response = msg.run();
5965 case RESPONSE_APPLY:
5966 use_rhythmic_rodent = true;
5973 if (use_rhythmic_rodent) {
5974 show_rhythm_ferret ();
5978 AnalysisFeatureList::const_iterator x;
5980 pl->clear_changes ();
5981 pl->clear_owned_changes ();
5983 x = positions.begin();
5985 if (x == positions.end()) {
5990 pl->remove_region (r);
5994 while (x != positions.end()) {
5996 /* deal with positons that are out of scope of present region bounds */
5997 if (*x <= 0 || *x > r->length()) {
6002 /* file start = original start + how far we from the initial position ?
6005 framepos_t file_start = r->start() + pos;
6007 /* length = next position - current position
6010 framepos_t len = (*x) - pos;
6012 /* XXX we do we really want to allow even single-sample regions?
6013 shouldn't we have some kind of lower limit on region size?
6022 if (RegionFactory::region_name (new_name, r->name())) {
6026 /* do NOT announce new regions 1 by one, just wait till they are all done */
6030 plist.add (ARDOUR::Properties::start, file_start);
6031 plist.add (ARDOUR::Properties::length, len);
6032 plist.add (ARDOUR::Properties::name, new_name);
6033 plist.add (ARDOUR::Properties::layer, 0);
6035 boost::shared_ptr<Region> nr = RegionFactory::create (r->sources(), plist, false);
6036 /* because we set annouce to false, manually add the new region to the
6039 RegionFactory::map_add (nr);
6041 pl->add_region (nr, r->position() + pos);
6044 new_regions.push_front(nr);
6053 RegionFactory::region_name (new_name, r->name());
6055 /* Add the final region */
6058 plist.add (ARDOUR::Properties::start, r->start() + pos);
6059 plist.add (ARDOUR::Properties::length, r->last_frame() - (r->position() + pos) + 1);
6060 plist.add (ARDOUR::Properties::name, new_name);
6061 plist.add (ARDOUR::Properties::layer, 0);
6063 boost::shared_ptr<Region> nr = RegionFactory::create (r->sources(), plist, false);
6064 /* because we set annouce to false, manually add the new region to the
6067 RegionFactory::map_add (nr);
6068 pl->add_region (nr, r->position() + pos);
6071 new_regions.push_front(nr);
6076 /* We might have removed regions, which alters other regions' layering_index,
6077 so we need to do a recursive diff here.
6079 vector<Command*> cmds;
6081 _session->add_commands (cmds);
6083 _session->add_command (new StatefulDiffCommand (pl));
6087 for (list<boost::shared_ptr<Region> >::iterator i = new_regions.begin(); i != new_regions.end(); ++i){
6088 set_selected_regionview_from_region_list ((*i), Selection::Add);
6094 Editor::place_transient()
6100 RegionSelection rs = get_regions_from_selection_and_edit_point ();
6106 framepos_t where = get_preferred_edit_position();
6108 _session->begin_reversible_command (_("place transient"));
6110 for (RegionSelection::iterator r = rs.begin(); r != rs.end(); ++r) {
6111 framepos_t position = (*r)->region()->position();
6112 (*r)->region()->add_transient(where - position);
6115 _session->commit_reversible_command ();
6119 Editor::remove_transient(ArdourCanvas::Item* item)
6125 ArdourCanvas::Line* _line = reinterpret_cast<ArdourCanvas::Line*> (item);
6128 AudioRegionView* _arv = reinterpret_cast<AudioRegionView*> (item->get_data ("regionview"));
6129 _arv->remove_transient (*(float*) _line->get_data ("position"));
6133 Editor::snap_regions_to_grid ()
6135 list <boost::shared_ptr<Playlist > > used_playlists;
6137 RegionSelection rs = get_regions_from_selection_and_entered ();
6139 if (!_session || rs.empty()) {
6143 _session->begin_reversible_command (_("snap regions to grid"));
6145 for (RegionSelection::iterator r = rs.begin(); r != rs.end(); ++r) {
6147 boost::shared_ptr<Playlist> pl = (*r)->region()->playlist();
6149 if (!pl->frozen()) {
6150 /* we haven't seen this playlist before */
6152 /* remember used playlists so we can thaw them later */
6153 used_playlists.push_back(pl);
6157 framepos_t start_frame = (*r)->region()->first_frame ();
6158 snap_to (start_frame);
6159 (*r)->region()->set_position (start_frame);
6162 while (used_playlists.size() > 0) {
6163 list <boost::shared_ptr<Playlist > >::iterator i = used_playlists.begin();
6165 used_playlists.pop_front();
6168 _session->commit_reversible_command ();
6172 Editor::close_region_gaps ()
6174 list <boost::shared_ptr<Playlist > > used_playlists;
6176 RegionSelection rs = get_regions_from_selection_and_entered ();
6178 if (!_session || rs.empty()) {
6182 Dialog dialog (_("Close Region Gaps"));
6185 table.set_spacings (12);
6186 table.set_border_width (12);
6187 Label* l = manage (left_aligned_label (_("Crossfade length")));
6188 table.attach (*l, 0, 1, 0, 1);
6190 SpinButton spin_crossfade (1, 0);
6191 spin_crossfade.set_range (0, 15);
6192 spin_crossfade.set_increments (1, 1);
6193 spin_crossfade.set_value (5);
6194 table.attach (spin_crossfade, 1, 2, 0, 1);
6196 table.attach (*manage (new Label (_("ms"))), 2, 3, 0, 1);
6198 l = manage (left_aligned_label (_("Pull-back length")));
6199 table.attach (*l, 0, 1, 1, 2);
6201 SpinButton spin_pullback (1, 0);
6202 spin_pullback.set_range (0, 100);
6203 spin_pullback.set_increments (1, 1);
6204 spin_pullback.set_value(30);
6205 table.attach (spin_pullback, 1, 2, 1, 2);
6207 table.attach (*manage (new Label (_("ms"))), 2, 3, 1, 2);
6209 dialog.get_vbox()->pack_start (table);
6210 dialog.add_button (Stock::CANCEL, RESPONSE_CANCEL);
6211 dialog.add_button (_("Ok"), RESPONSE_ACCEPT);
6214 if (dialog.run () == RESPONSE_CANCEL) {
6218 framepos_t crossfade_len = spin_crossfade.get_value();
6219 framepos_t pull_back_frames = spin_pullback.get_value();
6221 crossfade_len = lrintf (crossfade_len * _session->frame_rate()/1000);
6222 pull_back_frames = lrintf (pull_back_frames * _session->frame_rate()/1000);
6224 /* Iterate over the region list and make adjacent regions overlap by crossfade_len_ms */
6226 _session->begin_reversible_command (_("close region gaps"));
6229 boost::shared_ptr<Region> last_region;
6231 rs.sort_by_position_and_track();
6233 for (RegionSelection::iterator r = rs.begin(); r != rs.end(); ++r) {
6235 boost::shared_ptr<Playlist> pl = (*r)->region()->playlist();
6237 if (!pl->frozen()) {
6238 /* we haven't seen this playlist before */
6240 /* remember used playlists so we can thaw them later */
6241 used_playlists.push_back(pl);
6245 framepos_t position = (*r)->region()->position();
6247 if (idx == 0 || position < last_region->position()){
6248 last_region = (*r)->region();
6253 (*r)->region()->trim_front( (position - pull_back_frames));
6254 last_region->trim_end( (position - pull_back_frames + crossfade_len));
6256 last_region = (*r)->region();
6261 while (used_playlists.size() > 0) {
6262 list <boost::shared_ptr<Playlist > >::iterator i = used_playlists.begin();
6264 used_playlists.pop_front();
6267 _session->commit_reversible_command ();
6271 Editor::tab_to_transient (bool forward)
6273 AnalysisFeatureList positions;
6275 RegionSelection rs = get_regions_from_selection_and_entered ();
6281 framepos_t pos = _session->audible_frame ();
6283 if (!selection->tracks.empty()) {
6285 /* don't waste time searching for transients in duplicate playlists.
6288 TrackViewList ts = selection->tracks.filter_to_unique_playlists ();
6290 for (TrackViewList::iterator t = ts.begin(); t != ts.end(); ++t) {
6292 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (*t);
6295 boost::shared_ptr<Track> tr = rtv->track();
6297 boost::shared_ptr<Playlist> pl = tr->playlist ();
6299 framepos_t result = pl->find_next_transient (pos, forward ? 1 : -1);
6302 positions.push_back (result);
6315 for (RegionSelection::iterator r = rs.begin(); r != rs.end(); ++r) {
6316 (*r)->region()->get_transients (positions);
6320 TransientDetector::cleanup_transients (positions, _session->frame_rate(), 3.0);
6323 AnalysisFeatureList::iterator x;
6325 for (x = positions.begin(); x != positions.end(); ++x) {
6331 if (x != positions.end ()) {
6332 _session->request_locate (*x);
6336 AnalysisFeatureList::reverse_iterator x;
6338 for (x = positions.rbegin(); x != positions.rend(); ++x) {
6344 if (x != positions.rend ()) {
6345 _session->request_locate (*x);
6351 Editor::playhead_forward_to_grid ()
6357 framepos_t pos = playhead_cursor->current_frame ();
6358 if (pos < max_framepos - 1) {
6360 snap_to_internal (pos, 1, false);
6361 _session->request_locate (pos);
6367 Editor::playhead_backward_to_grid ()
6373 framepos_t pos = playhead_cursor->current_frame ();
6376 snap_to_internal (pos, -1, false);
6377 _session->request_locate (pos);
6382 Editor::set_track_height (Height h)
6384 TrackSelection& ts (selection->tracks);
6386 for (TrackSelection::iterator x = ts.begin(); x != ts.end(); ++x) {
6387 (*x)->set_height_enum (h);
6392 Editor::toggle_tracks_active ()
6394 TrackSelection& ts (selection->tracks);
6396 bool target = false;
6402 for (TrackSelection::iterator x = ts.begin(); x != ts.end(); ++x) {
6403 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*>(*x);
6407 target = !rtv->_route->active();
6410 rtv->_route->set_active (target, this);
6416 Editor::remove_tracks ()
6418 TrackSelection& ts (selection->tracks);
6424 vector<string> choices;
6428 const char* trackstr;
6430 vector<boost::shared_ptr<Route> > routes;
6431 bool special_bus = false;
6433 for (TrackSelection::iterator x = ts.begin(); x != ts.end(); ++x) {
6434 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (*x);
6436 if (rtv->is_track()) {
6442 routes.push_back (rtv->_route);
6444 if (rtv->route()->is_master() || rtv->route()->is_monitor()) {
6449 if (special_bus && !Config->get_allow_special_bus_removal()) {
6450 MessageDialog msg (_("That would be bad news ...."),
6454 msg.set_secondary_text (string_compose (_(
6455 "Removing the master or monitor bus is such a bad idea\n\
6456 that %1 is not going to allow it.\n\
6458 If you really want to do this sort of thing\n\
6459 edit your ardour.rc file to set the\n\
6460 \"allow-special-bus-removal\" option to be \"yes\""), PROGRAM_NAME));
6467 if (ntracks + nbusses == 0) {
6472 trackstr = _("tracks");
6474 trackstr = _("track");
6478 busstr = _("busses");
6485 prompt = string_compose (_("Do you really want to remove %1 %2 and %3 %4?\n"
6486 "(You may also lose the playlists associated with the %2)\n\n"
6487 "This action cannot be undone, and the session file will be overwritten!"),
6488 ntracks, trackstr, nbusses, busstr);
6490 prompt = string_compose (_("Do you really want to remove %1 %2?\n"
6491 "(You may also lose the playlists associated with the %2)\n\n"
6492 "This action cannot be undone, and the session file will be overwritten!"),
6495 } else if (nbusses) {
6496 prompt = string_compose (_("Do you really want to remove %1 %2?\n\n"
6497 "This action cannot be undon, and the session file will be overwritten"),
6501 choices.push_back (_("No, do nothing."));
6502 if (ntracks + nbusses > 1) {
6503 choices.push_back (_("Yes, remove them."));
6505 choices.push_back (_("Yes, remove it."));
6510 title = string_compose (_("Remove %1"), trackstr);
6512 title = string_compose (_("Remove %1"), busstr);
6515 Choice prompter (title, prompt, choices);
6517 if (prompter.run () != 1) {
6521 for (vector<boost::shared_ptr<Route> >::iterator x = routes.begin(); x != routes.end(); ++x) {
6522 _session->remove_route (*x);
6527 Editor::do_insert_time ()
6529 if (selection->tracks.empty()) {
6533 InsertTimeDialog d (*this);
6534 int response = d.run ();
6536 if (response != RESPONSE_OK) {
6540 if (d.distance() == 0) {
6544 InsertTimeOption opt = d.intersected_region_action ();
6547 get_preferred_edit_position(),
6553 d.move_glued_markers(),
6554 d.move_locked_markers(),
6560 Editor::insert_time (
6561 framepos_t pos, framecnt_t frames, InsertTimeOption opt,
6562 bool all_playlists, bool ignore_music_glue, bool markers_too, bool glued_markers_too, bool locked_markers_too, bool tempo_too
6565 bool commit = false;
6567 if (Config->get_edit_mode() == Lock) {
6571 begin_reversible_command (_("insert time"));
6573 TrackViewList ts = selection->tracks.filter_to_unique_playlists ();
6575 for (TrackViewList::iterator x = ts.begin(); x != ts.end(); ++x) {
6579 /* don't operate on any playlist more than once, which could
6580 * happen if "all playlists" is enabled, but there is more
6581 * than 1 track using playlists "from" a given track.
6584 set<boost::shared_ptr<Playlist> > pl;
6586 if (all_playlists) {
6587 RouteTimeAxisView* rtav = dynamic_cast<RouteTimeAxisView*> (*x);
6589 vector<boost::shared_ptr<Playlist> > all = _session->playlists->playlists_for_track (rtav->track ());
6590 for (vector<boost::shared_ptr<Playlist> >::iterator p = all.begin(); p != all.end(); ++p) {
6595 if ((*x)->playlist ()) {
6596 pl.insert ((*x)->playlist ());
6600 for (set<boost::shared_ptr<Playlist> >::iterator i = pl.begin(); i != pl.end(); ++i) {
6602 (*i)->clear_changes ();
6603 (*i)->clear_owned_changes ();
6605 if (opt == SplitIntersected) {
6609 (*i)->shift (pos, frames, (opt == MoveIntersected), ignore_music_glue);
6611 vector<Command*> cmds;
6613 _session->add_commands (cmds);
6615 _session->add_command (new StatefulDiffCommand (*i));
6620 RouteTimeAxisView* rtav = dynamic_cast<RouteTimeAxisView*> (*x);
6622 rtav->route ()->shift (pos, frames);
6630 XMLNode& before (_session->locations()->get_state());
6631 Locations::LocationList copy (_session->locations()->list());
6633 for (Locations::LocationList::iterator i = copy.begin(); i != copy.end(); ++i) {
6635 Locations::LocationList::const_iterator tmp;
6637 bool const was_locked = (*i)->locked ();
6638 if (locked_markers_too) {
6642 if ((*i)->position_lock_style() == AudioTime || glued_markers_too) {
6644 if ((*i)->start() >= pos) {
6645 (*i)->set_start ((*i)->start() + frames);
6646 if (!(*i)->is_mark()) {
6647 (*i)->set_end ((*i)->end() + frames);
6660 XMLNode& after (_session->locations()->get_state());
6661 _session->add_command (new MementoCommand<Locations>(*_session->locations(), &before, &after));
6666 _session->tempo_map().insert_time (pos, frames);
6670 commit_reversible_command ();
6675 Editor::fit_selected_tracks ()
6677 if (!selection->tracks.empty()) {
6678 fit_tracks (selection->tracks);
6682 /* no selected tracks - use tracks with selected regions */
6684 if (!selection->regions.empty()) {
6685 for (RegionSelection::iterator r = selection->regions.begin(); r != selection->regions.end(); ++r) {
6686 tvl.push_back (&(*r)->get_time_axis_view ());
6692 } else if (internal_editing()) {
6693 /* no selected tracks, or regions, but in internal edit mode, so follow the mouse and use
6696 if (entered_track) {
6697 tvl.push_back (entered_track);
6705 Editor::fit_tracks (TrackViewList & tracks)
6707 if (tracks.empty()) {
6711 uint32_t child_heights = 0;
6712 int visible_tracks = 0;
6714 for (TrackSelection::iterator t = tracks.begin(); t != tracks.end(); ++t) {
6716 if (!(*t)->marked_for_display()) {
6720 child_heights += (*t)->effective_height() - (*t)->current_height();
6724 uint32_t h = (uint32_t) floor ((_visible_canvas_height - child_heights) / visible_tracks);
6725 double first_y_pos = DBL_MAX;
6727 if (h < TimeAxisView::preset_height (HeightSmall)) {
6728 MessageDialog msg (*this, _("There are too many tracks to fit in the current window"));
6729 /* too small to be displayed */
6733 undo_visual_stack.push_back (current_visual_state (true));
6734 no_save_visual = true;
6736 /* build a list of all tracks, including children */
6739 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
6741 TimeAxisView::Children c = (*i)->get_child_list ();
6742 for (TimeAxisView::Children::iterator j = c.begin(); j != c.end(); ++j) {
6743 all.push_back (j->get());
6747 /* operate on all tracks, hide unselected ones that are in the middle of selected ones */
6749 bool prev_was_selected = false;
6750 bool is_selected = tracks.contains (all.front());
6751 bool next_is_selected;
6753 for (TrackViewList::iterator t = all.begin(); t != all.end(); ++t) {
6755 TrackViewList::iterator next;
6760 if (next != all.end()) {
6761 next_is_selected = tracks.contains (*next);
6763 next_is_selected = false;
6766 if ((*t)->marked_for_display ()) {
6768 (*t)->set_height (h);
6769 first_y_pos = std::min ((*t)->y_position (), first_y_pos);
6771 if (prev_was_selected && next_is_selected) {
6772 hide_track_in_display (*t);
6777 prev_was_selected = is_selected;
6778 is_selected = next_is_selected;
6782 set the controls_layout height now, because waiting for its size
6783 request signal handler will cause the vertical adjustment setting to fail
6786 controls_layout.property_height () = _full_canvas_height;
6787 vertical_adjustment.set_value (first_y_pos);
6789 redo_visual_stack.push_back (current_visual_state (true));
6793 Editor::save_visual_state (uint32_t n)
6795 while (visual_states.size() <= n) {
6796 visual_states.push_back (0);
6799 if (visual_states[n] != 0) {
6800 delete visual_states[n];
6803 visual_states[n] = current_visual_state (true);
6808 Editor::goto_visual_state (uint32_t n)
6810 if (visual_states.size() <= n) {
6814 if (visual_states[n] == 0) {
6818 use_visual_state (*visual_states[n]);
6822 Editor::start_visual_state_op (uint32_t n)
6824 save_visual_state (n);
6826 PopUp* pup = new PopUp (WIN_POS_MOUSE, 1000, true);
6828 snprintf (buf, sizeof (buf), _("Saved view %u"), n+1);
6829 pup->set_text (buf);
6834 Editor::cancel_visual_state_op (uint32_t n)
6836 goto_visual_state (n);
6840 Editor::toggle_region_mute ()
6842 if (_ignore_region_action) {
6846 RegionSelection rs = get_regions_from_selection_and_entered ();
6852 if (rs.size() > 1) {
6853 begin_reversible_command (_("mute regions"));
6855 begin_reversible_command (_("mute region"));
6858 for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
6860 (*i)->region()->playlist()->clear_changes ();
6861 (*i)->region()->set_muted (!(*i)->region()->muted ());
6862 _session->add_command (new StatefulDiffCommand ((*i)->region()->playlist()));
6866 commit_reversible_command ();
6870 Editor::combine_regions ()
6872 /* foreach track with selected regions, take all selected regions
6873 and join them into a new region containing the subregions (as a
6877 typedef set<RouteTimeAxisView*> RTVS;
6880 if (selection->regions.empty()) {
6884 for (RegionSelection::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
6885 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*>(&(*i)->get_time_axis_view());
6888 tracks.insert (rtv);
6892 begin_reversible_command (_("combine regions"));
6894 vector<RegionView*> new_selection;
6896 for (RTVS::iterator i = tracks.begin(); i != tracks.end(); ++i) {
6899 if ((rv = (*i)->combine_regions ()) != 0) {
6900 new_selection.push_back (rv);
6904 selection->clear_regions ();
6905 for (vector<RegionView*>::iterator i = new_selection.begin(); i != new_selection.end(); ++i) {
6906 selection->add (*i);
6909 commit_reversible_command ();
6913 Editor::uncombine_regions ()
6915 typedef set<RouteTimeAxisView*> RTVS;
6918 if (selection->regions.empty()) {
6922 for (RegionSelection::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
6923 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*>(&(*i)->get_time_axis_view());
6926 tracks.insert (rtv);
6930 begin_reversible_command (_("uncombine regions"));
6932 for (RTVS::iterator i = tracks.begin(); i != tracks.end(); ++i) {
6933 (*i)->uncombine_regions ();
6936 commit_reversible_command ();
6940 Editor::toggle_midi_input_active (bool flip_others)
6943 boost::shared_ptr<RouteList> rl (new RouteList);
6945 for (TrackSelection::iterator i = selection->tracks.begin(); i != selection->tracks.end(); ++i) {
6946 RouteTimeAxisView *rtav = dynamic_cast<RouteTimeAxisView *>(*i);
6952 boost::shared_ptr<MidiTrack> mt = rtav->midi_track();
6955 rl->push_back (rtav->route());
6956 onoff = !mt->input_active();
6960 _session->set_exclusive_input_active (rl, onoff, flip_others);