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);
2387 entry.set_text (rs.front()->region()->name());
2388 entry.select_region (0, -1);
2390 entry.signal_activate().connect (sigc::bind (sigc::mem_fun (d, &Dialog::response), RESPONSE_OK));
2396 int const ret = d.run();
2400 if (ret != RESPONSE_OK) {
2404 std::string str = entry.get_text();
2405 strip_whitespace_edges (str);
2407 rs.front()->region()->set_name (str);
2408 _regions->redisplay ();
2413 Editor::audition_playlist_region_via_route (boost::shared_ptr<Region> region, Route& route)
2415 if (_session->is_auditioning()) {
2416 _session->cancel_audition ();
2419 // note: some potential for creativity here, because region doesn't
2420 // have to belong to the playlist that Route is handling
2422 // bool was_soloed = route.soloed();
2424 route.set_solo (true, this);
2426 _session->request_bounded_roll (region->position(), region->position() + region->length());
2428 /* XXX how to unset the solo state ? */
2431 /** Start an audition of the first selected region */
2433 Editor::play_edit_range ()
2435 framepos_t start, end;
2437 if (get_edit_op_range (start, end)) {
2438 _session->request_bounded_roll (start, end);
2443 Editor::play_selected_region ()
2445 framepos_t start = max_framepos;
2448 RegionSelection rs = get_regions_from_selection_and_entered ();
2454 for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
2455 if ((*i)->region()->position() < start) {
2456 start = (*i)->region()->position();
2458 if ((*i)->region()->last_frame() + 1 > end) {
2459 end = (*i)->region()->last_frame() + 1;
2463 _session->request_bounded_roll (start, end);
2467 Editor::audition_playlist_region_standalone (boost::shared_ptr<Region> region)
2469 _session->audition_region (region);
2473 Editor::region_from_selection ()
2475 if (clicked_axisview == 0) {
2479 if (selection->time.empty()) {
2483 framepos_t start = selection->time[clicked_selection].start;
2484 framepos_t end = selection->time[clicked_selection].end;
2486 TrackViewList tracks = get_tracks_for_range_action ();
2488 framepos_t selection_cnt = end - start + 1;
2490 for (TrackSelection::iterator i = tracks.begin(); i != tracks.end(); ++i) {
2491 boost::shared_ptr<Region> current;
2492 boost::shared_ptr<Playlist> pl;
2493 framepos_t internal_start;
2496 if ((pl = (*i)->playlist()) == 0) {
2500 if ((current = pl->top_region_at (start)) == 0) {
2504 internal_start = start - current->position();
2505 RegionFactory::region_name (new_name, current->name(), true);
2509 plist.add (ARDOUR::Properties::start, current->start() + internal_start);
2510 plist.add (ARDOUR::Properties::length, selection_cnt);
2511 plist.add (ARDOUR::Properties::name, new_name);
2512 plist.add (ARDOUR::Properties::layer, 0);
2514 boost::shared_ptr<Region> region (RegionFactory::create (current, plist));
2519 Editor::create_region_from_selection (vector<boost::shared_ptr<Region> >& new_regions)
2521 if (selection->time.empty() || selection->tracks.empty()) {
2525 framepos_t start = selection->time[clicked_selection].start;
2526 framepos_t end = selection->time[clicked_selection].end;
2528 TrackViewList ts = selection->tracks.filter_to_unique_playlists ();
2529 sort_track_selection (ts);
2531 for (TrackSelection::iterator i = ts.begin(); i != ts.end(); ++i) {
2532 boost::shared_ptr<Region> current;
2533 boost::shared_ptr<Playlist> playlist;
2534 framepos_t internal_start;
2537 if ((playlist = (*i)->playlist()) == 0) {
2541 if ((current = playlist->top_region_at(start)) == 0) {
2545 internal_start = start - current->position();
2546 RegionFactory::region_name (new_name, current->name(), true);
2550 plist.add (ARDOUR::Properties::start, current->start() + internal_start);
2551 plist.add (ARDOUR::Properties::length, end - start + 1);
2552 plist.add (ARDOUR::Properties::name, new_name);
2554 new_regions.push_back (RegionFactory::create (current, plist));
2559 Editor::split_multichannel_region ()
2561 RegionSelection rs = get_regions_from_selection_and_entered ();
2567 vector< boost::shared_ptr<Region> > v;
2569 for (list<RegionView*>::iterator x = rs.begin(); x != rs.end(); ++x) {
2570 (*x)->region()->separate_by_channel (*_session, v);
2575 Editor::new_region_from_selection ()
2577 region_from_selection ();
2578 cancel_selection ();
2582 add_if_covered (RegionView* rv, const AudioRange* ar, RegionSelection* rs)
2584 switch (rv->region()->coverage (ar->start, ar->end - 1)) {
2585 case Evoral::OverlapNone:
2593 * - selected tracks, or if there are none...
2594 * - tracks containing selected regions, or if there are none...
2599 Editor::get_tracks_for_range_action () const
2603 if (selection->tracks.empty()) {
2605 /* use tracks with selected regions */
2607 RegionSelection rs = selection->regions;
2609 for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
2610 TimeAxisView* tv = &(*i)->get_time_axis_view();
2612 if (!t.contains (tv)) {
2618 /* no regions and no tracks: use all tracks */
2624 t = selection->tracks;
2627 return t.filter_to_unique_playlists();
2631 Editor::separate_regions_between (const TimeSelection& ts)
2633 bool in_command = false;
2634 boost::shared_ptr<Playlist> playlist;
2635 RegionSelection new_selection;
2637 TrackViewList tmptracks = get_tracks_for_range_action ();
2638 sort_track_selection (tmptracks);
2640 for (TrackSelection::iterator i = tmptracks.begin(); i != tmptracks.end(); ++i) {
2642 RouteTimeAxisView* rtv;
2644 if ((rtv = dynamic_cast<RouteTimeAxisView*> ((*i))) != 0) {
2646 if (rtv->is_track()) {
2648 /* no edits to destructive tracks */
2650 if (rtv->track()->destructive()) {
2654 if ((playlist = rtv->playlist()) != 0) {
2656 playlist->clear_changes ();
2658 /* XXX need to consider musical time selections here at some point */
2660 double speed = rtv->track()->speed();
2663 for (list<AudioRange>::const_iterator t = ts.begin(); t != ts.end(); ++t) {
2665 sigc::connection c = rtv->view()->RegionViewAdded.connect (
2666 sigc::mem_fun(*this, &Editor::collect_new_region_view));
2668 latest_regionviews.clear ();
2670 playlist->partition ((framepos_t)((*t).start * speed),
2671 (framepos_t)((*t).end * speed), false);
2675 if (!latest_regionviews.empty()) {
2677 rtv->view()->foreach_regionview (sigc::bind (
2678 sigc::ptr_fun (add_if_covered),
2679 &(*t), &new_selection));
2682 begin_reversible_command (_("separate"));
2686 /* pick up changes to existing regions */
2688 vector<Command*> cmds;
2689 playlist->rdiff (cmds);
2690 _session->add_commands (cmds);
2692 /* pick up changes to the playlist itself (adds/removes)
2695 _session->add_command(new StatefulDiffCommand (playlist));
2704 selection->set (new_selection);
2705 set_mouse_mode (MouseObject);
2707 commit_reversible_command ();
2711 struct PlaylistState {
2712 boost::shared_ptr<Playlist> playlist;
2716 /** Take tracks from get_tracks_for_range_action and cut any regions
2717 * on those tracks so that the tracks are empty over the time
2721 Editor::separate_region_from_selection ()
2723 /* preferentially use *all* ranges in the time selection if we're in range mode
2724 to allow discontiguous operation, since get_edit_op_range() currently
2725 returns a single range.
2728 if (!selection->time.empty()) {
2730 separate_regions_between (selection->time);
2737 if (get_edit_op_range (start, end)) {
2739 AudioRange ar (start, end, 1);
2743 separate_regions_between (ts);
2749 Editor::separate_region_from_punch ()
2751 Location* loc = _session->locations()->auto_punch_location();
2753 separate_regions_using_location (*loc);
2758 Editor::separate_region_from_loop ()
2760 Location* loc = _session->locations()->auto_loop_location();
2762 separate_regions_using_location (*loc);
2767 Editor::separate_regions_using_location (Location& loc)
2769 if (loc.is_mark()) {
2773 AudioRange ar (loc.start(), loc.end(), 1);
2778 separate_regions_between (ts);
2781 /** Separate regions under the selected region */
2783 Editor::separate_under_selected_regions ()
2785 vector<PlaylistState> playlists;
2789 rs = get_regions_from_selection_and_entered();
2791 if (!_session || rs.empty()) {
2795 begin_reversible_command (_("separate region under"));
2797 list<boost::shared_ptr<Region> > regions_to_remove;
2799 for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
2800 // we can't just remove the region(s) in this loop because
2801 // this removes them from the RegionSelection, and they thus
2802 // disappear from underneath the iterator, and the ++i above
2803 // SEGVs in a puzzling fashion.
2805 // so, first iterate over the regions to be removed from rs and
2806 // add them to the regions_to_remove list, and then
2807 // iterate over the list to actually remove them.
2809 regions_to_remove.push_back ((*i)->region());
2812 for (list<boost::shared_ptr<Region> >::iterator rl = regions_to_remove.begin(); rl != regions_to_remove.end(); ++rl) {
2814 boost::shared_ptr<Playlist> playlist = (*rl)->playlist();
2817 // is this check necessary?
2821 vector<PlaylistState>::iterator i;
2823 //only take state if this is a new playlist.
2824 for (i = playlists.begin(); i != playlists.end(); ++i) {
2825 if ((*i).playlist == playlist) {
2830 if (i == playlists.end()) {
2832 PlaylistState before;
2833 before.playlist = playlist;
2834 before.before = &playlist->get_state();
2836 playlist->freeze ();
2837 playlists.push_back(before);
2840 //Partition on the region bounds
2841 playlist->partition ((*rl)->first_frame() - 1, (*rl)->last_frame() + 1, true);
2843 //Re-add region that was just removed due to the partition operation
2844 playlist->add_region( (*rl), (*rl)->first_frame() );
2847 vector<PlaylistState>::iterator pl;
2849 for (pl = playlists.begin(); pl != playlists.end(); ++pl) {
2850 (*pl).playlist->thaw ();
2851 _session->add_command(new MementoCommand<Playlist>(*(*pl).playlist, (*pl).before, &(*pl).playlist->get_state()));
2854 commit_reversible_command ();
2858 Editor::crop_region_to_selection ()
2860 if (!selection->time.empty()) {
2862 crop_region_to (selection->time.start(), selection->time.end_frame());
2869 if (get_edit_op_range (start, end)) {
2870 crop_region_to (start, end);
2877 Editor::crop_region_to (framepos_t start, framepos_t end)
2879 vector<boost::shared_ptr<Playlist> > playlists;
2880 boost::shared_ptr<Playlist> playlist;
2883 if (selection->tracks.empty()) {
2884 ts = track_views.filter_to_unique_playlists();
2886 ts = selection->tracks.filter_to_unique_playlists ();
2889 sort_track_selection (ts);
2891 for (TrackSelection::iterator i = ts.begin(); i != ts.end(); ++i) {
2893 RouteTimeAxisView* rtv;
2895 if ((rtv = dynamic_cast<RouteTimeAxisView*> ((*i))) != 0) {
2897 boost::shared_ptr<Track> t = rtv->track();
2899 if (t != 0 && ! t->destructive()) {
2901 if ((playlist = rtv->playlist()) != 0) {
2902 playlists.push_back (playlist);
2908 if (playlists.empty()) {
2912 framepos_t the_start;
2916 begin_reversible_command (_("trim to selection"));
2918 for (vector<boost::shared_ptr<Playlist> >::iterator i = playlists.begin(); i != playlists.end(); ++i) {
2920 boost::shared_ptr<Region> region;
2924 if ((region = (*i)->top_region_at(the_start)) == 0) {
2928 /* now adjust lengths to that we do the right thing
2929 if the selection extends beyond the region
2932 the_start = max (the_start, (framepos_t) region->position());
2933 if (max_framepos - the_start < region->length()) {
2934 the_end = the_start + region->length() - 1;
2936 the_end = max_framepos;
2938 the_end = min (end, the_end);
2939 cnt = the_end - the_start + 1;
2941 region->clear_changes ();
2942 region->trim_to (the_start, cnt);
2943 _session->add_command (new StatefulDiffCommand (region));
2946 commit_reversible_command ();
2950 Editor::region_fill_track ()
2952 RegionSelection rs = get_regions_from_selection_and_entered ();
2954 if (!_session || rs.empty()) {
2958 framepos_t const end = _session->current_end_frame ();
2960 begin_reversible_command (Operations::region_fill);
2962 for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
2964 boost::shared_ptr<Region> region ((*i)->region());
2966 boost::shared_ptr<Playlist> pl = region->playlist();
2968 if (end <= region->last_frame()) {
2972 double times = (double) (end - region->last_frame()) / (double) region->length();
2978 pl->clear_changes ();
2979 pl->add_region (RegionFactory::create (region, true), region->last_frame(), times);
2980 _session->add_command (new StatefulDiffCommand (pl));
2983 commit_reversible_command ();
2987 Editor::region_fill_selection ()
2989 if (clicked_routeview == 0 || !clicked_routeview->is_audio_track()) {
2993 if (selection->time.empty()) {
2997 boost::shared_ptr<Region> region = _regions->get_single_selection ();
3002 framepos_t start = selection->time[clicked_selection].start;
3003 framepos_t end = selection->time[clicked_selection].end;
3005 boost::shared_ptr<Playlist> playlist;
3007 if (selection->tracks.empty()) {
3011 framepos_t selection_length = end - start;
3012 float times = (float)selection_length / region->length();
3014 begin_reversible_command (Operations::fill_selection);
3016 TrackViewList ts = selection->tracks.filter_to_unique_playlists ();
3018 for (TrackViewList::iterator i = ts.begin(); i != ts.end(); ++i) {
3020 if ((playlist = (*i)->playlist()) == 0) {
3024 playlist->clear_changes ();
3025 playlist->add_region (RegionFactory::create (region, true), start, times);
3026 _session->add_command (new StatefulDiffCommand (playlist));
3029 commit_reversible_command ();
3033 Editor::set_region_sync_position ()
3035 set_sync_point (get_preferred_edit_position (), get_regions_from_selection_and_edit_point ());
3039 Editor::set_sync_point (framepos_t where, const RegionSelection& rs)
3041 bool in_command = false;
3043 for (RegionSelection::const_iterator r = rs.begin(); r != rs.end(); ++r) {
3045 if (!(*r)->region()->covers (where)) {
3049 boost::shared_ptr<Region> region ((*r)->region());
3052 begin_reversible_command (_("set sync point"));
3056 region->clear_changes ();
3057 region->set_sync_position (where);
3058 _session->add_command(new StatefulDiffCommand (region));
3062 commit_reversible_command ();
3066 /** Remove the sync positions of the selection */
3068 Editor::remove_region_sync ()
3070 RegionSelection rs = get_regions_from_selection_and_entered ();
3076 begin_reversible_command (_("remove region sync"));
3078 for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
3080 (*i)->region()->clear_changes ();
3081 (*i)->region()->clear_sync_position ();
3082 _session->add_command(new StatefulDiffCommand ((*i)->region()));
3085 commit_reversible_command ();
3089 Editor::naturalize_region ()
3091 RegionSelection rs = get_regions_from_selection_and_entered ();
3097 if (rs.size() > 1) {
3098 begin_reversible_command (_("move regions to original position"));
3100 begin_reversible_command (_("move region to original position"));
3103 for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
3104 (*i)->region()->clear_changes ();
3105 (*i)->region()->move_to_natural_position ();
3106 _session->add_command (new StatefulDiffCommand ((*i)->region()));
3109 commit_reversible_command ();
3113 Editor::align_regions (RegionPoint what)
3115 RegionSelection const rs = get_regions_from_selection_and_edit_point ();
3121 begin_reversible_command (_("align selection"));
3123 framepos_t const position = get_preferred_edit_position ();
3125 for (RegionSelection::const_iterator i = rs.begin(); i != rs.end(); ++i) {
3126 align_region_internal ((*i)->region(), what, position);
3129 commit_reversible_command ();
3132 struct RegionSortByTime {
3133 bool operator() (const RegionView* a, const RegionView* b) {
3134 return a->region()->position() < b->region()->position();
3139 Editor::align_regions_relative (RegionPoint point)
3141 RegionSelection const rs = get_regions_from_selection_and_edit_point ();
3147 framepos_t const position = get_preferred_edit_position ();
3149 framepos_t distance = 0;
3153 list<RegionView*> sorted;
3154 rs.by_position (sorted);
3156 boost::shared_ptr<Region> r ((*sorted.begin())->region());
3161 if (position > r->position()) {
3162 distance = position - r->position();
3164 distance = r->position() - position;
3170 if (position > r->last_frame()) {
3171 distance = position - r->last_frame();
3172 pos = r->position() + distance;
3174 distance = r->last_frame() - position;
3175 pos = r->position() - distance;
3181 pos = r->adjust_to_sync (position);
3182 if (pos > r->position()) {
3183 distance = pos - r->position();
3185 distance = r->position() - pos;
3191 if (pos == r->position()) {
3195 begin_reversible_command (_("align selection (relative)"));
3197 /* move first one specially */
3199 r->clear_changes ();
3200 r->set_position (pos);
3201 _session->add_command(new StatefulDiffCommand (r));
3203 /* move rest by the same amount */
3207 for (list<RegionView*>::iterator i = sorted.begin(); i != sorted.end(); ++i) {
3209 boost::shared_ptr<Region> region ((*i)->region());
3211 region->clear_changes ();
3214 region->set_position (region->position() + distance);
3216 region->set_position (region->position() - distance);
3219 _session->add_command(new StatefulDiffCommand (region));
3223 commit_reversible_command ();
3227 Editor::align_region (boost::shared_ptr<Region> region, RegionPoint point, framepos_t position)
3229 begin_reversible_command (_("align region"));
3230 align_region_internal (region, point, position);
3231 commit_reversible_command ();
3235 Editor::align_region_internal (boost::shared_ptr<Region> region, RegionPoint point, framepos_t position)
3237 region->clear_changes ();
3241 region->set_position (region->adjust_to_sync (position));
3245 if (position > region->length()) {
3246 region->set_position (position - region->length());
3251 region->set_position (position);
3255 _session->add_command(new StatefulDiffCommand (region));
3259 Editor::trim_region_front ()
3265 Editor::trim_region_back ()
3267 trim_region (false);
3271 Editor::trim_region (bool front)
3273 framepos_t where = get_preferred_edit_position();
3274 RegionSelection rs = get_regions_from_selection_and_edit_point ();
3280 begin_reversible_command (front ? _("trim front") : _("trim back"));
3282 for (list<RegionView*>::const_iterator i = rs.by_layer().begin(); i != rs.by_layer().end(); ++i) {
3283 if (!(*i)->region()->locked()) {
3285 (*i)->region()->clear_changes ();
3288 (*i)->region()->trim_front (where);
3289 maybe_locate_with_edit_preroll ( where );
3291 (*i)->region()->trim_end (where);
3292 maybe_locate_with_edit_preroll ( where );
3295 _session->add_command (new StatefulDiffCommand ((*i)->region()));
3299 commit_reversible_command ();
3302 /** Trim the end of the selected regions to the position of the edit cursor */
3304 Editor::trim_region_to_loop ()
3306 Location* loc = _session->locations()->auto_loop_location();
3310 trim_region_to_location (*loc, _("trim to loop"));
3314 Editor::trim_region_to_punch ()
3316 Location* loc = _session->locations()->auto_punch_location();
3320 trim_region_to_location (*loc, _("trim to punch"));
3324 Editor::trim_region_to_location (const Location& loc, const char* str)
3326 RegionSelection rs = get_regions_from_selection_and_entered ();
3328 begin_reversible_command (str);
3330 for (RegionSelection::iterator x = rs.begin(); x != rs.end(); ++x) {
3331 RegionView* rv = (*x);
3333 /* require region to span proposed trim */
3334 switch (rv->region()->coverage (loc.start(), loc.end())) {
3335 case Evoral::OverlapInternal:
3341 RouteTimeAxisView* tav = dynamic_cast<RouteTimeAxisView*> (&rv->get_time_axis_view());
3350 if (tav->track() != 0) {
3351 speed = tav->track()->speed();
3354 start = session_frame_to_track_frame (loc.start(), speed);
3355 end = session_frame_to_track_frame (loc.end(), speed);
3357 rv->region()->clear_changes ();
3358 rv->region()->trim_to (start, (end - start));
3359 _session->add_command(new StatefulDiffCommand (rv->region()));
3362 commit_reversible_command ();
3366 Editor::trim_region_to_previous_region_end ()
3368 return trim_to_region(false);
3372 Editor::trim_region_to_next_region_start ()
3374 return trim_to_region(true);
3378 Editor::trim_to_region(bool forward)
3380 RegionSelection rs = get_regions_from_selection_and_entered ();
3382 begin_reversible_command (_("trim to region"));
3384 boost::shared_ptr<Region> next_region;
3386 for (RegionSelection::iterator x = rs.begin(); x != rs.end(); ++x) {
3388 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (*x);
3394 AudioTimeAxisView* atav = dynamic_cast<AudioTimeAxisView*> (&arv->get_time_axis_view());
3402 if (atav->track() != 0) {
3403 speed = atav->track()->speed();
3407 boost::shared_ptr<Region> region = arv->region();
3408 boost::shared_ptr<Playlist> playlist (region->playlist());
3410 region->clear_changes ();
3414 next_region = playlist->find_next_region (region->first_frame(), Start, 1);
3420 region->trim_end((framepos_t) ( (next_region->first_frame() - 1) * speed));
3421 arv->region_changed (PropertyChange (ARDOUR::Properties::length));
3425 next_region = playlist->find_next_region (region->first_frame(), Start, 0);
3431 region->trim_front((framepos_t) ((next_region->last_frame() + 1) * speed));
3433 arv->region_changed (ARDOUR::bounds_change);
3436 _session->add_command(new StatefulDiffCommand (region));
3439 commit_reversible_command ();
3443 Editor::unfreeze_route ()
3445 if (clicked_routeview == 0 || !clicked_routeview->is_track()) {
3449 clicked_routeview->track()->unfreeze ();
3453 Editor::_freeze_thread (void* arg)
3455 return static_cast<Editor*>(arg)->freeze_thread ();
3459 Editor::freeze_thread ()
3461 /* create event pool because we may need to talk to the session */
3462 SessionEvent::create_per_thread_pool ("freeze events", 64);
3463 /* create per-thread buffers for process() tree to use */
3464 current_interthread_info->process_thread.get_buffers ();
3465 clicked_routeview->audio_track()->freeze_me (*current_interthread_info);
3466 current_interthread_info->done = true;
3467 current_interthread_info->process_thread.drop_buffers();
3472 Editor::freeze_route ()
3478 /* stop transport before we start. this is important */
3480 _session->request_transport_speed (0.0);
3482 /* wait for just a little while, because the above call is asynchronous */
3486 if (clicked_routeview == 0 || !clicked_routeview->is_audio_track()) {
3490 if (!clicked_routeview->track()->bounceable (clicked_routeview->track()->main_outs(), true)) {
3492 _("This track/bus cannot be frozen because the signal adds or loses channels before reaching the outputs.\n"
3493 "This is typically caused by plugins that generate stereo output from mono input or vice versa.")
3495 d.set_title (_("Cannot freeze"));
3500 if (clicked_routeview->track()->has_external_redirects()) {
3501 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"
3502 "Freezing will only process the signal as far as the first send/insert/return."),
3503 clicked_routeview->track()->name()), true, MESSAGE_INFO, BUTTONS_NONE, true);
3505 d.add_button (_("Freeze anyway"), Gtk::RESPONSE_OK);
3506 d.add_button (_("Don't freeze"), Gtk::RESPONSE_CANCEL);
3507 d.set_title (_("Freeze Limits"));
3509 int response = d.run ();
3512 case Gtk::RESPONSE_CANCEL:
3519 InterThreadInfo itt;
3520 current_interthread_info = &itt;
3522 InterthreadProgressWindow ipw (current_interthread_info, _("Freeze"), _("Cancel Freeze"));
3524 pthread_create_and_store (X_("freezer"), &itt.thread, _freeze_thread, this);
3526 set_canvas_cursor (_cursors->wait);
3528 while (!itt.done && !itt.cancel) {
3529 gtk_main_iteration ();
3532 current_interthread_info = 0;
3533 set_canvas_cursor (current_canvas_cursor);
3537 Editor::bounce_range_selection (bool replace, bool enable_processing)
3539 if (selection->time.empty()) {
3543 TrackSelection views = selection->tracks;
3545 for (TrackViewList::iterator i = views.begin(); i != views.end(); ++i) {
3547 if (enable_processing) {
3549 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (*i);
3551 if (rtv && rtv->track() && replace && enable_processing && !rtv->track()->bounceable (rtv->track()->main_outs(), false)) {
3553 _("You can't perform this operation because the processing of the signal "
3554 "will cause one or more of the tracks to end up with a region with more channels than this track has inputs.\n\n"
3555 "You can do this without processing, which is a different operation.")
3557 d.set_title (_("Cannot bounce"));
3564 framepos_t start = selection->time[clicked_selection].start;
3565 framepos_t end = selection->time[clicked_selection].end;
3566 framepos_t cnt = end - start + 1;
3568 begin_reversible_command (_("bounce range"));
3570 for (TrackViewList::iterator i = views.begin(); i != views.end(); ++i) {
3572 RouteTimeAxisView* rtv;
3574 if ((rtv = dynamic_cast<RouteTimeAxisView*> (*i)) == 0) {
3578 boost::shared_ptr<Playlist> playlist;
3580 if ((playlist = rtv->playlist()) == 0) {
3584 InterThreadInfo itt;
3586 playlist->clear_changes ();
3587 playlist->clear_owned_changes ();
3589 boost::shared_ptr<Region> r;
3591 if (enable_processing) {
3592 r = rtv->track()->bounce_range (start, start+cnt, itt, rtv->track()->main_outs(), false);
3594 r = rtv->track()->bounce_range (start, start+cnt, itt, boost::shared_ptr<Processor>(), false);
3602 list<AudioRange> ranges;
3603 ranges.push_back (AudioRange (start, start+cnt, 0));
3604 playlist->cut (ranges); // discard result
3605 playlist->add_region (r, start);
3608 vector<Command*> cmds;
3609 playlist->rdiff (cmds);
3610 _session->add_commands (cmds);
3612 _session->add_command (new StatefulDiffCommand (playlist));
3615 commit_reversible_command ();
3618 /** Delete selected regions, automation points or a time range */
3625 /** Cut selected regions, automation points or a time range */
3632 /** Copy selected regions, automation points or a time range */
3640 /** @return true if a Cut, Copy or Clear is possible */
3642 Editor::can_cut_copy () const
3644 switch (effective_mouse_mode()) {
3647 if (!selection->regions.empty() || !selection->points.empty()) {
3653 if (!selection->time.empty()) {
3666 /** Cut, copy or clear selected regions, automation points or a time range.
3667 * @param op Operation (Cut, Copy or Clear)
3670 Editor::cut_copy (CutCopyOp op)
3672 /* only cancel selection if cut/copy is successful.*/
3678 opname = _("delete");
3687 opname = _("clear");
3691 /* if we're deleting something, and the mouse is still pressed,
3692 the thing we started a drag for will be gone when we release
3693 the mouse button(s). avoid this. see part 2 at the end of
3697 if (op == Delete || op == Cut || op == Clear) {
3698 if (_drags->active ()) {
3703 if ( op != Clear ) //"Delete" doesn't change copy/paste buf
3704 cut_buffer->clear ();
3706 if (entered_marker) {
3708 /* cut/delete op while pointing at a marker */
3711 Location* loc = find_location_from_marker (entered_marker, ignored);
3713 if (_session && loc) {
3714 Glib::signal_idle().connect (sigc::bind (sigc::mem_fun(*this, &Editor::really_remove_marker), loc));
3721 if (internal_editing()) {
3723 switch (effective_mouse_mode()) {
3736 /* we only want to cut regions if some are selected */
3738 if (!selection->regions.empty()) {
3739 rs = selection->regions;
3742 switch (effective_mouse_mode()) {
3745 //find regions's gain line
3746 AudioRegionView *rview = dynamic_cast<AudioRegionView*>(clicked_regionview);
3747 AutomationTimeAxisView *tview = dynamic_cast<AutomationTimeAxisView*>(clicked_trackview);
3749 AudioRegionGainLine *line = rview->get_gain_line();
3752 //cut region gain points in the selection
3753 AutomationList& alist (line->the_list());
3754 XMLNode &before = alist.get_state();
3755 AutomationList* what_we_got = 0;
3756 if ((what_we_got = alist.cut (selection->time.front().start - rview->audio_region()->position(), selection->time.front().end - rview->audio_region()->position())) != 0) {
3757 session->add_command(new MementoCommand<AutomationList>(alist, &before, &alist.get_state()));
3762 rview->set_envelope_visible(true);
3763 rview->audio_region()->set_envelope_active(true);
3766 AutomationLine *line = *(tview->lines.begin());
3769 //cut auto points in the selection
3770 AutomationList& alist (line->the_list());
3771 XMLNode &before = alist.get_state();
3772 AutomationList* what_we_got = 0;
3773 if ((what_we_got = alist.cut (selection->time.front().start, selection->time.front().end)) != 0) {
3774 session->add_command(new MementoCommand<AutomationList>(alist, &before, &alist.get_state()));
3784 if (!rs.empty() || !selection->points.empty()) {
3785 begin_reversible_command (opname + _(" objects"));
3788 cut_copy_regions (op, rs);
3790 if (op == Cut || op == Delete) {
3791 selection->clear_regions ();
3795 if (!selection->points.empty()) {
3796 cut_copy_points (op);
3798 if (op == Cut || op == Delete) {
3799 selection->clear_points ();
3803 commit_reversible_command ();
3807 if (selection->time.empty()) {
3808 framepos_t start, end;
3809 if (!get_edit_op_range (start, end)) {
3812 selection->set (start, end);
3815 begin_reversible_command (opname + _(" range"));
3816 cut_copy_ranges (op);
3817 commit_reversible_command ();
3819 if (op == Cut || op == Delete) {
3820 selection->clear_time ();
3830 if (op == Delete || op == Cut || op == Clear) {
3835 struct AutomationRecord {
3836 AutomationRecord () : state (0) {}
3837 AutomationRecord (XMLNode* s) : state (s) {}
3839 XMLNode* state; ///< state before any operation
3840 boost::shared_ptr<Evoral::ControlList> copy; ///< copied events for the cut buffer
3843 /** Cut, copy or clear selected automation points.
3844 * @param op Operation (Cut, Copy or Clear)
3847 Editor::cut_copy_points (CutCopyOp op)
3849 if (selection->points.empty ()) {
3853 /* XXX: not ideal, as there may be more than one track involved in the point selection */
3854 _last_cut_copy_source_track = &selection->points.front()->line().trackview;
3856 /* Keep a record of the AutomationLists that we end up using in this operation */
3857 typedef std::map<boost::shared_ptr<AutomationList>, AutomationRecord> Lists;
3860 /* Go through all selected points, making an AutomationRecord for each distinct AutomationList */
3861 for (PointSelection::iterator i = selection->points.begin(); i != selection->points.end(); ++i) {
3862 boost::shared_ptr<AutomationList> al = (*i)->line().the_list();
3863 if (lists.find (al) == lists.end ()) {
3864 /* We haven't seen this list yet, so make a record for it. This includes
3865 taking a copy of its current state, in case this is needed for undo later.
3867 lists[al] = AutomationRecord (&al->get_state ());
3871 if (op == Cut || op == Copy) {
3872 /* This operation will involve putting things in the cut buffer, so create an empty
3873 ControlList for each of our source lists to put the cut buffer data in.
3875 for (Lists::iterator i = lists.begin(); i != lists.end(); ++i) {
3876 i->second.copy = i->first->create (i->first->parameter ());
3879 /* Add all selected points to the relevant copy ControlLists */
3880 for (PointSelection::iterator i = selection->points.begin(); i != selection->points.end(); ++i) {
3881 boost::shared_ptr<AutomationList> al = (*i)->line().the_list();
3882 AutomationList::const_iterator j = (*i)->model ();
3883 lists[al].copy->add ((*j)->when, (*j)->value);
3886 for (Lists::iterator i = lists.begin(); i != lists.end(); ++i) {
3887 /* Correct this copy list so that it starts at time 0 */
3888 double const start = i->second.copy->front()->when;
3889 for (AutomationList::iterator j = i->second.copy->begin(); j != i->second.copy->end(); ++j) {
3890 (*j)->when -= start;
3893 /* And add it to the cut buffer */
3894 cut_buffer->add (i->second.copy);
3898 if (op == Delete || op == Cut) {
3899 /* This operation needs to remove things from the main AutomationList, so do that now */
3901 for (Lists::iterator i = lists.begin(); i != lists.end(); ++i) {
3902 i->first->freeze ();
3905 /* Remove each selected point from its AutomationList */
3906 for (PointSelection::iterator i = selection->points.begin(); i != selection->points.end(); ++i) {
3907 boost::shared_ptr<AutomationList> al = (*i)->line().the_list();
3908 al->erase ((*i)->model ());
3911 /* Thaw the lists and add undo records for them */
3912 for (Lists::iterator i = lists.begin(); i != lists.end(); ++i) {
3913 boost::shared_ptr<AutomationList> al = i->first;
3915 _session->add_command (new MementoCommand<AutomationList> (*al.get(), i->second.state, &(al->get_state ())));
3920 /** Cut, copy or clear selected automation points.
3921 * @param op Operation (Cut, Copy or Clear)
3924 Editor::cut_copy_midi (CutCopyOp op)
3926 for (MidiRegionSelection::iterator i = selection->midi_regions.begin(); i != selection->midi_regions.end(); ++i) {
3927 MidiRegionView* mrv = *i;
3928 mrv->cut_copy_clear (op);
3934 struct lt_playlist {
3935 bool operator () (const PlaylistState& a, const PlaylistState& b) {
3936 return a.playlist < b.playlist;
3940 struct PlaylistMapping {
3942 boost::shared_ptr<Playlist> pl;
3944 PlaylistMapping (TimeAxisView* tvp) : tv (tvp) {}
3947 /** Remove `clicked_regionview' */
3949 Editor::remove_clicked_region ()
3951 if (clicked_routeview == 0 || clicked_regionview == 0) {
3955 boost::shared_ptr<Playlist> playlist = clicked_routeview->playlist();
3957 begin_reversible_command (_("remove region"));
3958 playlist->clear_changes ();
3959 playlist->clear_owned_changes ();
3960 playlist->remove_region (clicked_regionview->region());
3962 /* We might have removed regions, which alters other regions' layering_index,
3963 so we need to do a recursive diff here.
3965 vector<Command*> cmds;
3966 playlist->rdiff (cmds);
3967 _session->add_commands (cmds);
3969 _session->add_command(new StatefulDiffCommand (playlist));
3970 commit_reversible_command ();
3974 /** Remove the selected regions */
3976 Editor::remove_selected_regions ()
3978 RegionSelection rs = get_regions_from_selection_and_entered ();
3980 if (!_session || rs.empty()) {
3984 begin_reversible_command (_("remove region"));
3986 list<boost::shared_ptr<Region> > regions_to_remove;
3988 for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
3989 // we can't just remove the region(s) in this loop because
3990 // this removes them from the RegionSelection, and they thus
3991 // disappear from underneath the iterator, and the ++i above
3992 // SEGVs in a puzzling fashion.
3994 // so, first iterate over the regions to be removed from rs and
3995 // add them to the regions_to_remove list, and then
3996 // iterate over the list to actually remove them.
3998 regions_to_remove.push_back ((*i)->region());
4001 vector<boost::shared_ptr<Playlist> > playlists;
4003 for (list<boost::shared_ptr<Region> >::iterator rl = regions_to_remove.begin(); rl != regions_to_remove.end(); ++rl) {
4005 boost::shared_ptr<Playlist> playlist = (*rl)->playlist();
4008 // is this check necessary?
4012 /* get_regions_from_selection_and_entered() guarantees that
4013 the playlists involved are unique, so there is no need
4017 playlists.push_back (playlist);
4019 playlist->clear_changes ();
4020 playlist->clear_owned_changes ();
4021 playlist->freeze ();
4022 playlist->remove_region (*rl);
4025 vector<boost::shared_ptr<Playlist> >::iterator pl;
4027 for (pl = playlists.begin(); pl != playlists.end(); ++pl) {
4030 /* We might have removed regions, which alters other regions' layering_index,
4031 so we need to do a recursive diff here.
4033 vector<Command*> cmds;
4034 (*pl)->rdiff (cmds);
4035 _session->add_commands (cmds);
4037 _session->add_command(new StatefulDiffCommand (*pl));
4040 commit_reversible_command ();
4043 /** Cut, copy or clear selected regions.
4044 * @param op Operation (Cut, Copy or Clear)
4047 Editor::cut_copy_regions (CutCopyOp op, RegionSelection& rs)
4049 /* we can't use a std::map here because the ordering is important, and we can't trivially sort
4050 a map when we want ordered access to both elements. i think.
4053 vector<PlaylistMapping> pmap;
4055 framepos_t first_position = max_framepos;
4057 typedef set<boost::shared_ptr<Playlist> > FreezeList;
4058 FreezeList freezelist;
4060 /* get ordering correct before we cut/copy */
4062 rs.sort_by_position_and_track ();
4064 for (RegionSelection::iterator x = rs.begin(); x != rs.end(); ++x) {
4066 first_position = min ((framepos_t) (*x)->region()->position(), first_position);
4068 if (op == Cut || op == Clear || op == Delete) {
4069 boost::shared_ptr<Playlist> pl = (*x)->region()->playlist();
4072 FreezeList::iterator fl;
4074 // only take state if this is a new playlist.
4075 for (fl = freezelist.begin(); fl != freezelist.end(); ++fl) {
4081 if (fl == freezelist.end()) {
4082 pl->clear_changes();
4083 pl->clear_owned_changes ();
4085 freezelist.insert (pl);
4090 TimeAxisView* tv = &(*x)->get_time_axis_view();
4091 vector<PlaylistMapping>::iterator z;
4093 for (z = pmap.begin(); z != pmap.end(); ++z) {
4094 if ((*z).tv == tv) {
4099 if (z == pmap.end()) {
4100 pmap.push_back (PlaylistMapping (tv));
4104 for (RegionSelection::iterator x = rs.begin(); x != rs.end(); ) {
4106 boost::shared_ptr<Playlist> pl = (*x)->region()->playlist();
4109 /* region not yet associated with a playlist (e.g. unfinished
4116 TimeAxisView& tv = (*x)->get_time_axis_view();
4117 boost::shared_ptr<Playlist> npl;
4118 RegionSelection::iterator tmp;
4125 vector<PlaylistMapping>::iterator z;
4127 for (z = pmap.begin(); z != pmap.end(); ++z) {
4128 if ((*z).tv == &tv) {
4133 assert (z != pmap.end());
4136 npl = PlaylistFactory::create (pl->data_type(), *_session, "cutlist", true);
4144 boost::shared_ptr<Region> r = (*x)->region();
4145 boost::shared_ptr<Region> _xx;
4151 pl->remove_region (r);
4155 _xx = RegionFactory::create (r);
4156 npl->add_region (_xx, r->position() - first_position);
4157 pl->remove_region (r);
4161 /* copy region before adding, so we're not putting same object into two different playlists */
4162 npl->add_region (RegionFactory::create (r), r->position() - first_position);
4166 pl->remove_region (r);
4175 list<boost::shared_ptr<Playlist> > foo;
4177 /* the pmap is in the same order as the tracks in which selected regions occured */
4179 for (vector<PlaylistMapping>::iterator i = pmap.begin(); i != pmap.end(); ++i) {
4182 foo.push_back ((*i).pl);
4187 cut_buffer->set (foo);
4191 _last_cut_copy_source_track = 0;
4193 _last_cut_copy_source_track = pmap.front().tv;
4197 for (FreezeList::iterator pl = freezelist.begin(); pl != freezelist.end(); ++pl) {
4200 /* We might have removed regions, which alters other regions' layering_index,
4201 so we need to do a recursive diff here.
4203 vector<Command*> cmds;
4204 (*pl)->rdiff (cmds);
4205 _session->add_commands (cmds);
4207 _session->add_command (new StatefulDiffCommand (*pl));
4212 Editor::cut_copy_ranges (CutCopyOp op)
4214 TrackViewList ts = selection->tracks.filter_to_unique_playlists ();
4216 /* Sort the track selection now, so that it if is used, the playlists
4217 selected by the calls below to cut_copy_clear are in the order that
4218 their tracks appear in the editor. This makes things like paste
4219 of ranges work properly.
4222 sort_track_selection (ts);
4225 if (!entered_track) {
4228 ts.push_back (entered_track);
4231 for (TrackViewList::iterator i = ts.begin(); i != ts.end(); ++i) {
4232 (*i)->cut_copy_clear (*selection, op);
4237 Editor::paste (float times, bool from_context)
4239 DEBUG_TRACE (DEBUG::CutNPaste, "paste to preferred edit pos\n");
4241 paste_internal (get_preferred_edit_position (false, from_context), times);
4245 Editor::mouse_paste ()
4250 if (!mouse_frame (where, ignored)) {
4255 paste_internal (where, 1);
4259 Editor::paste_internal (framepos_t position, float times)
4261 DEBUG_TRACE (DEBUG::CutNPaste, string_compose ("apparent paste position is %1\n", position));
4263 if (internal_editing()) {
4264 if (cut_buffer->midi_notes.empty()) {
4268 if (cut_buffer->empty()) {
4273 if (position == max_framepos) {
4274 position = get_preferred_edit_position();
4275 DEBUG_TRACE (DEBUG::CutNPaste, string_compose ("preferred edit position is %1\n", position));
4279 TrackViewList::iterator i;
4282 /* get everything in the correct order */
4284 if (_edit_point == Editing::EditAtMouse && entered_track) {
4285 /* With the mouse edit point, paste onto the track under the mouse */
4286 ts.push_back (entered_track);
4287 } else if (!selection->tracks.empty()) {
4288 /* Otherwise, if there are some selected tracks, paste to them */
4289 ts = selection->tracks.filter_to_unique_playlists ();
4290 sort_track_selection (ts);
4291 } else if (_last_cut_copy_source_track) {
4292 /* Otherwise paste to the track that the cut/copy came from;
4293 see discussion in mantis #3333.
4295 ts.push_back (_last_cut_copy_source_track);
4298 if (internal_editing ()) {
4300 /* undo/redo is handled by individual tracks/regions */
4302 for (nth = 0, i = ts.begin(); i != ts.end(); ++i, ++nth) {
4305 RegionSelection::iterator r;
4306 MidiNoteSelection::iterator cb;
4308 get_regions_at (rs, position, ts);
4310 for (cb = cut_buffer->midi_notes.begin(), r = rs.begin();
4311 cb != cut_buffer->midi_notes.end() && r != rs.end(); ++r) {
4312 MidiRegionView* mrv = dynamic_cast<MidiRegionView*> (*r);
4314 mrv->paste (position, times, **cb);
4322 /* we do redo (do you do voodoo?) */
4324 begin_reversible_command (Operations::paste);
4326 for (nth = 0, i = ts.begin(); i != ts.end(); ++i, ++nth) {
4327 (*i)->paste (position, times, *cut_buffer, nth);
4330 commit_reversible_command ();
4335 Editor::duplicate_some_regions (RegionSelection& regions, float times)
4337 boost::shared_ptr<Playlist> playlist;
4338 RegionSelection sel = regions; // clear (below) may clear the argument list if its the current region selection
4339 RegionSelection foo;
4341 framepos_t const start_frame = regions.start ();
4342 framepos_t const end_frame = regions.end_frame ();
4344 begin_reversible_command (Operations::duplicate_region);
4346 selection->clear_regions ();
4348 for (RegionSelection::iterator i = sel.begin(); i != sel.end(); ++i) {
4350 boost::shared_ptr<Region> r ((*i)->region());
4352 TimeAxisView& tv = (*i)->get_time_axis_view();
4353 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (&tv);
4354 latest_regionviews.clear ();
4355 sigc::connection c = rtv->view()->RegionViewAdded.connect (sigc::mem_fun(*this, &Editor::collect_new_region_view));
4357 playlist = (*i)->region()->playlist();
4358 playlist->clear_changes ();
4359 playlist->duplicate (r, end_frame + (r->first_frame() - start_frame), times);
4360 _session->add_command(new StatefulDiffCommand (playlist));
4364 foo.insert (foo.end(), latest_regionviews.begin(), latest_regionviews.end());
4367 commit_reversible_command ();
4370 selection->set (foo);
4375 Editor::duplicate_selection (float times)
4377 if (selection->time.empty() || selection->tracks.empty()) {
4381 boost::shared_ptr<Playlist> playlist;
4382 vector<boost::shared_ptr<Region> > new_regions;
4383 vector<boost::shared_ptr<Region> >::iterator ri;
4385 create_region_from_selection (new_regions);
4387 if (new_regions.empty()) {
4391 begin_reversible_command (_("duplicate selection"));
4393 ri = new_regions.begin();
4395 TrackViewList ts = selection->tracks.filter_to_unique_playlists ();
4397 for (TrackViewList::iterator i = ts.begin(); i != ts.end(); ++i) {
4398 if ((playlist = (*i)->playlist()) == 0) {
4401 playlist->clear_changes ();
4402 playlist->duplicate (*ri, selection->time[clicked_selection].end, times);
4403 _session->add_command (new StatefulDiffCommand (playlist));
4406 if (ri == new_regions.end()) {
4411 commit_reversible_command ();
4414 /** Reset all selected points to the relevant default value */
4416 Editor::reset_point_selection ()
4418 for (PointSelection::iterator i = selection->points.begin(); i != selection->points.end(); ++i) {
4419 ARDOUR::AutomationList::iterator j = (*i)->model ();
4420 (*j)->value = (*i)->line().the_list()->default_value ();
4425 Editor::center_playhead ()
4427 float page = _canvas_width * frames_per_unit;
4428 center_screen_internal (playhead_cursor->current_frame, page);
4432 Editor::center_edit_point ()
4434 float page = _canvas_width * frames_per_unit;
4435 center_screen_internal (get_preferred_edit_position(), page);
4438 /** Caller must begin and commit a reversible command */
4440 Editor::clear_playlist (boost::shared_ptr<Playlist> playlist)
4442 playlist->clear_changes ();
4444 _session->add_command (new StatefulDiffCommand (playlist));
4448 Editor::nudge_track (bool use_edit, bool forwards)
4450 boost::shared_ptr<Playlist> playlist;
4451 framepos_t distance;
4452 framepos_t next_distance;
4456 start = get_preferred_edit_position();
4461 if ((distance = get_nudge_distance (start, next_distance)) == 0) {
4465 if (selection->tracks.empty()) {
4469 begin_reversible_command (_("nudge track"));
4471 TrackViewList ts = selection->tracks.filter_to_unique_playlists ();
4473 for (TrackViewList::iterator i = ts.begin(); i != ts.end(); ++i) {
4475 if ((playlist = (*i)->playlist()) == 0) {
4479 playlist->clear_changes ();
4480 playlist->clear_owned_changes ();
4482 playlist->nudge_after (start, distance, forwards);
4484 vector<Command*> cmds;
4486 playlist->rdiff (cmds);
4487 _session->add_commands (cmds);
4489 _session->add_command (new StatefulDiffCommand (playlist));
4492 commit_reversible_command ();
4496 Editor::remove_last_capture ()
4498 vector<string> choices;
4505 if (Config->get_verify_remove_last_capture()) {
4506 prompt = _("Do you really want to destroy the last capture?"
4507 "\n(This is destructive and cannot be undone)");
4509 choices.push_back (_("No, do nothing."));
4510 choices.push_back (_("Yes, destroy it."));
4512 Gtkmm2ext::Choice prompter (_("Destroy last capture"), prompt, choices);
4514 if (prompter.run () == 1) {
4515 _session->remove_last_capture ();
4516 _regions->redisplay ();
4520 _session->remove_last_capture();
4521 _regions->redisplay ();
4526 Editor::normalize_region ()
4532 RegionSelection rs = get_regions_from_selection_and_entered ();
4538 NormalizeDialog dialog (rs.size() > 1);
4540 if (dialog.run () == RESPONSE_CANCEL) {
4544 set_canvas_cursor (_cursors->wait);
4547 /* XXX: should really only count audio regions here */
4548 int const regions = rs.size ();
4550 /* Make a list of the selected audio regions' maximum amplitudes, and also
4551 obtain the maximum amplitude of them all.
4553 list<double> max_amps;
4555 for (RegionSelection::const_iterator i = rs.begin(); i != rs.end(); ++i) {
4556 AudioRegionView const * arv = dynamic_cast<AudioRegionView const *> (*i);
4558 dialog.descend (1.0 / regions);
4559 double const a = arv->audio_region()->maximum_amplitude (&dialog);
4562 /* the user cancelled the operation */
4563 set_canvas_cursor (current_canvas_cursor);
4567 max_amps.push_back (a);
4568 max_amp = max (max_amp, a);
4573 begin_reversible_command (_("normalize"));
4575 list<double>::const_iterator a = max_amps.begin ();
4577 for (RegionSelection::iterator r = rs.begin(); r != rs.end(); ++r) {
4578 AudioRegionView* const arv = dynamic_cast<AudioRegionView*> (*r);
4583 arv->region()->clear_changes ();
4585 double const amp = dialog.normalize_individually() ? *a : max_amp;
4587 arv->audio_region()->normalize (amp, dialog.target ());
4588 _session->add_command (new StatefulDiffCommand (arv->region()));
4593 commit_reversible_command ();
4594 set_canvas_cursor (current_canvas_cursor);
4599 Editor::reset_region_scale_amplitude ()
4605 RegionSelection rs = get_regions_from_selection_and_entered ();
4611 begin_reversible_command ("reset gain");
4613 for (RegionSelection::iterator r = rs.begin(); r != rs.end(); ++r) {
4614 AudioRegionView* const arv = dynamic_cast<AudioRegionView*>(*r);
4617 arv->region()->clear_changes ();
4618 arv->audio_region()->set_scale_amplitude (1.0f);
4619 _session->add_command (new StatefulDiffCommand (arv->region()));
4622 commit_reversible_command ();
4626 Editor::adjust_region_gain (bool up)
4628 RegionSelection rs = get_regions_from_selection_and_entered ();
4630 if (!_session || rs.empty()) {
4634 begin_reversible_command ("adjust region gain");
4636 for (RegionSelection::iterator r = rs.begin(); r != rs.end(); ++r) {
4637 AudioRegionView* const arv = dynamic_cast<AudioRegionView*>(*r);
4642 arv->region()->clear_changes ();
4644 double dB = accurate_coefficient_to_dB (arv->audio_region()->scale_amplitude ());
4652 arv->audio_region()->set_scale_amplitude (dB_to_coefficient (dB));
4653 _session->add_command (new StatefulDiffCommand (arv->region()));
4656 commit_reversible_command ();
4661 Editor::reverse_region ()
4667 Reverse rev (*_session);
4668 apply_filter (rev, _("reverse regions"));
4672 Editor::strip_region_silence ()
4678 RegionSelection rs = get_regions_from_selection_and_entered ();
4684 std::list<RegionView*> audio_only;
4686 for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
4687 AudioRegionView* const arv = dynamic_cast<AudioRegionView*> (*i);
4689 audio_only.push_back (arv);
4693 StripSilenceDialog d (_session, audio_only);
4694 int const r = d.run ();
4698 if (r == Gtk::RESPONSE_OK) {
4699 ARDOUR::AudioIntervalMap silences;
4700 d.silences (silences);
4701 StripSilence s (*_session, silences, d.fade_length());
4702 apply_filter (s, _("strip silence"), &d);
4707 Editor::apply_midi_note_edit_op_to_region (MidiOperator& op, MidiRegionView& mrv)
4709 Evoral::Sequence<Evoral::MusicalTime>::Notes selected;
4710 mrv.selection_as_notelist (selected, true);
4712 vector<Evoral::Sequence<Evoral::MusicalTime>::Notes> v;
4713 v.push_back (selected);
4715 framepos_t pos_frames = mrv.midi_region()->position();
4716 double pos_beats = _session->tempo_map().framewalk_to_beats(0, pos_frames);
4718 return op (mrv.midi_region()->model(), pos_beats, v);
4722 Editor::apply_midi_note_edit_op (MidiOperator& op)
4726 RegionSelection rs = get_regions_from_selection_and_entered ();
4732 begin_reversible_command (op.name ());
4734 for (RegionSelection::iterator r = rs.begin(); r != rs.end(); ) {
4735 RegionSelection::iterator tmp = r;
4738 MidiRegionView* const mrv = dynamic_cast<MidiRegionView*> (*r);
4741 cmd = apply_midi_note_edit_op_to_region (op, *mrv);
4744 _session->add_command (cmd);
4751 commit_reversible_command ();
4755 Editor::fork_region ()
4757 RegionSelection rs = get_regions_from_selection_and_entered ();
4763 begin_reversible_command (_("Fork Region(s)"));
4765 set_canvas_cursor (_cursors->wait);
4768 for (RegionSelection::iterator r = rs.begin(); r != rs.end(); ) {
4769 RegionSelection::iterator tmp = r;
4772 MidiRegionView* const mrv = dynamic_cast<MidiRegionView*>(*r);
4775 boost::shared_ptr<Playlist> playlist = mrv->region()->playlist();
4776 boost::shared_ptr<MidiRegion> newregion = mrv->midi_region()->clone ();
4778 playlist->clear_changes ();
4779 playlist->replace_region (mrv->region(), newregion, mrv->region()->position());
4780 _session->add_command(new StatefulDiffCommand (playlist));
4786 commit_reversible_command ();
4788 set_canvas_cursor (current_canvas_cursor);
4792 Editor::quantize_region ()
4794 int selected_midi_region_cnt = 0;
4800 RegionSelection rs = get_regions_from_selection_and_entered ();
4806 for (RegionSelection::iterator r = rs.begin(); r != rs.end(); ++r) {
4807 MidiRegionView* const mrv = dynamic_cast<MidiRegionView*> (*r);
4809 selected_midi_region_cnt++;
4813 if (selected_midi_region_cnt == 0) {
4817 QuantizeDialog* qd = new QuantizeDialog (*this);
4820 const int r = qd->run ();
4823 if (r == Gtk::RESPONSE_OK) {
4824 Quantize quant (*_session, qd->snap_start(), qd->snap_end(),
4825 qd->start_grid_size(), qd->end_grid_size(),
4826 qd->strength(), qd->swing(), qd->threshold());
4828 apply_midi_note_edit_op (quant);
4833 Editor::insert_patch_change (bool from_context)
4835 RegionSelection rs = get_regions_from_selection_and_entered ();
4841 const framepos_t p = get_preferred_edit_position (false, from_context);
4843 /* XXX: bit of a hack; use the MIDNAM from the first selected region;
4844 there may be more than one, but the PatchChangeDialog can only offer
4845 one set of patch menus.
4847 MidiRegionView* first = dynamic_cast<MidiRegionView*> (rs.front ());
4849 Evoral::PatchChange<Evoral::MusicalTime> empty (0, 0, 0, 0);
4850 PatchChangeDialog d (0, _session, empty, first->instrument_info(), Gtk::Stock::ADD);
4852 if (d.run() == RESPONSE_CANCEL) {
4856 for (RegionSelection::iterator i = rs.begin (); i != rs.end(); ++i) {
4857 MidiRegionView* const mrv = dynamic_cast<MidiRegionView*> (*i);
4859 if (p >= mrv->region()->first_frame() && p <= mrv->region()->last_frame()) {
4860 mrv->add_patch_change (p - mrv->region()->position(), d.patch ());
4867 Editor::apply_filter (Filter& filter, string command, ProgressReporter* progress)
4869 RegionSelection rs = get_regions_from_selection_and_entered ();
4875 begin_reversible_command (command);
4877 set_canvas_cursor (_cursors->wait);
4881 int const N = rs.size ();
4883 for (RegionSelection::iterator r = rs.begin(); r != rs.end(); ) {
4884 RegionSelection::iterator tmp = r;
4887 AudioRegionView* const arv = dynamic_cast<AudioRegionView*>(*r);
4889 boost::shared_ptr<Playlist> playlist = arv->region()->playlist();
4892 progress->descend (1.0 / N);
4895 if (arv->audio_region()->apply (filter, progress) == 0) {
4897 playlist->clear_changes ();
4898 playlist->clear_owned_changes ();
4900 if (filter.results.empty ()) {
4902 /* no regions returned; remove the old one */
4903 playlist->remove_region (arv->region ());
4907 std::vector<boost::shared_ptr<Region> >::iterator res = filter.results.begin ();
4909 /* first region replaces the old one */
4910 playlist->replace_region (arv->region(), *res, (*res)->position());
4914 while (res != filter.results.end()) {
4915 playlist->add_region (*res, (*res)->position());
4921 /* We might have removed regions, which alters other regions' layering_index,
4922 so we need to do a recursive diff here.
4924 vector<Command*> cmds;
4925 playlist->rdiff (cmds);
4926 _session->add_commands (cmds);
4928 _session->add_command(new StatefulDiffCommand (playlist));
4934 progress->ascend ();
4942 commit_reversible_command ();
4945 set_canvas_cursor (current_canvas_cursor);
4949 Editor::external_edit_region ()
4955 Editor::reset_region_gain_envelopes ()
4957 RegionSelection rs = get_regions_from_selection_and_entered ();
4959 if (!_session || rs.empty()) {
4963 _session->begin_reversible_command (_("reset region gain"));
4965 for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
4966 AudioRegionView* const arv = dynamic_cast<AudioRegionView*>(*i);
4968 boost::shared_ptr<AutomationList> alist (arv->audio_region()->envelope());
4969 XMLNode& before (alist->get_state());
4971 arv->audio_region()->set_default_envelope ();
4972 _session->add_command (new MementoCommand<AutomationList>(*arv->audio_region()->envelope().get(), &before, &alist->get_state()));
4976 _session->commit_reversible_command ();
4980 Editor::set_region_gain_visibility (RegionView* rv)
4982 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (rv);
4984 arv->update_envelope_visibility();
4989 Editor::set_gain_envelope_visibility ()
4995 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
4996 AudioTimeAxisView* v = dynamic_cast<AudioTimeAxisView*>(*i);
4998 v->audio_view()->foreach_regionview (sigc::mem_fun (this, &Editor::set_region_gain_visibility));
5004 Editor::toggle_gain_envelope_active ()
5006 if (_ignore_region_action) {
5010 RegionSelection rs = get_regions_from_selection_and_entered ();
5012 if (!_session || rs.empty()) {
5016 _session->begin_reversible_command (_("region gain envelope active"));
5018 for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
5019 AudioRegionView* const arv = dynamic_cast<AudioRegionView*>(*i);
5021 arv->region()->clear_changes ();
5022 arv->audio_region()->set_envelope_active (!arv->audio_region()->envelope_active());
5023 _session->add_command (new StatefulDiffCommand (arv->region()));
5027 _session->commit_reversible_command ();
5031 Editor::toggle_region_lock ()
5033 if (_ignore_region_action) {
5037 RegionSelection rs = get_regions_from_selection_and_entered ();
5039 if (!_session || rs.empty()) {
5043 _session->begin_reversible_command (_("toggle region lock"));
5045 for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
5046 (*i)->region()->clear_changes ();
5047 (*i)->region()->set_locked (!(*i)->region()->locked());
5048 _session->add_command (new StatefulDiffCommand ((*i)->region()));
5051 _session->commit_reversible_command ();
5055 Editor::toggle_region_video_lock ()
5057 if (_ignore_region_action) {
5061 RegionSelection rs = get_regions_from_selection_and_entered ();
5063 if (!_session || rs.empty()) {
5067 _session->begin_reversible_command (_("Toggle Video Lock"));
5069 for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
5070 (*i)->region()->clear_changes ();
5071 (*i)->region()->set_video_locked (!(*i)->region()->video_locked());
5072 _session->add_command (new StatefulDiffCommand ((*i)->region()));
5075 _session->commit_reversible_command ();
5079 Editor::toggle_region_lock_style ()
5081 if (_ignore_region_action) {
5085 RegionSelection rs = get_regions_from_selection_and_entered ();
5087 if (!_session || rs.empty()) {
5091 _session->begin_reversible_command (_("region lock style"));
5093 for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
5094 (*i)->region()->clear_changes ();
5095 PositionLockStyle const ns = (*i)->region()->position_lock_style() == AudioTime ? MusicTime : AudioTime;
5096 (*i)->region()->set_position_lock_style (ns);
5097 _session->add_command (new StatefulDiffCommand ((*i)->region()));
5100 _session->commit_reversible_command ();
5104 Editor::toggle_opaque_region ()
5106 if (_ignore_region_action) {
5110 RegionSelection rs = get_regions_from_selection_and_entered ();
5112 if (!_session || rs.empty()) {
5116 _session->begin_reversible_command (_("change region opacity"));
5118 for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
5119 (*i)->region()->clear_changes ();
5120 (*i)->region()->set_opaque (!(*i)->region()->opaque());
5121 _session->add_command (new StatefulDiffCommand ((*i)->region()));
5124 _session->commit_reversible_command ();
5128 Editor::toggle_record_enable ()
5130 bool new_state = false;
5132 for (TrackSelection::iterator i = selection->tracks.begin(); i != selection->tracks.end(); ++i) {
5133 RouteTimeAxisView *rtav = dynamic_cast<RouteTimeAxisView *>(*i);
5136 if (!rtav->is_track())
5140 new_state = !rtav->track()->record_enabled();
5144 rtav->track()->set_record_enabled (new_state, this);
5149 Editor::toggle_solo ()
5151 bool new_state = false;
5153 boost::shared_ptr<RouteList> rl (new RouteList);
5155 for (TrackSelection::iterator i = selection->tracks.begin(); i != selection->tracks.end(); ++i) {
5156 RouteTimeAxisView *rtav = dynamic_cast<RouteTimeAxisView *>(*i);
5163 new_state = !rtav->route()->soloed ();
5167 rl->push_back (rtav->route());
5170 _session->set_solo (rl, new_state, Session::rt_cleanup, true);
5174 Editor::toggle_mute ()
5176 bool new_state = false;
5178 boost::shared_ptr<RouteList> rl (new RouteList);
5180 for (TrackSelection::iterator i = selection->tracks.begin(); i != selection->tracks.end(); ++i) {
5181 RouteTimeAxisView *rtav = dynamic_cast<RouteTimeAxisView *>(*i);
5188 new_state = !rtav->route()->muted();
5192 rl->push_back (rtav->route());
5195 _session->set_mute (rl, new_state, Session::rt_cleanup, true);
5199 Editor::toggle_solo_isolate ()
5204 Editor::set_fade_length (bool in)
5206 RegionSelection rs = get_regions_from_selection_and_entered ();
5212 /* we need a region to measure the offset from the start */
5214 RegionView* rv = rs.front ();
5216 framepos_t pos = get_preferred_edit_position();
5220 if (pos > rv->region()->last_frame() || pos < rv->region()->first_frame()) {
5221 /* edit point is outside the relevant region */
5226 if (pos <= rv->region()->position()) {
5230 len = pos - rv->region()->position();
5231 cmd = _("set fade in length");
5233 if (pos >= rv->region()->last_frame()) {
5237 len = rv->region()->last_frame() - pos;
5238 cmd = _("set fade out length");
5241 begin_reversible_command (cmd);
5243 for (RegionSelection::iterator x = rs.begin(); x != rs.end(); ++x) {
5244 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (*x);
5250 boost::shared_ptr<AutomationList> alist;
5252 alist = tmp->audio_region()->fade_in();
5254 alist = tmp->audio_region()->fade_out();
5257 XMLNode &before = alist->get_state();
5260 tmp->audio_region()->set_fade_in_length (len);
5261 tmp->audio_region()->set_fade_in_active (true);
5263 tmp->audio_region()->set_fade_out_length (len);
5264 tmp->audio_region()->set_fade_out_active (true);
5267 XMLNode &after = alist->get_state();
5268 _session->add_command(new MementoCommand<AutomationList>(*alist, &before, &after));
5271 commit_reversible_command ();
5275 Editor::set_fade_in_shape (FadeShape shape)
5277 RegionSelection rs = get_regions_from_selection_and_entered ();
5283 begin_reversible_command (_("set fade in shape"));
5285 for (RegionSelection::iterator x = rs.begin(); x != rs.end(); ++x) {
5286 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (*x);
5292 boost::shared_ptr<AutomationList> alist = tmp->audio_region()->fade_in();
5293 XMLNode &before = alist->get_state();
5295 tmp->audio_region()->set_fade_in_shape (shape);
5297 XMLNode &after = alist->get_state();
5298 _session->add_command(new MementoCommand<AutomationList>(*alist.get(), &before, &after));
5301 commit_reversible_command ();
5306 Editor::set_fade_out_shape (FadeShape shape)
5308 RegionSelection rs = get_regions_from_selection_and_entered ();
5314 begin_reversible_command (_("set fade out shape"));
5316 for (RegionSelection::iterator x = rs.begin(); x != rs.end(); ++x) {
5317 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (*x);
5323 boost::shared_ptr<AutomationList> alist = tmp->audio_region()->fade_out();
5324 XMLNode &before = alist->get_state();
5326 tmp->audio_region()->set_fade_out_shape (shape);
5328 XMLNode &after = alist->get_state();
5329 _session->add_command(new MementoCommand<AutomationList>(*alist.get(), &before, &after));
5332 commit_reversible_command ();
5336 Editor::set_fade_in_active (bool yn)
5338 RegionSelection rs = get_regions_from_selection_and_entered ();
5344 begin_reversible_command (_("set fade in active"));
5346 for (RegionSelection::iterator x = rs.begin(); x != rs.end(); ++x) {
5347 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (*x);
5354 boost::shared_ptr<AudioRegion> ar (tmp->audio_region());
5356 ar->clear_changes ();
5357 ar->set_fade_in_active (yn);
5358 _session->add_command (new StatefulDiffCommand (ar));
5361 commit_reversible_command ();
5365 Editor::set_fade_out_active (bool yn)
5367 RegionSelection rs = get_regions_from_selection_and_entered ();
5373 begin_reversible_command (_("set fade out active"));
5375 for (RegionSelection::iterator x = rs.begin(); x != rs.end(); ++x) {
5376 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (*x);
5382 boost::shared_ptr<AudioRegion> ar (tmp->audio_region());
5384 ar->clear_changes ();
5385 ar->set_fade_out_active (yn);
5386 _session->add_command(new StatefulDiffCommand (ar));
5389 commit_reversible_command ();
5393 Editor::toggle_region_fades (int dir)
5395 if (_ignore_region_action) {
5399 boost::shared_ptr<AudioRegion> ar;
5402 RegionSelection rs = get_regions_from_selection_and_entered ();
5408 RegionSelection::iterator i;
5409 for (i = rs.begin(); i != rs.end(); ++i) {
5410 if ((ar = boost::dynamic_pointer_cast<AudioRegion>((*i)->region())) != 0) {
5412 yn = ar->fade_out_active ();
5414 yn = ar->fade_in_active ();
5420 if (i == rs.end()) {
5424 /* XXX should this undo-able? */
5426 for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
5427 if ((ar = boost::dynamic_pointer_cast<AudioRegion>((*i)->region())) == 0) {
5430 if (dir == 1 || dir == 0) {
5431 ar->set_fade_in_active (!yn);
5434 if (dir == -1 || dir == 0) {
5435 ar->set_fade_out_active (!yn);
5441 /** Update region fade visibility after its configuration has been changed */
5443 Editor::update_region_fade_visibility ()
5445 bool _fade_visibility = _session->config.get_show_region_fades ();
5447 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
5448 AudioTimeAxisView* v = dynamic_cast<AudioTimeAxisView*>(*i);
5450 if (_fade_visibility) {
5451 v->audio_view()->show_all_fades ();
5453 v->audio_view()->hide_all_fades ();
5460 Editor::set_edit_point ()
5465 if (!mouse_frame (where, ignored)) {
5471 if (selection->markers.empty()) {
5473 mouse_add_new_marker (where);
5478 Location* loc = find_location_from_marker (selection->markers.front(), ignored);
5481 loc->move_to (where);
5487 Editor::set_playhead_cursor ()
5489 if (entered_marker) {
5490 _session->request_locate (entered_marker->position(), _session->transport_rolling());
5495 if (!mouse_frame (where, ignored)) {
5502 _session->request_locate (where, _session->transport_rolling());
5506 if ( Config->get_always_play_range() )
5507 cancel_time_selection();
5511 Editor::split_region ()
5513 if ( !selection->time.empty()) {
5514 separate_regions_between (selection->time);
5518 RegionSelection rs = get_regions_from_selection_and_edit_point ();
5520 framepos_t where = get_preferred_edit_position ();
5526 split_regions_at (where, rs);
5529 struct EditorOrderRouteSorter {
5530 bool operator() (boost::shared_ptr<Route> a, boost::shared_ptr<Route> b) {
5531 return a->order_key (EditorSort) < b->order_key (EditorSort);
5536 Editor::select_next_route()
5538 if (selection->tracks.empty()) {
5539 selection->set (track_views.front());
5543 TimeAxisView* current = selection->tracks.front();
5547 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
5548 if (*i == current) {
5550 if (i != track_views.end()) {
5553 current = (*(track_views.begin()));
5554 //selection->set (*(track_views.begin()));
5559 rui = dynamic_cast<RouteUI *>(current);
5560 } while ( current->hidden() || (rui != NULL && !rui->route()->active()));
5562 selection->set(current);
5564 ensure_track_visible(current);
5568 Editor::select_prev_route()
5570 if (selection->tracks.empty()) {
5571 selection->set (track_views.front());
5575 TimeAxisView* current = selection->tracks.front();
5579 for (TrackViewList::reverse_iterator i = track_views.rbegin(); i != track_views.rend(); ++i) {
5580 if (*i == current) {
5582 if (i != track_views.rend()) {
5585 current = *(track_views.rbegin());
5590 rui = dynamic_cast<RouteUI *>(current);
5591 } while ( current->hidden() || (rui != NULL && !rui->route()->active()));
5593 selection->set (current);
5595 ensure_track_visible(current);
5599 Editor::ensure_track_visible(TimeAxisView *track)
5601 if (track->hidden())
5604 double const current_view_min_y = vertical_adjustment.get_value();
5605 double const current_view_max_y = vertical_adjustment.get_value() + vertical_adjustment.get_page_size() - canvas_timebars_vsize;
5607 double const track_min_y = track->y_position ();
5608 double const track_max_y = track->y_position () + track->effective_height ();
5610 if (track_min_y >= current_view_min_y &&
5611 track_max_y <= current_view_max_y) {
5617 if (track_min_y < current_view_min_y) {
5618 // Track is above the current view
5619 new_value = track_min_y;
5621 // Track is below the current view
5622 new_value = track->y_position () + track->effective_height() + canvas_timebars_vsize - vertical_adjustment.get_page_size();
5625 vertical_adjustment.set_value(new_value);
5629 Editor::set_loop_from_selection (bool play)
5631 if (_session == 0 || selection->time.empty()) {
5635 framepos_t start = selection->time[clicked_selection].start;
5636 framepos_t end = selection->time[clicked_selection].end;
5638 set_loop_range (start, end, _("set loop range from selection"));
5641 _session->request_play_loop (true);
5642 _session->request_locate (start, true);
5647 Editor::set_loop_from_edit_range (bool play)
5649 if (_session == 0) {
5656 if (!get_edit_op_range (start, end)) {
5660 set_loop_range (start, end, _("set loop range from edit range"));
5663 _session->request_play_loop (true);
5664 _session->request_locate (start, true);
5669 Editor::set_loop_from_region (bool play)
5671 framepos_t start = max_framepos;
5674 RegionSelection rs = get_regions_from_selection_and_entered ();
5680 for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
5681 if ((*i)->region()->position() < start) {
5682 start = (*i)->region()->position();
5684 if ((*i)->region()->last_frame() + 1 > end) {
5685 end = (*i)->region()->last_frame() + 1;
5689 set_loop_range (start, end, _("set loop range from region"));
5692 _session->request_play_loop (true);
5693 _session->request_locate (start, true);
5698 Editor::set_punch_from_selection ()
5700 if (_session == 0 || selection->time.empty()) {
5704 framepos_t start = selection->time[clicked_selection].start;
5705 framepos_t end = selection->time[clicked_selection].end;
5707 set_punch_range (start, end, _("set punch range from selection"));
5711 Editor::set_punch_from_edit_range ()
5713 if (_session == 0) {
5720 if (!get_edit_op_range (start, end)) {
5724 set_punch_range (start, end, _("set punch range from edit range"));
5728 Editor::set_punch_from_region ()
5730 framepos_t start = max_framepos;
5733 RegionSelection rs = get_regions_from_selection_and_entered ();
5739 for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
5740 if ((*i)->region()->position() < start) {
5741 start = (*i)->region()->position();
5743 if ((*i)->region()->last_frame() + 1 > end) {
5744 end = (*i)->region()->last_frame() + 1;
5748 set_punch_range (start, end, _("set punch range from region"));
5752 Editor::pitch_shift_region ()
5754 RegionSelection rs = get_regions_from_selection_and_entered ();
5756 RegionSelection audio_rs;
5757 for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
5758 if (dynamic_cast<AudioRegionView*> (*i)) {
5759 audio_rs.push_back (*i);
5763 if (audio_rs.empty()) {
5767 pitch_shift (audio_rs, 1.2);
5771 Editor::transpose_region ()
5773 RegionSelection rs = get_regions_from_selection_and_entered ();
5775 list<MidiRegionView*> midi_region_views;
5776 for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
5777 MidiRegionView* mrv = dynamic_cast<MidiRegionView*> (*i);
5779 midi_region_views.push_back (mrv);
5784 int const r = d.run ();
5785 if (r != RESPONSE_ACCEPT) {
5789 for (list<MidiRegionView*>::iterator i = midi_region_views.begin(); i != midi_region_views.end(); ++i) {
5790 (*i)->midi_region()->transpose (d.semitones ());
5795 Editor::set_tempo_from_region ()
5797 RegionSelection rs = get_regions_from_selection_and_entered ();
5799 if (!_session || rs.empty()) {
5803 RegionView* rv = rs.front();
5805 define_one_bar (rv->region()->position(), rv->region()->last_frame() + 1);
5809 Editor::use_range_as_bar ()
5811 framepos_t start, end;
5812 if (get_edit_op_range (start, end)) {
5813 define_one_bar (start, end);
5818 Editor::define_one_bar (framepos_t start, framepos_t end)
5820 framepos_t length = end - start;
5822 const Meter& m (_session->tempo_map().meter_at (start));
5824 /* length = 1 bar */
5826 /* now we want frames per beat.
5827 we have frames per bar, and beats per bar, so ...
5830 /* XXXX METER MATH */
5832 double frames_per_beat = length / m.divisions_per_bar();
5834 /* beats per minute = */
5836 double beats_per_minute = (_session->frame_rate() * 60.0) / frames_per_beat;
5838 /* now decide whether to:
5840 (a) set global tempo
5841 (b) add a new tempo marker
5845 const TempoSection& t (_session->tempo_map().tempo_section_at (start));
5847 bool do_global = false;
5849 if ((_session->tempo_map().n_tempos() == 1) && (_session->tempo_map().n_meters() == 1)) {
5851 /* only 1 tempo & 1 meter: ask if the user wants to set the tempo
5852 at the start, or create a new marker
5855 vector<string> options;
5856 options.push_back (_("Cancel"));
5857 options.push_back (_("Add new marker"));
5858 options.push_back (_("Set global tempo"));
5861 _("Define one bar"),
5862 _("Do you want to set the global tempo or add a new tempo marker?"),
5866 c.set_default_response (2);
5882 /* more than 1 tempo and/or meter section already, go ahead do the "usual":
5883 if the marker is at the region starter, change it, otherwise add
5888 begin_reversible_command (_("set tempo from region"));
5889 XMLNode& before (_session->tempo_map().get_state());
5892 _session->tempo_map().change_initial_tempo (beats_per_minute, t.note_type());
5893 } else if (t.frame() == start) {
5894 _session->tempo_map().change_existing_tempo_at (start, beats_per_minute, t.note_type());
5896 Timecode::BBT_Time bbt;
5897 _session->tempo_map().bbt_time (start, bbt);
5898 _session->tempo_map().add_tempo (Tempo (beats_per_minute, t.note_type()), bbt);
5901 XMLNode& after (_session->tempo_map().get_state());
5903 _session->add_command (new MementoCommand<TempoMap>(_session->tempo_map(), &before, &after));
5904 commit_reversible_command ();
5908 Editor::split_region_at_transients ()
5910 AnalysisFeatureList positions;
5912 RegionSelection rs = get_regions_from_selection_and_entered ();
5914 if (!_session || rs.empty()) {
5918 _session->begin_reversible_command (_("split regions"));
5920 for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ) {
5922 RegionSelection::iterator tmp;
5927 boost::shared_ptr<AudioRegion> ar = boost::dynamic_pointer_cast<AudioRegion> ((*i)->region());
5929 if (ar && (ar->get_transients (positions) == 0)) {
5930 split_region_at_points ((*i)->region(), positions, true);
5937 _session->commit_reversible_command ();
5942 Editor::split_region_at_points (boost::shared_ptr<Region> r, AnalysisFeatureList& positions, bool can_ferret, bool select_new)
5944 bool use_rhythmic_rodent = false;
5946 boost::shared_ptr<Playlist> pl = r->playlist();
5948 list<boost::shared_ptr<Region> > new_regions;
5954 if (positions.empty()) {
5959 if (positions.size() > 20 && can_ferret) {
5960 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);
5961 MessageDialog msg (msgstr,
5964 Gtk::BUTTONS_OK_CANCEL);
5967 msg.add_button (_("Call for the Ferret!"), RESPONSE_APPLY);
5968 msg.set_secondary_text (_("Press OK to continue with this split operation\nor ask the Ferret dialog to tune the analysis"));
5970 msg.set_secondary_text (_("Press OK to continue with this split operation"));
5973 msg.set_title (_("Excessive split?"));
5976 int response = msg.run();
5982 case RESPONSE_APPLY:
5983 use_rhythmic_rodent = true;
5990 if (use_rhythmic_rodent) {
5991 show_rhythm_ferret ();
5995 AnalysisFeatureList::const_iterator x;
5997 pl->clear_changes ();
5998 pl->clear_owned_changes ();
6000 x = positions.begin();
6002 if (x == positions.end()) {
6007 pl->remove_region (r);
6011 while (x != positions.end()) {
6013 /* deal with positons that are out of scope of present region bounds */
6014 if (*x <= 0 || *x > r->length()) {
6019 /* file start = original start + how far we from the initial position ?
6022 framepos_t file_start = r->start() + pos;
6024 /* length = next position - current position
6027 framepos_t len = (*x) - pos;
6029 /* XXX we do we really want to allow even single-sample regions?
6030 shouldn't we have some kind of lower limit on region size?
6039 if (RegionFactory::region_name (new_name, r->name())) {
6043 /* do NOT announce new regions 1 by one, just wait till they are all done */
6047 plist.add (ARDOUR::Properties::start, file_start);
6048 plist.add (ARDOUR::Properties::length, len);
6049 plist.add (ARDOUR::Properties::name, new_name);
6050 plist.add (ARDOUR::Properties::layer, 0);
6052 boost::shared_ptr<Region> nr = RegionFactory::create (r->sources(), plist, false);
6053 /* because we set annouce to false, manually add the new region to the
6056 RegionFactory::map_add (nr);
6058 pl->add_region (nr, r->position() + pos);
6061 new_regions.push_front(nr);
6070 RegionFactory::region_name (new_name, r->name());
6072 /* Add the final region */
6075 plist.add (ARDOUR::Properties::start, r->start() + pos);
6076 plist.add (ARDOUR::Properties::length, r->last_frame() - (r->position() + pos) + 1);
6077 plist.add (ARDOUR::Properties::name, new_name);
6078 plist.add (ARDOUR::Properties::layer, 0);
6080 boost::shared_ptr<Region> nr = RegionFactory::create (r->sources(), plist, false);
6081 /* because we set annouce to false, manually add the new region to the
6084 RegionFactory::map_add (nr);
6085 pl->add_region (nr, r->position() + pos);
6088 new_regions.push_front(nr);
6093 /* We might have removed regions, which alters other regions' layering_index,
6094 so we need to do a recursive diff here.
6096 vector<Command*> cmds;
6098 _session->add_commands (cmds);
6100 _session->add_command (new StatefulDiffCommand (pl));
6104 for (list<boost::shared_ptr<Region> >::iterator i = new_regions.begin(); i != new_regions.end(); ++i){
6105 set_selected_regionview_from_region_list ((*i), Selection::Add);
6111 Editor::place_transient()
6117 RegionSelection rs = get_regions_from_selection_and_edit_point ();
6123 framepos_t where = get_preferred_edit_position();
6125 _session->begin_reversible_command (_("place transient"));
6127 for (RegionSelection::iterator r = rs.begin(); r != rs.end(); ++r) {
6128 framepos_t position = (*r)->region()->position();
6129 (*r)->region()->add_transient(where - position);
6132 _session->commit_reversible_command ();
6136 Editor::remove_transient(ArdourCanvas::Item* item)
6142 ArdourCanvas::Line* _line = reinterpret_cast<ArdourCanvas::Line*> (item);
6145 AudioRegionView* _arv = reinterpret_cast<AudioRegionView*> (item->get_data ("regionview"));
6146 _arv->remove_transient (*(float*) _line->get_data ("position"));
6150 Editor::snap_regions_to_grid ()
6152 list <boost::shared_ptr<Playlist > > used_playlists;
6154 RegionSelection rs = get_regions_from_selection_and_entered ();
6156 if (!_session || rs.empty()) {
6160 _session->begin_reversible_command (_("snap regions to grid"));
6162 for (RegionSelection::iterator r = rs.begin(); r != rs.end(); ++r) {
6164 boost::shared_ptr<Playlist> pl = (*r)->region()->playlist();
6166 if (!pl->frozen()) {
6167 /* we haven't seen this playlist before */
6169 /* remember used playlists so we can thaw them later */
6170 used_playlists.push_back(pl);
6174 framepos_t start_frame = (*r)->region()->first_frame ();
6175 snap_to (start_frame);
6176 (*r)->region()->set_position (start_frame);
6179 while (used_playlists.size() > 0) {
6180 list <boost::shared_ptr<Playlist > >::iterator i = used_playlists.begin();
6182 used_playlists.pop_front();
6185 _session->commit_reversible_command ();
6189 Editor::close_region_gaps ()
6191 list <boost::shared_ptr<Playlist > > used_playlists;
6193 RegionSelection rs = get_regions_from_selection_and_entered ();
6195 if (!_session || rs.empty()) {
6199 Dialog dialog (_("Close Region Gaps"));
6202 table.set_spacings (12);
6203 table.set_border_width (12);
6204 Label* l = manage (left_aligned_label (_("Crossfade length")));
6205 table.attach (*l, 0, 1, 0, 1);
6207 SpinButton spin_crossfade (1, 0);
6208 spin_crossfade.set_range (0, 15);
6209 spin_crossfade.set_increments (1, 1);
6210 spin_crossfade.set_value (5);
6211 table.attach (spin_crossfade, 1, 2, 0, 1);
6213 table.attach (*manage (new Label (_("ms"))), 2, 3, 0, 1);
6215 l = manage (left_aligned_label (_("Pull-back length")));
6216 table.attach (*l, 0, 1, 1, 2);
6218 SpinButton spin_pullback (1, 0);
6219 spin_pullback.set_range (0, 100);
6220 spin_pullback.set_increments (1, 1);
6221 spin_pullback.set_value(30);
6222 table.attach (spin_pullback, 1, 2, 1, 2);
6224 table.attach (*manage (new Label (_("ms"))), 2, 3, 1, 2);
6226 dialog.get_vbox()->pack_start (table);
6227 dialog.add_button (Stock::CANCEL, RESPONSE_CANCEL);
6228 dialog.add_button (_("Ok"), RESPONSE_ACCEPT);
6231 if (dialog.run () == RESPONSE_CANCEL) {
6235 framepos_t crossfade_len = spin_crossfade.get_value();
6236 framepos_t pull_back_frames = spin_pullback.get_value();
6238 crossfade_len = lrintf (crossfade_len * _session->frame_rate()/1000);
6239 pull_back_frames = lrintf (pull_back_frames * _session->frame_rate()/1000);
6241 /* Iterate over the region list and make adjacent regions overlap by crossfade_len_ms */
6243 _session->begin_reversible_command (_("close region gaps"));
6246 boost::shared_ptr<Region> last_region;
6248 rs.sort_by_position_and_track();
6250 for (RegionSelection::iterator r = rs.begin(); r != rs.end(); ++r) {
6252 boost::shared_ptr<Playlist> pl = (*r)->region()->playlist();
6254 if (!pl->frozen()) {
6255 /* we haven't seen this playlist before */
6257 /* remember used playlists so we can thaw them later */
6258 used_playlists.push_back(pl);
6262 framepos_t position = (*r)->region()->position();
6264 if (idx == 0 || position < last_region->position()){
6265 last_region = (*r)->region();
6270 (*r)->region()->trim_front( (position - pull_back_frames));
6271 last_region->trim_end( (position - pull_back_frames + crossfade_len));
6273 last_region = (*r)->region();
6278 while (used_playlists.size() > 0) {
6279 list <boost::shared_ptr<Playlist > >::iterator i = used_playlists.begin();
6281 used_playlists.pop_front();
6284 _session->commit_reversible_command ();
6288 Editor::tab_to_transient (bool forward)
6290 AnalysisFeatureList positions;
6292 RegionSelection rs = get_regions_from_selection_and_entered ();
6298 framepos_t pos = _session->audible_frame ();
6300 if (!selection->tracks.empty()) {
6302 /* don't waste time searching for transients in duplicate playlists.
6305 TrackViewList ts = selection->tracks.filter_to_unique_playlists ();
6307 for (TrackViewList::iterator t = ts.begin(); t != ts.end(); ++t) {
6309 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (*t);
6312 boost::shared_ptr<Track> tr = rtv->track();
6314 boost::shared_ptr<Playlist> pl = tr->playlist ();
6316 framepos_t result = pl->find_next_transient (pos, forward ? 1 : -1);
6319 positions.push_back (result);
6332 for (RegionSelection::iterator r = rs.begin(); r != rs.end(); ++r) {
6333 (*r)->region()->get_transients (positions);
6337 TransientDetector::cleanup_transients (positions, _session->frame_rate(), 3.0);
6340 AnalysisFeatureList::iterator x;
6342 for (x = positions.begin(); x != positions.end(); ++x) {
6348 if (x != positions.end ()) {
6349 _session->request_locate (*x);
6353 AnalysisFeatureList::reverse_iterator x;
6355 for (x = positions.rbegin(); x != positions.rend(); ++x) {
6361 if (x != positions.rend ()) {
6362 _session->request_locate (*x);
6368 Editor::playhead_forward_to_grid ()
6370 if (!_session) return;
6371 framepos_t pos = playhead_cursor->current_frame;
6372 if (pos < max_framepos - 1) {
6374 snap_to_internal (pos, 1, false);
6375 _session->request_locate (pos);
6381 Editor::playhead_backward_to_grid ()
6383 if (!_session) return;
6384 framepos_t pos = playhead_cursor->current_frame;
6387 snap_to_internal (pos, -1, false);
6388 _session->request_locate (pos);
6393 Editor::set_track_height (Height h)
6395 TrackSelection& ts (selection->tracks);
6397 for (TrackSelection::iterator x = ts.begin(); x != ts.end(); ++x) {
6398 (*x)->set_height_enum (h);
6403 Editor::toggle_tracks_active ()
6405 TrackSelection& ts (selection->tracks);
6407 bool target = false;
6413 for (TrackSelection::iterator x = ts.begin(); x != ts.end(); ++x) {
6414 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*>(*x);
6418 target = !rtv->_route->active();
6421 rtv->_route->set_active (target, this);
6427 Editor::remove_tracks ()
6429 TrackSelection& ts (selection->tracks);
6435 vector<string> choices;
6439 const char* trackstr;
6441 vector<boost::shared_ptr<Route> > routes;
6442 bool special_bus = false;
6444 for (TrackSelection::iterator x = ts.begin(); x != ts.end(); ++x) {
6445 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (*x);
6447 if (rtv->is_track()) {
6453 routes.push_back (rtv->_route);
6455 if (rtv->route()->is_master() || rtv->route()->is_monitor()) {
6460 if (special_bus && !Config->get_allow_special_bus_removal()) {
6461 MessageDialog msg (_("That would be bad news ...."),
6465 msg.set_secondary_text (string_compose (_(
6466 "Removing the master or monitor bus is such a bad idea\n\
6467 that %1 is not going to allow it.\n\
6469 If you really want to do this sort of thing\n\
6470 edit your ardour.rc file to set the\n\
6471 \"allow-special-bus-removal\" option to be \"yes\""), PROGRAM_NAME));
6478 if (ntracks + nbusses == 0) {
6483 trackstr = _("tracks");
6485 trackstr = _("track");
6489 busstr = _("busses");
6496 prompt = string_compose (_("Do you really want to remove %1 %2 and %3 %4?\n"
6497 "(You may also lose the playlists associated with the %2)\n\n"
6498 "This action cannot be undone, and the session file will be overwritten!"),
6499 ntracks, trackstr, nbusses, busstr);
6501 prompt = string_compose (_("Do you really want to remove %1 %2?\n"
6502 "(You may also lose the playlists associated with the %2)\n\n"
6503 "This action cannot be undone, and the session file will be overwritten!"),
6506 } else if (nbusses) {
6507 prompt = string_compose (_("Do you really want to remove %1 %2?\n\n"
6508 "This action cannot be undon, and the session file will be overwritten"),
6512 choices.push_back (_("No, do nothing."));
6513 if (ntracks + nbusses > 1) {
6514 choices.push_back (_("Yes, remove them."));
6516 choices.push_back (_("Yes, remove it."));
6521 title = string_compose (_("Remove %1"), trackstr);
6523 title = string_compose (_("Remove %1"), busstr);
6526 Choice prompter (title, prompt, choices);
6528 if (prompter.run () != 1) {
6532 for (vector<boost::shared_ptr<Route> >::iterator x = routes.begin(); x != routes.end(); ++x) {
6533 _session->remove_route (*x);
6538 Editor::do_insert_time ()
6540 if (selection->tracks.empty()) {
6544 InsertTimeDialog d (*this);
6545 int response = d.run ();
6547 if (response != RESPONSE_OK) {
6551 if (d.distance() == 0) {
6555 InsertTimeOption opt = d.intersected_region_action ();
6558 get_preferred_edit_position(),
6564 d.move_glued_markers(),
6565 d.move_locked_markers(),
6571 Editor::insert_time (
6572 framepos_t pos, framecnt_t frames, InsertTimeOption opt,
6573 bool all_playlists, bool ignore_music_glue, bool markers_too, bool glued_markers_too, bool locked_markers_too, bool tempo_too
6576 bool commit = false;
6578 if (Config->get_edit_mode() == Lock) {
6582 begin_reversible_command (_("insert time"));
6584 TrackViewList ts = selection->tracks.filter_to_unique_playlists ();
6586 for (TrackViewList::iterator x = ts.begin(); x != ts.end(); ++x) {
6590 /* don't operate on any playlist more than once, which could
6591 * happen if "all playlists" is enabled, but there is more
6592 * than 1 track using playlists "from" a given track.
6595 set<boost::shared_ptr<Playlist> > pl;
6597 if (all_playlists) {
6598 RouteTimeAxisView* rtav = dynamic_cast<RouteTimeAxisView*> (*x);
6600 vector<boost::shared_ptr<Playlist> > all = _session->playlists->playlists_for_track (rtav->track ());
6601 for (vector<boost::shared_ptr<Playlist> >::iterator p = all.begin(); p != all.end(); ++p) {
6606 if ((*x)->playlist ()) {
6607 pl.insert ((*x)->playlist ());
6611 for (set<boost::shared_ptr<Playlist> >::iterator i = pl.begin(); i != pl.end(); ++i) {
6613 (*i)->clear_changes ();
6614 (*i)->clear_owned_changes ();
6616 if (opt == SplitIntersected) {
6620 (*i)->shift (pos, frames, (opt == MoveIntersected), ignore_music_glue);
6622 vector<Command*> cmds;
6624 _session->add_commands (cmds);
6626 _session->add_command (new StatefulDiffCommand (*i));
6631 RouteTimeAxisView* rtav = dynamic_cast<RouteTimeAxisView*> (*x);
6633 rtav->route ()->shift (pos, frames);
6641 XMLNode& before (_session->locations()->get_state());
6642 Locations::LocationList copy (_session->locations()->list());
6644 for (Locations::LocationList::iterator i = copy.begin(); i != copy.end(); ++i) {
6646 Locations::LocationList::const_iterator tmp;
6648 bool const was_locked = (*i)->locked ();
6649 if (locked_markers_too) {
6653 if ((*i)->position_lock_style() == AudioTime || glued_markers_too) {
6655 if ((*i)->start() >= pos) {
6656 (*i)->set_start ((*i)->start() + frames);
6657 if (!(*i)->is_mark()) {
6658 (*i)->set_end ((*i)->end() + frames);
6671 XMLNode& after (_session->locations()->get_state());
6672 _session->add_command (new MementoCommand<Locations>(*_session->locations(), &before, &after));
6677 _session->tempo_map().insert_time (pos, frames);
6681 commit_reversible_command ();
6686 Editor::fit_selected_tracks ()
6688 if (!selection->tracks.empty()) {
6689 fit_tracks (selection->tracks);
6693 /* no selected tracks - use tracks with selected regions */
6695 if (!selection->regions.empty()) {
6696 for (RegionSelection::iterator r = selection->regions.begin(); r != selection->regions.end(); ++r) {
6697 tvl.push_back (&(*r)->get_time_axis_view ());
6703 } else if (internal_editing()) {
6704 /* no selected tracks, or regions, but in internal edit mode, so follow the mouse and use
6707 if (entered_track) {
6708 tvl.push_back (entered_track);
6716 Editor::fit_tracks (TrackViewList & tracks)
6718 if (tracks.empty()) {
6722 uint32_t child_heights = 0;
6723 int visible_tracks = 0;
6725 for (TrackSelection::iterator t = tracks.begin(); t != tracks.end(); ++t) {
6727 if (!(*t)->marked_for_display()) {
6731 child_heights += (*t)->effective_height() - (*t)->current_height();
6735 uint32_t h = (uint32_t) floor ((_canvas_height - child_heights - canvas_timebars_vsize) / visible_tracks);
6736 double first_y_pos = DBL_MAX;
6738 if (h < TimeAxisView::preset_height (HeightSmall)) {
6739 MessageDialog msg (*this, _("There are too many tracks to fit in the current window"));
6740 /* too small to be displayed */
6744 undo_visual_stack.push_back (current_visual_state (true));
6745 no_save_visual = true;
6747 /* build a list of all tracks, including children */
6750 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
6752 TimeAxisView::Children c = (*i)->get_child_list ();
6753 for (TimeAxisView::Children::iterator j = c.begin(); j != c.end(); ++j) {
6754 all.push_back (j->get());
6758 /* operate on all tracks, hide unselected ones that are in the middle of selected ones */
6760 bool prev_was_selected = false;
6761 bool is_selected = tracks.contains (all.front());
6762 bool next_is_selected;
6764 for (TrackViewList::iterator t = all.begin(); t != all.end(); ++t) {
6766 TrackViewList::iterator next;
6771 if (next != all.end()) {
6772 next_is_selected = tracks.contains (*next);
6774 next_is_selected = false;
6777 if ((*t)->marked_for_display ()) {
6779 (*t)->set_height (h);
6780 first_y_pos = std::min ((*t)->y_position (), first_y_pos);
6782 if (prev_was_selected && next_is_selected) {
6783 hide_track_in_display (*t);
6788 prev_was_selected = is_selected;
6789 is_selected = next_is_selected;
6793 set the controls_layout height now, because waiting for its size
6794 request signal handler will cause the vertical adjustment setting to fail
6797 controls_layout.property_height () = full_canvas_height - canvas_timebars_vsize;
6798 vertical_adjustment.set_value (first_y_pos);
6800 redo_visual_stack.push_back (current_visual_state (true));
6804 Editor::save_visual_state (uint32_t n)
6806 while (visual_states.size() <= n) {
6807 visual_states.push_back (0);
6810 if (visual_states[n] != 0) {
6811 delete visual_states[n];
6814 visual_states[n] = current_visual_state (true);
6819 Editor::goto_visual_state (uint32_t n)
6821 if (visual_states.size() <= n) {
6825 if (visual_states[n] == 0) {
6829 use_visual_state (*visual_states[n]);
6833 Editor::start_visual_state_op (uint32_t n)
6835 save_visual_state (n);
6837 PopUp* pup = new PopUp (WIN_POS_MOUSE, 1000, true);
6839 snprintf (buf, sizeof (buf), _("Saved view %u"), n+1);
6840 pup->set_text (buf);
6845 Editor::cancel_visual_state_op (uint32_t n)
6847 goto_visual_state (n);
6851 Editor::toggle_region_mute ()
6853 if (_ignore_region_action) {
6857 RegionSelection rs = get_regions_from_selection_and_entered ();
6863 if (rs.size() > 1) {
6864 begin_reversible_command (_("mute regions"));
6866 begin_reversible_command (_("mute region"));
6869 for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
6871 (*i)->region()->playlist()->clear_changes ();
6872 (*i)->region()->set_muted (!(*i)->region()->muted ());
6873 _session->add_command (new StatefulDiffCommand ((*i)->region()->playlist()));
6877 commit_reversible_command ();
6881 Editor::combine_regions ()
6883 /* foreach track with selected regions, take all selected regions
6884 and join them into a new region containing the subregions (as a
6888 typedef set<RouteTimeAxisView*> RTVS;
6891 if (selection->regions.empty()) {
6895 for (RegionSelection::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
6896 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*>(&(*i)->get_time_axis_view());
6899 tracks.insert (rtv);
6903 begin_reversible_command (_("combine regions"));
6905 vector<RegionView*> new_selection;
6907 for (RTVS::iterator i = tracks.begin(); i != tracks.end(); ++i) {
6910 if ((rv = (*i)->combine_regions ()) != 0) {
6911 new_selection.push_back (rv);
6915 selection->clear_regions ();
6916 for (vector<RegionView*>::iterator i = new_selection.begin(); i != new_selection.end(); ++i) {
6917 selection->add (*i);
6920 commit_reversible_command ();
6924 Editor::uncombine_regions ()
6926 typedef set<RouteTimeAxisView*> RTVS;
6929 if (selection->regions.empty()) {
6933 for (RegionSelection::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
6934 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*>(&(*i)->get_time_axis_view());
6937 tracks.insert (rtv);
6941 begin_reversible_command (_("uncombine regions"));
6943 for (RTVS::iterator i = tracks.begin(); i != tracks.end(); ++i) {
6944 (*i)->uncombine_regions ();
6947 commit_reversible_command ();
6951 Editor::toggle_midi_input_active (bool flip_others)
6954 boost::shared_ptr<RouteList> rl (new RouteList);
6956 for (TrackSelection::iterator i = selection->tracks.begin(); i != selection->tracks.end(); ++i) {
6957 RouteTimeAxisView *rtav = dynamic_cast<RouteTimeAxisView *>(*i);
6963 boost::shared_ptr<MidiTrack> mt = rtav->midi_track();
6966 rl->push_back (rtav->route());
6967 onoff = !mt->input_active();
6971 _session->set_exclusive_input_active (rl, onoff, flip_others);