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 "ardour_ui.h"
61 #include "time_axis_view.h"
62 #include "route_time_axis.h"
63 #include "audio_time_axis.h"
64 #include "automation_time_axis.h"
65 #include "control_point.h"
66 #include "streamview.h"
67 #include "audio_streamview.h"
68 #include "audio_region_view.h"
69 #include "midi_region_view.h"
70 #include "rgb_macros.h"
71 #include "selection_templates.h"
72 #include "selection.h"
74 #include "gtk-custom-hruler.h"
75 #include "gui_thread.h"
78 #include "editor_drag.h"
79 #include "strip_silence_dialog.h"
80 #include "editor_routes.h"
81 #include "editor_regions.h"
82 #include "quantize_dialog.h"
83 #include "interthread_progress_window.h"
84 #include "insert_time_dialog.h"
85 #include "normalize_dialog.h"
86 #include "editor_cursors.h"
87 #include "mouse_cursors.h"
88 #include "patch_change_dialog.h"
89 #include "transpose_dialog.h"
94 using namespace ARDOUR;
97 using namespace Gtkmm2ext;
98 using namespace Editing;
99 using Gtkmm2ext::Keyboard;
101 /***********************************************************************
103 ***********************************************************************/
106 Editor::undo (uint32_t n)
108 if (_drags->active ()) {
118 Editor::redo (uint32_t n)
120 if (_drags->active ()) {
130 Editor::split_regions_at (framepos_t where, RegionSelection& regions)
134 list <boost::shared_ptr<Playlist > > used_playlists;
136 if (regions.empty()) {
140 begin_reversible_command (_("split"));
142 // if splitting a single region, and snap-to is using
143 // region boundaries, don't pay attention to them
145 if (regions.size() == 1) {
146 switch (_snap_type) {
147 case SnapToRegionStart:
148 case SnapToRegionSync:
149 case SnapToRegionEnd:
158 EditorFreeze(); /* Emit Signal */
161 for (RegionSelection::iterator a = regions.begin(); a != regions.end(); ) {
163 RegionSelection::iterator tmp;
165 /* XXX this test needs to be more complicated, to make sure we really
166 have something to split.
169 if (!(*a)->region()->covers (where)) {
177 boost::shared_ptr<Playlist> pl = (*a)->region()->playlist();
185 /* we haven't seen this playlist before */
187 /* remember used playlists so we can thaw them later */
188 used_playlists.push_back(pl);
193 pl->clear_changes ();
194 pl->split_region ((*a)->region(), where);
195 _session->add_command (new StatefulDiffCommand (pl));
201 while (used_playlists.size() > 0) {
202 list <boost::shared_ptr<Playlist > >::iterator i = used_playlists.begin();
204 used_playlists.pop_front();
207 commit_reversible_command ();
210 EditorThaw(); /* Emit Signal */
214 /** Move one extreme of the current range selection. If more than one range is selected,
215 * the start of the earliest range or the end of the latest range is moved.
217 * @param move_end true to move the end of the current range selection, false to move
219 * @param next true to move the extreme to the next region boundary, false to move to
223 Editor::move_range_selection_start_or_end_to_region_boundary (bool move_end, bool next)
225 if (selection->time.start() == selection->time.end_frame()) {
229 framepos_t start = selection->time.start ();
230 framepos_t end = selection->time.end_frame ();
232 /* the position of the thing we may move */
233 framepos_t pos = move_end ? end : start;
234 int dir = next ? 1 : -1;
236 /* so we don't find the current region again */
237 if (dir > 0 || pos > 0) {
241 framepos_t const target = get_region_boundary (pos, dir, true, false);
256 begin_reversible_command (_("alter selection"));
257 selection->set_preserving_all_ranges (start, end);
258 commit_reversible_command ();
262 Editor::nudge_forward_release (GdkEventButton* ev)
264 if (ev->state & Keyboard::PrimaryModifier) {
265 nudge_forward (false, true);
267 nudge_forward (false, false);
273 Editor::nudge_backward_release (GdkEventButton* ev)
275 if (ev->state & Keyboard::PrimaryModifier) {
276 nudge_backward (false, true);
278 nudge_backward (false, false);
285 Editor::nudge_forward (bool next, bool force_playhead)
288 framepos_t next_distance;
294 RegionSelection rs = get_regions_from_selection_and_entered ();
296 if (!force_playhead && !rs.empty()) {
298 begin_reversible_command (_("nudge regions forward"));
300 for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
301 boost::shared_ptr<Region> r ((*i)->region());
303 distance = get_nudge_distance (r->position(), next_distance);
306 distance = next_distance;
310 r->set_position (r->position() + distance);
311 _session->add_command (new StatefulDiffCommand (r));
314 commit_reversible_command ();
317 } else if (!force_playhead && !selection->markers.empty()) {
321 begin_reversible_command (_("nudge location forward"));
323 for (MarkerSelection::iterator i = selection->markers.begin(); i != selection->markers.end(); ++i) {
325 Location* loc = find_location_from_marker ((*i), is_start);
329 XMLNode& before (loc->get_state());
332 distance = get_nudge_distance (loc->start(), next_distance);
334 distance = next_distance;
336 if (max_framepos - distance > loc->start() + loc->length()) {
337 loc->set_start (loc->start() + distance);
339 loc->set_start (max_framepos - loc->length());
342 distance = get_nudge_distance (loc->end(), next_distance);
344 distance = next_distance;
346 if (max_framepos - distance > loc->end()) {
347 loc->set_end (loc->end() + distance);
349 loc->set_end (max_framepos);
352 XMLNode& after (loc->get_state());
353 _session->add_command (new MementoCommand<Location>(*loc, &before, &after));
357 commit_reversible_command ();
360 distance = get_nudge_distance (playhead_cursor->current_frame, next_distance);
361 _session->request_locate (playhead_cursor->current_frame + distance);
366 Editor::nudge_backward (bool next, bool force_playhead)
369 framepos_t next_distance;
375 RegionSelection rs = get_regions_from_selection_and_entered ();
377 if (!force_playhead && !rs.empty()) {
379 begin_reversible_command (_("nudge regions backward"));
381 for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
382 boost::shared_ptr<Region> r ((*i)->region());
384 distance = get_nudge_distance (r->position(), next_distance);
387 distance = next_distance;
392 if (r->position() > distance) {
393 r->set_position (r->position() - distance);
397 _session->add_command (new StatefulDiffCommand (r));
400 commit_reversible_command ();
402 } else if (!force_playhead && !selection->markers.empty()) {
406 begin_reversible_command (_("nudge location forward"));
408 for (MarkerSelection::iterator i = selection->markers.begin(); i != selection->markers.end(); ++i) {
410 Location* loc = find_location_from_marker ((*i), is_start);
414 XMLNode& before (loc->get_state());
417 distance = get_nudge_distance (loc->start(), next_distance);
419 distance = next_distance;
421 if (distance < loc->start()) {
422 loc->set_start (loc->start() - distance);
427 distance = get_nudge_distance (loc->end(), next_distance);
430 distance = next_distance;
433 if (distance < loc->end() - loc->length()) {
434 loc->set_end (loc->end() - distance);
436 loc->set_end (loc->length());
440 XMLNode& after (loc->get_state());
441 _session->add_command (new MementoCommand<Location>(*loc, &before, &after));
445 commit_reversible_command ();
449 distance = get_nudge_distance (playhead_cursor->current_frame, next_distance);
451 if (playhead_cursor->current_frame > distance) {
452 _session->request_locate (playhead_cursor->current_frame - distance);
454 _session->goto_start();
460 Editor::nudge_forward_capture_offset ()
462 RegionSelection rs = get_regions_from_selection_and_entered ();
464 if (!_session || rs.empty()) {
468 begin_reversible_command (_("nudge forward"));
470 framepos_t const distance = _session->worst_output_latency();
472 for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
473 boost::shared_ptr<Region> r ((*i)->region());
476 r->set_position (r->position() + distance);
477 _session->add_command(new StatefulDiffCommand (r));
480 commit_reversible_command ();
484 Editor::nudge_backward_capture_offset ()
486 RegionSelection rs = get_regions_from_selection_and_entered ();
488 if (!_session || rs.empty()) {
492 begin_reversible_command (_("nudge backward"));
494 framepos_t const distance = _session->worst_output_latency();
496 for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
497 boost::shared_ptr<Region> r ((*i)->region());
501 if (r->position() > distance) {
502 r->set_position (r->position() - distance);
506 _session->add_command(new StatefulDiffCommand (r));
509 commit_reversible_command ();
515 Editor::move_to_start ()
517 _session->goto_start ();
521 Editor::move_to_end ()
524 _session->request_locate (_session->current_end_frame());
528 Editor::build_region_boundary_cache ()
531 vector<RegionPoint> interesting_points;
532 boost::shared_ptr<Region> r;
533 TrackViewList tracks;
536 region_boundary_cache.clear ();
542 switch (_snap_type) {
543 case SnapToRegionStart:
544 interesting_points.push_back (Start);
546 case SnapToRegionEnd:
547 interesting_points.push_back (End);
549 case SnapToRegionSync:
550 interesting_points.push_back (SyncPoint);
552 case SnapToRegionBoundary:
553 interesting_points.push_back (Start);
554 interesting_points.push_back (End);
557 fatal << string_compose (_("build_region_boundary_cache called with snap_type = %1"), _snap_type) << endmsg;
562 TimeAxisView *ontrack = 0;
565 if (!selection->tracks.empty()) {
566 tlist = selection->tracks.filter_to_unique_playlists ();
568 tlist = track_views.filter_to_unique_playlists ();
571 while (pos < _session->current_end_frame() && !at_end) {
574 framepos_t lpos = max_framepos;
576 for (vector<RegionPoint>::iterator p = interesting_points.begin(); p != interesting_points.end(); ++p) {
578 if ((r = find_next_region (pos, *p, 1, tlist, &ontrack)) == 0) {
579 if (*p == interesting_points.back()) {
582 /* move to next point type */
588 rpos = r->first_frame();
592 rpos = r->last_frame();
596 rpos = r->sync_position ();
604 RouteTimeAxisView *rtav;
606 if (ontrack != 0 && (rtav = dynamic_cast<RouteTimeAxisView*>(ontrack)) != 0 ) {
607 if (rtav->track() != 0) {
608 speed = rtav->track()->speed();
612 rpos = track_frame_to_session_frame (rpos, speed);
618 /* prevent duplicates, but we don't use set<> because we want to be able
622 vector<framepos_t>::iterator ri;
624 for (ri = region_boundary_cache.begin(); ri != region_boundary_cache.end(); ++ri) {
630 if (ri == region_boundary_cache.end()) {
631 region_boundary_cache.push_back (rpos);
638 /* finally sort to be sure that the order is correct */
640 sort (region_boundary_cache.begin(), region_boundary_cache.end());
643 boost::shared_ptr<Region>
644 Editor::find_next_region (framepos_t frame, RegionPoint point, int32_t dir, TrackViewList& tracks, TimeAxisView **ontrack)
646 TrackViewList::iterator i;
647 framepos_t closest = max_framepos;
648 boost::shared_ptr<Region> ret;
652 framepos_t track_frame;
653 RouteTimeAxisView *rtav;
655 for (i = tracks.begin(); i != tracks.end(); ++i) {
658 boost::shared_ptr<Region> r;
661 if ( (rtav = dynamic_cast<RouteTimeAxisView*>(*i)) != 0 ) {
662 if (rtav->track()!=0)
663 track_speed = rtav->track()->speed();
666 track_frame = session_frame_to_track_frame(frame, track_speed);
668 if ((r = (*i)->find_next_region (track_frame, point, dir)) == 0) {
674 rpos = r->first_frame ();
678 rpos = r->last_frame ();
682 rpos = r->sync_position ();
686 // rpos is a "track frame", converting it to "_session frame"
687 rpos = track_frame_to_session_frame(rpos, track_speed);
690 distance = rpos - frame;
692 distance = frame - rpos;
695 if (distance < closest) {
707 Editor::find_next_region_boundary (framepos_t pos, int32_t dir, const TrackViewList& tracks)
709 framecnt_t distance = max_framepos;
710 framepos_t current_nearest = -1;
712 for (TrackViewList::const_iterator i = tracks.begin(); i != tracks.end(); ++i) {
713 framepos_t contender;
716 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (*i);
722 if ((contender = rtv->find_next_region_boundary (pos, dir)) < 0) {
726 d = ::llabs (pos - contender);
729 current_nearest = contender;
734 return current_nearest;
738 Editor::get_region_boundary (framepos_t pos, int32_t dir, bool with_selection, bool only_onscreen)
743 if (with_selection && Config->get_region_boundaries_from_selected_tracks()) {
745 if (!selection->tracks.empty()) {
747 target = find_next_region_boundary (pos, dir, selection->tracks);
751 if (only_onscreen || Config->get_region_boundaries_from_onscreen_tracks()) {
752 get_onscreen_tracks (tvl);
753 target = find_next_region_boundary (pos, dir, tvl);
755 target = find_next_region_boundary (pos, dir, track_views);
761 if (only_onscreen || Config->get_region_boundaries_from_onscreen_tracks()) {
762 get_onscreen_tracks (tvl);
763 target = find_next_region_boundary (pos, dir, tvl);
765 target = find_next_region_boundary (pos, dir, track_views);
773 Editor::cursor_to_region_boundary (bool with_selection, int32_t dir)
775 framepos_t pos = playhead_cursor->current_frame;
782 // so we don't find the current region again..
783 if (dir > 0 || pos > 0) {
787 if ((target = get_region_boundary (pos, dir, with_selection, false)) < 0) {
791 _session->request_locate (target);
795 Editor::cursor_to_next_region_boundary (bool with_selection)
797 cursor_to_region_boundary (with_selection, 1);
801 Editor::cursor_to_previous_region_boundary (bool with_selection)
803 cursor_to_region_boundary (with_selection, -1);
807 Editor::cursor_to_region_point (EditorCursor* cursor, RegionPoint point, int32_t dir)
809 boost::shared_ptr<Region> r;
810 framepos_t pos = cursor->current_frame;
816 TimeAxisView *ontrack = 0;
818 // so we don't find the current region again..
822 if (!selection->tracks.empty()) {
824 r = find_next_region (pos, point, dir, selection->tracks, &ontrack);
826 } else if (clicked_axisview) {
829 t.push_back (clicked_axisview);
831 r = find_next_region (pos, point, dir, t, &ontrack);
835 r = find_next_region (pos, point, dir, track_views, &ontrack);
844 pos = r->first_frame ();
848 pos = r->last_frame ();
852 pos = r->sync_position ();
857 RouteTimeAxisView *rtav;
859 if ( ontrack != 0 && (rtav = dynamic_cast<RouteTimeAxisView*>(ontrack)) != 0 ) {
860 if (rtav->track() != 0) {
861 speed = rtav->track()->speed();
865 pos = track_frame_to_session_frame(pos, speed);
867 if (cursor == playhead_cursor) {
868 _session->request_locate (pos);
870 cursor->set_position (pos);
875 Editor::cursor_to_next_region_point (EditorCursor* cursor, RegionPoint point)
877 cursor_to_region_point (cursor, point, 1);
881 Editor::cursor_to_previous_region_point (EditorCursor* cursor, RegionPoint point)
883 cursor_to_region_point (cursor, point, -1);
887 Editor::cursor_to_selection_start (EditorCursor *cursor)
891 switch (mouse_mode) {
893 if (!selection->regions.empty()) {
894 pos = selection->regions.start();
899 if (!selection->time.empty()) {
900 pos = selection->time.start ();
908 if (cursor == playhead_cursor) {
909 _session->request_locate (pos);
911 cursor->set_position (pos);
916 Editor::cursor_to_selection_end (EditorCursor *cursor)
920 switch (mouse_mode) {
922 if (!selection->regions.empty()) {
923 pos = selection->regions.end_frame();
928 if (!selection->time.empty()) {
929 pos = selection->time.end_frame ();
937 if (cursor == playhead_cursor) {
938 _session->request_locate (pos);
940 cursor->set_position (pos);
945 Editor::selected_marker_to_region_boundary (bool with_selection, int32_t dir)
955 if (selection->markers.empty()) {
959 if (!mouse_frame (mouse, ignored)) {
963 add_location_mark (mouse);
966 if ((loc = find_location_from_marker (selection->markers.front(), ignored)) == 0) {
970 framepos_t pos = loc->start();
972 // so we don't find the current region again..
973 if (dir > 0 || pos > 0) {
977 if ((target = get_region_boundary (pos, dir, with_selection, false)) < 0) {
981 loc->move_to (target);
985 Editor::selected_marker_to_next_region_boundary (bool with_selection)
987 selected_marker_to_region_boundary (with_selection, 1);
991 Editor::selected_marker_to_previous_region_boundary (bool with_selection)
993 selected_marker_to_region_boundary (with_selection, -1);
997 Editor::selected_marker_to_region_point (RegionPoint point, int32_t dir)
999 boost::shared_ptr<Region> r;
1004 if (!_session || selection->markers.empty()) {
1008 if ((loc = find_location_from_marker (selection->markers.front(), ignored)) == 0) {
1012 TimeAxisView *ontrack = 0;
1016 // so we don't find the current region again..
1020 if (!selection->tracks.empty()) {
1022 r = find_next_region (pos, point, dir, selection->tracks, &ontrack);
1026 r = find_next_region (pos, point, dir, track_views, &ontrack);
1035 pos = r->first_frame ();
1039 pos = r->last_frame ();
1043 pos = r->adjust_to_sync (r->first_frame());
1048 RouteTimeAxisView *rtav;
1050 if (ontrack != 0 && (rtav = dynamic_cast<RouteTimeAxisView*>(ontrack)) != 0) {
1051 if (rtav->track() != 0) {
1052 speed = rtav->track()->speed();
1056 pos = track_frame_to_session_frame(pos, speed);
1062 Editor::selected_marker_to_next_region_point (RegionPoint point)
1064 selected_marker_to_region_point (point, 1);
1068 Editor::selected_marker_to_previous_region_point (RegionPoint point)
1070 selected_marker_to_region_point (point, -1);
1074 Editor::selected_marker_to_selection_start ()
1080 if (!_session || selection->markers.empty()) {
1084 if ((loc = find_location_from_marker (selection->markers.front(), ignored)) == 0) {
1088 switch (mouse_mode) {
1090 if (!selection->regions.empty()) {
1091 pos = selection->regions.start();
1096 if (!selection->time.empty()) {
1097 pos = selection->time.start ();
1109 Editor::selected_marker_to_selection_end ()
1115 if (!_session || selection->markers.empty()) {
1119 if ((loc = find_location_from_marker (selection->markers.front(), ignored)) == 0) {
1123 switch (mouse_mode) {
1125 if (!selection->regions.empty()) {
1126 pos = selection->regions.end_frame();
1131 if (!selection->time.empty()) {
1132 pos = selection->time.end_frame ();
1144 Editor::scroll_playhead (bool forward)
1146 framepos_t pos = playhead_cursor->current_frame;
1147 framecnt_t delta = (framecnt_t) floor (current_page_frames() / 0.8);
1150 if (pos == max_framepos) {
1154 if (pos < max_framepos - delta) {
1173 _session->request_locate (pos);
1177 Editor::cursor_align (bool playhead_to_edit)
1183 if (playhead_to_edit) {
1185 if (selection->markers.empty()) {
1189 _session->request_locate (selection->markers.front()->position(), _session->transport_rolling());
1192 /* move selected markers to playhead */
1194 for (MarkerSelection::iterator i = selection->markers.begin(); i != selection->markers.end(); ++i) {
1197 Location* loc = find_location_from_marker (*i, ignored);
1199 if (loc->is_mark()) {
1200 loc->set_start (playhead_cursor->current_frame);
1202 loc->set (playhead_cursor->current_frame,
1203 playhead_cursor->current_frame + loc->length());
1210 Editor::scroll_backward (float pages)
1212 framepos_t const one_page = (framepos_t) rint (_canvas_width * frames_per_unit);
1213 framepos_t const cnt = (framepos_t) floor (pages * one_page);
1216 if (leftmost_frame < cnt) {
1219 frame = leftmost_frame - cnt;
1222 reset_x_origin (frame);
1226 Editor::scroll_forward (float pages)
1228 framepos_t const one_page = (framepos_t) rint (_canvas_width * frames_per_unit);
1229 framepos_t const cnt = (framepos_t) floor (pages * one_page);
1232 if (max_framepos - cnt < leftmost_frame) {
1233 frame = max_framepos - cnt;
1235 frame = leftmost_frame + cnt;
1238 reset_x_origin (frame);
1242 Editor::scroll_tracks_down ()
1244 double vert_value = vertical_adjustment.get_value() + vertical_adjustment.get_page_size();
1245 if (vert_value > vertical_adjustment.get_upper() - _canvas_height) {
1246 vert_value = vertical_adjustment.get_upper() - _canvas_height;
1249 vertical_adjustment.set_value (vert_value);
1253 Editor::scroll_tracks_up ()
1255 vertical_adjustment.set_value (vertical_adjustment.get_value() - vertical_adjustment.get_page_size());
1259 Editor::scroll_tracks_down_line ()
1261 double vert_value = vertical_adjustment.get_value() + 60;
1263 if (vert_value > vertical_adjustment.get_upper() - _canvas_height) {
1264 vert_value = vertical_adjustment.get_upper() - _canvas_height;
1267 vertical_adjustment.set_value (vert_value);
1271 Editor::scroll_tracks_up_line ()
1273 reset_y_origin (vertical_adjustment.get_value() - 60);
1279 Editor::tav_zoom_step (bool coarser)
1281 _routes->suspend_redisplay ();
1285 if (selection->tracks.empty()) {
1288 ts = &selection->tracks;
1291 for (TrackViewList::iterator i = ts->begin(); i != ts->end(); ++i) {
1292 TimeAxisView *tv = (static_cast<TimeAxisView*>(*i));
1293 tv->step_height (coarser);
1296 _routes->resume_redisplay ();
1300 Editor::tav_zoom_smooth (bool coarser, bool force_all)
1302 _routes->suspend_redisplay ();
1306 if (selection->tracks.empty() || force_all) {
1309 ts = &selection->tracks;
1312 for (TrackViewList::iterator i = ts->begin(); i != ts->end(); ++i) {
1313 TimeAxisView *tv = (static_cast<TimeAxisView*>(*i));
1314 uint32_t h = tv->current_height ();
1319 if (h >= TimeAxisView::preset_height (HeightSmall)) {
1324 tv->set_height (h + 5);
1328 _routes->resume_redisplay ();
1332 Editor::clamp_frames_per_unit (double& fpu) const
1334 bool clamped = false;
1341 if (max_framepos / fpu < 800) {
1342 fpu = max_framepos / 800.0;
1350 Editor::temporal_zoom_step (bool coarser)
1352 ENSURE_GUI_THREAD (*this, &Editor::temporal_zoom_step, coarser)
1354 double nfpu = frames_per_unit;
1357 nfpu = min (9e6, nfpu * 1.61803399);
1359 nfpu = max (1.0, nfpu / 1.61803399);
1362 temporal_zoom (nfpu);
1366 Editor::temporal_zoom (double fpu)
1372 framepos_t current_page = current_page_frames();
1373 framepos_t current_leftmost = leftmost_frame;
1374 framepos_t current_rightmost;
1375 framepos_t current_center;
1376 framepos_t new_page_size;
1377 framepos_t half_page_size;
1378 framepos_t leftmost_after_zoom = 0;
1380 bool in_track_canvas;
1384 clamp_frames_per_unit (fpu);
1385 if (fpu == frames_per_unit) {
1391 // Imposing an arbitrary limit to zoom out as too much zoom out produces
1392 // segfaults for lack of memory. If somebody decides this is not high enough I
1393 // believe it can be raisen to higher values but some limit must be in place.
1398 new_page_size = (framepos_t) floor (_canvas_width * nfpu);
1399 half_page_size = new_page_size / 2;
1401 switch (zoom_focus) {
1403 leftmost_after_zoom = current_leftmost;
1406 case ZoomFocusRight:
1407 current_rightmost = leftmost_frame + current_page;
1408 if (current_rightmost < new_page_size) {
1409 leftmost_after_zoom = 0;
1411 leftmost_after_zoom = current_rightmost - new_page_size;
1415 case ZoomFocusCenter:
1416 current_center = current_leftmost + (current_page/2);
1417 if (current_center < half_page_size) {
1418 leftmost_after_zoom = 0;
1420 leftmost_after_zoom = current_center - half_page_size;
1424 case ZoomFocusPlayhead:
1425 /* centre playhead */
1426 l = playhead_cursor->current_frame - (new_page_size * 0.5);
1429 leftmost_after_zoom = 0;
1430 } else if (l > max_framepos) {
1431 leftmost_after_zoom = max_framepos - new_page_size;
1433 leftmost_after_zoom = (framepos_t) l;
1437 case ZoomFocusMouse:
1438 /* try to keep the mouse over the same point in the display */
1440 if (!mouse_frame (where, in_track_canvas)) {
1441 /* use playhead instead */
1442 where = playhead_cursor->current_frame;
1444 if (where < half_page_size) {
1445 leftmost_after_zoom = 0;
1447 leftmost_after_zoom = where - half_page_size;
1452 l = - ((new_page_size * ((where - current_leftmost)/(double)current_page)) - where);
1455 leftmost_after_zoom = 0;
1456 } else if (l > max_framepos) {
1457 leftmost_after_zoom = max_framepos - new_page_size;
1459 leftmost_after_zoom = (framepos_t) l;
1466 /* try to keep the edit point in the same place */
1467 where = get_preferred_edit_position ();
1471 double l = - ((new_page_size * ((where - current_leftmost)/(double)current_page)) - where);
1474 leftmost_after_zoom = 0;
1475 } else if (l > max_framepos) {
1476 leftmost_after_zoom = max_framepos - new_page_size;
1478 leftmost_after_zoom = (framepos_t) l;
1482 /* edit point not defined */
1489 // leftmost_after_zoom = min (leftmost_after_zoom, _session->current_end_frame());
1491 reposition_and_zoom (leftmost_after_zoom, nfpu);
1495 Editor::temporal_zoom_region (bool both_axes)
1497 framepos_t start = max_framepos;
1499 set<TimeAxisView*> tracks;
1501 RegionSelection rs = get_regions_from_selection_and_entered ();
1507 for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
1509 if ((*i)->region()->position() < start) {
1510 start = (*i)->region()->position();
1513 if ((*i)->region()->last_frame() + 1 > end) {
1514 end = (*i)->region()->last_frame() + 1;
1517 tracks.insert (&((*i)->get_time_axis_view()));
1520 /* now comes an "interesting" hack ... make sure we leave a little space
1521 at each end of the editor so that the zoom doesn't fit the region
1522 precisely to the screen.
1525 GdkScreen* screen = gdk_screen_get_default ();
1526 gint pixwidth = gdk_screen_get_width (screen);
1527 gint mmwidth = gdk_screen_get_width_mm (screen);
1528 double pix_per_mm = (double) pixwidth/ (double) mmwidth;
1529 double one_centimeter_in_pixels = pix_per_mm * 10.0;
1531 if ((start == 0 && end == 0) || end < start) {
1535 framepos_t range = end - start;
1536 double new_fpu = (double)range / (double)_canvas_width;
1537 framepos_t extra_samples = (framepos_t) floor (one_centimeter_in_pixels * new_fpu);
1539 if (start > extra_samples) {
1540 start -= extra_samples;
1545 if (max_framepos - extra_samples > end) {
1546 end += extra_samples;
1551 /* if we're zooming on both axes we need to save track heights etc.
1554 undo_visual_stack.push_back (current_visual_state (both_axes));
1556 PBD::Unwinder<bool> nsv (no_save_visual, true);
1558 temporal_zoom_by_frame (start, end);
1561 uint32_t per_track_height = (uint32_t) floor ((_canvas_height - canvas_timebars_vsize - 10.0) / tracks.size());
1563 /* set visible track heights appropriately */
1565 for (set<TimeAxisView*>::iterator t = tracks.begin(); t != tracks.end(); ++t) {
1566 (*t)->set_height (per_track_height);
1569 /* hide irrelevant tracks */
1571 _routes->suspend_redisplay ();
1573 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
1574 if (find (tracks.begin(), tracks.end(), (*i)) == tracks.end()) {
1575 hide_track_in_display (*i);
1579 _routes->resume_redisplay ();
1581 vertical_adjustment.set_value (0.0);
1584 redo_visual_stack.push_back (current_visual_state (both_axes));
1588 Editor::zoom_to_region (bool both_axes)
1590 temporal_zoom_region (both_axes);
1594 Editor::temporal_zoom_selection ()
1596 if (!selection) return;
1598 if (selection->time.empty()) {
1602 framepos_t start = selection->time[clicked_selection].start;
1603 framepos_t end = selection->time[clicked_selection].end;
1605 temporal_zoom_by_frame (start, end);
1609 Editor::temporal_zoom_session ()
1611 ENSURE_GUI_THREAD (*this, &Editor::temporal_zoom_session)
1614 framecnt_t const l = _session->current_end_frame() - _session->current_start_frame();
1615 double s = _session->current_start_frame() - l * 0.01;
1619 framecnt_t const e = _session->current_end_frame() + l * 0.01;
1620 temporal_zoom_by_frame (framecnt_t (s), e);
1625 Editor::temporal_zoom_by_frame (framepos_t start, framepos_t end)
1627 if (!_session) return;
1629 if ((start == 0 && end == 0) || end < start) {
1633 framepos_t range = end - start;
1635 double new_fpu = (double)range / (double)_canvas_width;
1637 framepos_t new_page = (framepos_t) floor (_canvas_width * new_fpu);
1638 framepos_t middle = (framepos_t) floor( (double)start + ((double)range / 2.0f ));
1639 framepos_t new_leftmost = (framepos_t) floor( (double)middle - ((double)new_page/2.0f));
1641 if (new_leftmost > middle) {
1645 if (new_leftmost < 0) {
1649 reposition_and_zoom (new_leftmost, new_fpu);
1653 Editor::temporal_zoom_to_frame (bool coarser, framepos_t frame)
1658 double range_before = frame - leftmost_frame;
1661 new_fpu = frames_per_unit;
1664 new_fpu *= 1.61803399;
1665 range_before *= 1.61803399;
1667 new_fpu = max(1.0,(new_fpu/1.61803399));
1668 range_before /= 1.61803399;
1671 if (new_fpu == frames_per_unit) {
1675 framepos_t new_leftmost = frame - (framepos_t)range_before;
1677 if (new_leftmost > frame) {
1681 if (new_leftmost < 0) {
1685 reposition_and_zoom (new_leftmost, new_fpu);
1690 Editor::choose_new_marker_name(string &name) {
1692 if (!Config->get_name_new_markers()) {
1693 /* don't prompt user for a new name */
1697 ArdourPrompter dialog (true);
1699 dialog.set_prompt (_("New Name:"));
1701 dialog.set_title (_("New Location Marker"));
1703 dialog.set_name ("MarkNameWindow");
1704 dialog.set_size_request (250, -1);
1705 dialog.set_position (Gtk::WIN_POS_MOUSE);
1707 dialog.add_button (Stock::OK, RESPONSE_ACCEPT);
1708 dialog.set_initial_text (name);
1712 switch (dialog.run ()) {
1713 case RESPONSE_ACCEPT:
1719 dialog.get_result(name);
1726 Editor::add_location_from_selection ()
1730 if (selection->time.empty()) {
1734 if (_session == 0 || clicked_axisview == 0) {
1738 framepos_t start = selection->time[clicked_selection].start;
1739 framepos_t end = selection->time[clicked_selection].end;
1741 _session->locations()->next_available_name(rangename,"selection");
1742 Location *location = new Location (*_session, start, end, rangename, Location::IsRangeMarker);
1744 _session->begin_reversible_command (_("add marker"));
1745 XMLNode &before = _session->locations()->get_state();
1746 _session->locations()->add (location, true);
1747 XMLNode &after = _session->locations()->get_state();
1748 _session->add_command(new MementoCommand<Locations>(*(_session->locations()), &before, &after));
1749 _session->commit_reversible_command ();
1753 Editor::add_location_mark (framepos_t where)
1757 select_new_marker = true;
1759 _session->locations()->next_available_name(markername,"mark");
1760 if (!choose_new_marker_name(markername)) {
1763 Location *location = new Location (*_session, where, where, markername, Location::IsMark);
1764 _session->begin_reversible_command (_("add marker"));
1765 XMLNode &before = _session->locations()->get_state();
1766 _session->locations()->add (location, true);
1767 XMLNode &after = _session->locations()->get_state();
1768 _session->add_command(new MementoCommand<Locations>(*(_session->locations()), &before, &after));
1769 _session->commit_reversible_command ();
1773 Editor::add_location_from_playhead_cursor ()
1775 add_location_mark (_session->audible_frame());
1778 /** Add a range marker around each selected region */
1780 Editor::add_locations_from_region ()
1782 RegionSelection rs = get_regions_from_selection_and_entered ();
1788 _session->begin_reversible_command (selection->regions.size () > 1 ? _("add markers") : _("add marker"));
1789 XMLNode &before = _session->locations()->get_state();
1791 for (RegionSelection::iterator i = rs.begin (); i != rs.end (); ++i) {
1793 boost::shared_ptr<Region> region = (*i)->region ();
1795 Location *location = new Location (*_session, region->position(), region->last_frame(), region->name(), Location::IsRangeMarker);
1797 _session->locations()->add (location, true);
1800 XMLNode &after = _session->locations()->get_state();
1801 _session->add_command (new MementoCommand<Locations>(*(_session->locations()), &before, &after));
1802 _session->commit_reversible_command ();
1805 /** Add a single range marker around all selected regions */
1807 Editor::add_location_from_region ()
1809 RegionSelection rs = get_regions_from_selection_and_entered ();
1815 _session->begin_reversible_command (_("add marker"));
1816 XMLNode &before = _session->locations()->get_state();
1820 if (rs.size() > 1) {
1821 _session->locations()->next_available_name(markername, "regions");
1823 RegionView* rv = *(rs.begin());
1824 boost::shared_ptr<Region> region = rv->region();
1825 markername = region->name();
1828 if (!choose_new_marker_name(markername)) {
1832 // single range spanning all selected
1833 Location *location = new Location (*_session, selection->regions.start(), selection->regions.end_frame(), markername, Location::IsRangeMarker);
1834 _session->locations()->add (location, true);
1836 XMLNode &after = _session->locations()->get_state();
1837 _session->add_command (new MementoCommand<Locations>(*(_session->locations()), &before, &after));
1838 _session->commit_reversible_command ();
1844 Editor::jump_forward_to_mark ()
1850 framepos_t pos = _session->locations()->first_mark_after (playhead_cursor->current_frame);
1856 _session->request_locate (pos, _session->transport_rolling());
1860 Editor::jump_backward_to_mark ()
1866 framepos_t pos = _session->locations()->first_mark_before (playhead_cursor->current_frame);
1872 _session->request_locate (pos, _session->transport_rolling());
1878 framepos_t const pos = _session->audible_frame ();
1881 _session->locations()->next_available_name (markername, "mark");
1883 if (!choose_new_marker_name (markername)) {
1887 _session->locations()->add (new Location (*_session, pos, 0, markername, Location::IsMark), true);
1891 Editor::clear_markers ()
1894 _session->begin_reversible_command (_("clear markers"));
1895 XMLNode &before = _session->locations()->get_state();
1896 _session->locations()->clear_markers ();
1897 XMLNode &after = _session->locations()->get_state();
1898 _session->add_command(new MementoCommand<Locations>(*(_session->locations()), &before, &after));
1899 _session->commit_reversible_command ();
1904 Editor::clear_ranges ()
1907 _session->begin_reversible_command (_("clear ranges"));
1908 XMLNode &before = _session->locations()->get_state();
1910 Location * looploc = _session->locations()->auto_loop_location();
1911 Location * punchloc = _session->locations()->auto_punch_location();
1912 Location * sessionloc = _session->locations()->session_range_location();
1914 _session->locations()->clear_ranges ();
1916 if (looploc) _session->locations()->add (looploc);
1917 if (punchloc) _session->locations()->add (punchloc);
1918 if (sessionloc) _session->locations()->add (sessionloc);
1920 XMLNode &after = _session->locations()->get_state();
1921 _session->add_command(new MementoCommand<Locations>(*(_session->locations()), &before, &after));
1922 _session->commit_reversible_command ();
1927 Editor::clear_locations ()
1929 _session->begin_reversible_command (_("clear locations"));
1930 XMLNode &before = _session->locations()->get_state();
1931 _session->locations()->clear ();
1932 XMLNode &after = _session->locations()->get_state();
1933 _session->add_command(new MementoCommand<Locations>(*(_session->locations()), &before, &after));
1934 _session->commit_reversible_command ();
1935 _session->locations()->clear ();
1939 Editor::unhide_markers ()
1941 for (LocationMarkerMap::iterator i = location_markers.begin(); i != location_markers.end(); ++i) {
1942 Location *l = (*i).first;
1943 if (l->is_hidden() && l->is_mark()) {
1944 l->set_hidden(false, this);
1950 Editor::unhide_ranges ()
1952 for (LocationMarkerMap::iterator i = location_markers.begin(); i != location_markers.end(); ++i) {
1953 Location *l = (*i).first;
1954 if (l->is_hidden() && l->is_range_marker()) {
1955 l->set_hidden(false, this);
1960 /* INSERT/REPLACE */
1963 Editor::insert_region_list_drag (boost::shared_ptr<Region> region, int x, int y)
1968 RouteTimeAxisView *rtv = 0;
1969 boost::shared_ptr<Playlist> playlist;
1971 track_canvas->window_to_world (x, y, wx, wy);
1974 event.type = GDK_BUTTON_RELEASE;
1975 event.button.x = wx;
1976 event.button.y = wy;
1978 where = event_frame (&event, &cx, &cy);
1980 if (where < leftmost_frame || where > leftmost_frame + current_page_frames()) {
1981 /* clearly outside canvas area */
1985 std::pair<TimeAxisView*, int> tv = trackview_by_y_position (cy);
1986 if (tv.first == 0) {
1990 if ((rtv = dynamic_cast<RouteTimeAxisView*> (tv.first)) == 0) {
1994 if ((playlist = rtv->playlist()) == 0) {
2000 begin_reversible_command (_("insert dragged region"));
2001 playlist->clear_changes ();
2002 playlist->add_region (RegionFactory::create (region, true), where, 1.0);
2003 _session->add_command(new StatefulDiffCommand (playlist));
2004 commit_reversible_command ();
2008 Editor::insert_route_list_drag (boost::shared_ptr<Route> route, int x, int y)
2012 RouteTimeAxisView *dest_rtv = 0;
2013 RouteTimeAxisView *source_rtv = 0;
2015 track_canvas->window_to_world (x, y, wx, wy);
2016 wx += horizontal_position ();
2017 wy += vertical_adjustment.get_value();
2020 event.type = GDK_BUTTON_RELEASE;
2021 event.button.x = wx;
2022 event.button.y = wy;
2024 event_frame (&event, &cx, &cy);
2026 std::pair<TimeAxisView*, int> const tv = trackview_by_y_position (cy);
2027 if (tv.first == 0) {
2031 if ((dest_rtv = dynamic_cast<RouteTimeAxisView*> (tv.first)) == 0) {
2035 /* use this drag source to add underlay to a track. But we really don't care
2036 about the Route, only the view of the route, so find it first */
2037 for(TrackViewList::iterator it = track_views.begin(); it != track_views.end(); ++it) {
2038 if((source_rtv = dynamic_cast<RouteTimeAxisView*>(*it)) == 0) {
2042 if(source_rtv->route() == route && source_rtv != dest_rtv) {
2043 dest_rtv->add_underlay(source_rtv->view());
2050 Editor::insert_region_list_selection (float times)
2052 RouteTimeAxisView *tv = 0;
2053 boost::shared_ptr<Playlist> playlist;
2055 if (clicked_routeview != 0) {
2056 tv = clicked_routeview;
2057 } else if (!selection->tracks.empty()) {
2058 if ((tv = dynamic_cast<RouteTimeAxisView*>(selection->tracks.front())) == 0) {
2061 } else if (entered_track != 0) {
2062 if ((tv = dynamic_cast<RouteTimeAxisView*>(entered_track)) == 0) {
2069 if ((playlist = tv->playlist()) == 0) {
2073 boost::shared_ptr<Region> region = _regions->get_single_selection ();
2078 begin_reversible_command (_("insert region"));
2079 playlist->clear_changes ();
2080 playlist->add_region ((RegionFactory::create (region, true)), get_preferred_edit_position(), times);
2081 _session->add_command(new StatefulDiffCommand (playlist));
2082 commit_reversible_command ();
2085 /* BUILT-IN EFFECTS */
2088 Editor::reverse_selection ()
2093 /* GAIN ENVELOPE EDITING */
2096 Editor::edit_envelope ()
2103 Editor::transition_to_rolling (bool fwd)
2109 if (_session->config.get_external_sync()) {
2110 switch (Config->get_sync_source()) {
2114 /* transport controlled by the master */
2119 if (_session->is_auditioning()) {
2120 _session->cancel_audition ();
2124 _session->request_transport_speed (fwd ? 1.0f : -1.0f);
2128 Editor::play_from_start ()
2130 _session->request_locate (_session->current_start_frame(), true);
2134 Editor::play_from_edit_point ()
2136 _session->request_locate (get_preferred_edit_position(), true);
2140 Editor::play_from_edit_point_and_return ()
2142 framepos_t start_frame;
2143 framepos_t return_frame;
2145 start_frame = get_preferred_edit_position (true);
2147 if (_session->transport_rolling()) {
2148 _session->request_locate (start_frame, false);
2152 /* don't reset the return frame if its already set */
2154 if ((return_frame = _session->requested_return_frame()) < 0) {
2155 return_frame = _session->audible_frame();
2158 if (start_frame >= 0) {
2159 _session->request_roll_at_and_return (start_frame, return_frame);
2164 Editor::play_selection ()
2166 if (selection->time.empty()) {
2170 _session->request_play_range (&selection->time, true);
2174 Editor::get_preroll ()
2176 return 1.0 /*Config->get_edit_preroll_seconds()*/ * _session->frame_rate();
2181 Editor::maybe_locate_with_edit_preroll ( framepos_t location )
2183 if ( _session->transport_rolling() || !Config->get_always_play_range() )
2186 location -= get_preroll();
2188 //don't try to locate before the beginning of time
2192 //if follow_playhead is on, keep the playhead on the screen
2193 if ( _follow_playhead )
2194 if ( location < leftmost_frame )
2195 location = leftmost_frame;
2197 _session->request_locate( location );
2201 Editor::play_with_preroll ()
2203 if (selection->time.empty()) {
2206 framepos_t preroll = get_preroll();
2208 framepos_t start = 0;
2209 if (selection->time[clicked_selection].start > preroll)
2210 start = selection->time[clicked_selection].start - preroll;
2212 framepos_t end = selection->time[clicked_selection].end + preroll;
2214 AudioRange ar (start, end, 0);
2215 list<AudioRange> lar;
2218 _session->request_play_range (&lar, true);
2223 Editor::play_location (Location& location)
2225 if (location.start() <= location.end()) {
2229 _session->request_bounded_roll (location.start(), location.end());
2233 Editor::loop_location (Location& location)
2235 if (location.start() <= location.end()) {
2241 if ((tll = transport_loop_location()) != 0) {
2242 tll->set (location.start(), location.end());
2244 // enable looping, reposition and start rolling
2245 _session->request_play_loop (true);
2246 _session->request_locate (tll->start(), true);
2251 Editor::do_layer_operation (LayerOperation op)
2253 if (selection->regions.empty ()) {
2257 bool const multiple = selection->regions.size() > 1;
2261 begin_reversible_command (_("raise regions"));
2263 begin_reversible_command (_("raise region"));
2269 begin_reversible_command (_("raise regions to top"));
2271 begin_reversible_command (_("raise region to top"));
2277 begin_reversible_command (_("lower regions"));
2279 begin_reversible_command (_("lower region"));
2285 begin_reversible_command (_("lower regions to bottom"));
2287 begin_reversible_command (_("lower region"));
2292 set<boost::shared_ptr<Playlist> > playlists = selection->regions.playlists ();
2293 for (set<boost::shared_ptr<Playlist> >::iterator i = playlists.begin(); i != playlists.end(); ++i) {
2294 (*i)->clear_owned_changes ();
2297 for (RegionSelection::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
2298 boost::shared_ptr<Region> r = (*i)->region ();
2310 r->lower_to_bottom ();
2314 for (set<boost::shared_ptr<Playlist> >::iterator i = playlists.begin(); i != playlists.end(); ++i) {
2315 vector<Command*> cmds;
2317 _session->add_commands (cmds);
2320 commit_reversible_command ();
2324 Editor::raise_region ()
2326 do_layer_operation (Raise);
2330 Editor::raise_region_to_top ()
2332 do_layer_operation (RaiseToTop);
2336 Editor::lower_region ()
2338 do_layer_operation (Lower);
2342 Editor::lower_region_to_bottom ()
2344 do_layer_operation (LowerToBottom);
2347 /** Show the region editor for the selected regions */
2349 Editor::show_region_properties ()
2351 selection->foreach_regionview (&RegionView::show_region_editor);
2354 /** Show the midi list editor for the selected MIDI regions */
2356 Editor::show_midi_list_editor ()
2358 selection->foreach_midi_regionview (&MidiRegionView::show_list_editor);
2362 Editor::rename_region ()
2364 RegionSelection rs = get_regions_from_selection_and_entered ();
2370 ArdourDialog d (*this, _("Rename Region"), true, false);
2372 Label label (_("New name:"));
2375 hbox.set_spacing (6);
2376 hbox.pack_start (label, false, false);
2377 hbox.pack_start (entry, true, true);
2379 d.get_vbox()->set_border_width (12);
2380 d.get_vbox()->pack_start (hbox, false, false);
2382 d.add_button(Gtk::Stock::CANCEL, Gtk::RESPONSE_CANCEL);
2383 d.add_button(Gtk::Stock::OK, Gtk::RESPONSE_OK);
2385 d.set_size_request (300, -1);
2386 d.set_position (Gtk::WIN_POS_MOUSE);
2388 entry.set_text (rs.front()->region()->name());
2389 entry.select_region (0, -1);
2391 entry.signal_activate().connect (sigc::bind (sigc::mem_fun (d, &Dialog::response), RESPONSE_OK));
2397 int const ret = d.run();
2401 if (ret != RESPONSE_OK) {
2405 std::string str = entry.get_text();
2406 strip_whitespace_edges (str);
2408 rs.front()->region()->set_name (str);
2409 _regions->redisplay ();
2414 Editor::audition_playlist_region_via_route (boost::shared_ptr<Region> region, Route& route)
2416 if (_session->is_auditioning()) {
2417 _session->cancel_audition ();
2420 // note: some potential for creativity here, because region doesn't
2421 // have to belong to the playlist that Route is handling
2423 // bool was_soloed = route.soloed();
2425 route.set_solo (true, this);
2427 _session->request_bounded_roll (region->position(), region->position() + region->length());
2429 /* XXX how to unset the solo state ? */
2432 /** Start an audition of the first selected region */
2434 Editor::play_edit_range ()
2436 framepos_t start, end;
2438 if (get_edit_op_range (start, end)) {
2439 _session->request_bounded_roll (start, end);
2444 Editor::play_selected_region ()
2446 framepos_t start = max_framepos;
2449 RegionSelection rs = get_regions_from_selection_and_entered ();
2455 for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
2456 if ((*i)->region()->position() < start) {
2457 start = (*i)->region()->position();
2459 if ((*i)->region()->last_frame() + 1 > end) {
2460 end = (*i)->region()->last_frame() + 1;
2464 _session->request_bounded_roll (start, end);
2468 Editor::audition_playlist_region_standalone (boost::shared_ptr<Region> region)
2470 _session->audition_region (region);
2474 Editor::region_from_selection ()
2476 if (clicked_axisview == 0) {
2480 if (selection->time.empty()) {
2484 framepos_t start = selection->time[clicked_selection].start;
2485 framepos_t end = selection->time[clicked_selection].end;
2487 TrackViewList tracks = get_tracks_for_range_action ();
2489 framepos_t selection_cnt = end - start + 1;
2491 for (TrackSelection::iterator i = tracks.begin(); i != tracks.end(); ++i) {
2492 boost::shared_ptr<Region> current;
2493 boost::shared_ptr<Playlist> pl;
2494 framepos_t internal_start;
2497 if ((pl = (*i)->playlist()) == 0) {
2501 if ((current = pl->top_region_at (start)) == 0) {
2505 internal_start = start - current->position();
2506 RegionFactory::region_name (new_name, current->name(), true);
2510 plist.add (ARDOUR::Properties::start, current->start() + internal_start);
2511 plist.add (ARDOUR::Properties::length, selection_cnt);
2512 plist.add (ARDOUR::Properties::name, new_name);
2513 plist.add (ARDOUR::Properties::layer, 0);
2515 boost::shared_ptr<Region> region (RegionFactory::create (current, plist));
2520 Editor::create_region_from_selection (vector<boost::shared_ptr<Region> >& new_regions)
2522 if (selection->time.empty() || selection->tracks.empty()) {
2526 framepos_t start = selection->time[clicked_selection].start;
2527 framepos_t end = selection->time[clicked_selection].end;
2529 TrackViewList ts = selection->tracks.filter_to_unique_playlists ();
2530 sort_track_selection (ts);
2532 for (TrackSelection::iterator i = ts.begin(); i != ts.end(); ++i) {
2533 boost::shared_ptr<Region> current;
2534 boost::shared_ptr<Playlist> playlist;
2535 framepos_t internal_start;
2538 if ((playlist = (*i)->playlist()) == 0) {
2542 if ((current = playlist->top_region_at(start)) == 0) {
2546 internal_start = start - current->position();
2547 RegionFactory::region_name (new_name, current->name(), true);
2551 plist.add (ARDOUR::Properties::start, current->start() + internal_start);
2552 plist.add (ARDOUR::Properties::length, end - start + 1);
2553 plist.add (ARDOUR::Properties::name, new_name);
2555 new_regions.push_back (RegionFactory::create (current, plist));
2560 Editor::split_multichannel_region ()
2562 RegionSelection rs = get_regions_from_selection_and_entered ();
2568 vector< boost::shared_ptr<Region> > v;
2570 for (list<RegionView*>::iterator x = rs.begin(); x != rs.end(); ++x) {
2571 (*x)->region()->separate_by_channel (*_session, v);
2576 Editor::new_region_from_selection ()
2578 region_from_selection ();
2579 cancel_selection ();
2583 add_if_covered (RegionView* rv, const AudioRange* ar, RegionSelection* rs)
2585 switch (rv->region()->coverage (ar->start, ar->end - 1)) {
2586 case Evoral::OverlapNone:
2594 * - selected tracks, or if there are none...
2595 * - tracks containing selected regions, or if there are none...
2600 Editor::get_tracks_for_range_action () const
2604 if (selection->tracks.empty()) {
2606 /* use tracks with selected regions */
2608 RegionSelection rs = selection->regions;
2610 for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
2611 TimeAxisView* tv = &(*i)->get_time_axis_view();
2613 if (!t.contains (tv)) {
2619 /* no regions and no tracks: use all tracks */
2625 t = selection->tracks;
2628 return t.filter_to_unique_playlists();
2632 Editor::separate_regions_between (const TimeSelection& ts)
2634 bool in_command = false;
2635 boost::shared_ptr<Playlist> playlist;
2636 RegionSelection new_selection;
2638 TrackViewList tmptracks = get_tracks_for_range_action ();
2639 sort_track_selection (tmptracks);
2641 for (TrackSelection::iterator i = tmptracks.begin(); i != tmptracks.end(); ++i) {
2643 RouteTimeAxisView* rtv;
2645 if ((rtv = dynamic_cast<RouteTimeAxisView*> ((*i))) != 0) {
2647 if (rtv->is_track()) {
2649 /* no edits to destructive tracks */
2651 if (rtv->track()->destructive()) {
2655 if ((playlist = rtv->playlist()) != 0) {
2657 playlist->clear_changes ();
2659 /* XXX need to consider musical time selections here at some point */
2661 double speed = rtv->track()->speed();
2664 for (list<AudioRange>::const_iterator t = ts.begin(); t != ts.end(); ++t) {
2666 sigc::connection c = rtv->view()->RegionViewAdded.connect (
2667 sigc::mem_fun(*this, &Editor::collect_new_region_view));
2669 latest_regionviews.clear ();
2671 playlist->partition ((framepos_t)((*t).start * speed),
2672 (framepos_t)((*t).end * speed), false);
2676 if (!latest_regionviews.empty()) {
2678 rtv->view()->foreach_regionview (sigc::bind (
2679 sigc::ptr_fun (add_if_covered),
2680 &(*t), &new_selection));
2683 begin_reversible_command (_("separate"));
2687 /* pick up changes to existing regions */
2689 vector<Command*> cmds;
2690 playlist->rdiff (cmds);
2691 _session->add_commands (cmds);
2693 /* pick up changes to the playlist itself (adds/removes)
2696 _session->add_command(new StatefulDiffCommand (playlist));
2705 selection->set (new_selection);
2706 set_mouse_mode (MouseObject);
2708 commit_reversible_command ();
2712 struct PlaylistState {
2713 boost::shared_ptr<Playlist> playlist;
2717 /** Take tracks from get_tracks_for_range_action and cut any regions
2718 * on those tracks so that the tracks are empty over the time
2722 Editor::separate_region_from_selection ()
2724 /* preferentially use *all* ranges in the time selection if we're in range mode
2725 to allow discontiguous operation, since get_edit_op_range() currently
2726 returns a single range.
2729 if (!selection->time.empty()) {
2731 separate_regions_between (selection->time);
2738 if (get_edit_op_range (start, end)) {
2740 AudioRange ar (start, end, 1);
2744 separate_regions_between (ts);
2750 Editor::separate_region_from_punch ()
2752 Location* loc = _session->locations()->auto_punch_location();
2754 separate_regions_using_location (*loc);
2759 Editor::separate_region_from_loop ()
2761 Location* loc = _session->locations()->auto_loop_location();
2763 separate_regions_using_location (*loc);
2768 Editor::separate_regions_using_location (Location& loc)
2770 if (loc.is_mark()) {
2774 AudioRange ar (loc.start(), loc.end(), 1);
2779 separate_regions_between (ts);
2782 /** Separate regions under the selected region */
2784 Editor::separate_under_selected_regions ()
2786 vector<PlaylistState> playlists;
2790 rs = get_regions_from_selection_and_entered();
2792 if (!_session || rs.empty()) {
2796 begin_reversible_command (_("separate region under"));
2798 list<boost::shared_ptr<Region> > regions_to_remove;
2800 for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
2801 // we can't just remove the region(s) in this loop because
2802 // this removes them from the RegionSelection, and they thus
2803 // disappear from underneath the iterator, and the ++i above
2804 // SEGVs in a puzzling fashion.
2806 // so, first iterate over the regions to be removed from rs and
2807 // add them to the regions_to_remove list, and then
2808 // iterate over the list to actually remove them.
2810 regions_to_remove.push_back ((*i)->region());
2813 for (list<boost::shared_ptr<Region> >::iterator rl = regions_to_remove.begin(); rl != regions_to_remove.end(); ++rl) {
2815 boost::shared_ptr<Playlist> playlist = (*rl)->playlist();
2818 // is this check necessary?
2822 vector<PlaylistState>::iterator i;
2824 //only take state if this is a new playlist.
2825 for (i = playlists.begin(); i != playlists.end(); ++i) {
2826 if ((*i).playlist == playlist) {
2831 if (i == playlists.end()) {
2833 PlaylistState before;
2834 before.playlist = playlist;
2835 before.before = &playlist->get_state();
2837 playlist->freeze ();
2838 playlists.push_back(before);
2841 //Partition on the region bounds
2842 playlist->partition ((*rl)->first_frame() - 1, (*rl)->last_frame() + 1, true);
2844 //Re-add region that was just removed due to the partition operation
2845 playlist->add_region( (*rl), (*rl)->first_frame() );
2848 vector<PlaylistState>::iterator pl;
2850 for (pl = playlists.begin(); pl != playlists.end(); ++pl) {
2851 (*pl).playlist->thaw ();
2852 _session->add_command(new MementoCommand<Playlist>(*(*pl).playlist, (*pl).before, &(*pl).playlist->get_state()));
2855 commit_reversible_command ();
2859 Editor::crop_region_to_selection ()
2861 if (!selection->time.empty()) {
2863 crop_region_to (selection->time.start(), selection->time.end_frame());
2870 if (get_edit_op_range (start, end)) {
2871 crop_region_to (start, end);
2878 Editor::crop_region_to (framepos_t start, framepos_t end)
2880 vector<boost::shared_ptr<Playlist> > playlists;
2881 boost::shared_ptr<Playlist> playlist;
2884 if (selection->tracks.empty()) {
2885 ts = track_views.filter_to_unique_playlists();
2887 ts = selection->tracks.filter_to_unique_playlists ();
2890 sort_track_selection (ts);
2892 for (TrackSelection::iterator i = ts.begin(); i != ts.end(); ++i) {
2894 RouteTimeAxisView* rtv;
2896 if ((rtv = dynamic_cast<RouteTimeAxisView*> ((*i))) != 0) {
2898 boost::shared_ptr<Track> t = rtv->track();
2900 if (t != 0 && ! t->destructive()) {
2902 if ((playlist = rtv->playlist()) != 0) {
2903 playlists.push_back (playlist);
2909 if (playlists.empty()) {
2913 framepos_t the_start;
2917 begin_reversible_command (_("trim to selection"));
2919 for (vector<boost::shared_ptr<Playlist> >::iterator i = playlists.begin(); i != playlists.end(); ++i) {
2921 boost::shared_ptr<Region> region;
2925 if ((region = (*i)->top_region_at(the_start)) == 0) {
2929 /* now adjust lengths to that we do the right thing
2930 if the selection extends beyond the region
2933 the_start = max (the_start, (framepos_t) region->position());
2934 if (max_framepos - the_start < region->length()) {
2935 the_end = the_start + region->length() - 1;
2937 the_end = max_framepos;
2939 the_end = min (end, the_end);
2940 cnt = the_end - the_start + 1;
2942 region->clear_changes ();
2943 region->trim_to (the_start, cnt);
2944 _session->add_command (new StatefulDiffCommand (region));
2947 commit_reversible_command ();
2951 Editor::region_fill_track ()
2953 RegionSelection rs = get_regions_from_selection_and_entered ();
2955 if (!_session || rs.empty()) {
2959 framepos_t const end = _session->current_end_frame ();
2961 begin_reversible_command (Operations::region_fill);
2963 for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
2965 boost::shared_ptr<Region> region ((*i)->region());
2967 boost::shared_ptr<Playlist> pl = region->playlist();
2969 if (end <= region->last_frame()) {
2973 double times = (double) (end - region->last_frame()) / (double) region->length();
2979 pl->clear_changes ();
2980 pl->add_region (RegionFactory::create (region, true), region->last_frame(), times);
2981 _session->add_command (new StatefulDiffCommand (pl));
2984 commit_reversible_command ();
2988 Editor::region_fill_selection ()
2990 if (clicked_routeview == 0 || !clicked_routeview->is_audio_track()) {
2994 if (selection->time.empty()) {
2998 boost::shared_ptr<Region> region = _regions->get_single_selection ();
3003 framepos_t start = selection->time[clicked_selection].start;
3004 framepos_t end = selection->time[clicked_selection].end;
3006 boost::shared_ptr<Playlist> playlist;
3008 if (selection->tracks.empty()) {
3012 framepos_t selection_length = end - start;
3013 float times = (float)selection_length / region->length();
3015 begin_reversible_command (Operations::fill_selection);
3017 TrackViewList ts = selection->tracks.filter_to_unique_playlists ();
3019 for (TrackViewList::iterator i = ts.begin(); i != ts.end(); ++i) {
3021 if ((playlist = (*i)->playlist()) == 0) {
3025 playlist->clear_changes ();
3026 playlist->add_region (RegionFactory::create (region, true), start, times);
3027 _session->add_command (new StatefulDiffCommand (playlist));
3030 commit_reversible_command ();
3034 Editor::set_region_sync_position ()
3036 set_sync_point (get_preferred_edit_position (), get_regions_from_selection_and_edit_point ());
3040 Editor::set_sync_point (framepos_t where, const RegionSelection& rs)
3042 bool in_command = false;
3044 for (RegionSelection::const_iterator r = rs.begin(); r != rs.end(); ++r) {
3046 if (!(*r)->region()->covers (where)) {
3050 boost::shared_ptr<Region> region ((*r)->region());
3053 begin_reversible_command (_("set sync point"));
3057 region->clear_changes ();
3058 region->set_sync_position (where);
3059 _session->add_command(new StatefulDiffCommand (region));
3063 commit_reversible_command ();
3067 /** Remove the sync positions of the selection */
3069 Editor::remove_region_sync ()
3071 RegionSelection rs = get_regions_from_selection_and_entered ();
3077 begin_reversible_command (_("remove region sync"));
3079 for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
3081 (*i)->region()->clear_changes ();
3082 (*i)->region()->clear_sync_position ();
3083 _session->add_command(new StatefulDiffCommand ((*i)->region()));
3086 commit_reversible_command ();
3090 Editor::naturalize_region ()
3092 RegionSelection rs = get_regions_from_selection_and_entered ();
3098 if (rs.size() > 1) {
3099 begin_reversible_command (_("move regions to original position"));
3101 begin_reversible_command (_("move region to original position"));
3104 for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
3105 (*i)->region()->clear_changes ();
3106 (*i)->region()->move_to_natural_position ();
3107 _session->add_command (new StatefulDiffCommand ((*i)->region()));
3110 commit_reversible_command ();
3114 Editor::align_regions (RegionPoint what)
3116 RegionSelection const rs = get_regions_from_selection_and_edit_point ();
3122 begin_reversible_command (_("align selection"));
3124 framepos_t const position = get_preferred_edit_position ();
3126 for (RegionSelection::const_iterator i = rs.begin(); i != rs.end(); ++i) {
3127 align_region_internal ((*i)->region(), what, position);
3130 commit_reversible_command ();
3133 struct RegionSortByTime {
3134 bool operator() (const RegionView* a, const RegionView* b) {
3135 return a->region()->position() < b->region()->position();
3140 Editor::align_regions_relative (RegionPoint point)
3142 RegionSelection const rs = get_regions_from_selection_and_edit_point ();
3148 framepos_t const position = get_preferred_edit_position ();
3150 framepos_t distance = 0;
3154 list<RegionView*> sorted;
3155 rs.by_position (sorted);
3157 boost::shared_ptr<Region> r ((*sorted.begin())->region());
3162 if (position > r->position()) {
3163 distance = position - r->position();
3165 distance = r->position() - position;
3171 if (position > r->last_frame()) {
3172 distance = position - r->last_frame();
3173 pos = r->position() + distance;
3175 distance = r->last_frame() - position;
3176 pos = r->position() - distance;
3182 pos = r->adjust_to_sync (position);
3183 if (pos > r->position()) {
3184 distance = pos - r->position();
3186 distance = r->position() - pos;
3192 if (pos == r->position()) {
3196 begin_reversible_command (_("align selection (relative)"));
3198 /* move first one specially */
3200 r->clear_changes ();
3201 r->set_position (pos);
3202 _session->add_command(new StatefulDiffCommand (r));
3204 /* move rest by the same amount */
3208 for (list<RegionView*>::iterator i = sorted.begin(); i != sorted.end(); ++i) {
3210 boost::shared_ptr<Region> region ((*i)->region());
3212 region->clear_changes ();
3215 region->set_position (region->position() + distance);
3217 region->set_position (region->position() - distance);
3220 _session->add_command(new StatefulDiffCommand (region));
3224 commit_reversible_command ();
3228 Editor::align_region (boost::shared_ptr<Region> region, RegionPoint point, framepos_t position)
3230 begin_reversible_command (_("align region"));
3231 align_region_internal (region, point, position);
3232 commit_reversible_command ();
3236 Editor::align_region_internal (boost::shared_ptr<Region> region, RegionPoint point, framepos_t position)
3238 region->clear_changes ();
3242 region->set_position (region->adjust_to_sync (position));
3246 if (position > region->length()) {
3247 region->set_position (position - region->length());
3252 region->set_position (position);
3256 _session->add_command(new StatefulDiffCommand (region));
3260 Editor::trim_region_front ()
3266 Editor::trim_region_back ()
3268 trim_region (false);
3272 Editor::trim_region (bool front)
3274 framepos_t where = get_preferred_edit_position();
3275 RegionSelection rs = get_regions_from_selection_and_edit_point ();
3281 begin_reversible_command (front ? _("trim front") : _("trim back"));
3283 for (list<RegionView*>::const_iterator i = rs.by_layer().begin(); i != rs.by_layer().end(); ++i) {
3284 if (!(*i)->region()->locked()) {
3286 (*i)->region()->clear_changes ();
3289 (*i)->region()->trim_front (where);
3290 maybe_locate_with_edit_preroll ( where );
3292 (*i)->region()->trim_end (where);
3293 maybe_locate_with_edit_preroll ( where );
3296 _session->add_command (new StatefulDiffCommand ((*i)->region()));
3300 commit_reversible_command ();
3303 /** Trim the end of the selected regions to the position of the edit cursor */
3305 Editor::trim_region_to_loop ()
3307 Location* loc = _session->locations()->auto_loop_location();
3311 trim_region_to_location (*loc, _("trim to loop"));
3315 Editor::trim_region_to_punch ()
3317 Location* loc = _session->locations()->auto_punch_location();
3321 trim_region_to_location (*loc, _("trim to punch"));
3325 Editor::trim_region_to_location (const Location& loc, const char* str)
3327 RegionSelection rs = get_regions_from_selection_and_entered ();
3329 begin_reversible_command (str);
3331 for (RegionSelection::iterator x = rs.begin(); x != rs.end(); ++x) {
3332 RegionView* rv = (*x);
3334 /* require region to span proposed trim */
3335 switch (rv->region()->coverage (loc.start(), loc.end())) {
3336 case Evoral::OverlapInternal:
3342 RouteTimeAxisView* tav = dynamic_cast<RouteTimeAxisView*> (&rv->get_time_axis_view());
3351 if (tav->track() != 0) {
3352 speed = tav->track()->speed();
3355 start = session_frame_to_track_frame (loc.start(), speed);
3356 end = session_frame_to_track_frame (loc.end(), speed);
3358 rv->region()->clear_changes ();
3359 rv->region()->trim_to (start, (end - start));
3360 _session->add_command(new StatefulDiffCommand (rv->region()));
3363 commit_reversible_command ();
3367 Editor::trim_region_to_previous_region_end ()
3369 return trim_to_region(false);
3373 Editor::trim_region_to_next_region_start ()
3375 return trim_to_region(true);
3379 Editor::trim_to_region(bool forward)
3381 RegionSelection rs = get_regions_from_selection_and_entered ();
3383 begin_reversible_command (_("trim to region"));
3385 boost::shared_ptr<Region> next_region;
3387 for (RegionSelection::iterator x = rs.begin(); x != rs.end(); ++x) {
3389 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (*x);
3395 AudioTimeAxisView* atav = dynamic_cast<AudioTimeAxisView*> (&arv->get_time_axis_view());
3403 if (atav->track() != 0) {
3404 speed = atav->track()->speed();
3408 boost::shared_ptr<Region> region = arv->region();
3409 boost::shared_ptr<Playlist> playlist (region->playlist());
3411 region->clear_changes ();
3415 next_region = playlist->find_next_region (region->first_frame(), Start, 1);
3421 region->trim_end((framepos_t) ( (next_region->first_frame() - 1) * speed));
3422 arv->region_changed (PropertyChange (ARDOUR::Properties::length));
3426 next_region = playlist->find_next_region (region->first_frame(), Start, 0);
3432 region->trim_front((framepos_t) ((next_region->last_frame() + 1) * speed));
3434 arv->region_changed (ARDOUR::bounds_change);
3437 _session->add_command(new StatefulDiffCommand (region));
3440 commit_reversible_command ();
3444 Editor::unfreeze_route ()
3446 if (clicked_routeview == 0 || !clicked_routeview->is_track()) {
3450 clicked_routeview->track()->unfreeze ();
3454 Editor::_freeze_thread (void* arg)
3456 return static_cast<Editor*>(arg)->freeze_thread ();
3460 Editor::freeze_thread ()
3462 /* create event pool because we may need to talk to the session */
3463 SessionEvent::create_per_thread_pool ("freeze events", 64);
3464 /* create per-thread buffers for process() tree to use */
3465 current_interthread_info->process_thread.get_buffers ();
3466 clicked_routeview->audio_track()->freeze_me (*current_interthread_info);
3467 current_interthread_info->done = true;
3468 current_interthread_info->process_thread.drop_buffers();
3473 Editor::freeze_route ()
3479 /* stop transport before we start. this is important */
3481 _session->request_transport_speed (0.0);
3483 /* wait for just a little while, because the above call is asynchronous */
3487 if (clicked_routeview == 0 || !clicked_routeview->is_audio_track()) {
3491 if (!clicked_routeview->track()->bounceable (clicked_routeview->track()->main_outs(), true)) {
3493 _("This track/bus cannot be frozen because the signal adds or loses channels before reaching the outputs.\n"
3494 "This is typically caused by plugins that generate stereo output from mono input or vice versa.")
3496 d.set_title (_("Cannot freeze"));
3501 if (clicked_routeview->track()->has_external_redirects()) {
3502 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"
3503 "Freezing will only process the signal as far as the first send/insert/return."),
3504 clicked_routeview->track()->name()), true, MESSAGE_INFO, BUTTONS_NONE, true);
3506 d.add_button (_("Freeze anyway"), Gtk::RESPONSE_OK);
3507 d.add_button (_("Don't freeze"), Gtk::RESPONSE_CANCEL);
3508 d.set_title (_("Freeze Limits"));
3510 int response = d.run ();
3513 case Gtk::RESPONSE_CANCEL:
3520 InterThreadInfo itt;
3521 current_interthread_info = &itt;
3523 InterthreadProgressWindow ipw (current_interthread_info, _("Freeze"), _("Cancel Freeze"));
3525 pthread_create_and_store (X_("freezer"), &itt.thread, _freeze_thread, this);
3527 set_canvas_cursor (_cursors->wait);
3529 while (!itt.done && !itt.cancel) {
3530 gtk_main_iteration ();
3533 current_interthread_info = 0;
3534 set_canvas_cursor (current_canvas_cursor);
3538 Editor::bounce_range_selection (bool replace, bool enable_processing)
3540 if (selection->time.empty()) {
3544 TrackSelection views = selection->tracks;
3546 for (TrackViewList::iterator i = views.begin(); i != views.end(); ++i) {
3548 if (enable_processing) {
3550 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (*i);
3552 if (rtv && rtv->track() && replace && enable_processing && !rtv->track()->bounceable (rtv->track()->main_outs(), false)) {
3554 _("You can't perform this operation because the processing of the signal "
3555 "will cause one or more of the tracks to end up with a region with more channels than this track has inputs.\n\n"
3556 "You can do this without processing, which is a different operation.")
3558 d.set_title (_("Cannot bounce"));
3565 framepos_t start = selection->time[clicked_selection].start;
3566 framepos_t end = selection->time[clicked_selection].end;
3567 framepos_t cnt = end - start + 1;
3569 begin_reversible_command (_("bounce range"));
3571 for (TrackViewList::iterator i = views.begin(); i != views.end(); ++i) {
3573 RouteTimeAxisView* rtv;
3575 if ((rtv = dynamic_cast<RouteTimeAxisView*> (*i)) == 0) {
3579 boost::shared_ptr<Playlist> playlist;
3581 if ((playlist = rtv->playlist()) == 0) {
3585 InterThreadInfo itt;
3587 playlist->clear_changes ();
3588 playlist->clear_owned_changes ();
3590 boost::shared_ptr<Region> r;
3592 if (enable_processing) {
3593 r = rtv->track()->bounce_range (start, start+cnt, itt, rtv->track()->main_outs(), false);
3595 r = rtv->track()->bounce_range (start, start+cnt, itt, boost::shared_ptr<Processor>(), false);
3603 list<AudioRange> ranges;
3604 ranges.push_back (AudioRange (start, start+cnt, 0));
3605 playlist->cut (ranges); // discard result
3606 playlist->add_region (r, start);
3609 vector<Command*> cmds;
3610 playlist->rdiff (cmds);
3611 _session->add_commands (cmds);
3613 _session->add_command (new StatefulDiffCommand (playlist));
3616 commit_reversible_command ();
3619 /** Delete selected regions, automation points or a time range */
3626 /** Cut selected regions, automation points or a time range */
3633 /** Copy selected regions, automation points or a time range */
3641 /** @return true if a Cut, Copy or Clear is possible */
3643 Editor::can_cut_copy () const
3645 switch (effective_mouse_mode()) {
3648 if (!selection->regions.empty() || !selection->points.empty()) {
3654 if (!selection->time.empty()) {
3667 /** Cut, copy or clear selected regions, automation points or a time range.
3668 * @param op Operation (Cut, Copy or Clear)
3671 Editor::cut_copy (CutCopyOp op)
3673 /* only cancel selection if cut/copy is successful.*/
3679 opname = _("delete");
3688 opname = _("clear");
3692 /* if we're deleting something, and the mouse is still pressed,
3693 the thing we started a drag for will be gone when we release
3694 the mouse button(s). avoid this. see part 2 at the end of
3698 if (op == Delete || op == Cut || op == Clear) {
3699 if (_drags->active ()) {
3704 if ( op != Clear ) //"Delete" doesn't change copy/paste buf
3705 cut_buffer->clear ();
3707 if (entered_marker) {
3709 /* cut/delete op while pointing at a marker */
3712 Location* loc = find_location_from_marker (entered_marker, ignored);
3714 if (_session && loc) {
3715 Glib::signal_idle().connect (sigc::bind (sigc::mem_fun(*this, &Editor::really_remove_marker), loc));
3722 if (internal_editing()) {
3724 switch (effective_mouse_mode()) {
3737 /* we only want to cut regions if some are selected */
3739 if (!selection->regions.empty()) {
3740 rs = get_regions_from_selection ();
3743 switch (effective_mouse_mode()) {
3746 //find regions's gain line
3747 AudioRegionView *rview = dynamic_cast<AudioRegionView*>(clicked_regionview);
3748 AutomationTimeAxisView *tview = dynamic_cast<AutomationTimeAxisView*>(clicked_trackview);
3750 AudioRegionGainLine *line = rview->get_gain_line();
3753 //cut region gain points in the selection
3754 AutomationList& alist (line->the_list());
3755 XMLNode &before = alist.get_state();
3756 AutomationList* what_we_got = 0;
3757 if ((what_we_got = alist.cut (selection->time.front().start - rview->audio_region()->position(), selection->time.front().end - rview->audio_region()->position())) != 0) {
3758 session->add_command(new MementoCommand<AutomationList>(alist, &before, &alist.get_state()));
3763 rview->set_envelope_visible(true);
3764 rview->audio_region()->set_envelope_active(true);
3767 AutomationLine *line = *(tview->lines.begin());
3770 //cut auto points in the selection
3771 AutomationList& alist (line->the_list());
3772 XMLNode &before = alist.get_state();
3773 AutomationList* what_we_got = 0;
3774 if ((what_we_got = alist.cut (selection->time.front().start, selection->time.front().end)) != 0) {
3775 session->add_command(new MementoCommand<AutomationList>(alist, &before, &alist.get_state()));
3785 if (!rs.empty() || !selection->points.empty()) {
3786 begin_reversible_command (opname + _(" objects"));
3789 cut_copy_regions (op, rs);
3791 if (op == Cut || op == Delete) {
3792 selection->clear_regions ();
3796 if (!selection->points.empty()) {
3797 cut_copy_points (op);
3799 if (op == Cut || op == Delete) {
3800 selection->clear_points ();
3804 commit_reversible_command ();
3808 if (selection->time.empty()) {
3809 framepos_t start, end;
3810 if (!get_edit_op_range (start, end)) {
3813 selection->set (start, end);
3816 begin_reversible_command (opname + _(" range"));
3817 cut_copy_ranges (op);
3818 commit_reversible_command ();
3820 if (op == Cut || op == Delete) {
3821 selection->clear_time ();
3831 if (op == Delete || op == Cut || op == Clear) {
3836 struct AutomationRecord {
3837 AutomationRecord () : state (0) {}
3838 AutomationRecord (XMLNode* s) : state (s) {}
3840 XMLNode* state; ///< state before any operation
3841 boost::shared_ptr<Evoral::ControlList> copy; ///< copied events for the cut buffer
3844 /** Cut, copy or clear selected automation points.
3845 * @param op Operation (Cut, Copy or Clear)
3848 Editor::cut_copy_points (CutCopyOp op)
3850 if (selection->points.empty ()) {
3854 /* XXX: not ideal, as there may be more than one track involved in the point selection */
3855 _last_cut_copy_source_track = &selection->points.front()->line().trackview;
3857 /* Keep a record of the AutomationLists that we end up using in this operation */
3858 typedef std::map<boost::shared_ptr<AutomationList>, AutomationRecord> Lists;
3861 /* Go through all selected points, making an AutomationRecord for each distinct AutomationList */
3862 for (PointSelection::iterator i = selection->points.begin(); i != selection->points.end(); ++i) {
3863 boost::shared_ptr<AutomationList> al = (*i)->line().the_list();
3864 if (lists.find (al) == lists.end ()) {
3865 /* We haven't seen this list yet, so make a record for it. This includes
3866 taking a copy of its current state, in case this is needed for undo later.
3868 lists[al] = AutomationRecord (&al->get_state ());
3872 if (op == Cut || op == Copy) {
3873 /* This operation will involve putting things in the cut buffer, so create an empty
3874 ControlList for each of our source lists to put the cut buffer data in.
3876 for (Lists::iterator i = lists.begin(); i != lists.end(); ++i) {
3877 i->second.copy = i->first->create (i->first->parameter ());
3880 /* Add all selected points to the relevant copy ControlLists */
3881 for (PointSelection::iterator i = selection->points.begin(); i != selection->points.end(); ++i) {
3882 boost::shared_ptr<AutomationList> al = (*i)->line().the_list();
3883 AutomationList::const_iterator j = (*i)->model ();
3884 lists[al].copy->add ((*j)->when, (*j)->value);
3887 for (Lists::iterator i = lists.begin(); i != lists.end(); ++i) {
3888 /* Correct this copy list so that it starts at time 0 */
3889 double const start = i->second.copy->front()->when;
3890 for (AutomationList::iterator j = i->second.copy->begin(); j != i->second.copy->end(); ++j) {
3891 (*j)->when -= start;
3894 /* And add it to the cut buffer */
3895 cut_buffer->add (i->second.copy);
3899 if (op == Delete || op == Cut) {
3900 /* This operation needs to remove things from the main AutomationList, so do that now */
3902 for (Lists::iterator i = lists.begin(); i != lists.end(); ++i) {
3903 i->first->freeze ();
3906 /* Remove each selected point from its AutomationList */
3907 for (PointSelection::iterator i = selection->points.begin(); i != selection->points.end(); ++i) {
3908 boost::shared_ptr<AutomationList> al = (*i)->line().the_list();
3909 al->erase ((*i)->model ());
3912 /* Thaw the lists and add undo records for them */
3913 for (Lists::iterator i = lists.begin(); i != lists.end(); ++i) {
3914 boost::shared_ptr<AutomationList> al = i->first;
3916 _session->add_command (new MementoCommand<AutomationList> (*al.get(), i->second.state, &(al->get_state ())));
3921 /** Cut, copy or clear selected automation points.
3922 * @param op Operation (Cut, Copy or Clear)
3925 Editor::cut_copy_midi (CutCopyOp op)
3927 for (MidiRegionSelection::iterator i = selection->midi_regions.begin(); i != selection->midi_regions.end(); ++i) {
3928 MidiRegionView* mrv = *i;
3929 mrv->cut_copy_clear (op);
3935 struct lt_playlist {
3936 bool operator () (const PlaylistState& a, const PlaylistState& b) {
3937 return a.playlist < b.playlist;
3941 struct PlaylistMapping {
3943 boost::shared_ptr<Playlist> pl;
3945 PlaylistMapping (TimeAxisView* tvp) : tv (tvp) {}
3948 /** Remove `clicked_regionview' */
3950 Editor::remove_clicked_region ()
3952 if (clicked_routeview == 0 || clicked_regionview == 0) {
3956 boost::shared_ptr<Playlist> playlist = clicked_routeview->playlist();
3958 begin_reversible_command (_("remove region"));
3959 playlist->clear_changes ();
3960 playlist->clear_owned_changes ();
3961 playlist->remove_region (clicked_regionview->region());
3963 /* We might have removed regions, which alters other regions' layering_index,
3964 so we need to do a recursive diff here.
3966 vector<Command*> cmds;
3967 playlist->rdiff (cmds);
3968 _session->add_commands (cmds);
3970 _session->add_command(new StatefulDiffCommand (playlist));
3971 commit_reversible_command ();
3975 /** Remove the selected regions */
3977 Editor::remove_selected_regions ()
3979 RegionSelection rs = get_regions_from_selection_and_entered ();
3981 if (!_session || rs.empty()) {
3985 begin_reversible_command (_("remove region"));
3987 list<boost::shared_ptr<Region> > regions_to_remove;
3989 for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
3990 // we can't just remove the region(s) in this loop because
3991 // this removes them from the RegionSelection, and they thus
3992 // disappear from underneath the iterator, and the ++i above
3993 // SEGVs in a puzzling fashion.
3995 // so, first iterate over the regions to be removed from rs and
3996 // add them to the regions_to_remove list, and then
3997 // iterate over the list to actually remove them.
3999 regions_to_remove.push_back ((*i)->region());
4002 vector<boost::shared_ptr<Playlist> > playlists;
4004 for (list<boost::shared_ptr<Region> >::iterator rl = regions_to_remove.begin(); rl != regions_to_remove.end(); ++rl) {
4006 boost::shared_ptr<Playlist> playlist = (*rl)->playlist();
4009 // is this check necessary?
4013 /* get_regions_from_selection_and_entered() guarantees that
4014 the playlists involved are unique, so there is no need
4018 playlists.push_back (playlist);
4020 playlist->clear_changes ();
4021 playlist->clear_owned_changes ();
4022 playlist->freeze ();
4023 playlist->remove_region (*rl);
4026 vector<boost::shared_ptr<Playlist> >::iterator pl;
4028 for (pl = playlists.begin(); pl != playlists.end(); ++pl) {
4031 /* We might have removed regions, which alters other regions' layering_index,
4032 so we need to do a recursive diff here.
4034 vector<Command*> cmds;
4035 (*pl)->rdiff (cmds);
4036 _session->add_commands (cmds);
4038 _session->add_command(new StatefulDiffCommand (*pl));
4041 commit_reversible_command ();
4044 /** Cut, copy or clear selected regions.
4045 * @param op Operation (Cut, Copy or Clear)
4048 Editor::cut_copy_regions (CutCopyOp op, RegionSelection& rs)
4050 /* we can't use a std::map here because the ordering is important, and we can't trivially sort
4051 a map when we want ordered access to both elements. i think.
4054 vector<PlaylistMapping> pmap;
4056 framepos_t first_position = max_framepos;
4058 typedef set<boost::shared_ptr<Playlist> > FreezeList;
4059 FreezeList freezelist;
4061 /* get ordering correct before we cut/copy */
4063 rs.sort_by_position_and_track ();
4065 for (RegionSelection::iterator x = rs.begin(); x != rs.end(); ++x) {
4067 first_position = min ((framepos_t) (*x)->region()->position(), first_position);
4069 if (op == Cut || op == Clear || op == Delete) {
4070 boost::shared_ptr<Playlist> pl = (*x)->region()->playlist();
4073 FreezeList::iterator fl;
4075 // only take state if this is a new playlist.
4076 for (fl = freezelist.begin(); fl != freezelist.end(); ++fl) {
4082 if (fl == freezelist.end()) {
4083 pl->clear_changes();
4084 pl->clear_owned_changes ();
4086 freezelist.insert (pl);
4091 TimeAxisView* tv = &(*x)->get_time_axis_view();
4092 vector<PlaylistMapping>::iterator z;
4094 for (z = pmap.begin(); z != pmap.end(); ++z) {
4095 if ((*z).tv == tv) {
4100 if (z == pmap.end()) {
4101 pmap.push_back (PlaylistMapping (tv));
4105 for (RegionSelection::iterator x = rs.begin(); x != rs.end(); ) {
4107 boost::shared_ptr<Playlist> pl = (*x)->region()->playlist();
4110 /* region not yet associated with a playlist (e.g. unfinished
4117 TimeAxisView& tv = (*x)->get_time_axis_view();
4118 boost::shared_ptr<Playlist> npl;
4119 RegionSelection::iterator tmp;
4126 vector<PlaylistMapping>::iterator z;
4128 for (z = pmap.begin(); z != pmap.end(); ++z) {
4129 if ((*z).tv == &tv) {
4134 assert (z != pmap.end());
4137 npl = PlaylistFactory::create (pl->data_type(), *_session, "cutlist", true);
4145 boost::shared_ptr<Region> r = (*x)->region();
4146 boost::shared_ptr<Region> _xx;
4152 pl->remove_region (r);
4156 _xx = RegionFactory::create (r);
4157 npl->add_region (_xx, r->position() - first_position);
4158 pl->remove_region (r);
4162 /* copy region before adding, so we're not putting same object into two different playlists */
4163 npl->add_region (RegionFactory::create (r), r->position() - first_position);
4167 pl->remove_region (r);
4176 list<boost::shared_ptr<Playlist> > foo;
4178 /* the pmap is in the same order as the tracks in which selected regions occured */
4180 for (vector<PlaylistMapping>::iterator i = pmap.begin(); i != pmap.end(); ++i) {
4183 foo.push_back ((*i).pl);
4188 cut_buffer->set (foo);
4192 _last_cut_copy_source_track = 0;
4194 _last_cut_copy_source_track = pmap.front().tv;
4198 for (FreezeList::iterator pl = freezelist.begin(); pl != freezelist.end(); ++pl) {
4201 /* We might have removed regions, which alters other regions' layering_index,
4202 so we need to do a recursive diff here.
4204 vector<Command*> cmds;
4205 (*pl)->rdiff (cmds);
4206 _session->add_commands (cmds);
4208 _session->add_command (new StatefulDiffCommand (*pl));
4213 Editor::cut_copy_ranges (CutCopyOp op)
4215 TrackViewList ts = selection->tracks.filter_to_unique_playlists ();
4217 /* Sort the track selection now, so that it if is used, the playlists
4218 selected by the calls below to cut_copy_clear are in the order that
4219 their tracks appear in the editor. This makes things like paste
4220 of ranges work properly.
4223 sort_track_selection (ts);
4226 if (!entered_track) {
4229 ts.push_back (entered_track);
4232 for (TrackViewList::iterator i = ts.begin(); i != ts.end(); ++i) {
4233 (*i)->cut_copy_clear (*selection, op);
4238 Editor::paste (float times, bool from_context)
4240 DEBUG_TRACE (DEBUG::CutNPaste, "paste to preferred edit pos\n");
4242 paste_internal (get_preferred_edit_position (false, from_context), times);
4246 Editor::mouse_paste ()
4251 if (!mouse_frame (where, ignored)) {
4256 paste_internal (where, 1);
4260 Editor::paste_internal (framepos_t position, float times)
4262 DEBUG_TRACE (DEBUG::CutNPaste, string_compose ("apparent paste position is %1\n", position));
4264 if (internal_editing()) {
4265 if (cut_buffer->midi_notes.empty()) {
4269 if (cut_buffer->empty()) {
4274 if (position == max_framepos) {
4275 position = get_preferred_edit_position();
4276 DEBUG_TRACE (DEBUG::CutNPaste, string_compose ("preferred edit position is %1\n", position));
4280 TrackViewList::iterator i;
4283 /* get everything in the correct order */
4285 if (_edit_point == Editing::EditAtMouse && entered_track) {
4286 /* With the mouse edit point, paste onto the track under the mouse */
4287 ts.push_back (entered_track);
4288 } else if (!selection->tracks.empty()) {
4289 /* Otherwise, if there are some selected tracks, paste to them */
4290 ts = selection->tracks.filter_to_unique_playlists ();
4291 sort_track_selection (ts);
4292 } else if (_last_cut_copy_source_track) {
4293 /* Otherwise paste to the track that the cut/copy came from;
4294 see discussion in mantis #3333.
4296 ts.push_back (_last_cut_copy_source_track);
4299 if (internal_editing ()) {
4301 /* undo/redo is handled by individual tracks/regions */
4303 for (nth = 0, i = ts.begin(); i != ts.end(); ++i, ++nth) {
4306 RegionSelection::iterator r;
4307 MidiNoteSelection::iterator cb;
4309 get_regions_at (rs, position, ts);
4311 for (cb = cut_buffer->midi_notes.begin(), r = rs.begin();
4312 cb != cut_buffer->midi_notes.end() && r != rs.end(); ++r) {
4313 MidiRegionView* mrv = dynamic_cast<MidiRegionView*> (*r);
4315 mrv->paste (position, times, **cb);
4323 /* we do redo (do you do voodoo?) */
4325 begin_reversible_command (Operations::paste);
4327 for (nth = 0, i = ts.begin(); i != ts.end(); ++i, ++nth) {
4328 (*i)->paste (position, times, *cut_buffer, nth);
4331 commit_reversible_command ();
4336 Editor::duplicate_some_regions (RegionSelection& regions, float times)
4338 boost::shared_ptr<Playlist> playlist;
4339 RegionSelection sel = regions; // clear (below) may clear the argument list if its the current region selection
4340 RegionSelection foo;
4342 framepos_t const start_frame = regions.start ();
4343 framepos_t const end_frame = regions.end_frame ();
4345 begin_reversible_command (Operations::duplicate_region);
4347 selection->clear_regions ();
4349 for (RegionSelection::iterator i = sel.begin(); i != sel.end(); ++i) {
4351 boost::shared_ptr<Region> r ((*i)->region());
4353 TimeAxisView& tv = (*i)->get_time_axis_view();
4354 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (&tv);
4355 latest_regionviews.clear ();
4356 sigc::connection c = rtv->view()->RegionViewAdded.connect (sigc::mem_fun(*this, &Editor::collect_new_region_view));
4358 playlist = (*i)->region()->playlist();
4359 playlist->clear_changes ();
4360 playlist->duplicate (r, end_frame + (r->first_frame() - start_frame), times);
4361 _session->add_command(new StatefulDiffCommand (playlist));
4365 foo.insert (foo.end(), latest_regionviews.begin(), latest_regionviews.end());
4368 commit_reversible_command ();
4371 selection->set (foo);
4376 Editor::duplicate_selection (float times)
4378 if (selection->time.empty() || selection->tracks.empty()) {
4382 boost::shared_ptr<Playlist> playlist;
4383 vector<boost::shared_ptr<Region> > new_regions;
4384 vector<boost::shared_ptr<Region> >::iterator ri;
4386 create_region_from_selection (new_regions);
4388 if (new_regions.empty()) {
4392 begin_reversible_command (_("duplicate selection"));
4394 ri = new_regions.begin();
4396 TrackViewList ts = selection->tracks.filter_to_unique_playlists ();
4398 for (TrackViewList::iterator i = ts.begin(); i != ts.end(); ++i) {
4399 if ((playlist = (*i)->playlist()) == 0) {
4402 playlist->clear_changes ();
4403 playlist->duplicate (*ri, selection->time[clicked_selection].end, times);
4404 _session->add_command (new StatefulDiffCommand (playlist));
4407 if (ri == new_regions.end()) {
4412 commit_reversible_command ();
4415 /** Reset all selected points to the relevant default value */
4417 Editor::reset_point_selection ()
4419 for (PointSelection::iterator i = selection->points.begin(); i != selection->points.end(); ++i) {
4420 ARDOUR::AutomationList::iterator j = (*i)->model ();
4421 (*j)->value = (*i)->line().the_list()->default_value ();
4426 Editor::center_playhead ()
4428 float page = _canvas_width * frames_per_unit;
4429 center_screen_internal (playhead_cursor->current_frame, page);
4433 Editor::center_edit_point ()
4435 float page = _canvas_width * frames_per_unit;
4436 center_screen_internal (get_preferred_edit_position(), page);
4439 /** Caller must begin and commit a reversible command */
4441 Editor::clear_playlist (boost::shared_ptr<Playlist> playlist)
4443 playlist->clear_changes ();
4445 _session->add_command (new StatefulDiffCommand (playlist));
4449 Editor::nudge_track (bool use_edit, bool forwards)
4451 boost::shared_ptr<Playlist> playlist;
4452 framepos_t distance;
4453 framepos_t next_distance;
4457 start = get_preferred_edit_position();
4462 if ((distance = get_nudge_distance (start, next_distance)) == 0) {
4466 if (selection->tracks.empty()) {
4470 begin_reversible_command (_("nudge track"));
4472 TrackViewList ts = selection->tracks.filter_to_unique_playlists ();
4474 for (TrackViewList::iterator i = ts.begin(); i != ts.end(); ++i) {
4476 if ((playlist = (*i)->playlist()) == 0) {
4480 playlist->clear_changes ();
4481 playlist->clear_owned_changes ();
4483 playlist->nudge_after (start, distance, forwards);
4485 vector<Command*> cmds;
4487 playlist->rdiff (cmds);
4488 _session->add_commands (cmds);
4490 _session->add_command (new StatefulDiffCommand (playlist));
4493 commit_reversible_command ();
4497 Editor::remove_last_capture ()
4499 vector<string> choices;
4506 if (Config->get_verify_remove_last_capture()) {
4507 prompt = _("Do you really want to destroy the last capture?"
4508 "\n(This is destructive and cannot be undone)");
4510 choices.push_back (_("No, do nothing."));
4511 choices.push_back (_("Yes, destroy it."));
4513 Gtkmm2ext::Choice prompter (_("Destroy last capture"), prompt, choices);
4515 if (prompter.run () == 1) {
4516 _session->remove_last_capture ();
4517 _regions->redisplay ();
4521 _session->remove_last_capture();
4522 _regions->redisplay ();
4527 Editor::normalize_region ()
4533 RegionSelection rs = get_regions_from_selection_and_entered ();
4539 NormalizeDialog dialog (rs.size() > 1);
4541 if (dialog.run () == RESPONSE_CANCEL) {
4545 set_canvas_cursor (_cursors->wait);
4548 /* XXX: should really only count audio regions here */
4549 int const regions = rs.size ();
4551 /* Make a list of the selected audio regions' maximum amplitudes, and also
4552 obtain the maximum amplitude of them all.
4554 list<double> max_amps;
4556 for (RegionSelection::const_iterator i = rs.begin(); i != rs.end(); ++i) {
4557 AudioRegionView const * arv = dynamic_cast<AudioRegionView const *> (*i);
4559 dialog.descend (1.0 / regions);
4560 double const a = arv->audio_region()->maximum_amplitude (&dialog);
4563 /* the user cancelled the operation */
4564 set_canvas_cursor (current_canvas_cursor);
4568 max_amps.push_back (a);
4569 max_amp = max (max_amp, a);
4574 begin_reversible_command (_("normalize"));
4576 list<double>::const_iterator a = max_amps.begin ();
4578 for (RegionSelection::iterator r = rs.begin(); r != rs.end(); ++r) {
4579 AudioRegionView* const arv = dynamic_cast<AudioRegionView*> (*r);
4584 arv->region()->clear_changes ();
4586 double const amp = dialog.normalize_individually() ? *a : max_amp;
4588 arv->audio_region()->normalize (amp, dialog.target ());
4589 _session->add_command (new StatefulDiffCommand (arv->region()));
4594 commit_reversible_command ();
4595 set_canvas_cursor (current_canvas_cursor);
4600 Editor::reset_region_scale_amplitude ()
4606 RegionSelection rs = get_regions_from_selection_and_entered ();
4612 begin_reversible_command ("reset gain");
4614 for (RegionSelection::iterator r = rs.begin(); r != rs.end(); ++r) {
4615 AudioRegionView* const arv = dynamic_cast<AudioRegionView*>(*r);
4618 arv->region()->clear_changes ();
4619 arv->audio_region()->set_scale_amplitude (1.0f);
4620 _session->add_command (new StatefulDiffCommand (arv->region()));
4623 commit_reversible_command ();
4627 Editor::adjust_region_gain (bool up)
4629 RegionSelection rs = get_regions_from_selection_and_entered ();
4631 if (!_session || rs.empty()) {
4635 begin_reversible_command ("adjust region gain");
4637 for (RegionSelection::iterator r = rs.begin(); r != rs.end(); ++r) {
4638 AudioRegionView* const arv = dynamic_cast<AudioRegionView*>(*r);
4643 arv->region()->clear_changes ();
4645 double dB = accurate_coefficient_to_dB (arv->audio_region()->scale_amplitude ());
4653 arv->audio_region()->set_scale_amplitude (dB_to_coefficient (dB));
4654 _session->add_command (new StatefulDiffCommand (arv->region()));
4657 commit_reversible_command ();
4662 Editor::reverse_region ()
4668 Reverse rev (*_session);
4669 apply_filter (rev, _("reverse regions"));
4673 Editor::strip_region_silence ()
4679 RegionSelection rs = get_regions_from_selection_and_entered ();
4685 std::list<RegionView*> audio_only;
4687 for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
4688 AudioRegionView* const arv = dynamic_cast<AudioRegionView*> (*i);
4690 audio_only.push_back (arv);
4694 StripSilenceDialog d (_session, audio_only);
4695 int const r = d.run ();
4699 if (r == Gtk::RESPONSE_OK) {
4700 ARDOUR::AudioIntervalMap silences;
4701 d.silences (silences);
4702 StripSilence s (*_session, silences, d.fade_length());
4703 apply_filter (s, _("strip silence"), &d);
4708 Editor::apply_midi_note_edit_op_to_region (MidiOperator& op, MidiRegionView& mrv)
4710 Evoral::Sequence<Evoral::MusicalTime>::Notes selected;
4711 mrv.selection_as_notelist (selected, true);
4713 vector<Evoral::Sequence<Evoral::MusicalTime>::Notes> v;
4714 v.push_back (selected);
4716 framepos_t pos_frames = mrv.midi_region()->position();
4717 double pos_beats = _session->tempo_map().framewalk_to_beats(0, pos_frames);
4719 return op (mrv.midi_region()->model(), pos_beats, v);
4723 Editor::apply_midi_note_edit_op (MidiOperator& op)
4727 RegionSelection rs = get_regions_from_selection_and_entered ();
4733 begin_reversible_command (op.name ());
4735 for (RegionSelection::iterator r = rs.begin(); r != rs.end(); ) {
4736 RegionSelection::iterator tmp = r;
4739 MidiRegionView* const mrv = dynamic_cast<MidiRegionView*> (*r);
4742 cmd = apply_midi_note_edit_op_to_region (op, *mrv);
4745 _session->add_command (cmd);
4752 commit_reversible_command ();
4756 Editor::fork_region ()
4758 RegionSelection rs = get_regions_from_selection_and_entered ();
4764 begin_reversible_command (_("Fork Region(s)"));
4766 set_canvas_cursor (_cursors->wait);
4769 for (RegionSelection::iterator r = rs.begin(); r != rs.end(); ) {
4770 RegionSelection::iterator tmp = r;
4773 MidiRegionView* const mrv = dynamic_cast<MidiRegionView*>(*r);
4776 boost::shared_ptr<Playlist> playlist = mrv->region()->playlist();
4777 boost::shared_ptr<MidiRegion> newregion = mrv->midi_region()->clone ();
4779 playlist->clear_changes ();
4780 playlist->replace_region (mrv->region(), newregion, mrv->region()->position());
4781 _session->add_command(new StatefulDiffCommand (playlist));
4787 commit_reversible_command ();
4789 set_canvas_cursor (current_canvas_cursor);
4793 Editor::quantize_region ()
4795 int selected_midi_region_cnt = 0;
4801 RegionSelection rs = get_regions_from_selection_and_entered ();
4807 for (RegionSelection::iterator r = rs.begin(); r != rs.end(); ++r) {
4808 MidiRegionView* const mrv = dynamic_cast<MidiRegionView*> (*r);
4810 selected_midi_region_cnt++;
4814 if (selected_midi_region_cnt == 0) {
4818 QuantizeDialog* qd = new QuantizeDialog (*this);
4821 const int r = qd->run ();
4824 if (r == Gtk::RESPONSE_OK) {
4825 Quantize quant (*_session, qd->snap_start(), qd->snap_end(),
4826 qd->start_grid_size(), qd->end_grid_size(),
4827 qd->strength(), qd->swing(), qd->threshold());
4829 apply_midi_note_edit_op (quant);
4834 Editor::insert_patch_change (bool from_context)
4836 RegionSelection rs = get_regions_from_selection_and_entered ();
4842 const framepos_t p = get_preferred_edit_position (false, from_context);
4844 /* XXX: bit of a hack; use the MIDNAM from the first selected region;
4845 there may be more than one, but the PatchChangeDialog can only offer
4846 one set of patch menus.
4848 MidiRegionView* first = dynamic_cast<MidiRegionView*> (rs.front ());
4850 Evoral::PatchChange<Evoral::MusicalTime> empty (0, 0, 0, 0);
4851 PatchChangeDialog d (0, _session, empty, first->instrument_info(), Gtk::Stock::ADD);
4853 if (d.run() == RESPONSE_CANCEL) {
4857 for (RegionSelection::iterator i = rs.begin (); i != rs.end(); ++i) {
4858 MidiRegionView* const mrv = dynamic_cast<MidiRegionView*> (*i);
4860 if (p >= mrv->region()->first_frame() && p <= mrv->region()->last_frame()) {
4861 mrv->add_patch_change (p - mrv->region()->position(), d.patch ());
4868 Editor::apply_filter (Filter& filter, string command, ProgressReporter* progress)
4870 RegionSelection rs = get_regions_from_selection_and_entered ();
4876 begin_reversible_command (command);
4878 set_canvas_cursor (_cursors->wait);
4882 int const N = rs.size ();
4884 for (RegionSelection::iterator r = rs.begin(); r != rs.end(); ) {
4885 RegionSelection::iterator tmp = r;
4888 AudioRegionView* const arv = dynamic_cast<AudioRegionView*>(*r);
4890 boost::shared_ptr<Playlist> playlist = arv->region()->playlist();
4893 progress->descend (1.0 / N);
4896 if (arv->audio_region()->apply (filter, progress) == 0) {
4898 playlist->clear_changes ();
4899 playlist->clear_owned_changes ();
4901 if (filter.results.empty ()) {
4903 /* no regions returned; remove the old one */
4904 playlist->remove_region (arv->region ());
4908 std::vector<boost::shared_ptr<Region> >::iterator res = filter.results.begin ();
4910 /* first region replaces the old one */
4911 playlist->replace_region (arv->region(), *res, (*res)->position());
4915 while (res != filter.results.end()) {
4916 playlist->add_region (*res, (*res)->position());
4922 /* We might have removed regions, which alters other regions' layering_index,
4923 so we need to do a recursive diff here.
4925 vector<Command*> cmds;
4926 playlist->rdiff (cmds);
4927 _session->add_commands (cmds);
4929 _session->add_command(new StatefulDiffCommand (playlist));
4935 progress->ascend ();
4943 commit_reversible_command ();
4946 set_canvas_cursor (current_canvas_cursor);
4950 Editor::external_edit_region ()
4956 Editor::reset_region_gain_envelopes ()
4958 RegionSelection rs = get_regions_from_selection_and_entered ();
4960 if (!_session || rs.empty()) {
4964 _session->begin_reversible_command (_("reset region gain"));
4966 for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
4967 AudioRegionView* const arv = dynamic_cast<AudioRegionView*>(*i);
4969 boost::shared_ptr<AutomationList> alist (arv->audio_region()->envelope());
4970 XMLNode& before (alist->get_state());
4972 arv->audio_region()->set_default_envelope ();
4973 _session->add_command (new MementoCommand<AutomationList>(*arv->audio_region()->envelope().get(), &before, &alist->get_state()));
4977 _session->commit_reversible_command ();
4981 Editor::set_region_gain_visibility (RegionView* rv)
4983 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (rv);
4985 arv->update_envelope_visibility();
4990 Editor::set_gain_envelope_visibility ()
4996 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
4997 AudioTimeAxisView* v = dynamic_cast<AudioTimeAxisView*>(*i);
4999 v->audio_view()->foreach_regionview (sigc::mem_fun (this, &Editor::set_region_gain_visibility));
5005 Editor::toggle_gain_envelope_active ()
5007 if (_ignore_region_action) {
5011 RegionSelection rs = get_regions_from_selection_and_entered ();
5013 if (!_session || rs.empty()) {
5017 _session->begin_reversible_command (_("region gain envelope active"));
5019 for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
5020 AudioRegionView* const arv = dynamic_cast<AudioRegionView*>(*i);
5022 arv->region()->clear_changes ();
5023 arv->audio_region()->set_envelope_active (!arv->audio_region()->envelope_active());
5024 _session->add_command (new StatefulDiffCommand (arv->region()));
5028 _session->commit_reversible_command ();
5032 Editor::toggle_region_lock ()
5034 if (_ignore_region_action) {
5038 RegionSelection rs = get_regions_from_selection_and_entered ();
5040 if (!_session || rs.empty()) {
5044 _session->begin_reversible_command (_("toggle region lock"));
5046 for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
5047 (*i)->region()->clear_changes ();
5048 (*i)->region()->set_locked (!(*i)->region()->locked());
5049 _session->add_command (new StatefulDiffCommand ((*i)->region()));
5052 _session->commit_reversible_command ();
5056 Editor::toggle_region_lock_style ()
5058 if (_ignore_region_action) {
5062 RegionSelection rs = get_regions_from_selection_and_entered ();
5064 if (!_session || rs.empty()) {
5068 _session->begin_reversible_command (_("region lock style"));
5070 for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
5071 (*i)->region()->clear_changes ();
5072 PositionLockStyle const ns = (*i)->region()->position_lock_style() == AudioTime ? MusicTime : AudioTime;
5073 (*i)->region()->set_position_lock_style (ns);
5074 _session->add_command (new StatefulDiffCommand ((*i)->region()));
5077 _session->commit_reversible_command ();
5081 Editor::toggle_opaque_region ()
5083 if (_ignore_region_action) {
5087 RegionSelection rs = get_regions_from_selection_and_entered ();
5089 if (!_session || rs.empty()) {
5093 _session->begin_reversible_command (_("change region opacity"));
5095 for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
5096 (*i)->region()->clear_changes ();
5097 (*i)->region()->set_opaque (!(*i)->region()->opaque());
5098 _session->add_command (new StatefulDiffCommand ((*i)->region()));
5101 _session->commit_reversible_command ();
5105 Editor::toggle_record_enable ()
5107 bool new_state = false;
5109 for (TrackSelection::iterator i = selection->tracks.begin(); i != selection->tracks.end(); ++i) {
5110 RouteTimeAxisView *rtav = dynamic_cast<RouteTimeAxisView *>(*i);
5113 if (!rtav->is_track())
5117 new_state = !rtav->track()->record_enabled();
5121 rtav->track()->set_record_enabled (new_state, this);
5126 Editor::toggle_solo ()
5128 bool new_state = false;
5130 boost::shared_ptr<RouteList> rl (new RouteList);
5132 for (TrackSelection::iterator i = selection->tracks.begin(); i != selection->tracks.end(); ++i) {
5133 RouteTimeAxisView *rtav = dynamic_cast<RouteTimeAxisView *>(*i);
5140 new_state = !rtav->route()->soloed ();
5144 rl->push_back (rtav->route());
5147 _session->set_solo (rl, new_state, Session::rt_cleanup, true);
5151 Editor::toggle_mute ()
5153 bool new_state = false;
5155 boost::shared_ptr<RouteList> rl (new RouteList);
5157 for (TrackSelection::iterator i = selection->tracks.begin(); i != selection->tracks.end(); ++i) {
5158 RouteTimeAxisView *rtav = dynamic_cast<RouteTimeAxisView *>(*i);
5165 new_state = !rtav->route()->muted();
5169 rl->push_back (rtav->route());
5172 _session->set_mute (rl, new_state, Session::rt_cleanup, true);
5176 Editor::toggle_solo_isolate ()
5181 Editor::set_fade_length (bool in)
5183 RegionSelection rs = get_regions_from_selection_and_entered ();
5189 /* we need a region to measure the offset from the start */
5191 RegionView* rv = rs.front ();
5193 framepos_t pos = get_preferred_edit_position();
5197 if (pos > rv->region()->last_frame() || pos < rv->region()->first_frame()) {
5198 /* edit point is outside the relevant region */
5203 if (pos <= rv->region()->position()) {
5207 len = pos - rv->region()->position();
5208 cmd = _("set fade in length");
5210 if (pos >= rv->region()->last_frame()) {
5214 len = rv->region()->last_frame() - pos;
5215 cmd = _("set fade out length");
5218 begin_reversible_command (cmd);
5220 for (RegionSelection::iterator x = rs.begin(); x != rs.end(); ++x) {
5221 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (*x);
5227 boost::shared_ptr<AutomationList> alist;
5229 alist = tmp->audio_region()->fade_in();
5231 alist = tmp->audio_region()->fade_out();
5234 XMLNode &before = alist->get_state();
5237 tmp->audio_region()->set_fade_in_length (len);
5238 tmp->audio_region()->set_fade_in_active (true);
5240 tmp->audio_region()->set_fade_out_length (len);
5241 tmp->audio_region()->set_fade_out_active (true);
5244 XMLNode &after = alist->get_state();
5245 _session->add_command(new MementoCommand<AutomationList>(*alist, &before, &after));
5248 commit_reversible_command ();
5252 Editor::set_fade_in_shape (FadeShape shape)
5254 RegionSelection rs = get_regions_from_selection_and_entered ();
5260 begin_reversible_command (_("set fade in shape"));
5262 for (RegionSelection::iterator x = rs.begin(); x != rs.end(); ++x) {
5263 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (*x);
5269 boost::shared_ptr<AutomationList> alist = tmp->audio_region()->fade_in();
5270 XMLNode &before = alist->get_state();
5272 tmp->audio_region()->set_fade_in_shape (shape);
5274 XMLNode &after = alist->get_state();
5275 _session->add_command(new MementoCommand<AutomationList>(*alist.get(), &before, &after));
5278 commit_reversible_command ();
5283 Editor::set_fade_out_shape (FadeShape shape)
5285 RegionSelection rs = get_regions_from_selection_and_entered ();
5291 begin_reversible_command (_("set fade out shape"));
5293 for (RegionSelection::iterator x = rs.begin(); x != rs.end(); ++x) {
5294 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (*x);
5300 boost::shared_ptr<AutomationList> alist = tmp->audio_region()->fade_out();
5301 XMLNode &before = alist->get_state();
5303 tmp->audio_region()->set_fade_out_shape (shape);
5305 XMLNode &after = alist->get_state();
5306 _session->add_command(new MementoCommand<AutomationList>(*alist.get(), &before, &after));
5309 commit_reversible_command ();
5313 Editor::set_fade_in_active (bool yn)
5315 RegionSelection rs = get_regions_from_selection_and_entered ();
5321 begin_reversible_command (_("set fade in active"));
5323 for (RegionSelection::iterator x = rs.begin(); x != rs.end(); ++x) {
5324 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (*x);
5331 boost::shared_ptr<AudioRegion> ar (tmp->audio_region());
5333 ar->clear_changes ();
5334 ar->set_fade_in_active (yn);
5335 _session->add_command (new StatefulDiffCommand (ar));
5338 commit_reversible_command ();
5342 Editor::set_fade_out_active (bool yn)
5344 RegionSelection rs = get_regions_from_selection_and_entered ();
5350 begin_reversible_command (_("set fade out active"));
5352 for (RegionSelection::iterator x = rs.begin(); x != rs.end(); ++x) {
5353 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (*x);
5359 boost::shared_ptr<AudioRegion> ar (tmp->audio_region());
5361 ar->clear_changes ();
5362 ar->set_fade_out_active (yn);
5363 _session->add_command(new StatefulDiffCommand (ar));
5366 commit_reversible_command ();
5370 Editor::toggle_region_fades (int dir)
5372 if (_ignore_region_action) {
5376 boost::shared_ptr<AudioRegion> ar;
5379 RegionSelection rs = get_regions_from_selection_and_entered ();
5385 RegionSelection::iterator i;
5386 for (i = rs.begin(); i != rs.end(); ++i) {
5387 if ((ar = boost::dynamic_pointer_cast<AudioRegion>((*i)->region())) != 0) {
5389 yn = ar->fade_out_active ();
5391 yn = ar->fade_in_active ();
5397 if (i == rs.end()) {
5401 /* XXX should this undo-able? */
5403 for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
5404 if ((ar = boost::dynamic_pointer_cast<AudioRegion>((*i)->region())) == 0) {
5407 if (dir == 1 || dir == 0) {
5408 ar->set_fade_in_active (!yn);
5411 if (dir == -1 || dir == 0) {
5412 ar->set_fade_out_active (!yn);
5418 /** Update region fade visibility after its configuration has been changed */
5420 Editor::update_region_fade_visibility ()
5422 bool _fade_visibility = _session->config.get_show_region_fades ();
5424 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
5425 AudioTimeAxisView* v = dynamic_cast<AudioTimeAxisView*>(*i);
5427 if (_fade_visibility) {
5428 v->audio_view()->show_all_fades ();
5430 v->audio_view()->hide_all_fades ();
5437 Editor::set_edit_point ()
5442 if (!mouse_frame (where, ignored)) {
5448 if (selection->markers.empty()) {
5450 mouse_add_new_marker (where);
5455 Location* loc = find_location_from_marker (selection->markers.front(), ignored);
5458 loc->move_to (where);
5464 Editor::set_playhead_cursor ()
5466 if (entered_marker) {
5467 _session->request_locate (entered_marker->position(), _session->transport_rolling());
5472 if (!mouse_frame (where, ignored)) {
5479 _session->request_locate (where, _session->transport_rolling());
5483 if ( Config->get_always_play_range() )
5484 cancel_time_selection();
5488 Editor::split_region ()
5490 if ( !selection->time.empty()) {
5491 separate_regions_between (selection->time);
5495 RegionSelection rs = get_regions_from_selection_and_edit_point ();
5497 framepos_t where = get_preferred_edit_position ();
5503 split_regions_at (where, rs);
5506 struct EditorOrderRouteSorter {
5507 bool operator() (boost::shared_ptr<Route> a, boost::shared_ptr<Route> b) {
5508 return a->order_key (EditorSort) < b->order_key (EditorSort);
5513 Editor::select_next_route()
5515 if (selection->tracks.empty()) {
5516 selection->set (track_views.front());
5520 TimeAxisView* current = selection->tracks.front();
5524 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
5525 if (*i == current) {
5527 if (i != track_views.end()) {
5530 current = (*(track_views.begin()));
5531 //selection->set (*(track_views.begin()));
5536 rui = dynamic_cast<RouteUI *>(current);
5537 } while ( current->hidden() || (rui != NULL && !rui->route()->active()));
5539 selection->set(current);
5541 ensure_track_visible(current);
5545 Editor::select_prev_route()
5547 if (selection->tracks.empty()) {
5548 selection->set (track_views.front());
5552 TimeAxisView* current = selection->tracks.front();
5556 for (TrackViewList::reverse_iterator i = track_views.rbegin(); i != track_views.rend(); ++i) {
5557 if (*i == current) {
5559 if (i != track_views.rend()) {
5562 current = *(track_views.rbegin());
5567 rui = dynamic_cast<RouteUI *>(current);
5568 } while ( current->hidden() || (rui != NULL && !rui->route()->active()));
5570 selection->set (current);
5572 ensure_track_visible(current);
5576 Editor::ensure_track_visible(TimeAxisView *track)
5578 if (track->hidden())
5581 double const current_view_min_y = vertical_adjustment.get_value();
5582 double const current_view_max_y = vertical_adjustment.get_value() + vertical_adjustment.get_page_size() - canvas_timebars_vsize;
5584 double const track_min_y = track->y_position ();
5585 double const track_max_y = track->y_position () + track->effective_height ();
5587 if (track_min_y >= current_view_min_y &&
5588 track_max_y <= current_view_max_y) {
5594 if (track_min_y < current_view_min_y) {
5595 // Track is above the current view
5596 new_value = track_min_y;
5598 // Track is below the current view
5599 new_value = track->y_position () + track->effective_height() + canvas_timebars_vsize - vertical_adjustment.get_page_size();
5602 vertical_adjustment.set_value(new_value);
5606 Editor::set_loop_from_selection (bool play)
5608 if (_session == 0 || selection->time.empty()) {
5612 framepos_t start = selection->time[clicked_selection].start;
5613 framepos_t end = selection->time[clicked_selection].end;
5615 set_loop_range (start, end, _("set loop range from selection"));
5618 _session->request_play_loop (true);
5619 _session->request_locate (start, true);
5624 Editor::set_loop_from_edit_range (bool play)
5626 if (_session == 0) {
5633 if (!get_edit_op_range (start, end)) {
5637 set_loop_range (start, end, _("set loop range from edit range"));
5640 _session->request_play_loop (true);
5641 _session->request_locate (start, true);
5646 Editor::set_loop_from_region (bool play)
5648 framepos_t start = max_framepos;
5651 RegionSelection rs = get_regions_from_selection_and_entered ();
5657 for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
5658 if ((*i)->region()->position() < start) {
5659 start = (*i)->region()->position();
5661 if ((*i)->region()->last_frame() + 1 > end) {
5662 end = (*i)->region()->last_frame() + 1;
5666 set_loop_range (start, end, _("set loop range from region"));
5669 _session->request_play_loop (true);
5670 _session->request_locate (start, true);
5675 Editor::set_punch_from_selection ()
5677 if (_session == 0 || selection->time.empty()) {
5681 framepos_t start = selection->time[clicked_selection].start;
5682 framepos_t end = selection->time[clicked_selection].end;
5684 set_punch_range (start, end, _("set punch range from selection"));
5688 Editor::set_punch_from_edit_range ()
5690 if (_session == 0) {
5697 if (!get_edit_op_range (start, end)) {
5701 set_punch_range (start, end, _("set punch range from edit range"));
5705 Editor::set_punch_from_region ()
5707 framepos_t start = max_framepos;
5710 RegionSelection rs = get_regions_from_selection_and_entered ();
5716 for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
5717 if ((*i)->region()->position() < start) {
5718 start = (*i)->region()->position();
5720 if ((*i)->region()->last_frame() + 1 > end) {
5721 end = (*i)->region()->last_frame() + 1;
5725 set_punch_range (start, end, _("set punch range from region"));
5729 Editor::pitch_shift_region ()
5731 RegionSelection rs = get_regions_from_selection_and_entered ();
5733 RegionSelection audio_rs;
5734 for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
5735 if (dynamic_cast<AudioRegionView*> (*i)) {
5736 audio_rs.push_back (*i);
5740 if (audio_rs.empty()) {
5744 pitch_shift (audio_rs, 1.2);
5748 Editor::transpose_region ()
5750 RegionSelection rs = get_regions_from_selection_and_entered ();
5752 list<MidiRegionView*> midi_region_views;
5753 for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
5754 MidiRegionView* mrv = dynamic_cast<MidiRegionView*> (*i);
5756 midi_region_views.push_back (mrv);
5761 int const r = d.run ();
5762 if (r != RESPONSE_ACCEPT) {
5766 for (list<MidiRegionView*>::iterator i = midi_region_views.begin(); i != midi_region_views.end(); ++i) {
5767 (*i)->midi_region()->transpose (d.semitones ());
5772 Editor::set_tempo_from_region ()
5774 RegionSelection rs = get_regions_from_selection_and_entered ();
5776 if (!_session || rs.empty()) {
5780 RegionView* rv = rs.front();
5782 define_one_bar (rv->region()->position(), rv->region()->last_frame() + 1);
5786 Editor::use_range_as_bar ()
5788 framepos_t start, end;
5789 if (get_edit_op_range (start, end)) {
5790 define_one_bar (start, end);
5795 Editor::define_one_bar (framepos_t start, framepos_t end)
5797 framepos_t length = end - start;
5799 const Meter& m (_session->tempo_map().meter_at (start));
5801 /* length = 1 bar */
5803 /* now we want frames per beat.
5804 we have frames per bar, and beats per bar, so ...
5807 /* XXXX METER MATH */
5809 double frames_per_beat = length / m.divisions_per_bar();
5811 /* beats per minute = */
5813 double beats_per_minute = (_session->frame_rate() * 60.0) / frames_per_beat;
5815 /* now decide whether to:
5817 (a) set global tempo
5818 (b) add a new tempo marker
5822 const TempoSection& t (_session->tempo_map().tempo_section_at (start));
5824 bool do_global = false;
5826 if ((_session->tempo_map().n_tempos() == 1) && (_session->tempo_map().n_meters() == 1)) {
5828 /* only 1 tempo & 1 meter: ask if the user wants to set the tempo
5829 at the start, or create a new marker
5832 vector<string> options;
5833 options.push_back (_("Cancel"));
5834 options.push_back (_("Add new marker"));
5835 options.push_back (_("Set global tempo"));
5838 _("Define one bar"),
5839 _("Do you want to set the global tempo or add a new tempo marker?"),
5843 c.set_default_response (2);
5859 /* more than 1 tempo and/or meter section already, go ahead do the "usual":
5860 if the marker is at the region starter, change it, otherwise add
5865 begin_reversible_command (_("set tempo from region"));
5866 XMLNode& before (_session->tempo_map().get_state());
5869 _session->tempo_map().change_initial_tempo (beats_per_minute, t.note_type());
5870 } else if (t.frame() == start) {
5871 _session->tempo_map().change_existing_tempo_at (start, beats_per_minute, t.note_type());
5873 Timecode::BBT_Time bbt;
5874 _session->tempo_map().bbt_time (start, bbt);
5875 _session->tempo_map().add_tempo (Tempo (beats_per_minute, t.note_type()), bbt);
5878 XMLNode& after (_session->tempo_map().get_state());
5880 _session->add_command (new MementoCommand<TempoMap>(_session->tempo_map(), &before, &after));
5881 commit_reversible_command ();
5885 Editor::split_region_at_transients ()
5887 AnalysisFeatureList positions;
5889 RegionSelection rs = get_regions_from_selection_and_entered ();
5891 if (!_session || rs.empty()) {
5895 _session->begin_reversible_command (_("split regions"));
5897 for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ) {
5899 RegionSelection::iterator tmp;
5904 boost::shared_ptr<AudioRegion> ar = boost::dynamic_pointer_cast<AudioRegion> ((*i)->region());
5906 if (ar && (ar->get_transients (positions) == 0)) {
5907 split_region_at_points ((*i)->region(), positions, true);
5914 _session->commit_reversible_command ();
5919 Editor::split_region_at_points (boost::shared_ptr<Region> r, AnalysisFeatureList& positions, bool can_ferret, bool select_new)
5921 bool use_rhythmic_rodent = false;
5923 boost::shared_ptr<Playlist> pl = r->playlist();
5925 list<boost::shared_ptr<Region> > new_regions;
5931 if (positions.empty()) {
5936 if (positions.size() > 20 && can_ferret) {
5937 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);
5938 MessageDialog msg (msgstr,
5941 Gtk::BUTTONS_OK_CANCEL);
5944 msg.add_button (_("Call for the Ferret!"), RESPONSE_APPLY);
5945 msg.set_secondary_text (_("Press OK to continue with this split operation\nor ask the Ferret dialog to tune the analysis"));
5947 msg.set_secondary_text (_("Press OK to continue with this split operation"));
5950 msg.set_title (_("Excessive split?"));
5953 int response = msg.run();
5959 case RESPONSE_APPLY:
5960 use_rhythmic_rodent = true;
5967 if (use_rhythmic_rodent) {
5968 show_rhythm_ferret ();
5972 AnalysisFeatureList::const_iterator x;
5974 pl->clear_changes ();
5975 pl->clear_owned_changes ();
5977 x = positions.begin();
5979 if (x == positions.end()) {
5984 pl->remove_region (r);
5988 while (x != positions.end()) {
5990 /* deal with positons that are out of scope of present region bounds */
5991 if (*x <= 0 || *x > r->length()) {
5996 /* file start = original start + how far we from the initial position ?
5999 framepos_t file_start = r->start() + pos;
6001 /* length = next position - current position
6004 framepos_t len = (*x) - pos;
6006 /* XXX we do we really want to allow even single-sample regions?
6007 shouldn't we have some kind of lower limit on region size?
6016 if (RegionFactory::region_name (new_name, r->name())) {
6020 /* do NOT announce new regions 1 by one, just wait till they are all done */
6024 plist.add (ARDOUR::Properties::start, file_start);
6025 plist.add (ARDOUR::Properties::length, len);
6026 plist.add (ARDOUR::Properties::name, new_name);
6027 plist.add (ARDOUR::Properties::layer, 0);
6029 boost::shared_ptr<Region> nr = RegionFactory::create (r->sources(), plist, false);
6030 /* because we set annouce to false, manually add the new region to the
6033 RegionFactory::map_add (nr);
6035 pl->add_region (nr, r->position() + pos);
6038 new_regions.push_front(nr);
6047 RegionFactory::region_name (new_name, r->name());
6049 /* Add the final region */
6052 plist.add (ARDOUR::Properties::start, r->start() + pos);
6053 plist.add (ARDOUR::Properties::length, r->last_frame() - (r->position() + pos) + 1);
6054 plist.add (ARDOUR::Properties::name, new_name);
6055 plist.add (ARDOUR::Properties::layer, 0);
6057 boost::shared_ptr<Region> nr = RegionFactory::create (r->sources(), plist, false);
6058 /* because we set annouce to false, manually add the new region to the
6061 RegionFactory::map_add (nr);
6062 pl->add_region (nr, r->position() + pos);
6065 new_regions.push_front(nr);
6070 /* We might have removed regions, which alters other regions' layering_index,
6071 so we need to do a recursive diff here.
6073 vector<Command*> cmds;
6075 _session->add_commands (cmds);
6077 _session->add_command (new StatefulDiffCommand (pl));
6081 for (list<boost::shared_ptr<Region> >::iterator i = new_regions.begin(); i != new_regions.end(); ++i){
6082 set_selected_regionview_from_region_list ((*i), Selection::Add);
6088 Editor::place_transient()
6094 RegionSelection rs = get_regions_from_selection_and_edit_point ();
6100 framepos_t where = get_preferred_edit_position();
6102 _session->begin_reversible_command (_("place transient"));
6104 for (RegionSelection::iterator r = rs.begin(); r != rs.end(); ++r) {
6105 framepos_t position = (*r)->region()->position();
6106 (*r)->region()->add_transient(where - position);
6109 _session->commit_reversible_command ();
6113 Editor::remove_transient(ArdourCanvas::Item* item)
6119 ArdourCanvas::Line* _line = reinterpret_cast<ArdourCanvas::Line*> (item);
6122 AudioRegionView* _arv = reinterpret_cast<AudioRegionView*> (item->get_data ("regionview"));
6123 _arv->remove_transient (*(float*) _line->get_data ("position"));
6127 Editor::snap_regions_to_grid ()
6129 list <boost::shared_ptr<Playlist > > used_playlists;
6131 RegionSelection rs = get_regions_from_selection_and_entered ();
6133 if (!_session || rs.empty()) {
6137 _session->begin_reversible_command (_("snap regions to grid"));
6139 for (RegionSelection::iterator r = rs.begin(); r != rs.end(); ++r) {
6141 boost::shared_ptr<Playlist> pl = (*r)->region()->playlist();
6143 if (!pl->frozen()) {
6144 /* we haven't seen this playlist before */
6146 /* remember used playlists so we can thaw them later */
6147 used_playlists.push_back(pl);
6151 framepos_t start_frame = (*r)->region()->first_frame ();
6152 snap_to (start_frame);
6153 (*r)->region()->set_position (start_frame);
6156 while (used_playlists.size() > 0) {
6157 list <boost::shared_ptr<Playlist > >::iterator i = used_playlists.begin();
6159 used_playlists.pop_front();
6162 _session->commit_reversible_command ();
6166 Editor::close_region_gaps ()
6168 list <boost::shared_ptr<Playlist > > used_playlists;
6170 RegionSelection rs = get_regions_from_selection_and_entered ();
6172 if (!_session || rs.empty()) {
6176 Dialog dialog (_("Close Region Gaps"));
6179 table.set_spacings (12);
6180 table.set_border_width (12);
6181 Label* l = manage (left_aligned_label (_("Crossfade length")));
6182 table.attach (*l, 0, 1, 0, 1);
6184 SpinButton spin_crossfade (1, 0);
6185 spin_crossfade.set_range (0, 15);
6186 spin_crossfade.set_increments (1, 1);
6187 spin_crossfade.set_value (5);
6188 table.attach (spin_crossfade, 1, 2, 0, 1);
6190 table.attach (*manage (new Label (_("ms"))), 2, 3, 0, 1);
6192 l = manage (left_aligned_label (_("Pull-back length")));
6193 table.attach (*l, 0, 1, 1, 2);
6195 SpinButton spin_pullback (1, 0);
6196 spin_pullback.set_range (0, 100);
6197 spin_pullback.set_increments (1, 1);
6198 spin_pullback.set_value(30);
6199 table.attach (spin_pullback, 1, 2, 1, 2);
6201 table.attach (*manage (new Label (_("ms"))), 2, 3, 1, 2);
6203 dialog.get_vbox()->pack_start (table);
6204 dialog.add_button (Stock::CANCEL, RESPONSE_CANCEL);
6205 dialog.add_button (_("Ok"), RESPONSE_ACCEPT);
6208 if (dialog.run () == RESPONSE_CANCEL) {
6212 framepos_t crossfade_len = spin_crossfade.get_value();
6213 framepos_t pull_back_frames = spin_pullback.get_value();
6215 crossfade_len = lrintf (crossfade_len * _session->frame_rate()/1000);
6216 pull_back_frames = lrintf (pull_back_frames * _session->frame_rate()/1000);
6218 /* Iterate over the region list and make adjacent regions overlap by crossfade_len_ms */
6220 _session->begin_reversible_command (_("close region gaps"));
6223 boost::shared_ptr<Region> last_region;
6225 rs.sort_by_position_and_track();
6227 for (RegionSelection::iterator r = rs.begin(); r != rs.end(); ++r) {
6229 boost::shared_ptr<Playlist> pl = (*r)->region()->playlist();
6231 if (!pl->frozen()) {
6232 /* we haven't seen this playlist before */
6234 /* remember used playlists so we can thaw them later */
6235 used_playlists.push_back(pl);
6239 framepos_t position = (*r)->region()->position();
6241 if (idx == 0 || position < last_region->position()){
6242 last_region = (*r)->region();
6247 (*r)->region()->trim_front( (position - pull_back_frames));
6248 last_region->trim_end( (position - pull_back_frames + crossfade_len));
6250 last_region = (*r)->region();
6255 while (used_playlists.size() > 0) {
6256 list <boost::shared_ptr<Playlist > >::iterator i = used_playlists.begin();
6258 used_playlists.pop_front();
6261 _session->commit_reversible_command ();
6265 Editor::tab_to_transient (bool forward)
6267 AnalysisFeatureList positions;
6269 RegionSelection rs = get_regions_from_selection_and_entered ();
6275 framepos_t pos = _session->audible_frame ();
6277 if (!selection->tracks.empty()) {
6279 /* don't waste time searching for transients in duplicate playlists.
6282 TrackViewList ts = selection->tracks.filter_to_unique_playlists ();
6284 for (TrackViewList::iterator t = ts.begin(); t != ts.end(); ++t) {
6286 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (*t);
6289 boost::shared_ptr<Track> tr = rtv->track();
6291 boost::shared_ptr<Playlist> pl = tr->playlist ();
6293 framepos_t result = pl->find_next_transient (pos, forward ? 1 : -1);
6296 positions.push_back (result);
6309 for (RegionSelection::iterator r = rs.begin(); r != rs.end(); ++r) {
6310 (*r)->region()->get_transients (positions);
6314 TransientDetector::cleanup_transients (positions, _session->frame_rate(), 3.0);
6317 AnalysisFeatureList::iterator x;
6319 for (x = positions.begin(); x != positions.end(); ++x) {
6325 if (x != positions.end ()) {
6326 _session->request_locate (*x);
6330 AnalysisFeatureList::reverse_iterator x;
6332 for (x = positions.rbegin(); x != positions.rend(); ++x) {
6338 if (x != positions.rend ()) {
6339 _session->request_locate (*x);
6345 Editor::playhead_forward_to_grid ()
6347 if (!_session) return;
6348 framepos_t pos = playhead_cursor->current_frame;
6349 if (pos < max_framepos - 1) {
6351 snap_to_internal (pos, 1, false);
6352 _session->request_locate (pos);
6358 Editor::playhead_backward_to_grid ()
6360 if (!_session) return;
6361 framepos_t pos = playhead_cursor->current_frame;
6364 snap_to_internal (pos, -1, false);
6365 _session->request_locate (pos);
6370 Editor::set_track_height (Height h)
6372 TrackSelection& ts (selection->tracks);
6374 for (TrackSelection::iterator x = ts.begin(); x != ts.end(); ++x) {
6375 (*x)->set_height_enum (h);
6380 Editor::toggle_tracks_active ()
6382 TrackSelection& ts (selection->tracks);
6384 bool target = false;
6390 for (TrackSelection::iterator x = ts.begin(); x != ts.end(); ++x) {
6391 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*>(*x);
6395 target = !rtv->_route->active();
6398 rtv->_route->set_active (target, this);
6404 Editor::remove_tracks ()
6406 TrackSelection& ts (selection->tracks);
6412 vector<string> choices;
6416 const char* trackstr;
6418 vector<boost::shared_ptr<Route> > routes;
6419 bool special_bus = false;
6421 for (TrackSelection::iterator x = ts.begin(); x != ts.end(); ++x) {
6422 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (*x);
6424 if (rtv->is_track()) {
6430 routes.push_back (rtv->_route);
6432 if (rtv->route()->is_master() || rtv->route()->is_monitor()) {
6437 if (special_bus && !Config->get_allow_special_bus_removal()) {
6438 MessageDialog msg (_("That would be bad news ...."),
6442 msg.set_secondary_text (string_compose (_(
6443 "Removing the master or monitor bus is such a bad idea\n\
6444 that %1 is not going to allow it.\n\
6446 If you really want to do this sort of thing\n\
6447 edit your ardour.rc file to set the\n\
6448 \"allow-special-bus-removal\" option to be \"yes\""), PROGRAM_NAME));
6455 if (ntracks + nbusses == 0) {
6460 trackstr = _("tracks");
6462 trackstr = _("track");
6466 busstr = _("busses");
6473 prompt = string_compose (_("Do you really want to remove %1 %2 and %3 %4?\n"
6474 "(You may also lose the playlists associated with the %2)\n\n"
6475 "This action cannot be undone, and the session file will be overwritten!"),
6476 ntracks, trackstr, nbusses, busstr);
6478 prompt = string_compose (_("Do you really want to remove %1 %2?\n"
6479 "(You may also lose the playlists associated with the %2)\n\n"
6480 "This action cannot be undone, and the session file will be overwritten!"),
6483 } else if (nbusses) {
6484 prompt = string_compose (_("Do you really want to remove %1 %2?\n\n"
6485 "This action cannot be undon, and the session file will be overwritten"),
6489 choices.push_back (_("No, do nothing."));
6490 if (ntracks + nbusses > 1) {
6491 choices.push_back (_("Yes, remove them."));
6493 choices.push_back (_("Yes, remove it."));
6498 title = string_compose (_("Remove %1"), trackstr);
6500 title = string_compose (_("Remove %1"), busstr);
6503 Choice prompter (title, prompt, choices);
6505 if (prompter.run () != 1) {
6509 for (vector<boost::shared_ptr<Route> >::iterator x = routes.begin(); x != routes.end(); ++x) {
6510 _session->remove_route (*x);
6515 Editor::do_insert_time ()
6517 if (selection->tracks.empty()) {
6521 InsertTimeDialog d (*this);
6522 int response = d.run ();
6524 if (response != RESPONSE_OK) {
6528 if (d.distance() == 0) {
6532 InsertTimeOption opt = d.intersected_region_action ();
6535 get_preferred_edit_position(),
6541 d.move_glued_markers(),
6542 d.move_locked_markers(),
6548 Editor::insert_time (
6549 framepos_t pos, framecnt_t frames, InsertTimeOption opt,
6550 bool all_playlists, bool ignore_music_glue, bool markers_too, bool glued_markers_too, bool locked_markers_too, bool tempo_too
6553 bool commit = false;
6555 if (Config->get_edit_mode() == Lock) {
6559 begin_reversible_command (_("insert time"));
6561 TrackViewList ts = selection->tracks.filter_to_unique_playlists ();
6563 for (TrackViewList::iterator x = ts.begin(); x != ts.end(); ++x) {
6567 /* don't operate on any playlist more than once, which could
6568 * happen if "all playlists" is enabled, but there is more
6569 * than 1 track using playlists "from" a given track.
6572 set<boost::shared_ptr<Playlist> > pl;
6574 if (all_playlists) {
6575 RouteTimeAxisView* rtav = dynamic_cast<RouteTimeAxisView*> (*x);
6577 vector<boost::shared_ptr<Playlist> > all = _session->playlists->playlists_for_track (rtav->track ());
6578 for (vector<boost::shared_ptr<Playlist> >::iterator p = all.begin(); p != all.end(); ++p) {
6583 if ((*x)->playlist ()) {
6584 pl.insert ((*x)->playlist ());
6588 for (set<boost::shared_ptr<Playlist> >::iterator i = pl.begin(); i != pl.end(); ++i) {
6590 (*i)->clear_changes ();
6591 (*i)->clear_owned_changes ();
6593 if (opt == SplitIntersected) {
6597 (*i)->shift (pos, frames, (opt == MoveIntersected), ignore_music_glue);
6599 vector<Command*> cmds;
6601 _session->add_commands (cmds);
6603 _session->add_command (new StatefulDiffCommand (*i));
6608 RouteTimeAxisView* rtav = dynamic_cast<RouteTimeAxisView*> (*x);
6610 rtav->route ()->shift (pos, frames);
6618 XMLNode& before (_session->locations()->get_state());
6619 Locations::LocationList copy (_session->locations()->list());
6621 for (Locations::LocationList::iterator i = copy.begin(); i != copy.end(); ++i) {
6623 Locations::LocationList::const_iterator tmp;
6625 bool const was_locked = (*i)->locked ();
6626 if (locked_markers_too) {
6630 if ((*i)->position_lock_style() == AudioTime || glued_markers_too) {
6632 if ((*i)->start() >= pos) {
6633 (*i)->set_start ((*i)->start() + frames);
6634 if (!(*i)->is_mark()) {
6635 (*i)->set_end ((*i)->end() + frames);
6648 XMLNode& after (_session->locations()->get_state());
6649 _session->add_command (new MementoCommand<Locations>(*_session->locations(), &before, &after));
6654 _session->tempo_map().insert_time (pos, frames);
6658 commit_reversible_command ();
6663 Editor::fit_selected_tracks ()
6665 if (!selection->tracks.empty()) {
6666 fit_tracks (selection->tracks);
6670 /* no selected tracks - use tracks with selected regions */
6672 if (!selection->regions.empty()) {
6673 for (RegionSelection::iterator r = selection->regions.begin(); r != selection->regions.end(); ++r) {
6674 tvl.push_back (&(*r)->get_time_axis_view ());
6680 } else if (internal_editing()) {
6681 /* no selected tracks, or regions, but in internal edit mode, so follow the mouse and use
6684 if (entered_track) {
6685 tvl.push_back (entered_track);
6693 Editor::fit_tracks (TrackViewList & tracks)
6695 if (tracks.empty()) {
6699 uint32_t child_heights = 0;
6700 int visible_tracks = 0;
6702 for (TrackSelection::iterator t = tracks.begin(); t != tracks.end(); ++t) {
6704 if (!(*t)->marked_for_display()) {
6708 child_heights += (*t)->effective_height() - (*t)->current_height();
6712 uint32_t h = (uint32_t) floor ((_canvas_height - child_heights - canvas_timebars_vsize) / visible_tracks);
6713 double first_y_pos = DBL_MAX;
6715 if (h < TimeAxisView::preset_height (HeightSmall)) {
6716 MessageDialog msg (*this, _("There are too many tracks to fit in the current window"));
6717 /* too small to be displayed */
6721 undo_visual_stack.push_back (current_visual_state (true));
6722 no_save_visual = true;
6724 /* build a list of all tracks, including children */
6727 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
6729 TimeAxisView::Children c = (*i)->get_child_list ();
6730 for (TimeAxisView::Children::iterator j = c.begin(); j != c.end(); ++j) {
6731 all.push_back (j->get());
6735 /* operate on all tracks, hide unselected ones that are in the middle of selected ones */
6737 bool prev_was_selected = false;
6738 bool is_selected = tracks.contains (all.front());
6739 bool next_is_selected;
6741 for (TrackViewList::iterator t = all.begin(); t != all.end(); ++t) {
6743 TrackViewList::iterator next;
6748 if (next != all.end()) {
6749 next_is_selected = tracks.contains (*next);
6751 next_is_selected = false;
6754 if ((*t)->marked_for_display ()) {
6756 (*t)->set_height (h);
6757 first_y_pos = std::min ((*t)->y_position (), first_y_pos);
6759 if (prev_was_selected && next_is_selected) {
6760 hide_track_in_display (*t);
6765 prev_was_selected = is_selected;
6766 is_selected = next_is_selected;
6770 set the controls_layout height now, because waiting for its size
6771 request signal handler will cause the vertical adjustment setting to fail
6774 controls_layout.property_height () = full_canvas_height - canvas_timebars_vsize;
6775 vertical_adjustment.set_value (first_y_pos);
6777 redo_visual_stack.push_back (current_visual_state (true));
6781 Editor::save_visual_state (uint32_t n)
6783 while (visual_states.size() <= n) {
6784 visual_states.push_back (0);
6787 if (visual_states[n] != 0) {
6788 delete visual_states[n];
6791 visual_states[n] = current_visual_state (true);
6796 Editor::goto_visual_state (uint32_t n)
6798 if (visual_states.size() <= n) {
6802 if (visual_states[n] == 0) {
6806 use_visual_state (*visual_states[n]);
6810 Editor::start_visual_state_op (uint32_t n)
6812 save_visual_state (n);
6814 PopUp* pup = new PopUp (WIN_POS_MOUSE, 1000, true);
6816 snprintf (buf, sizeof (buf), _("Saved view %u"), n+1);
6817 pup->set_text (buf);
6822 Editor::cancel_visual_state_op (uint32_t n)
6824 goto_visual_state (n);
6828 Editor::toggle_region_mute ()
6830 if (_ignore_region_action) {
6834 RegionSelection rs = get_regions_from_selection_and_entered ();
6840 if (rs.size() > 1) {
6841 begin_reversible_command (_("mute regions"));
6843 begin_reversible_command (_("mute region"));
6846 for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
6848 (*i)->region()->playlist()->clear_changes ();
6849 (*i)->region()->set_muted (!(*i)->region()->muted ());
6850 _session->add_command (new StatefulDiffCommand ((*i)->region()->playlist()));
6854 commit_reversible_command ();
6858 Editor::combine_regions ()
6860 /* foreach track with selected regions, take all selected regions
6861 and join them into a new region containing the subregions (as a
6865 typedef set<RouteTimeAxisView*> RTVS;
6868 if (selection->regions.empty()) {
6872 for (RegionSelection::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
6873 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*>(&(*i)->get_time_axis_view());
6876 tracks.insert (rtv);
6880 begin_reversible_command (_("combine regions"));
6882 vector<RegionView*> new_selection;
6884 for (RTVS::iterator i = tracks.begin(); i != tracks.end(); ++i) {
6887 if ((rv = (*i)->combine_regions ()) != 0) {
6888 new_selection.push_back (rv);
6892 selection->clear_regions ();
6893 for (vector<RegionView*>::iterator i = new_selection.begin(); i != new_selection.end(); ++i) {
6894 selection->add (*i);
6897 commit_reversible_command ();
6901 Editor::uncombine_regions ()
6903 typedef set<RouteTimeAxisView*> RTVS;
6906 if (selection->regions.empty()) {
6910 for (RegionSelection::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
6911 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*>(&(*i)->get_time_axis_view());
6914 tracks.insert (rtv);
6918 begin_reversible_command (_("uncombine regions"));
6920 for (RTVS::iterator i = tracks.begin(); i != tracks.end(); ++i) {
6921 (*i)->uncombine_regions ();
6924 commit_reversible_command ();
6928 Editor::toggle_midi_input_active (bool flip_others)
6931 boost::shared_ptr<RouteList> rl (new RouteList);
6933 for (TrackSelection::iterator i = selection->tracks.begin(); i != selection->tracks.end(); ++i) {
6934 RouteTimeAxisView *rtav = dynamic_cast<RouteTimeAxisView *>(*i);
6940 boost::shared_ptr<MidiTrack> mt = rtav->midi_track();
6943 rl->push_back (rtav->route());
6944 onoff = !mt->input_active();
6948 _session->set_exclusive_input_active (rl, onoff, flip_others);