2 Copyright (C) 2000-2004 Paul Davis
4 This program is free software; you can redistribute it and/or modify
5 it under the terms of the GNU General Public License as published by
6 the Free Software Foundation; either version 2 of the License, or
7 (at your option) any later version.
9 This program is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 GNU General Public License for more details.
14 You should have received a copy of the GNU General Public License
15 along with this program; if not, write to the Free Software
16 Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
20 /* Note: public Editor methods are documented in public_editor.h */
31 #include <gtkmm/messagedialog.h>
33 #include "pbd/error.h"
34 #include "pbd/basename.h"
35 #include "pbd/pthread_utils.h"
36 #include "pbd/memento_command.h"
37 #include "pbd/unwind.h"
38 #include "pbd/whitespace.h"
39 #include "pbd/stateful_diff_command.h"
41 #include "gtkmm2ext/utils.h"
43 #include "widgets/choice.h"
44 #include "widgets/popup.h"
45 #include "widgets/prompter.h"
47 #include "ardour/audio_track.h"
48 #include "ardour/audioregion.h"
49 #include "ardour/boost_debug.h"
50 #include "ardour/dB.h"
51 #include "ardour/location.h"
52 #include "ardour/midi_region.h"
53 #include "ardour/midi_track.h"
54 #include "ardour/operations.h"
55 #include "ardour/playlist_factory.h"
56 #include "ardour/profile.h"
57 #include "ardour/quantize.h"
58 #include "ardour/legatize.h"
59 #include "ardour/region_factory.h"
60 #include "ardour/reverse.h"
61 #include "ardour/selection.h"
62 #include "ardour/session.h"
63 #include "ardour/session_playlists.h"
64 #include "ardour/strip_silence.h"
65 #include "ardour/transient_detector.h"
66 #include "ardour/transport_master_manager.h"
67 #include "ardour/transpose.h"
68 #include "ardour/vca_manager.h"
70 #include "canvas/canvas.h"
73 #include "ardour_ui.h"
74 #include "audio_region_view.h"
75 #include "audio_streamview.h"
76 #include "audio_time_axis.h"
77 #include "automation_region_view.h"
78 #include "automation_time_axis.h"
79 #include "control_point.h"
83 #include "editor_cursors.h"
84 #include "editor_drag.h"
85 #include "editor_regions.h"
86 #include "editor_routes.h"
87 #include "gui_thread.h"
88 #include "insert_remove_time_dialog.h"
89 #include "interthread_progress_window.h"
90 #include "item_counts.h"
92 #include "midi_region_view.h"
94 #include "mixer_strip.h"
95 #include "mouse_cursors.h"
96 #include "normalize_dialog.h"
98 #include "paste_context.h"
99 #include "patch_change_dialog.h"
100 #include "quantize_dialog.h"
101 #include "region_gain_line.h"
102 #include "rgb_macros.h"
103 #include "route_time_axis.h"
104 #include "selection.h"
105 #include "selection_templates.h"
106 #include "streamview.h"
107 #include "strip_silence_dialog.h"
108 #include "time_axis_view.h"
110 #include "transpose_dialog.h"
111 #include "transform_dialog.h"
112 #include "ui_config.h"
113 #include "vca_time_axis.h"
115 #include "pbd/i18n.h"
118 using namespace ARDOUR;
121 using namespace Gtkmm2ext;
122 using namespace ArdourWidgets;
123 using namespace Editing;
124 using Gtkmm2ext::Keyboard;
126 /***********************************************************************
128 ***********************************************************************/
131 Editor::undo (uint32_t n)
133 if (_session && _session->actively_recording()) {
134 /* no undo allowed while recording. Session will check also,
135 but we don't even want to get to that.
140 if (_drags->active ()) {
146 if (_session->undo_depth() == 0) {
147 undo_action->set_sensitive(false);
149 redo_action->set_sensitive(true);
150 begin_selection_op_history ();
155 Editor::redo (uint32_t n)
157 if (_session && _session->actively_recording()) {
158 /* no redo allowed while recording. Session will check also,
159 but we don't even want to get to that.
164 if (_drags->active ()) {
170 if (_session->redo_depth() == 0) {
171 redo_action->set_sensitive(false);
173 undo_action->set_sensitive(true);
174 begin_selection_op_history ();
179 Editor::split_regions_at (MusicSample where, RegionSelection& regions)
183 RegionSelection pre_selected_regions = selection->regions;
184 bool working_on_selection = !pre_selected_regions.empty();
186 list<boost::shared_ptr<Playlist> > used_playlists;
187 list<RouteTimeAxisView*> used_trackviews;
189 if (regions.empty()) {
193 begin_reversible_command (_("split"));
196 if (regions.size() == 1) {
197 /* TODO: if splitting a single region, and snap-to is using
198 region boundaries, mabye we shouldn't pay attention to them? */
201 EditorFreeze(); /* Emit Signal */
204 for (RegionSelection::iterator a = regions.begin(); a != regions.end(); ) {
206 RegionSelection::iterator tmp;
208 /* XXX this test needs to be more complicated, to make sure we really
209 have something to split.
212 if (!(*a)->region()->covers (where.sample)) {
220 boost::shared_ptr<Playlist> pl = (*a)->region()->playlist();
228 /* we haven't seen this playlist before */
230 /* remember used playlists so we can thaw them later */
231 used_playlists.push_back(pl);
233 TimeAxisView& tv = (*a)->get_time_axis_view();
234 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (&tv);
236 used_trackviews.push_back (rtv);
243 pl->clear_changes ();
244 pl->split_region ((*a)->region(), where);
245 _session->add_command (new StatefulDiffCommand (pl));
251 latest_regionviews.clear ();
253 vector<sigc::connection> region_added_connections;
255 for (list<RouteTimeAxisView*>::iterator i = used_trackviews.begin(); i != used_trackviews.end(); ++i) {
256 region_added_connections.push_back ((*i)->view()->RegionViewAdded.connect (sigc::mem_fun(*this, &Editor::collect_new_region_view)));
259 while (used_playlists.size() > 0) {
260 list <boost::shared_ptr<Playlist > >::iterator i = used_playlists.begin();
262 used_playlists.pop_front();
265 for (vector<sigc::connection>::iterator c = region_added_connections.begin(); c != region_added_connections.end(); ++c) {
270 EditorThaw(); /* Emit Signal */
273 if (working_on_selection) {
274 // IFF we were working on selected regions, try to reinstate the other region selections that existed before the freeze/thaw.
276 RegionSelectionAfterSplit rsas = Config->get_region_selection_after_split();
277 /* There are three classes of regions that we might want selected after
278 splitting selected regions:
279 - regions selected before the split operation, and unaffected by it
280 - newly-created regions before the split
281 - newly-created regions after the split
284 if (rsas & Existing) {
285 // region selections that existed before the split.
286 selection->add (pre_selected_regions);
289 for (RegionSelection::iterator ri = latest_regionviews.begin(); ri != latest_regionviews.end(); ri++) {
290 if ((*ri)->region()->position() < where.sample) {
291 // new regions created before the split
292 if (rsas & NewlyCreatedLeft) {
293 selection->add (*ri);
296 // new regions created after the split
297 if (rsas & NewlyCreatedRight) {
298 selection->add (*ri);
304 commit_reversible_command ();
307 /** Move one extreme of the current range selection. If more than one range is selected,
308 * the start of the earliest range or the end of the latest range is moved.
310 * @param move_end true to move the end of the current range selection, false to move
312 * @param next true to move the extreme to the next region boundary, false to move to
316 Editor::move_range_selection_start_or_end_to_region_boundary (bool move_end, bool next)
318 if (selection->time.start() == selection->time.end_sample()) {
322 samplepos_t start = selection->time.start ();
323 samplepos_t end = selection->time.end_sample ();
325 /* the position of the thing we may move */
326 samplepos_t pos = move_end ? end : start;
327 int dir = next ? 1 : -1;
329 /* so we don't find the current region again */
330 if (dir > 0 || pos > 0) {
334 samplepos_t const target = get_region_boundary (pos, dir, true, false);
349 begin_reversible_selection_op (_("alter selection"));
350 selection->set_preserving_all_ranges (start, end);
351 commit_reversible_selection_op ();
355 Editor::nudge_forward_release (GdkEventButton* ev)
357 if (ev->state & Keyboard::PrimaryModifier) {
358 nudge_forward (false, true);
360 nudge_forward (false, false);
366 Editor::nudge_backward_release (GdkEventButton* ev)
368 if (ev->state & Keyboard::PrimaryModifier) {
369 nudge_backward (false, true);
371 nudge_backward (false, false);
378 Editor::nudge_forward (bool next, bool force_playhead)
380 samplepos_t distance;
381 samplepos_t next_distance;
387 RegionSelection rs = get_regions_from_selection_and_entered ();
389 if (!force_playhead && !rs.empty()) {
391 begin_reversible_command (_("nudge regions forward"));
393 for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
394 boost::shared_ptr<Region> r ((*i)->region());
396 distance = get_nudge_distance (r->position(), next_distance);
399 distance = next_distance;
403 r->set_position (r->position() + distance);
404 _session->add_command (new StatefulDiffCommand (r));
407 commit_reversible_command ();
410 } else if (!force_playhead && !selection->markers.empty()) {
413 bool in_command = false;
414 const int32_t divisions = get_grid_music_divisions (0);
416 for (MarkerSelection::iterator i = selection->markers.begin(); i != selection->markers.end(); ++i) {
418 Location* loc = find_location_from_marker ((*i), is_start);
422 XMLNode& before (loc->get_state());
425 distance = get_nudge_distance (loc->start(), next_distance);
427 distance = next_distance;
429 if (max_samplepos - distance > loc->start() + loc->length()) {
430 loc->set_start (loc->start() + distance, false, true, divisions);
432 loc->set_start (max_samplepos - loc->length(), false, true, divisions);
435 distance = get_nudge_distance (loc->end(), next_distance);
437 distance = next_distance;
439 if (max_samplepos - distance > loc->end()) {
440 loc->set_end (loc->end() + distance, false, true, divisions);
442 loc->set_end (max_samplepos, false, true, divisions);
444 if (loc->is_session_range()) {
445 _session->set_end_is_free (false);
449 begin_reversible_command (_("nudge location forward"));
452 XMLNode& after (loc->get_state());
453 _session->add_command (new MementoCommand<Location>(*loc, &before, &after));
458 commit_reversible_command ();
461 distance = get_nudge_distance (playhead_cursor->current_sample (), next_distance);
462 _session->request_locate (playhead_cursor->current_sample () + distance);
467 Editor::nudge_backward (bool next, bool force_playhead)
469 samplepos_t distance;
470 samplepos_t next_distance;
476 RegionSelection rs = get_regions_from_selection_and_entered ();
478 if (!force_playhead && !rs.empty()) {
480 begin_reversible_command (_("nudge regions backward"));
482 for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
483 boost::shared_ptr<Region> r ((*i)->region());
485 distance = get_nudge_distance (r->position(), next_distance);
488 distance = next_distance;
493 if (r->position() > distance) {
494 r->set_position (r->position() - distance);
498 _session->add_command (new StatefulDiffCommand (r));
501 commit_reversible_command ();
503 } else if (!force_playhead && !selection->markers.empty()) {
506 bool in_command = false;
508 for (MarkerSelection::iterator i = selection->markers.begin(); i != selection->markers.end(); ++i) {
510 Location* loc = find_location_from_marker ((*i), is_start);
514 XMLNode& before (loc->get_state());
517 distance = get_nudge_distance (loc->start(), next_distance);
519 distance = next_distance;
521 if (distance < loc->start()) {
522 loc->set_start (loc->start() - distance, false, true, get_grid_music_divisions(0));
524 loc->set_start (0, false, true, get_grid_music_divisions(0));
527 distance = get_nudge_distance (loc->end(), next_distance);
530 distance = next_distance;
533 if (distance < loc->end() - loc->length()) {
534 loc->set_end (loc->end() - distance, false, true, get_grid_music_divisions(0));
536 loc->set_end (loc->length(), false, true, get_grid_music_divisions(0));
538 if (loc->is_session_range()) {
539 _session->set_end_is_free (false);
543 begin_reversible_command (_("nudge location forward"));
546 XMLNode& after (loc->get_state());
547 _session->add_command (new MementoCommand<Location>(*loc, &before, &after));
551 commit_reversible_command ();
556 distance = get_nudge_distance (playhead_cursor->current_sample (), next_distance);
558 if (playhead_cursor->current_sample () > distance) {
559 _session->request_locate (playhead_cursor->current_sample () - distance);
561 _session->goto_start();
567 Editor::nudge_forward_capture_offset ()
569 RegionSelection rs = get_regions_from_selection_and_entered ();
571 if (!_session || rs.empty()) {
575 begin_reversible_command (_("nudge forward"));
577 samplepos_t const distance = _session->worst_output_latency();
579 for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
580 boost::shared_ptr<Region> r ((*i)->region());
583 r->set_position (r->position() + distance);
584 _session->add_command(new StatefulDiffCommand (r));
587 commit_reversible_command ();
591 Editor::nudge_backward_capture_offset ()
593 RegionSelection rs = get_regions_from_selection_and_entered ();
595 if (!_session || rs.empty()) {
599 begin_reversible_command (_("nudge backward"));
601 samplepos_t const distance = _session->worst_output_latency();
603 for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
604 boost::shared_ptr<Region> r ((*i)->region());
608 if (r->position() > distance) {
609 r->set_position (r->position() - distance);
613 _session->add_command(new StatefulDiffCommand (r));
616 commit_reversible_command ();
619 struct RegionSelectionPositionSorter {
620 bool operator() (RegionView* a, RegionView* b) {
621 return a->region()->position() < b->region()->position();
626 Editor::sequence_regions ()
629 samplepos_t r_end_prev;
637 RegionSelection rs = get_regions_from_selection_and_entered ();
638 rs.sort(RegionSelectionPositionSorter());
642 bool in_command = false;
644 for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
645 boost::shared_ptr<Region> r ((*i)->region());
653 if(r->position_locked())
660 r->set_position(r_end_prev);
664 begin_reversible_command (_("sequence regions"));
667 _session->add_command (new StatefulDiffCommand (r));
669 r_end=r->position() + r->length();
675 commit_reversible_command ();
684 Editor::move_to_start ()
686 _session->goto_start ();
690 Editor::move_to_end ()
693 _session->request_locate (_session->current_end_sample());
697 Editor::build_region_boundary_cache ()
700 /* TODO: maybe set a timer so we don't recalutate when lots of changes are coming in */
701 /* TODO: maybe somehow defer this until session is fully loaded. */
703 if (!_region_boundary_cache_dirty)
707 vector<RegionPoint> interesting_points;
708 boost::shared_ptr<Region> r;
709 TrackViewList tracks;
712 region_boundary_cache.clear ();
718 bool maybe_first_sample = false;
720 if (UIConfiguration::instance().get_snap_to_region_start()) {
721 interesting_points.push_back (Start);
722 maybe_first_sample = true;
725 if (UIConfiguration::instance().get_snap_to_region_end()) {
726 interesting_points.push_back (End);
729 if (UIConfiguration::instance().get_snap_to_region_sync()) {
730 interesting_points.push_back (SyncPoint);
733 /* if no snap selections are set, boundary cache should be left empty */
734 if ( interesting_points.empty() ) {
735 _region_boundary_cache_dirty = false;
739 TimeAxisView *ontrack = 0;
742 tlist = track_views.filter_to_unique_playlists ();
744 if (maybe_first_sample) {
745 TrackViewList::const_iterator i;
746 for (i = tlist.begin(); i != tlist.end(); ++i) {
747 boost::shared_ptr<Playlist> pl = (*i)->playlist();
748 if (pl && pl->count_regions_at (0)) {
749 region_boundary_cache.push_back (0);
755 //allow regions to snap to the video start (if any) as if it were a "region"
756 if (ARDOUR_UI::instance()->video_timeline) {
757 region_boundary_cache.push_back (ARDOUR_UI::instance()->video_timeline->get_video_start_offset());
760 std::pair<samplepos_t, samplepos_t> ext = session_gui_extents (false);
761 samplepos_t session_end = ext.second;
763 while (pos < session_end && !at_end) {
766 samplepos_t lpos = session_end;
768 for (vector<RegionPoint>::iterator p = interesting_points.begin(); p != interesting_points.end(); ++p) {
770 if ((r = find_next_region (pos, *p, 1, tlist, &ontrack)) == 0) {
771 if (*p == interesting_points.back()) {
774 /* move to next point type */
780 rpos = r->first_sample();
784 rpos = r->last_sample();
788 rpos = r->sync_position ();
799 /* prevent duplicates, but we don't use set<> because we want to be able
803 vector<samplepos_t>::iterator ri;
805 for (ri = region_boundary_cache.begin(); ri != region_boundary_cache.end(); ++ri) {
811 if (ri == region_boundary_cache.end()) {
812 region_boundary_cache.push_back (rpos);
819 /* finally sort to be sure that the order is correct */
821 sort (region_boundary_cache.begin(), region_boundary_cache.end());
823 _region_boundary_cache_dirty = false;
826 boost::shared_ptr<Region>
827 Editor::find_next_region (samplepos_t sample, RegionPoint point, int32_t dir, TrackViewList& tracks, TimeAxisView **ontrack)
829 TrackViewList::iterator i;
830 samplepos_t closest = max_samplepos;
831 boost::shared_ptr<Region> ret;
832 samplepos_t rpos = 0;
834 samplepos_t track_sample;
836 for (i = tracks.begin(); i != tracks.end(); ++i) {
838 samplecnt_t distance;
839 boost::shared_ptr<Region> r;
841 track_sample = sample;
843 if ((r = (*i)->find_next_region (track_sample, point, dir)) == 0) {
849 rpos = r->first_sample ();
853 rpos = r->last_sample ();
857 rpos = r->sync_position ();
862 distance = rpos - sample;
864 distance = sample - rpos;
867 if (distance < closest) {
879 Editor::find_next_region_boundary (samplepos_t pos, int32_t dir, const TrackViewList& tracks)
881 samplecnt_t distance = max_samplepos;
882 samplepos_t current_nearest = -1;
884 for (TrackViewList::const_iterator i = tracks.begin(); i != tracks.end(); ++i) {
885 samplepos_t contender;
888 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (*i);
894 if ((contender = rtv->find_next_region_boundary (pos, dir)) < 0) {
898 d = ::llabs (pos - contender);
901 current_nearest = contender;
906 return current_nearest;
910 Editor::get_region_boundary (samplepos_t pos, int32_t dir, bool with_selection, bool only_onscreen)
915 if (with_selection && Config->get_region_boundaries_from_selected_tracks()) {
917 if (!selection->tracks.empty()) {
919 target = find_next_region_boundary (pos, dir, selection->tracks);
923 if (only_onscreen || Config->get_region_boundaries_from_onscreen_tracks()) {
924 get_onscreen_tracks (tvl);
925 target = find_next_region_boundary (pos, dir, tvl);
927 target = find_next_region_boundary (pos, dir, track_views);
933 if (only_onscreen || Config->get_region_boundaries_from_onscreen_tracks()) {
934 get_onscreen_tracks (tvl);
935 target = find_next_region_boundary (pos, dir, tvl);
937 target = find_next_region_boundary (pos, dir, track_views);
945 Editor::cursor_to_region_boundary (bool with_selection, int32_t dir)
947 samplepos_t pos = playhead_cursor->current_sample ();
954 // so we don't find the current region again..
955 if (dir > 0 || pos > 0) {
959 if ((target = get_region_boundary (pos, dir, with_selection, false)) < 0) {
963 _session->request_locate (target);
967 Editor::cursor_to_next_region_boundary (bool with_selection)
969 cursor_to_region_boundary (with_selection, 1);
973 Editor::cursor_to_previous_region_boundary (bool with_selection)
975 cursor_to_region_boundary (with_selection, -1);
979 Editor::cursor_to_region_point (EditorCursor* cursor, RegionPoint point, int32_t dir)
981 boost::shared_ptr<Region> r;
982 samplepos_t pos = cursor->current_sample ();
988 TimeAxisView *ontrack = 0;
990 // so we don't find the current region again..
994 if (!selection->tracks.empty()) {
996 r = find_next_region (pos, point, dir, selection->tracks, &ontrack);
998 } else if (clicked_axisview) {
1001 t.push_back (clicked_axisview);
1003 r = find_next_region (pos, point, dir, t, &ontrack);
1007 r = find_next_region (pos, point, dir, track_views, &ontrack);
1016 pos = r->first_sample ();
1020 pos = r->last_sample ();
1024 pos = r->sync_position ();
1028 if (cursor == playhead_cursor) {
1029 _session->request_locate (pos);
1031 cursor->set_position (pos);
1036 Editor::cursor_to_next_region_point (EditorCursor* cursor, RegionPoint point)
1038 cursor_to_region_point (cursor, point, 1);
1042 Editor::cursor_to_previous_region_point (EditorCursor* cursor, RegionPoint point)
1044 cursor_to_region_point (cursor, point, -1);
1048 Editor::cursor_to_selection_start (EditorCursor *cursor)
1050 samplepos_t pos = 0;
1052 switch (mouse_mode) {
1054 if (!selection->regions.empty()) {
1055 pos = selection->regions.start();
1060 if (!selection->time.empty()) {
1061 pos = selection->time.start ();
1069 if (cursor == playhead_cursor) {
1070 _session->request_locate (pos);
1072 cursor->set_position (pos);
1077 Editor::cursor_to_selection_end (EditorCursor *cursor)
1079 samplepos_t pos = 0;
1081 switch (mouse_mode) {
1083 if (!selection->regions.empty()) {
1084 pos = selection->regions.end_sample();
1089 if (!selection->time.empty()) {
1090 pos = selection->time.end_sample ();
1098 if (cursor == playhead_cursor) {
1099 _session->request_locate (pos);
1101 cursor->set_position (pos);
1106 Editor::selected_marker_to_region_boundary (bool with_selection, int32_t dir)
1116 if (selection->markers.empty()) {
1120 if (!mouse_sample (mouse, ignored)) {
1124 add_location_mark (mouse);
1127 if ((loc = find_location_from_marker (selection->markers.front(), ignored)) == 0) {
1131 samplepos_t pos = loc->start();
1133 // so we don't find the current region again..
1134 if (dir > 0 || pos > 0) {
1138 if ((target = get_region_boundary (pos, dir, with_selection, false)) < 0) {
1142 loc->move_to (target, 0);
1146 Editor::selected_marker_to_next_region_boundary (bool with_selection)
1148 selected_marker_to_region_boundary (with_selection, 1);
1152 Editor::selected_marker_to_previous_region_boundary (bool with_selection)
1154 selected_marker_to_region_boundary (with_selection, -1);
1158 Editor::selected_marker_to_region_point (RegionPoint point, int32_t dir)
1160 boost::shared_ptr<Region> r;
1165 if (!_session || selection->markers.empty()) {
1169 if ((loc = find_location_from_marker (selection->markers.front(), ignored)) == 0) {
1173 TimeAxisView *ontrack = 0;
1177 // so we don't find the current region again..
1181 if (!selection->tracks.empty()) {
1183 r = find_next_region (pos, point, dir, selection->tracks, &ontrack);
1187 r = find_next_region (pos, point, dir, track_views, &ontrack);
1196 pos = r->first_sample ();
1200 pos = r->last_sample ();
1204 pos = r->adjust_to_sync (r->first_sample());
1208 loc->move_to (pos, 0);
1212 Editor::selected_marker_to_next_region_point (RegionPoint point)
1214 selected_marker_to_region_point (point, 1);
1218 Editor::selected_marker_to_previous_region_point (RegionPoint point)
1220 selected_marker_to_region_point (point, -1);
1224 Editor::selected_marker_to_selection_start ()
1226 samplepos_t pos = 0;
1230 if (!_session || selection->markers.empty()) {
1234 if ((loc = find_location_from_marker (selection->markers.front(), ignored)) == 0) {
1238 switch (mouse_mode) {
1240 if (!selection->regions.empty()) {
1241 pos = selection->regions.start();
1246 if (!selection->time.empty()) {
1247 pos = selection->time.start ();
1255 loc->move_to (pos, 0);
1259 Editor::selected_marker_to_selection_end ()
1261 samplepos_t pos = 0;
1265 if (!_session || selection->markers.empty()) {
1269 if ((loc = find_location_from_marker (selection->markers.front(), ignored)) == 0) {
1273 switch (mouse_mode) {
1275 if (!selection->regions.empty()) {
1276 pos = selection->regions.end_sample();
1281 if (!selection->time.empty()) {
1282 pos = selection->time.end_sample ();
1290 loc->move_to (pos, 0);
1294 Editor::scroll_playhead (bool forward)
1296 samplepos_t pos = playhead_cursor->current_sample ();
1297 samplecnt_t delta = (samplecnt_t) floor (current_page_samples() / 0.8);
1300 if (pos == max_samplepos) {
1304 if (pos < max_samplepos - delta) {
1307 pos = max_samplepos;
1323 _session->request_locate (pos);
1327 Editor::cursor_align (bool playhead_to_edit)
1333 if (playhead_to_edit) {
1335 if (selection->markers.empty()) {
1339 _session->request_locate (selection->markers.front()->position(), _session->transport_rolling());
1342 const int32_t divisions = get_grid_music_divisions (0);
1343 /* move selected markers to playhead */
1345 for (MarkerSelection::iterator i = selection->markers.begin(); i != selection->markers.end(); ++i) {
1348 Location* loc = find_location_from_marker (*i, ignored);
1350 if (loc->is_mark()) {
1351 loc->set_start (playhead_cursor->current_sample (), false, true, divisions);
1353 loc->set (playhead_cursor->current_sample (),
1354 playhead_cursor->current_sample () + loc->length(), true, divisions);
1361 Editor::scroll_backward (float pages)
1363 samplepos_t const one_page = (samplepos_t) rint (_visible_canvas_width * samples_per_pixel);
1364 samplepos_t const cnt = (samplepos_t) floor (pages * one_page);
1367 if (_leftmost_sample < cnt) {
1370 sample = _leftmost_sample - cnt;
1373 reset_x_origin (sample);
1377 Editor::scroll_forward (float pages)
1379 samplepos_t const one_page = (samplepos_t) rint (_visible_canvas_width * samples_per_pixel);
1380 samplepos_t const cnt = (samplepos_t) floor (pages * one_page);
1383 if (max_samplepos - cnt < _leftmost_sample) {
1384 sample = max_samplepos - cnt;
1386 sample = _leftmost_sample + cnt;
1389 reset_x_origin (sample);
1393 Editor::scroll_tracks_down ()
1395 double vert_value = vertical_adjustment.get_value() + vertical_adjustment.get_page_size();
1396 if (vert_value > vertical_adjustment.get_upper() - _visible_canvas_height) {
1397 vert_value = vertical_adjustment.get_upper() - _visible_canvas_height;
1400 vertical_adjustment.set_value (vert_value);
1404 Editor::scroll_tracks_up ()
1406 vertical_adjustment.set_value (vertical_adjustment.get_value() - vertical_adjustment.get_page_size());
1410 Editor::scroll_tracks_down_line ()
1412 double vert_value = vertical_adjustment.get_value() + 60;
1414 if (vert_value > vertical_adjustment.get_upper() - _visible_canvas_height) {
1415 vert_value = vertical_adjustment.get_upper() - _visible_canvas_height;
1418 vertical_adjustment.set_value (vert_value);
1422 Editor::scroll_tracks_up_line ()
1424 reset_y_origin (vertical_adjustment.get_value() - 60);
1428 Editor::select_topmost_track ()
1430 const double top_of_trackviews = vertical_adjustment.get_value();
1431 for (TrackViewList::iterator t = track_views.begin(); t != track_views.end(); ++t) {
1432 if ((*t)->hidden()) {
1435 std::pair<TimeAxisView*,double> res = (*t)->covers_y_position (top_of_trackviews);
1437 selection->set (*t);
1444 Editor::scroll_down_one_track (bool skip_child_views)
1446 TrackViewList::reverse_iterator next = track_views.rend();
1447 const double top_of_trackviews = vertical_adjustment.get_value();
1449 for (TrackViewList::reverse_iterator t = track_views.rbegin(); t != track_views.rend(); ++t) {
1450 if ((*t)->hidden()) {
1454 /* If this is the upper-most visible trackview, we want to display
1455 * the one above it (next)
1457 * Note that covers_y_position() is recursive and includes child views
1459 std::pair<TimeAxisView*,double> res = (*t)->covers_y_position (top_of_trackviews);
1462 if (skip_child_views) {
1465 /* automation lane (one level, non-recursive)
1467 * - if no automation lane exists -> move to next tack
1468 * - if the first (here: bottom-most) matches -> move to next tack
1469 * - if no y-axis match is found -> the current track is at the top
1470 * -> move to last (here: top-most) automation lane
1472 TimeAxisView::Children kids = (*t)->get_child_list();
1473 TimeAxisView::Children::reverse_iterator nkid = kids.rend();
1475 for (TimeAxisView::Children::reverse_iterator ci = kids.rbegin(); ci != kids.rend(); ++ci) {
1476 if ((*ci)->hidden()) {
1480 std::pair<TimeAxisView*,double> dev;
1481 dev = (*ci)->covers_y_position (top_of_trackviews);
1483 /* some automation lane is currently at the top */
1484 if (ci == kids.rbegin()) {
1485 /* first (bottom-most) autmation lane is at the top.
1486 * -> move to next track
1495 if (nkid != kids.rend()) {
1496 ensure_time_axis_view_is_visible (**nkid, true);
1504 /* move to the track below the first one that covers the */
1506 if (next != track_views.rend()) {
1507 ensure_time_axis_view_is_visible (**next, true);
1515 Editor::scroll_up_one_track (bool skip_child_views)
1517 TrackViewList::iterator prev = track_views.end();
1518 double top_of_trackviews = vertical_adjustment.get_value ();
1520 for (TrackViewList::iterator t = track_views.begin(); t != track_views.end(); ++t) {
1522 if ((*t)->hidden()) {
1526 /* find the trackview at the top of the trackview group
1528 * Note that covers_y_position() is recursive and includes child views
1530 std::pair<TimeAxisView*,double> res = (*t)->covers_y_position (top_of_trackviews);
1533 if (skip_child_views) {
1536 /* automation lane (one level, non-recursive)
1538 * - if no automation lane exists -> move to prev tack
1539 * - if no y-axis match is found -> the current track is at the top -> move to prev track
1540 * (actually last automation lane of previous track, see below)
1541 * - if first (top-most) lane is at the top -> move to this track
1542 * - else move up one lane
1544 TimeAxisView::Children kids = (*t)->get_child_list();
1545 TimeAxisView::Children::iterator pkid = kids.end();
1547 for (TimeAxisView::Children::iterator ci = kids.begin(); ci != kids.end(); ++ci) {
1548 if ((*ci)->hidden()) {
1552 std::pair<TimeAxisView*,double> dev;
1553 dev = (*ci)->covers_y_position (top_of_trackviews);
1555 /* some automation lane is currently at the top */
1556 if (ci == kids.begin()) {
1557 /* first (top-most) autmation lane is at the top.
1558 * jump directly to this track's top
1560 ensure_time_axis_view_is_visible (**t, true);
1563 else if (pkid != kids.end()) {
1564 /* some other automation lane is at the top.
1565 * move up to prev automation lane.
1567 ensure_time_axis_view_is_visible (**pkid, true);
1570 assert(0); // not reached
1581 if (prev != track_views.end()) {
1582 // move to bottom-most automation-lane of the previous track
1583 TimeAxisView::Children kids = (*prev)->get_child_list();
1584 TimeAxisView::Children::reverse_iterator pkid = kids.rend();
1585 if (!skip_child_views) {
1586 // find the last visible lane
1587 for (TimeAxisView::Children::reverse_iterator ci = kids.rbegin(); ci != kids.rend(); ++ci) {
1588 if (!(*ci)->hidden()) {
1594 if (pkid != kids.rend()) {
1595 ensure_time_axis_view_is_visible (**pkid, true);
1597 ensure_time_axis_view_is_visible (**prev, true);
1606 Editor::scroll_left_step ()
1608 samplepos_t xdelta = (current_page_samples() / 8);
1610 if (_leftmost_sample > xdelta) {
1611 reset_x_origin (_leftmost_sample - xdelta);
1619 Editor::scroll_right_step ()
1621 samplepos_t xdelta = (current_page_samples() / 8);
1623 if (max_samplepos - xdelta > _leftmost_sample) {
1624 reset_x_origin (_leftmost_sample + xdelta);
1626 reset_x_origin (max_samplepos - current_page_samples());
1631 Editor::scroll_left_half_page ()
1633 samplepos_t xdelta = (current_page_samples() / 2);
1634 if (_leftmost_sample > xdelta) {
1635 reset_x_origin (_leftmost_sample - xdelta);
1642 Editor::scroll_right_half_page ()
1644 samplepos_t xdelta = (current_page_samples() / 2);
1645 if (max_samplepos - xdelta > _leftmost_sample) {
1646 reset_x_origin (_leftmost_sample + xdelta);
1648 reset_x_origin (max_samplepos - current_page_samples());
1655 Editor::tav_zoom_step (bool coarser)
1657 DisplaySuspender ds;
1661 if (selection->tracks.empty()) {
1664 ts = &selection->tracks;
1667 for (TrackViewList::iterator i = ts->begin(); i != ts->end(); ++i) {
1668 TimeAxisView *tv = (static_cast<TimeAxisView*>(*i));
1669 tv->step_height (coarser);
1674 Editor::tav_zoom_smooth (bool coarser, bool force_all)
1676 DisplaySuspender ds;
1680 if (selection->tracks.empty() || force_all) {
1683 ts = &selection->tracks;
1686 for (TrackViewList::iterator i = ts->begin(); i != ts->end(); ++i) {
1687 TimeAxisView *tv = (static_cast<TimeAxisView*>(*i));
1688 uint32_t h = tv->current_height ();
1693 if (h >= TimeAxisView::preset_height (HeightSmall)) {
1698 tv->set_height (h + 5);
1704 Editor::temporal_zoom_step_mouse_focus_scale (bool zoom_out, double scale)
1706 Editing::ZoomFocus temp_focus = zoom_focus;
1707 zoom_focus = Editing::ZoomFocusMouse;
1708 temporal_zoom_step_scale (zoom_out, scale);
1709 zoom_focus = temp_focus;
1713 Editor::temporal_zoom_step_mouse_focus (bool zoom_out)
1715 temporal_zoom_step_mouse_focus_scale (zoom_out, 2.0);
1719 Editor::temporal_zoom_step (bool zoom_out)
1721 temporal_zoom_step_scale (zoom_out, 2.0);
1725 Editor::temporal_zoom_step_scale (bool zoom_out, double scale)
1727 ENSURE_GUI_THREAD (*this, &Editor::temporal_zoom_step, zoom_out, scale)
1729 samplecnt_t nspp = samples_per_pixel;
1733 if (nspp == samples_per_pixel) {
1738 if (nspp == samples_per_pixel) {
1743 //zoom-behavior-tweaks
1744 //limit our maximum zoom to the session gui extents value
1745 std::pair<samplepos_t, samplepos_t> ext = session_gui_extents();
1746 samplecnt_t session_extents_pp = (ext.second - ext.first) / _visible_canvas_width;
1747 if (nspp > session_extents_pp)
1748 nspp = session_extents_pp;
1750 temporal_zoom (nspp);
1754 Editor::temporal_zoom (samplecnt_t fpp)
1760 samplepos_t current_page = current_page_samples();
1761 samplepos_t current_leftmost = _leftmost_sample;
1762 samplepos_t current_rightmost;
1763 samplepos_t current_center;
1764 samplepos_t new_page_size;
1765 samplepos_t half_page_size;
1766 samplepos_t leftmost_after_zoom = 0;
1768 bool in_track_canvas;
1769 bool use_mouse_sample = true;
1773 if (fpp == samples_per_pixel) {
1777 // Imposing an arbitrary limit to zoom out as too much zoom out produces
1778 // segfaults for lack of memory. If somebody decides this is not high enough I
1779 // believe it can be raisen to higher values but some limit must be in place.
1781 // This constant represents 1 day @ 48kHz on a 1600 pixel wide display
1782 // all of which is used for the editor track displays. The whole day
1783 // would be 4147200000 samples, so 2592000 samples per pixel.
1785 nfpp = min (fpp, (samplecnt_t) 2592000);
1786 nfpp = max ((samplecnt_t) 1, nfpp);
1788 new_page_size = (samplepos_t) floor (_visible_canvas_width * nfpp);
1789 half_page_size = new_page_size / 2;
1791 switch (zoom_focus) {
1793 leftmost_after_zoom = current_leftmost;
1796 case ZoomFocusRight:
1797 current_rightmost = _leftmost_sample + current_page;
1798 if (current_rightmost < new_page_size) {
1799 leftmost_after_zoom = 0;
1801 leftmost_after_zoom = current_rightmost - new_page_size;
1805 case ZoomFocusCenter:
1806 current_center = current_leftmost + (current_page/2);
1807 if (current_center < half_page_size) {
1808 leftmost_after_zoom = 0;
1810 leftmost_after_zoom = current_center - half_page_size;
1814 case ZoomFocusPlayhead:
1815 /* centre playhead */
1816 l = playhead_cursor->current_sample () - (new_page_size * 0.5);
1819 leftmost_after_zoom = 0;
1820 } else if (l > max_samplepos) {
1821 leftmost_after_zoom = max_samplepos - new_page_size;
1823 leftmost_after_zoom = (samplepos_t) l;
1827 case ZoomFocusMouse:
1828 /* try to keep the mouse over the same point in the display */
1830 if (_drags->active()) {
1831 where = _drags->current_pointer_sample ();
1832 } else if (!mouse_sample (where, in_track_canvas)) {
1833 use_mouse_sample = false;
1836 if (use_mouse_sample) {
1837 l = - ((new_page_size * ((where - current_leftmost)/(double)current_page)) - where);
1840 leftmost_after_zoom = 0;
1841 } else if (l > max_samplepos) {
1842 leftmost_after_zoom = max_samplepos - new_page_size;
1844 leftmost_after_zoom = (samplepos_t) l;
1847 /* use playhead instead */
1848 where = playhead_cursor->current_sample ();
1850 if (where < half_page_size) {
1851 leftmost_after_zoom = 0;
1853 leftmost_after_zoom = where - half_page_size;
1859 /* try to keep the edit point in the same place */
1860 where = get_preferred_edit_position ();
1864 double l = - ((new_page_size * ((where - current_leftmost)/(double)current_page)) - where);
1867 leftmost_after_zoom = 0;
1868 } else if (l > max_samplepos) {
1869 leftmost_after_zoom = max_samplepos - new_page_size;
1871 leftmost_after_zoom = (samplepos_t) l;
1875 /* edit point not defined */
1882 // leftmost_after_zoom = min (leftmost_after_zoom, _session->current_end_sample());
1884 reposition_and_zoom (leftmost_after_zoom, nfpp);
1888 Editor::calc_extra_zoom_edges(samplepos_t &start, samplepos_t &end)
1890 /* this func helps make sure we leave a little space
1891 at each end of the editor so that the zoom doesn't fit the region
1892 precisely to the screen.
1895 GdkScreen* screen = gdk_screen_get_default ();
1896 const gint pixwidth = gdk_screen_get_width (screen);
1897 const gint mmwidth = gdk_screen_get_width_mm (screen);
1898 const double pix_per_mm = (double) pixwidth/ (double) mmwidth;
1899 const double one_centimeter_in_pixels = pix_per_mm * 10.0;
1901 const samplepos_t range = end - start;
1902 const samplecnt_t new_fpp = (samplecnt_t) ceil ((double) range / (double) _visible_canvas_width);
1903 const samplepos_t extra_samples = (samplepos_t) floor (one_centimeter_in_pixels * new_fpp);
1905 if (start > extra_samples) {
1906 start -= extra_samples;
1911 if (max_samplepos - extra_samples > end) {
1912 end += extra_samples;
1914 end = max_samplepos;
1919 Editor::get_selection_extents (samplepos_t &start, samplepos_t &end) const
1921 start = max_samplepos;
1925 //ToDo: if notes are selected, set extents to that selection
1927 //ToDo: if control points are selected, set extents to that selection
1929 if (!selection->regions.empty()) {
1930 RegionSelection rs = get_regions_from_selection_and_entered ();
1932 for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
1934 if ((*i)->region()->position() < start) {
1935 start = (*i)->region()->position();
1938 if ((*i)->region()->last_sample() + 1 > end) {
1939 end = (*i)->region()->last_sample() + 1;
1943 } else if (!selection->time.empty()) {
1944 start = selection->time.start();
1945 end = selection->time.end_sample();
1947 ret = false; //no selection found
1950 if ((start == 0 && end == 0) || end < start) {
1959 Editor::temporal_zoom_selection (Editing::ZoomAxis axes)
1961 if (!selection) return;
1963 if (selection->regions.empty() && selection->time.empty()) {
1964 if (axes == Horizontal || axes == Both) {
1965 temporal_zoom_step(true);
1967 if (axes == Vertical || axes == Both) {
1968 if (!track_views.empty()) {
1972 //implicit hack: by extending the top & bottom check outside the current view limits, we include the trackviews immediately above & below what is visible
1973 const double top = vertical_adjustment.get_value() - 10;
1974 const double btm = top + _visible_canvas_height + 10;
1976 for (TrackViewList::iterator iter = track_views.begin(); iter != track_views.end(); ++iter) {
1977 if ((*iter)->covered_by_y_range (top, btm)) {
1978 tvl.push_back(*iter);
1988 //ToDo: if notes are selected, zoom to that
1990 //ToDo: if control points are selected, zoom to that
1992 if (axes == Horizontal || axes == Both) {
1994 samplepos_t start, end;
1995 if (get_selection_extents (start, end)) {
1996 calc_extra_zoom_edges (start, end);
1997 temporal_zoom_by_sample (start, end);
2001 if (axes == Vertical || axes == Both) {
2005 //normally, we don't do anything "automatic" to the user's selection.
2006 //but in this case, we will clear the selection after a zoom-to-selection.
2011 Editor::temporal_zoom_session ()
2013 ENSURE_GUI_THREAD (*this, &Editor::temporal_zoom_session)
2016 samplecnt_t start = _session->current_start_sample();
2017 samplecnt_t end = _session->current_end_sample();
2019 if (_session->actively_recording ()) {
2020 samplepos_t cur = playhead_cursor->current_sample ();
2022 /* recording beyond the end marker; zoom out
2023 * by 5 seconds more so that if 'follow
2024 * playhead' is active we don't immediately
2027 end = cur + _session->sample_rate() * 5;
2031 if ((start == 0 && end == 0) || end < start) {
2035 calc_extra_zoom_edges(start, end);
2037 temporal_zoom_by_sample (start, end);
2042 Editor::temporal_zoom_extents ()
2044 ENSURE_GUI_THREAD (*this, &Editor::temporal_zoom_extents)
2047 std::pair<samplepos_t, samplepos_t> ext = session_gui_extents (false); //in this case we want to zoom to the extents explicitly; ignore the users prefs for extra padding
2049 samplecnt_t start = ext.first;
2050 samplecnt_t end = ext.second;
2052 if (_session->actively_recording ()) {
2053 samplepos_t cur = playhead_cursor->current_sample ();
2055 /* recording beyond the end marker; zoom out
2056 * by 5 seconds more so that if 'follow
2057 * playhead' is active we don't immediately
2060 end = cur + _session->sample_rate() * 5;
2064 if ((start == 0 && end == 0) || end < start) {
2068 calc_extra_zoom_edges(start, end);
2070 temporal_zoom_by_sample (start, end);
2075 Editor::temporal_zoom_by_sample (samplepos_t start, samplepos_t end)
2077 if (!_session) return;
2079 if ((start == 0 && end == 0) || end < start) {
2083 samplepos_t range = end - start;
2085 const samplecnt_t new_fpp = (samplecnt_t) ceil ((double) range / (double) _visible_canvas_width);
2087 samplepos_t new_page = range;
2088 samplepos_t middle = (samplepos_t) floor ((double) start + ((double) range / 2.0f));
2089 samplepos_t new_leftmost = (samplepos_t) floor ((double) middle - ((double) new_page / 2.0f));
2091 if (new_leftmost > middle) {
2095 if (new_leftmost < 0) {
2099 reposition_and_zoom (new_leftmost, new_fpp);
2103 Editor::temporal_zoom_to_sample (bool coarser, samplepos_t sample)
2109 samplecnt_t range_before = sample - _leftmost_sample;
2110 samplecnt_t new_spp;
2113 if (samples_per_pixel <= 1) {
2116 new_spp = samples_per_pixel + (samples_per_pixel/2);
2118 range_before += range_before/2;
2120 if (samples_per_pixel >= 1) {
2121 new_spp = samples_per_pixel - (samples_per_pixel/2);
2123 /* could bail out here since we cannot zoom any finer,
2124 but leave that to the equality test below
2126 new_spp = samples_per_pixel;
2129 range_before -= range_before/2;
2132 if (new_spp == samples_per_pixel) {
2136 /* zoom focus is automatically taken as @param sample when this
2140 samplepos_t new_leftmost = sample - (samplepos_t)range_before;
2142 if (new_leftmost > sample) {
2146 if (new_leftmost < 0) {
2150 reposition_and_zoom (new_leftmost, new_spp);
2155 Editor::choose_new_marker_name(string &name) {
2157 if (!UIConfiguration::instance().get_name_new_markers()) {
2158 /* don't prompt user for a new name */
2162 Prompter dialog (true);
2164 dialog.set_prompt (_("New Name:"));
2166 dialog.set_title (_("New Location Marker"));
2168 dialog.set_name ("MarkNameWindow");
2169 dialog.set_size_request (250, -1);
2170 dialog.set_position (Gtk::WIN_POS_MOUSE);
2172 dialog.add_button (Stock::OK, RESPONSE_ACCEPT);
2173 dialog.set_initial_text (name);
2177 switch (dialog.run ()) {
2178 case RESPONSE_ACCEPT:
2184 dialog.get_result(name);
2191 Editor::add_location_from_selection ()
2195 if (selection->time.empty()) {
2199 if (_session == 0 || clicked_axisview == 0) {
2203 samplepos_t start = selection->time[clicked_selection].start;
2204 samplepos_t end = selection->time[clicked_selection].end;
2206 _session->locations()->next_available_name(rangename,"selection");
2207 Location *location = new Location (*_session, start, end, rangename, Location::IsRangeMarker, get_grid_music_divisions(0));
2209 begin_reversible_command (_("add marker"));
2211 XMLNode &before = _session->locations()->get_state();
2212 _session->locations()->add (location, true);
2213 XMLNode &after = _session->locations()->get_state();
2214 _session->add_command(new MementoCommand<Locations>(*(_session->locations()), &before, &after));
2216 commit_reversible_command ();
2220 Editor::add_location_mark (samplepos_t where)
2224 select_new_marker = true;
2226 _session->locations()->next_available_name(markername,"mark");
2227 if (!choose_new_marker_name(markername)) {
2230 Location *location = new Location (*_session, where, where, markername, Location::IsMark, get_grid_music_divisions (0));
2231 begin_reversible_command (_("add marker"));
2233 XMLNode &before = _session->locations()->get_state();
2234 _session->locations()->add (location, true);
2235 XMLNode &after = _session->locations()->get_state();
2236 _session->add_command(new MementoCommand<Locations>(*(_session->locations()), &before, &after));
2238 commit_reversible_command ();
2242 Editor::set_session_start_from_playhead ()
2248 if ((loc = _session->locations()->session_range_location()) == 0) { //should never happen
2249 _session->set_session_extents (_session->audible_sample(), _session->audible_sample());
2251 XMLNode &before = loc->get_state();
2253 _session->set_session_extents (_session->audible_sample(), loc->end());
2255 XMLNode &after = loc->get_state();
2257 begin_reversible_command (_("Set session start"));
2259 _session->add_command (new MementoCommand<Location>(*loc, &before, &after));
2261 commit_reversible_command ();
2266 Editor::set_session_end_from_playhead ()
2272 if ((loc = _session->locations()->session_range_location()) == 0) { //should never happen
2273 _session->set_session_extents (_session->audible_sample(), _session->audible_sample());
2275 XMLNode &before = loc->get_state();
2277 _session->set_session_extents (loc->start(), _session->audible_sample());
2279 XMLNode &after = loc->get_state();
2281 begin_reversible_command (_("Set session start"));
2283 _session->add_command (new MementoCommand<Location>(*loc, &before, &after));
2285 commit_reversible_command ();
2288 _session->set_end_is_free (false);
2293 Editor::toggle_location_at_playhead_cursor ()
2295 if (!do_remove_location_at_playhead_cursor())
2297 add_location_from_playhead_cursor();
2302 Editor::add_location_from_playhead_cursor ()
2304 add_location_mark (_session->audible_sample());
2308 Editor::do_remove_location_at_playhead_cursor ()
2310 bool removed = false;
2313 XMLNode &before = _session->locations()->get_state();
2315 //find location(s) at this time
2316 Locations::LocationList locs;
2317 _session->locations()->find_all_between (_session->audible_sample(), _session->audible_sample()+1, locs, Location::Flags(0));
2318 for (Locations::LocationList::iterator i = locs.begin(); i != locs.end(); ++i) {
2319 if ((*i)->is_mark()) {
2320 _session->locations()->remove (*i);
2327 begin_reversible_command (_("remove marker"));
2328 XMLNode &after = _session->locations()->get_state();
2329 _session->add_command(new MementoCommand<Locations>(*(_session->locations()), &before, &after));
2330 commit_reversible_command ();
2337 Editor::remove_location_at_playhead_cursor ()
2339 do_remove_location_at_playhead_cursor ();
2342 /** Add a range marker around each selected region */
2344 Editor::add_locations_from_region ()
2346 RegionSelection rs = get_regions_from_selection_and_entered ();
2351 bool commit = false;
2353 XMLNode &before = _session->locations()->get_state();
2355 for (RegionSelection::iterator i = rs.begin (); i != rs.end (); ++i) {
2357 boost::shared_ptr<Region> region = (*i)->region ();
2359 Location *location = new Location (*_session, region->position(), region->last_sample(), region->name(), Location::IsRangeMarker, 0);
2361 _session->locations()->add (location, true);
2366 begin_reversible_command (selection->regions.size () > 1 ? _("add markers") : _("add marker"));
2367 XMLNode &after = _session->locations()->get_state();
2368 _session->add_command (new MementoCommand<Locations>(*(_session->locations()), &before, &after));
2369 commit_reversible_command ();
2373 /** Add a single range marker around all selected regions */
2375 Editor::add_location_from_region ()
2377 RegionSelection rs = get_regions_from_selection_and_entered ();
2383 XMLNode &before = _session->locations()->get_state();
2387 if (rs.size() > 1) {
2388 _session->locations()->next_available_name(markername, "regions");
2390 RegionView* rv = *(rs.begin());
2391 boost::shared_ptr<Region> region = rv->region();
2392 markername = region->name();
2395 if (!choose_new_marker_name(markername)) {
2399 // single range spanning all selected
2400 Location *location = new Location (*_session, selection->regions.start(), selection->regions.end_sample(), markername, Location::IsRangeMarker, 0);
2401 _session->locations()->add (location, true);
2403 begin_reversible_command (_("add marker"));
2404 XMLNode &after = _session->locations()->get_state();
2405 _session->add_command (new MementoCommand<Locations>(*(_session->locations()), &before, &after));
2406 commit_reversible_command ();
2412 Editor::jump_forward_to_mark ()
2418 samplepos_t pos = _session->locations()->first_mark_after (playhead_cursor->current_sample());
2424 _session->request_locate (pos, _session->transport_rolling());
2428 Editor::jump_backward_to_mark ()
2434 samplepos_t pos = _session->locations()->first_mark_before (playhead_cursor->current_sample());
2436 //handle the case where we are rolling, and we're less than one-half second past the mark, we want to go to the prior mark...
2437 if (_session->transport_rolling()) {
2438 if ((playhead_cursor->current_sample() - pos) < _session->sample_rate()/2) {
2439 samplepos_t prior = _session->locations()->first_mark_before (pos);
2448 _session->request_locate (pos, _session->transport_rolling());
2454 samplepos_t const pos = _session->audible_sample ();
2457 _session->locations()->next_available_name (markername, "mark");
2459 if (!choose_new_marker_name (markername)) {
2463 _session->locations()->add (new Location (*_session, pos, 0, markername, Location::IsMark, 0), true);
2467 Editor::clear_markers ()
2470 begin_reversible_command (_("clear markers"));
2472 XMLNode &before = _session->locations()->get_state();
2473 _session->locations()->clear_markers ();
2474 XMLNode &after = _session->locations()->get_state();
2475 _session->add_command(new MementoCommand<Locations>(*(_session->locations()), &before, &after));
2477 commit_reversible_command ();
2482 Editor::clear_ranges ()
2485 begin_reversible_command (_("clear ranges"));
2487 XMLNode &before = _session->locations()->get_state();
2489 _session->locations()->clear_ranges ();
2491 XMLNode &after = _session->locations()->get_state();
2492 _session->add_command(new MementoCommand<Locations>(*(_session->locations()), &before, &after));
2494 commit_reversible_command ();
2499 Editor::clear_locations ()
2501 begin_reversible_command (_("clear locations"));
2503 XMLNode &before = _session->locations()->get_state();
2504 _session->locations()->clear ();
2505 XMLNode &after = _session->locations()->get_state();
2506 _session->add_command(new MementoCommand<Locations>(*(_session->locations()), &before, &after));
2508 commit_reversible_command ();
2512 Editor::unhide_markers ()
2514 for (LocationMarkerMap::iterator i = location_markers.begin(); i != location_markers.end(); ++i) {
2515 Location *l = (*i).first;
2516 if (l->is_hidden() && l->is_mark()) {
2517 l->set_hidden(false, this);
2523 Editor::unhide_ranges ()
2525 for (LocationMarkerMap::iterator i = location_markers.begin(); i != location_markers.end(); ++i) {
2526 Location *l = (*i).first;
2527 if (l->is_hidden() && l->is_range_marker()) {
2528 l->set_hidden(false, this);
2533 /* INSERT/REPLACE */
2536 Editor::insert_region_list_selection (float times)
2538 RouteTimeAxisView *tv = 0;
2539 boost::shared_ptr<Playlist> playlist;
2541 if (clicked_routeview != 0) {
2542 tv = clicked_routeview;
2543 } else if (!selection->tracks.empty()) {
2544 if ((tv = dynamic_cast<RouteTimeAxisView*>(selection->tracks.front())) == 0) {
2547 } else if (entered_track != 0) {
2548 if ((tv = dynamic_cast<RouteTimeAxisView*>(entered_track)) == 0) {
2555 if ((playlist = tv->playlist()) == 0) {
2559 boost::shared_ptr<Region> region = _regions->get_single_selection ();
2564 begin_reversible_command (_("insert region"));
2565 playlist->clear_changes ();
2566 playlist->add_region ((RegionFactory::create (region, true)), get_preferred_edit_position(), times);
2567 if (Config->get_edit_mode() == Ripple)
2568 playlist->ripple (get_preferred_edit_position(), region->length() * times, boost::shared_ptr<Region>());
2570 _session->add_command(new StatefulDiffCommand (playlist));
2571 commit_reversible_command ();
2574 /* BUILT-IN EFFECTS */
2577 Editor::reverse_selection ()
2582 /* GAIN ENVELOPE EDITING */
2585 Editor::edit_envelope ()
2592 Editor::transition_to_rolling (bool fwd)
2598 if (_session->config.get_external_sync()) {
2599 switch (TransportMasterManager::instance().current()->type()) {
2603 /* transport controlled by the master */
2608 if (_session->is_auditioning()) {
2609 _session->cancel_audition ();
2613 _session->request_transport_speed (fwd ? 1.0f : -1.0f);
2617 Editor::play_from_start ()
2619 _session->request_locate (_session->current_start_sample(), true);
2623 Editor::play_from_edit_point ()
2625 _session->request_locate (get_preferred_edit_position(), true);
2629 Editor::play_from_edit_point_and_return ()
2631 samplepos_t start_sample;
2632 samplepos_t return_sample;
2634 start_sample = get_preferred_edit_position (EDIT_IGNORE_PHEAD);
2636 if (_session->transport_rolling()) {
2637 _session->request_locate (start_sample, false);
2641 /* don't reset the return sample if its already set */
2643 if ((return_sample = _session->requested_return_sample()) < 0) {
2644 return_sample = _session->audible_sample();
2647 if (start_sample >= 0) {
2648 _session->request_roll_at_and_return (start_sample, return_sample);
2653 Editor::play_selection ()
2655 samplepos_t start, end;
2656 if (!get_selection_extents (start, end))
2659 AudioRange ar (start, end, 0);
2660 list<AudioRange> lar;
2663 _session->request_play_range (&lar, true);
2668 Editor::maybe_locate_with_edit_preroll (samplepos_t location)
2670 if (_session->transport_rolling() || !UIConfiguration::instance().get_follow_edits() || _session->config.get_external_sync())
2673 location -= _session->preroll_samples (location);
2675 //don't try to locate before the beginning of time
2680 //if follow_playhead is on, keep the playhead on the screen
2681 if (_follow_playhead)
2682 if (location < _leftmost_sample)
2683 location = _leftmost_sample;
2685 _session->request_locate (location);
2689 Editor::play_with_preroll ()
2691 samplepos_t start, end;
2692 if (UIConfiguration::instance().get_follow_edits() && get_selection_extents (start, end)) {
2693 const samplepos_t preroll = _session->preroll_samples (start);
2695 samplepos_t ret = start;
2697 if (start > preroll) {
2698 start = start - preroll;
2701 end = end + preroll; //"post-roll"
2703 AudioRange ar (start, end, 0);
2704 list<AudioRange> lar;
2707 _session->request_play_range (&lar, true);
2708 _session->set_requested_return_sample (ret); //force auto-return to return to range start, without the preroll
2710 samplepos_t ph = playhead_cursor->current_sample ();
2711 const samplepos_t preroll = _session->preroll_samples (ph);
2714 start = ph - preroll;
2718 _session->request_locate (start, true);
2719 _session->set_requested_return_sample (ph); //force auto-return to return to playhead location, without the preroll
2724 Editor::rec_with_preroll ()
2726 samplepos_t ph = playhead_cursor->current_sample ();
2727 samplepos_t preroll = _session->preroll_samples (ph);
2728 _session->request_preroll_record_trim (ph, preroll);
2732 Editor::rec_with_count_in ()
2734 _session->request_count_in_record ();
2738 Editor::play_location (Location& location)
2740 if (location.start() <= location.end()) {
2744 _session->request_bounded_roll (location.start(), location.end());
2748 Editor::loop_location (Location& location)
2750 if (location.start() <= location.end()) {
2756 if ((tll = transport_loop_location()) != 0) {
2757 tll->set (location.start(), location.end());
2759 // enable looping, reposition and start rolling
2760 _session->request_locate (tll->start(), true);
2761 _session->request_play_loop (true);
2766 Editor::do_layer_operation (LayerOperation op)
2768 if (selection->regions.empty ()) {
2772 bool const multiple = selection->regions.size() > 1;
2776 begin_reversible_command (_("raise regions"));
2778 begin_reversible_command (_("raise region"));
2784 begin_reversible_command (_("raise regions to top"));
2786 begin_reversible_command (_("raise region to top"));
2792 begin_reversible_command (_("lower regions"));
2794 begin_reversible_command (_("lower region"));
2800 begin_reversible_command (_("lower regions to bottom"));
2802 begin_reversible_command (_("lower region"));
2807 set<boost::shared_ptr<Playlist> > playlists = selection->regions.playlists ();
2808 for (set<boost::shared_ptr<Playlist> >::iterator i = playlists.begin(); i != playlists.end(); ++i) {
2809 (*i)->clear_owned_changes ();
2812 for (RegionSelection::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
2813 boost::shared_ptr<Region> r = (*i)->region ();
2825 r->lower_to_bottom ();
2829 for (set<boost::shared_ptr<Playlist> >::iterator i = playlists.begin(); i != playlists.end(); ++i) {
2830 vector<Command*> cmds;
2832 _session->add_commands (cmds);
2835 commit_reversible_command ();
2839 Editor::raise_region ()
2841 do_layer_operation (Raise);
2845 Editor::raise_region_to_top ()
2847 do_layer_operation (RaiseToTop);
2851 Editor::lower_region ()
2853 do_layer_operation (Lower);
2857 Editor::lower_region_to_bottom ()
2859 do_layer_operation (LowerToBottom);
2862 /** Show the region editor for the selected regions */
2864 Editor::show_region_properties ()
2866 selection->foreach_regionview (&RegionView::show_region_editor);
2869 /** Show the midi list editor for the selected MIDI regions */
2871 Editor::show_midi_list_editor ()
2873 selection->foreach_midi_regionview (&MidiRegionView::show_list_editor);
2877 Editor::rename_region ()
2879 RegionSelection rs = get_regions_from_selection_and_entered ();
2885 ArdourDialog d (_("Rename Region"), true, false);
2887 Label label (_("New name:"));
2890 hbox.set_spacing (6);
2891 hbox.pack_start (label, false, false);
2892 hbox.pack_start (entry, true, true);
2894 d.get_vbox()->set_border_width (12);
2895 d.get_vbox()->pack_start (hbox, false, false);
2897 d.add_button(Gtk::Stock::CANCEL, Gtk::RESPONSE_CANCEL);
2898 d.add_button(Gtk::Stock::OK, Gtk::RESPONSE_OK);
2900 d.set_size_request (300, -1);
2902 entry.set_text (rs.front()->region()->name());
2903 entry.select_region (0, -1);
2905 entry.signal_activate().connect (sigc::bind (sigc::mem_fun (d, &Dialog::response), RESPONSE_OK));
2911 int const ret = d.run();
2915 if (ret != RESPONSE_OK) {
2919 std::string str = entry.get_text();
2920 strip_whitespace_edges (str);
2922 rs.front()->region()->set_name (str);
2923 _regions->redisplay ();
2927 /** Start an audition of the first selected region */
2929 Editor::play_edit_range ()
2931 samplepos_t start, end;
2933 if (get_edit_op_range (start, end)) {
2934 _session->request_bounded_roll (start, end);
2939 Editor::play_selected_region ()
2941 samplepos_t start = max_samplepos;
2942 samplepos_t end = 0;
2944 RegionSelection rs = get_regions_from_selection_and_entered ();
2950 for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
2951 if ((*i)->region()->position() < start) {
2952 start = (*i)->region()->position();
2954 if ((*i)->region()->last_sample() + 1 > end) {
2955 end = (*i)->region()->last_sample() + 1;
2959 _session->request_bounded_roll (start, end);
2963 Editor::audition_playlist_region_standalone (boost::shared_ptr<Region> region)
2965 _session->audition_region (region);
2969 Editor::region_from_selection ()
2971 if (clicked_axisview == 0) {
2975 if (selection->time.empty()) {
2979 samplepos_t start = selection->time[clicked_selection].start;
2980 samplepos_t end = selection->time[clicked_selection].end;
2982 TrackViewList tracks = get_tracks_for_range_action ();
2984 samplepos_t selection_cnt = end - start + 1;
2986 for (TrackSelection::iterator i = tracks.begin(); i != tracks.end(); ++i) {
2987 boost::shared_ptr<Region> current;
2988 boost::shared_ptr<Playlist> pl;
2989 samplepos_t internal_start;
2992 if ((pl = (*i)->playlist()) == 0) {
2996 if ((current = pl->top_region_at (start)) == 0) {
3000 internal_start = start - current->position();
3001 RegionFactory::region_name (new_name, current->name(), true);
3005 plist.add (ARDOUR::Properties::start, current->start() + internal_start);
3006 plist.add (ARDOUR::Properties::length, selection_cnt);
3007 plist.add (ARDOUR::Properties::name, new_name);
3008 plist.add (ARDOUR::Properties::layer, 0);
3010 boost::shared_ptr<Region> region (RegionFactory::create (current, plist));
3015 Editor::create_region_from_selection (vector<boost::shared_ptr<Region> >& new_regions)
3017 if (selection->time.empty() || selection->tracks.empty()) {
3021 samplepos_t start, end;
3022 if (clicked_selection) {
3023 start = selection->time[clicked_selection].start;
3024 end = selection->time[clicked_selection].end;
3026 start = selection->time.start();
3027 end = selection->time.end_sample();
3030 TrackViewList ts = selection->tracks.filter_to_unique_playlists ();
3031 sort_track_selection (ts);
3033 for (TrackSelection::iterator i = ts.begin(); i != ts.end(); ++i) {
3034 boost::shared_ptr<Region> current;
3035 boost::shared_ptr<Playlist> playlist;
3036 samplepos_t internal_start;
3039 if ((playlist = (*i)->playlist()) == 0) {
3043 if ((current = playlist->top_region_at(start)) == 0) {
3047 internal_start = start - current->position();
3048 RegionFactory::region_name (new_name, current->name(), true);
3052 plist.add (ARDOUR::Properties::start, current->start() + internal_start);
3053 plist.add (ARDOUR::Properties::length, end - start + 1);
3054 plist.add (ARDOUR::Properties::name, new_name);
3056 new_regions.push_back (RegionFactory::create (current, plist));
3061 Editor::split_multichannel_region ()
3063 RegionSelection rs = get_regions_from_selection_and_entered ();
3069 vector< boost::shared_ptr<Region> > v;
3071 for (list<RegionView*>::iterator x = rs.begin(); x != rs.end(); ++x) {
3072 (*x)->region()->separate_by_channel (v);
3077 Editor::new_region_from_selection ()
3079 region_from_selection ();
3080 cancel_selection ();
3084 add_if_covered (RegionView* rv, const AudioRange* ar, RegionSelection* rs)
3086 switch (rv->region()->coverage (ar->start, ar->end - 1)) {
3087 // n.b. -1 because AudioRange::end is one past the end, but coverage expects inclusive ranges
3088 case Evoral::OverlapNone:
3096 * - selected tracks, or if there are none...
3097 * - tracks containing selected regions, or if there are none...
3102 Editor::get_tracks_for_range_action () const
3106 if (selection->tracks.empty()) {
3108 /* use tracks with selected regions */
3110 RegionSelection rs = selection->regions;
3112 for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
3113 TimeAxisView* tv = &(*i)->get_time_axis_view();
3115 if (!t.contains (tv)) {
3121 /* no regions and no tracks: use all tracks */
3127 t = selection->tracks;
3130 return t.filter_to_unique_playlists();
3134 Editor::separate_regions_between (const TimeSelection& ts)
3136 bool in_command = false;
3137 boost::shared_ptr<Playlist> playlist;
3138 RegionSelection new_selection;
3140 TrackViewList tmptracks = get_tracks_for_range_action ();
3141 sort_track_selection (tmptracks);
3143 for (TrackSelection::iterator i = tmptracks.begin(); i != tmptracks.end(); ++i) {
3145 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> ((*i));
3151 if (!rtv->is_track()) {
3155 /* no edits to destructive tracks */
3157 if (rtv->track()->destructive()) {
3161 if ((playlist = rtv->playlist()) != 0) {
3163 playlist->clear_changes ();
3165 /* XXX need to consider musical time selections here at some point */
3167 for (list<AudioRange>::const_iterator t = ts.begin(); t != ts.end(); ++t) {
3169 sigc::connection c = rtv->view()->RegionViewAdded.connect (
3170 sigc::mem_fun(*this, &Editor::collect_new_region_view));
3172 latest_regionviews.clear ();
3174 playlist->partition ((*t).start, (*t).end, false);
3178 if (!latest_regionviews.empty()) {
3180 rtv->view()->foreach_regionview (sigc::bind (
3181 sigc::ptr_fun (add_if_covered),
3182 &(*t), &new_selection));
3185 begin_reversible_command (_("separate"));
3189 /* pick up changes to existing regions */
3191 vector<Command*> cmds;
3192 playlist->rdiff (cmds);
3193 _session->add_commands (cmds);
3195 /* pick up changes to the playlist itself (adds/removes)
3198 _session->add_command(new StatefulDiffCommand (playlist));
3205 // selection->set (new_selection);
3207 commit_reversible_command ();
3211 struct PlaylistState {
3212 boost::shared_ptr<Playlist> playlist;
3216 /** Take tracks from get_tracks_for_range_action and cut any regions
3217 * on those tracks so that the tracks are empty over the time
3221 Editor::separate_region_from_selection ()
3223 /* preferentially use *all* ranges in the time selection if we're in range mode
3224 to allow discontiguous operation, since get_edit_op_range() currently
3225 returns a single range.
3228 if (!selection->time.empty()) {
3230 separate_regions_between (selection->time);
3237 if (get_edit_op_range (start, end)) {
3239 AudioRange ar (start, end, 1);
3243 separate_regions_between (ts);
3249 Editor::separate_region_from_punch ()
3251 Location* loc = _session->locations()->auto_punch_location();
3253 separate_regions_using_location (*loc);
3258 Editor::separate_region_from_loop ()
3260 Location* loc = _session->locations()->auto_loop_location();
3262 separate_regions_using_location (*loc);
3267 Editor::separate_regions_using_location (Location& loc)
3269 if (loc.is_mark()) {
3273 AudioRange ar (loc.start(), loc.end(), 1);
3278 separate_regions_between (ts);
3281 /** Separate regions under the selected region */
3283 Editor::separate_under_selected_regions ()
3285 vector<PlaylistState> playlists;
3289 rs = get_regions_from_selection_and_entered();
3291 if (!_session || rs.empty()) {
3295 begin_reversible_command (_("separate region under"));
3297 list<boost::shared_ptr<Region> > regions_to_remove;
3299 for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
3300 // we can't just remove the region(s) in this loop because
3301 // this removes them from the RegionSelection, and they thus
3302 // disappear from underneath the iterator, and the ++i above
3303 // SEGVs in a puzzling fashion.
3305 // so, first iterate over the regions to be removed from rs and
3306 // add them to the regions_to_remove list, and then
3307 // iterate over the list to actually remove them.
3309 regions_to_remove.push_back ((*i)->region());
3312 for (list<boost::shared_ptr<Region> >::iterator rl = regions_to_remove.begin(); rl != regions_to_remove.end(); ++rl) {
3314 boost::shared_ptr<Playlist> playlist = (*rl)->playlist();
3317 // is this check necessary?
3321 vector<PlaylistState>::iterator i;
3323 //only take state if this is a new playlist.
3324 for (i = playlists.begin(); i != playlists.end(); ++i) {
3325 if ((*i).playlist == playlist) {
3330 if (i == playlists.end()) {
3332 PlaylistState before;
3333 before.playlist = playlist;
3334 before.before = &playlist->get_state();
3335 playlist->clear_changes ();
3336 playlist->freeze ();
3337 playlists.push_back(before);
3340 //Partition on the region bounds
3341 playlist->partition ((*rl)->first_sample() - 1, (*rl)->last_sample() + 1, true);
3343 //Re-add region that was just removed due to the partition operation
3344 playlist->add_region ((*rl), (*rl)->first_sample());
3347 vector<PlaylistState>::iterator pl;
3349 for (pl = playlists.begin(); pl != playlists.end(); ++pl) {
3350 (*pl).playlist->thaw ();
3351 _session->add_command(new MementoCommand<Playlist>(*(*pl).playlist, (*pl).before, &(*pl).playlist->get_state()));
3354 commit_reversible_command ();
3358 Editor::crop_region_to_selection ()
3360 if (!selection->time.empty()) {
3362 begin_reversible_command (_("Crop Regions to Time Selection"));
3363 for (std::list<AudioRange>::iterator i = selection->time.begin(); i != selection->time.end(); ++i) {
3364 crop_region_to ((*i).start, (*i).end);
3366 commit_reversible_command();
3372 if (get_edit_op_range (start, end)) {
3373 begin_reversible_command (_("Crop Regions to Edit Range"));
3375 crop_region_to (start, end);
3377 commit_reversible_command();
3384 Editor::crop_region_to (samplepos_t start, samplepos_t end)
3386 vector<boost::shared_ptr<Playlist> > playlists;
3387 boost::shared_ptr<Playlist> playlist;
3390 if (selection->tracks.empty()) {
3391 ts = track_views.filter_to_unique_playlists();
3393 ts = selection->tracks.filter_to_unique_playlists ();
3396 sort_track_selection (ts);
3398 for (TrackSelection::iterator i = ts.begin(); i != ts.end(); ++i) {
3400 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> ((*i));
3406 boost::shared_ptr<Track> t = rtv->track();
3408 if (t != 0 && ! t->destructive()) {
3410 if ((playlist = rtv->playlist()) != 0) {
3411 playlists.push_back (playlist);
3416 if (playlists.empty()) {
3421 samplepos_t new_start;
3422 samplepos_t new_end;
3423 samplecnt_t new_length;
3425 for (vector<boost::shared_ptr<Playlist> >::iterator i = playlists.begin(); i != playlists.end(); ++i) {
3427 /* Only the top regions at start and end have to be cropped */
3428 boost::shared_ptr<Region> region_at_start = (*i)->top_region_at(start);
3429 boost::shared_ptr<Region> region_at_end = (*i)->top_region_at(end);
3431 vector<boost::shared_ptr<Region> > regions;
3433 if (region_at_start != 0) {
3434 regions.push_back (region_at_start);
3436 if (region_at_end != 0) {
3437 regions.push_back (region_at_end);
3440 /* now adjust lengths */
3441 for (vector<boost::shared_ptr<Region> >::iterator i = regions.begin(); i != regions.end(); ++i) {
3443 pos = (*i)->position();
3444 new_start = max (start, pos);
3445 if (max_samplepos - pos > (*i)->length()) {
3446 new_end = pos + (*i)->length() - 1;
3448 new_end = max_samplepos;
3450 new_end = min (end, new_end);
3451 new_length = new_end - new_start + 1;
3453 (*i)->clear_changes ();
3454 (*i)->trim_to (new_start, new_length);
3455 _session->add_command (new StatefulDiffCommand (*i));
3461 Editor::region_fill_track ()
3463 boost::shared_ptr<Playlist> playlist;
3464 RegionSelection regions = get_regions_from_selection_and_entered ();
3465 RegionSelection foo;
3467 samplepos_t const end = _session->current_end_sample ();
3469 if (regions.empty () || regions.end_sample () + 1 >= end) {
3473 samplepos_t const start_sample = regions.start ();
3474 samplepos_t const end_sample = regions.end_sample ();
3475 samplecnt_t const gap = end_sample - start_sample + 1;
3477 begin_reversible_command (Operations::region_fill);
3479 selection->clear_regions ();
3481 for (RegionSelection::iterator i = regions.begin(); i != regions.end(); ++i) {
3483 boost::shared_ptr<Region> r ((*i)->region());
3485 TimeAxisView& tv = (*i)->get_time_axis_view();
3486 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (&tv);
3487 latest_regionviews.clear ();
3488 sigc::connection c = rtv->view()->RegionViewAdded.connect (sigc::mem_fun(*this, &Editor::collect_new_region_view));
3490 samplepos_t const position = end_sample + (r->first_sample() - start_sample + 1);
3491 playlist = (*i)->region()->playlist();
3492 playlist->clear_changes ();
3493 playlist->duplicate_until (r, position, gap, end);
3494 _session->add_command(new StatefulDiffCommand (playlist));
3498 foo.insert (foo.end(), latest_regionviews.begin(), latest_regionviews.end());
3502 selection->set (foo);
3505 commit_reversible_command ();
3509 Editor::set_region_sync_position ()
3511 set_sync_point (get_preferred_edit_position (), get_regions_from_selection_and_edit_point ());
3515 Editor::set_sync_point (samplepos_t where, const RegionSelection& rs)
3517 bool in_command = false;
3519 for (RegionSelection::const_iterator r = rs.begin(); r != rs.end(); ++r) {
3521 if (!(*r)->region()->covers (where)) {
3525 boost::shared_ptr<Region> region ((*r)->region());
3528 begin_reversible_command (_("set sync point"));
3532 region->clear_changes ();
3533 region->set_sync_position (where);
3534 _session->add_command(new StatefulDiffCommand (region));
3538 commit_reversible_command ();
3542 /** Remove the sync positions of the selection */
3544 Editor::remove_region_sync ()
3546 RegionSelection rs = get_regions_from_selection_and_entered ();
3552 begin_reversible_command (_("remove region sync"));
3554 for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
3556 (*i)->region()->clear_changes ();
3557 (*i)->region()->clear_sync_position ();
3558 _session->add_command(new StatefulDiffCommand ((*i)->region()));
3561 commit_reversible_command ();
3565 Editor::naturalize_region ()
3567 RegionSelection rs = get_regions_from_selection_and_entered ();
3573 if (rs.size() > 1) {
3574 begin_reversible_command (_("move regions to original position"));
3576 begin_reversible_command (_("move region to original position"));
3579 for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
3580 (*i)->region()->clear_changes ();
3581 (*i)->region()->move_to_natural_position ();
3582 _session->add_command (new StatefulDiffCommand ((*i)->region()));
3585 commit_reversible_command ();
3589 Editor::align_regions (RegionPoint what)
3591 RegionSelection const rs = get_regions_from_selection_and_edit_point ();
3597 begin_reversible_command (_("align selection"));
3599 samplepos_t const position = get_preferred_edit_position ();
3601 for (RegionSelection::const_iterator i = rs.begin(); i != rs.end(); ++i) {
3602 align_region_internal ((*i)->region(), what, position);
3605 commit_reversible_command ();
3608 struct RegionSortByTime {
3609 bool operator() (const RegionView* a, const RegionView* b) {
3610 return a->region()->position() < b->region()->position();
3615 Editor::align_regions_relative (RegionPoint point)
3617 RegionSelection const rs = get_regions_from_selection_and_edit_point ();
3623 samplepos_t const position = get_preferred_edit_position ();
3625 samplepos_t distance = 0;
3626 samplepos_t pos = 0;
3629 list<RegionView*> sorted;
3630 rs.by_position (sorted);
3632 boost::shared_ptr<Region> r ((*sorted.begin())->region());
3637 if (position > r->position()) {
3638 distance = position - r->position();
3640 distance = r->position() - position;
3646 if (position > r->last_sample()) {
3647 distance = position - r->last_sample();
3648 pos = r->position() + distance;
3650 distance = r->last_sample() - position;
3651 pos = r->position() - distance;
3657 pos = r->adjust_to_sync (position);
3658 if (pos > r->position()) {
3659 distance = pos - r->position();
3661 distance = r->position() - pos;
3667 if (pos == r->position()) {
3671 begin_reversible_command (_("align selection (relative)"));
3673 /* move first one specially */
3675 r->clear_changes ();
3676 r->set_position (pos);
3677 _session->add_command(new StatefulDiffCommand (r));
3679 /* move rest by the same amount */
3683 for (list<RegionView*>::iterator i = sorted.begin(); i != sorted.end(); ++i) {
3685 boost::shared_ptr<Region> region ((*i)->region());
3687 region->clear_changes ();
3690 region->set_position (region->position() + distance);
3692 region->set_position (region->position() - distance);
3695 _session->add_command(new StatefulDiffCommand (region));
3699 commit_reversible_command ();
3703 Editor::align_region (boost::shared_ptr<Region> region, RegionPoint point, samplepos_t position)
3705 begin_reversible_command (_("align region"));
3706 align_region_internal (region, point, position);
3707 commit_reversible_command ();
3711 Editor::align_region_internal (boost::shared_ptr<Region> region, RegionPoint point, samplepos_t position)
3713 region->clear_changes ();
3717 region->set_position (region->adjust_to_sync (position));
3721 if (position > region->length()) {
3722 region->set_position (position - region->length());
3727 region->set_position (position);
3731 _session->add_command(new StatefulDiffCommand (region));
3735 Editor::trim_region_front ()
3741 Editor::trim_region_back ()
3743 trim_region (false);
3747 Editor::trim_region (bool front)
3749 samplepos_t where = get_preferred_edit_position();
3750 RegionSelection rs = get_regions_from_selection_and_edit_point ();
3756 begin_reversible_command (front ? _("trim front") : _("trim back"));
3758 for (list<RegionView*>::const_iterator i = rs.by_layer().begin(); i != rs.by_layer().end(); ++i) {
3759 if (!(*i)->region()->locked()) {
3761 (*i)->region()->clear_changes ();
3764 (*i)->region()->trim_front (where);
3766 (*i)->region()->trim_end (where);
3769 _session->add_command (new StatefulDiffCommand ((*i)->region()));
3773 commit_reversible_command ();
3776 /** Trim the end of the selected regions to the position of the edit cursor */
3778 Editor::trim_region_to_loop ()
3780 Location* loc = _session->locations()->auto_loop_location();
3784 trim_region_to_location (*loc, _("trim to loop"));
3788 Editor::trim_region_to_punch ()
3790 Location* loc = _session->locations()->auto_punch_location();
3794 trim_region_to_location (*loc, _("trim to punch"));
3798 Editor::trim_region_to_location (const Location& loc, const char* str)
3800 RegionSelection rs = get_regions_from_selection_and_entered ();
3801 bool in_command = false;
3803 for (RegionSelection::iterator x = rs.begin(); x != rs.end(); ++x) {
3804 RegionView* rv = (*x);
3806 /* require region to span proposed trim */
3807 switch (rv->region()->coverage (loc.start(), loc.end())) {
3808 case Evoral::OverlapInternal:
3814 RouteTimeAxisView* tav = dynamic_cast<RouteTimeAxisView*> (&rv->get_time_axis_view());
3822 start = loc.start();
3825 rv->region()->clear_changes ();
3826 rv->region()->trim_to (start, (end - start));
3829 begin_reversible_command (str);
3832 _session->add_command(new StatefulDiffCommand (rv->region()));
3836 commit_reversible_command ();
3841 Editor::trim_region_to_previous_region_end ()
3843 return trim_to_region(false);
3847 Editor::trim_region_to_next_region_start ()
3849 return trim_to_region(true);
3853 Editor::trim_to_region(bool forward)
3855 RegionSelection rs = get_regions_from_selection_and_entered ();
3856 bool in_command = false;
3858 boost::shared_ptr<Region> next_region;
3860 for (RegionSelection::iterator x = rs.begin(); x != rs.end(); ++x) {
3862 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (*x);
3868 AudioTimeAxisView* atav = dynamic_cast<AudioTimeAxisView*> (&arv->get_time_axis_view());
3874 boost::shared_ptr<Region> region = arv->region();
3875 boost::shared_ptr<Playlist> playlist (region->playlist());
3877 region->clear_changes ();
3881 next_region = playlist->find_next_region (region->first_sample(), Start, 1);
3887 region->trim_end (next_region->first_sample() - 1);
3888 arv->region_changed (PropertyChange (ARDOUR::Properties::length));
3892 next_region = playlist->find_next_region (region->first_sample(), Start, 0);
3898 region->trim_front (next_region->last_sample() + 1);
3899 arv->region_changed (ARDOUR::bounds_change);
3903 begin_reversible_command (_("trim to region"));
3906 _session->add_command(new StatefulDiffCommand (region));
3910 commit_reversible_command ();
3915 Editor::unfreeze_route ()
3917 if (clicked_routeview == 0 || !clicked_routeview->is_track()) {
3921 clicked_routeview->track()->unfreeze ();
3925 Editor::_freeze_thread (void* arg)
3927 return static_cast<Editor*>(arg)->freeze_thread ();
3931 Editor::freeze_thread ()
3933 /* create event pool because we may need to talk to the session */
3934 SessionEvent::create_per_thread_pool ("freeze events", 64);
3935 /* create per-thread buffers for process() tree to use */
3936 clicked_routeview->audio_track()->freeze_me (*current_interthread_info);
3937 current_interthread_info->done = true;
3942 Editor::freeze_route ()
3948 /* stop transport before we start. this is important */
3950 _session->request_transport_speed (0.0);
3952 /* wait for just a little while, because the above call is asynchronous */
3954 Glib::usleep (250000);
3956 if (clicked_routeview == 0 || !clicked_routeview->is_audio_track()) {
3960 if (!clicked_routeview->track()->bounceable (clicked_routeview->track()->main_outs(), true)) {
3962 _("This track/bus cannot be frozen because the signal adds or loses channels before reaching the outputs.\n"
3963 "This is typically caused by plugins that generate stereo output from mono input or vice versa.")
3965 d.set_title (_("Cannot freeze"));
3970 if (clicked_routeview->track()->has_external_redirects()) {
3971 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"
3972 "Freezing will only process the signal as far as the first send/insert/return."),
3973 clicked_routeview->track()->name()), true, MESSAGE_INFO, BUTTONS_NONE, true);
3975 d.add_button (_("Freeze anyway"), Gtk::RESPONSE_OK);
3976 d.add_button (_("Don't freeze"), Gtk::RESPONSE_CANCEL);
3977 d.set_title (_("Freeze Limits"));
3979 int response = d.run ();
3982 case Gtk::RESPONSE_CANCEL:
3989 InterThreadInfo itt;
3990 current_interthread_info = &itt;
3992 InterthreadProgressWindow ipw (current_interthread_info, _("Freeze"), _("Cancel Freeze"));
3994 pthread_create_and_store (X_("freezer"), &itt.thread, _freeze_thread, this);
3996 CursorContext::Handle cursor_ctx = CursorContext::create(*this, _cursors->wait);
3998 while (!itt.done && !itt.cancel) {
3999 gtk_main_iteration ();
4002 pthread_join (itt.thread, 0);
4003 current_interthread_info = 0;
4007 Editor::bounce_range_selection (bool replace, bool enable_processing)
4009 if (selection->time.empty()) {
4013 TrackSelection views = selection->tracks;
4015 for (TrackViewList::iterator i = views.begin(); i != views.end(); ++i) {
4017 if (enable_processing) {
4019 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (*i);
4021 if (rtv && rtv->track() && replace && enable_processing && !rtv->track()->bounceable (rtv->track()->main_outs(), false)) {
4023 _("You can't perform this operation because the processing of the signal "
4024 "will cause one or more of the tracks to end up with a region with more channels than this track has inputs.\n\n"
4025 "You can do this without processing, which is a different operation.")
4027 d.set_title (_("Cannot bounce"));
4034 samplepos_t start = selection->time[clicked_selection].start;
4035 samplepos_t end = selection->time[clicked_selection].end;
4036 samplepos_t cnt = end - start + 1;
4037 bool in_command = false;
4039 for (TrackViewList::iterator i = views.begin(); i != views.end(); ++i) {
4041 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (*i);
4047 boost::shared_ptr<Playlist> playlist;
4049 if ((playlist = rtv->playlist()) == 0) {
4053 InterThreadInfo itt;
4055 playlist->clear_changes ();
4056 playlist->clear_owned_changes ();
4058 boost::shared_ptr<Region> r;
4060 if (enable_processing) {
4061 r = rtv->track()->bounce_range (start, start+cnt, itt, rtv->track()->main_outs(), false);
4063 r = rtv->track()->bounce_range (start, start+cnt, itt, boost::shared_ptr<Processor>(), false);
4071 list<AudioRange> ranges;
4072 ranges.push_back (AudioRange (start, start+cnt, 0));
4073 playlist->cut (ranges); // discard result
4074 playlist->add_region (r, start);
4078 begin_reversible_command (_("bounce range"));
4081 vector<Command*> cmds;
4082 playlist->rdiff (cmds);
4083 _session->add_commands (cmds);
4085 _session->add_command (new StatefulDiffCommand (playlist));
4089 commit_reversible_command ();
4093 /** Delete selected regions, automation points or a time range */
4097 //special case: if the user is pointing in the editor/mixer strip, they may be trying to delete a plugin.
4098 //we need this because the editor-mixer strip is in the editor window, so it doesn't get the bindings from the mix window
4099 bool deleted = false;
4100 if (current_mixer_strip && current_mixer_strip == MixerStrip::entered_mixer_strip())
4101 deleted = current_mixer_strip->delete_processors ();
4107 /** Cut selected regions, automation points or a time range */
4114 /** Copy selected regions, automation points or a time range */
4122 /** @return true if a Cut, Copy or Clear is possible */
4124 Editor::can_cut_copy () const
4126 if (!selection->time.empty() || !selection->regions.empty() || !selection->points.empty())
4133 /** Cut, copy or clear selected regions, automation points or a time range.
4134 * @param op Operation (Delete, Cut, Copy or Clear)
4137 Editor::cut_copy (CutCopyOp op)
4139 /* only cancel selection if cut/copy is successful.*/
4145 opname = _("delete");
4154 opname = _("clear");
4158 /* if we're deleting something, and the mouse is still pressed,
4159 the thing we started a drag for will be gone when we release
4160 the mouse button(s). avoid this. see part 2 at the end of
4164 if (op == Delete || op == Cut || op == Clear) {
4165 if (_drags->active ()) {
4170 if (op != Delete) { //"Delete" doesn't change copy/paste buf
4171 cut_buffer->clear ();
4174 if (entered_marker) {
4176 /* cut/delete op while pointing at a marker */
4179 Location* loc = find_location_from_marker (entered_marker, ignored);
4181 if (_session && loc) {
4182 entered_marker = NULL;
4183 Glib::signal_idle().connect (sigc::bind (sigc::mem_fun(*this, &Editor::really_remove_marker), loc));
4190 switch (mouse_mode) {
4193 begin_reversible_command (opname + ' ' + X_("MIDI"));
4195 commit_reversible_command ();
4201 bool did_edit = false;
4203 if (!selection->regions.empty() || !selection->points.empty()) {
4204 begin_reversible_command (opname + ' ' + _("objects"));
4207 if (!selection->regions.empty()) {
4208 cut_copy_regions (op, selection->regions);
4210 if (op == Cut || op == Delete) {
4211 selection->clear_regions ();
4215 if (!selection->points.empty()) {
4216 cut_copy_points (op);
4218 if (op == Cut || op == Delete) {
4219 selection->clear_points ();
4222 } else if (selection->time.empty()) {
4223 samplepos_t start, end;
4224 /* no time selection, see if we can get an edit range
4227 if (get_edit_op_range (start, end)) {
4228 selection->set (start, end);
4230 } else if (!selection->time.empty()) {
4231 begin_reversible_command (opname + ' ' + _("range"));
4234 cut_copy_ranges (op);
4236 if (op == Cut || op == Delete) {
4237 selection->clear_time ();
4242 /* reset repeated paste state */
4244 last_paste_pos = -1;
4245 commit_reversible_command ();
4248 if (op == Delete || op == Cut || op == Clear) {
4254 struct AutomationRecord {
4255 AutomationRecord () : state (0) , line(NULL) {}
4256 AutomationRecord (XMLNode* s, const AutomationLine* l) : state (s) , line (l) {}
4258 XMLNode* state; ///< state before any operation
4259 const AutomationLine* line; ///< line this came from
4260 boost::shared_ptr<Evoral::ControlList> copy; ///< copied events for the cut buffer
4263 struct PointsSelectionPositionSorter {
4264 bool operator() (ControlPoint* a, ControlPoint* b) {
4265 return (*(a->model()))->when < (*(b->model()))->when;
4269 /** Cut, copy or clear selected automation points.
4270 * @param op Operation (Cut, Copy or Clear)
4273 Editor::cut_copy_points (Editing::CutCopyOp op, Temporal::Beats earliest, bool midi)
4275 if (selection->points.empty ()) {
4279 /* XXX: not ideal, as there may be more than one track involved in the point selection */
4280 _last_cut_copy_source_track = &selection->points.front()->line().trackview;
4282 /* Keep a record of the AutomationLists that we end up using in this operation */
4283 typedef std::map<boost::shared_ptr<AutomationList>, AutomationRecord> Lists;
4286 /* user could select points in any order */
4287 selection->points.sort(PointsSelectionPositionSorter ());
4289 /* Go through all selected points, making an AutomationRecord for each distinct AutomationList */
4290 for (PointSelection::iterator sel_point = selection->points.begin(); sel_point != selection->points.end(); ++sel_point) {
4291 const AutomationLine& line = (*sel_point)->line();
4292 const boost::shared_ptr<AutomationList> al = line.the_list();
4293 if (lists.find (al) == lists.end ()) {
4294 /* We haven't seen this list yet, so make a record for it. This includes
4295 taking a copy of its current state, in case this is needed for undo later.
4297 lists[al] = AutomationRecord (&al->get_state (), &line);
4301 if (op == Cut || op == Copy) {
4302 /* This operation will involve putting things in the cut buffer, so create an empty
4303 ControlList for each of our source lists to put the cut buffer data in.
4305 for (Lists::iterator i = lists.begin(); i != lists.end(); ++i) {
4306 i->second.copy = i->first->create (i->first->parameter (), i->first->descriptor());
4309 /* Add all selected points to the relevant copy ControlLists */
4310 MusicSample start (std::numeric_limits<samplepos_t>::max(), 0);
4311 for (PointSelection::iterator sel_point = selection->points.begin(); sel_point != selection->points.end(); ++sel_point) {
4312 boost::shared_ptr<AutomationList> al = (*sel_point)->line().the_list();
4313 AutomationList::const_iterator ctrl_evt = (*sel_point)->model ();
4315 lists[al].copy->fast_simple_add ((*ctrl_evt)->when, (*ctrl_evt)->value);
4317 /* Update earliest MIDI start time in beats */
4318 earliest = std::min(earliest, Temporal::Beats((*ctrl_evt)->when));
4320 /* Update earliest session start time in samples */
4321 start.sample = std::min(start.sample, (*sel_point)->line().session_position(ctrl_evt));
4325 /* Snap start time backwards, so copy/paste is snap aligned. */
4327 if (earliest == std::numeric_limits<Temporal::Beats>::max()) {
4328 earliest = Temporal::Beats(); // Weird... don't offset
4330 earliest.round_down_to_beat();
4332 if (start.sample == std::numeric_limits<double>::max()) {
4333 start.sample = 0; // Weird... don't offset
4335 snap_to(start, RoundDownMaybe);
4338 const double line_offset = midi ? earliest.to_double() : start.sample;
4339 for (Lists::iterator i = lists.begin(); i != lists.end(); ++i) {
4340 /* Correct this copy list so that it is relative to the earliest
4341 start time, so relative ordering between points is preserved
4342 when copying from several lists and the paste starts at the
4343 earliest copied piece of data. */
4344 boost::shared_ptr<Evoral::ControlList> &al_cpy = i->second.copy;
4345 for (AutomationList::iterator ctrl_evt = al_cpy->begin(); ctrl_evt != al_cpy->end(); ++ctrl_evt) {
4346 (*ctrl_evt)->when -= line_offset;
4349 /* And add it to the cut buffer */
4350 cut_buffer->add (al_cpy);
4354 if (op == Delete || op == Cut) {
4355 /* This operation needs to remove things from the main AutomationList, so do that now */
4357 for (Lists::iterator i = lists.begin(); i != lists.end(); ++i) {
4358 i->first->freeze ();
4361 /* Remove each selected point from its AutomationList */
4362 for (PointSelection::iterator sel_point = selection->points.begin(); sel_point != selection->points.end(); ++sel_point) {
4363 AutomationLine& line = (*sel_point)->line ();
4364 boost::shared_ptr<AutomationList> al = line.the_list();
4368 if (dynamic_cast<AudioRegionGainLine*> (&line)) {
4369 /* removing of first and last gain point in region gain lines is prohibited*/
4370 if (line.is_last_point (*(*sel_point)) || line.is_first_point (*(*sel_point))) {
4376 al->erase ((*sel_point)->model ());
4380 /* Thaw the lists and add undo records for them */
4381 for (Lists::iterator i = lists.begin(); i != lists.end(); ++i) {
4382 boost::shared_ptr<AutomationList> al = i->first;
4384 _session->add_command (new MementoCommand<AutomationList> (*al.get(), i->second.state, &(al->get_state ())));
4389 /** Cut, copy or clear selected automation points.
4390 * @param op Operation (Cut, Copy or Clear)
4393 Editor::cut_copy_midi (CutCopyOp op)
4395 Temporal::Beats earliest = std::numeric_limits<Temporal::Beats>::max();
4396 for (MidiRegionSelection::iterator i = selection->midi_regions.begin(); i != selection->midi_regions.end(); ++i) {
4397 MidiRegionView* mrv = dynamic_cast<MidiRegionView*>(*i);
4399 if (!mrv->selection().empty()) {
4400 earliest = std::min(earliest, (*mrv->selection().begin())->note()->time());
4402 mrv->cut_copy_clear (op);
4404 /* XXX: not ideal, as there may be more than one track involved in the selection */
4405 _last_cut_copy_source_track = &mrv->get_time_axis_view();
4409 if (!selection->points.empty()) {
4410 cut_copy_points (op, earliest, true);
4411 if (op == Cut || op == Delete) {
4412 selection->clear_points ();
4417 struct lt_playlist {
4418 bool operator () (const PlaylistState& a, const PlaylistState& b) {
4419 return a.playlist < b.playlist;
4423 struct PlaylistMapping {
4425 boost::shared_ptr<Playlist> pl;
4427 PlaylistMapping (TimeAxisView* tvp) : tv (tvp) {}
4430 /** Remove `clicked_regionview' */
4432 Editor::remove_clicked_region ()
4434 if (clicked_routeview == 0 || clicked_regionview == 0) {
4438 begin_reversible_command (_("remove region"));
4440 boost::shared_ptr<Playlist> playlist = clicked_routeview->playlist();
4442 playlist->clear_changes ();
4443 playlist->clear_owned_changes ();
4444 playlist->remove_region (clicked_regionview->region());
4445 if (Config->get_edit_mode() == Ripple)
4446 playlist->ripple (clicked_regionview->region()->position(), -clicked_regionview->region()->length(), boost::shared_ptr<Region>());
4448 /* We might have removed regions, which alters other regions' layering_index,
4449 so we need to do a recursive diff here.
4451 vector<Command*> cmds;
4452 playlist->rdiff (cmds);
4453 _session->add_commands (cmds);
4455 _session->add_command(new StatefulDiffCommand (playlist));
4456 commit_reversible_command ();
4460 /** Remove the selected regions */
4462 Editor::remove_selected_regions ()
4464 RegionSelection rs = get_regions_from_selection_and_entered ();
4466 if (!_session || rs.empty()) {
4470 list<boost::shared_ptr<Region> > regions_to_remove;
4472 for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
4473 // we can't just remove the region(s) in this loop because
4474 // this removes them from the RegionSelection, and they thus
4475 // disappear from underneath the iterator, and the ++i above
4476 // SEGVs in a puzzling fashion.
4478 // so, first iterate over the regions to be removed from rs and
4479 // add them to the regions_to_remove list, and then
4480 // iterate over the list to actually remove them.
4482 regions_to_remove.push_back ((*i)->region());
4485 vector<boost::shared_ptr<Playlist> > playlists;
4487 for (list<boost::shared_ptr<Region> >::iterator rl = regions_to_remove.begin(); rl != regions_to_remove.end(); ++rl) {
4489 boost::shared_ptr<Playlist> playlist = (*rl)->playlist();
4492 // is this check necessary?
4496 /* get_regions_from_selection_and_entered() guarantees that
4497 the playlists involved are unique, so there is no need
4501 playlists.push_back (playlist);
4503 playlist->clear_changes ();
4504 playlist->clear_owned_changes ();
4505 playlist->freeze ();
4506 playlist->remove_region (*rl);
4507 if (Config->get_edit_mode() == Ripple)
4508 playlist->ripple ((*rl)->position(), -(*rl)->length(), boost::shared_ptr<Region>());
4512 vector<boost::shared_ptr<Playlist> >::iterator pl;
4513 bool in_command = false;
4515 for (pl = playlists.begin(); pl != playlists.end(); ++pl) {
4518 /* We might have removed regions, which alters other regions' layering_index,
4519 so we need to do a recursive diff here.
4523 begin_reversible_command (_("remove region"));
4526 vector<Command*> cmds;
4527 (*pl)->rdiff (cmds);
4528 _session->add_commands (cmds);
4530 _session->add_command(new StatefulDiffCommand (*pl));
4534 commit_reversible_command ();
4538 /** Cut, copy or clear selected regions.
4539 * @param op Operation (Cut, Copy or Clear)
4542 Editor::cut_copy_regions (CutCopyOp op, RegionSelection& rs)
4544 /* we can't use a std::map here because the ordering is important, and we can't trivially sort
4545 a map when we want ordered access to both elements. i think.
4548 vector<PlaylistMapping> pmap;
4550 samplepos_t first_position = max_samplepos;
4552 typedef set<boost::shared_ptr<Playlist> > FreezeList;
4553 FreezeList freezelist;
4555 /* get ordering correct before we cut/copy */
4557 rs.sort_by_position_and_track ();
4559 for (RegionSelection::iterator x = rs.begin(); x != rs.end(); ++x) {
4561 first_position = min ((samplepos_t) (*x)->region()->position(), first_position);
4563 if (op == Cut || op == Clear || op == Delete) {
4564 boost::shared_ptr<Playlist> pl = (*x)->region()->playlist();
4567 FreezeList::iterator fl;
4569 // only take state if this is a new playlist.
4570 for (fl = freezelist.begin(); fl != freezelist.end(); ++fl) {
4576 if (fl == freezelist.end()) {
4577 pl->clear_changes();
4578 pl->clear_owned_changes ();
4580 freezelist.insert (pl);
4585 TimeAxisView* tv = &(*x)->get_time_axis_view();
4586 vector<PlaylistMapping>::iterator z;
4588 for (z = pmap.begin(); z != pmap.end(); ++z) {
4589 if ((*z).tv == tv) {
4594 if (z == pmap.end()) {
4595 pmap.push_back (PlaylistMapping (tv));
4599 for (RegionSelection::iterator x = rs.begin(); x != rs.end(); ) {
4601 boost::shared_ptr<Playlist> pl = (*x)->region()->playlist();
4604 /* region not yet associated with a playlist (e.g. unfinished
4611 TimeAxisView& tv = (*x)->get_time_axis_view();
4612 boost::shared_ptr<Playlist> npl;
4613 RegionSelection::iterator tmp;
4620 vector<PlaylistMapping>::iterator z;
4622 for (z = pmap.begin(); z != pmap.end(); ++z) {
4623 if ((*z).tv == &tv) {
4628 assert (z != pmap.end());
4631 npl = PlaylistFactory::create (pl->data_type(), *_session, "cutlist", true);
4639 boost::shared_ptr<Region> r = (*x)->region();
4640 boost::shared_ptr<Region> _xx;
4646 pl->remove_region (r);
4647 if (Config->get_edit_mode() == Ripple)
4648 pl->ripple (r->position(), -r->length(), boost::shared_ptr<Region>());
4652 _xx = RegionFactory::create (r);
4653 npl->add_region (_xx, r->position() - first_position);
4654 pl->remove_region (r);
4655 if (Config->get_edit_mode() == Ripple)
4656 pl->ripple (r->position(), -r->length(), boost::shared_ptr<Region>());
4660 /* copy region before adding, so we're not putting same object into two different playlists */
4661 npl->add_region (RegionFactory::create (r), r->position() - first_position);
4665 pl->remove_region (r);
4666 if (Config->get_edit_mode() == Ripple)
4667 pl->ripple (r->position(), -r->length(), boost::shared_ptr<Region>());
4676 list<boost::shared_ptr<Playlist> > foo;
4678 /* the pmap is in the same order as the tracks in which selected regions occurred */
4680 for (vector<PlaylistMapping>::iterator i = pmap.begin(); i != pmap.end(); ++i) {
4683 foo.push_back ((*i).pl);
4688 cut_buffer->set (foo);
4692 _last_cut_copy_source_track = 0;
4694 _last_cut_copy_source_track = pmap.front().tv;
4698 for (FreezeList::iterator pl = freezelist.begin(); pl != freezelist.end(); ++pl) {
4701 /* We might have removed regions, which alters other regions' layering_index,
4702 so we need to do a recursive diff here.
4704 vector<Command*> cmds;
4705 (*pl)->rdiff (cmds);
4706 _session->add_commands (cmds);
4708 _session->add_command (new StatefulDiffCommand (*pl));
4713 Editor::cut_copy_ranges (CutCopyOp op)
4715 TrackViewList ts = selection->tracks.filter_to_unique_playlists ();
4717 /* Sort the track selection now, so that it if is used, the playlists
4718 selected by the calls below to cut_copy_clear are in the order that
4719 their tracks appear in the editor. This makes things like paste
4720 of ranges work properly.
4723 sort_track_selection (ts);
4726 if (!entered_track) {
4729 ts.push_back (entered_track);
4732 for (TrackViewList::iterator i = ts.begin(); i != ts.end(); ++i) {
4733 (*i)->cut_copy_clear (*selection, op);
4738 Editor::paste (float times, bool from_context)
4740 DEBUG_TRACE (DEBUG::CutNPaste, "paste to preferred edit pos\n");
4741 MusicSample where (get_preferred_edit_position (EDIT_IGNORE_NONE, from_context), 0);
4742 paste_internal (where.sample, times, 0);
4746 Editor::mouse_paste ()
4748 MusicSample where (0, 0);
4750 if (!mouse_sample (where.sample, ignored)) {
4755 paste_internal (where.sample, 1, where.division);
4759 Editor::paste_internal (samplepos_t position, float times, const int32_t sub_num)
4761 DEBUG_TRACE (DEBUG::CutNPaste, string_compose ("apparent paste position is %1\n", position));
4763 if (cut_buffer->empty(internal_editing())) {
4767 if (position == max_samplepos) {
4768 position = get_preferred_edit_position();
4769 DEBUG_TRACE (DEBUG::CutNPaste, string_compose ("preferred edit position is %1\n", position));
4772 if (position != last_paste_pos) {
4773 /* paste in new location, reset repeated paste state */
4775 last_paste_pos = position;
4778 /* get everything in the correct order */
4781 if (!selection->tracks.empty()) {
4782 /* If there is a track selection, paste into exactly those tracks and
4783 * only those tracks. This allows the user to be explicit and override
4784 * the below "do the reasonable thing" logic. */
4785 ts = selection->tracks.filter_to_unique_playlists ();
4786 sort_track_selection (ts);
4788 /* Figure out which track to base the paste at. */
4789 TimeAxisView* base_track = NULL;
4790 if (_edit_point == Editing::EditAtMouse && entered_track) {
4791 /* With the mouse edit point, paste onto the track under the mouse. */
4792 base_track = entered_track;
4793 } else if (_edit_point == Editing::EditAtMouse && entered_regionview) {
4794 /* With the mouse edit point, paste onto the track of the region under the mouse. */
4795 base_track = &entered_regionview->get_time_axis_view();
4796 } else if (_last_cut_copy_source_track) {
4797 /* Paste to the track that the cut/copy came from (see mantis #333). */
4798 base_track = _last_cut_copy_source_track;
4800 /* This is "impossible" since we've copied... well, do nothing. */
4804 /* Walk up to parent if necessary, so base track is a route. */
4805 while (base_track->get_parent()) {
4806 base_track = base_track->get_parent();
4809 /* Add base track and all tracks below it. The paste logic will select
4810 the appropriate object types from the cut buffer in relative order. */
4811 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
4812 if ((*i)->order() >= base_track->order()) {
4817 /* Sort tracks so the nth track of type T will pick the nth object of type T. */
4818 sort_track_selection (ts);
4820 /* Add automation children of each track in order, for pasting several lines. */
4821 for (TrackViewList::iterator i = ts.begin(); i != ts.end();) {
4822 /* Add any automation children for pasting several lines */
4823 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*>(*i++);
4828 typedef RouteTimeAxisView::AutomationTracks ATracks;
4829 const ATracks& atracks = rtv->automation_tracks();
4830 for (ATracks::const_iterator a = atracks.begin(); a != atracks.end(); ++a) {
4831 i = ts.insert(i, a->second.get());
4836 /* We now have a list of trackviews starting at base_track, including
4837 automation children, in the order shown in the editor, e.g. R1,
4838 R1.A1, R1.A2, R2, R2.A1, ... */
4841 begin_reversible_command (Operations::paste);
4843 if (ts.size() == 1 && cut_buffer->lines.size() == 1 &&
4844 dynamic_cast<AutomationTimeAxisView*>(ts.front())) {
4845 /* Only one line copied, and one automation track selected. Do a
4846 "greedy" paste from one automation type to another. */
4848 PasteContext ctx(paste_count, times, ItemCounts(), true);
4849 ts.front()->paste (position, *cut_buffer, ctx, sub_num);
4853 /* Paste into tracks */
4855 PasteContext ctx(paste_count, times, ItemCounts(), false);
4856 for (TrackViewList::iterator i = ts.begin(); i != ts.end(); ++i) {
4857 (*i)->paste (position, *cut_buffer, ctx, sub_num);
4863 commit_reversible_command ();
4867 Editor::duplicate_regions (float times)
4869 RegionSelection rs (get_regions_from_selection_and_entered());
4870 duplicate_some_regions (rs, times);
4874 Editor::duplicate_some_regions (RegionSelection& regions, float times)
4876 if (regions.empty ()) {
4880 boost::shared_ptr<Playlist> playlist;
4881 std::set<boost::shared_ptr<Playlist> > playlists; // list of unique playlists affected by duplication
4882 RegionSelection sel = regions; // clear (below) may clear the argument list if its the current region selection
4883 RegionSelection foo;
4885 samplepos_t const start_sample = regions.start ();
4886 samplepos_t const end_sample = regions.end_sample ();
4887 samplecnt_t const span = end_sample - start_sample + 1;
4889 begin_reversible_command (Operations::duplicate_region);
4891 selection->clear_regions ();
4893 /* ripple first so that we don't move the duplicates that will be added */
4895 if (Config->get_edit_mode() == Ripple) {
4897 /* convert RegionSelection into RegionList so that we can pass it to ripple and exclude the regions we will duplicate */
4901 for (RegionSelection::iterator i = sel.begin(); i != sel.end(); ++i) {
4902 exclude.push_back ((*i)->region());
4903 playlist = (*i)->region()->playlist();
4904 if (playlists.insert (playlist).second) {
4905 /* successfully inserted into set, so it's the first time we've seen this playlist */
4906 playlist->clear_changes ();
4910 for (set<boost::shared_ptr<Playlist> >::iterator p = playlists.begin(); p != playlists.end(); ++p) {
4911 (*p)->ripple (start_sample, span * times, &exclude);
4915 for (RegionSelection::iterator i = sel.begin(); i != sel.end(); ++i) {
4917 boost::shared_ptr<Region> r ((*i)->region());
4919 TimeAxisView& tv = (*i)->get_time_axis_view();
4920 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (&tv);
4921 latest_regionviews.clear ();
4922 sigc::connection c = rtv->view()->RegionViewAdded.connect (sigc::mem_fun(*this, &Editor::collect_new_region_view));
4924 samplepos_t const position = end_sample + (r->first_sample() - start_sample + 1);
4925 playlist = (*i)->region()->playlist();
4927 if (Config->get_edit_mode() != Ripple) {
4928 if (playlists.insert (playlist).second) {
4929 playlist->clear_changes ();
4933 playlist->duplicate (r, position, span, times);
4937 foo.insert (foo.end(), latest_regionviews.begin(), latest_regionviews.end());
4940 for (set<boost::shared_ptr<Playlist> >::iterator p = playlists.begin(); p != playlists.end(); ++p) {
4941 _session->add_command (new StatefulDiffCommand (*p));
4942 vector<Command*> cmds;
4944 _session->add_commands (cmds);
4948 selection->set (foo);
4951 commit_reversible_command ();
4955 Editor::duplicate_selection (float times)
4957 if (selection->time.empty() || selection->tracks.empty()) {
4961 boost::shared_ptr<Playlist> playlist;
4963 TrackViewList ts = selection->tracks.filter_to_unique_playlists ();
4965 bool in_command = false;
4967 for (TrackViewList::iterator i = ts.begin(); i != ts.end(); ++i) {
4968 if ((playlist = (*i)->playlist()) == 0) {
4971 playlist->clear_changes ();
4973 if (clicked_selection) {
4974 playlist->duplicate_range (selection->time[clicked_selection], times);
4976 playlist->duplicate_ranges (selection->time, times);
4980 begin_reversible_command (_("duplicate range selection"));
4983 _session->add_command (new StatefulDiffCommand (playlist));
4988 if (times == 1.0f) {
4989 // now "move" range selection to after the current range selection
4990 samplecnt_t distance = 0;
4992 if (clicked_selection) {
4994 selection->time[clicked_selection].end - selection->time[clicked_selection].start;
4996 distance = selection->time.end_sample () - selection->time.start ();
4999 selection->move_time (distance);
5001 commit_reversible_command ();
5005 /** Reset all selected points to the relevant default value */
5007 Editor::reset_point_selection ()
5009 for (PointSelection::iterator i = selection->points.begin(); i != selection->points.end(); ++i) {
5010 ARDOUR::AutomationList::iterator j = (*i)->model ();
5011 (*j)->value = (*i)->line().the_list()->descriptor ().normal;
5016 Editor::center_playhead ()
5018 float const page = _visible_canvas_width * samples_per_pixel;
5019 center_screen_internal (playhead_cursor->current_sample (), page);
5023 Editor::center_edit_point ()
5025 float const page = _visible_canvas_width * samples_per_pixel;
5026 center_screen_internal (get_preferred_edit_position(), page);
5029 /** Caller must begin and commit a reversible command */
5031 Editor::clear_playlist (boost::shared_ptr<Playlist> playlist)
5033 playlist->clear_changes ();
5035 _session->add_command (new StatefulDiffCommand (playlist));
5039 Editor::nudge_track (bool use_edit, bool forwards)
5041 boost::shared_ptr<Playlist> playlist;
5042 samplepos_t distance;
5043 samplepos_t next_distance;
5047 start = get_preferred_edit_position();
5052 if ((distance = get_nudge_distance (start, next_distance)) == 0) {
5056 if (selection->tracks.empty()) {
5060 TrackViewList ts = selection->tracks.filter_to_unique_playlists ();
5061 bool in_command = false;
5063 for (TrackViewList::iterator i = ts.begin(); i != ts.end(); ++i) {
5065 if ((playlist = (*i)->playlist()) == 0) {
5069 playlist->clear_changes ();
5070 playlist->clear_owned_changes ();
5072 playlist->nudge_after (start, distance, forwards);
5075 begin_reversible_command (_("nudge track"));
5078 vector<Command*> cmds;
5080 playlist->rdiff (cmds);
5081 _session->add_commands (cmds);
5083 _session->add_command (new StatefulDiffCommand (playlist));
5087 commit_reversible_command ();
5092 Editor::remove_last_capture ()
5094 vector<string> choices;
5101 if (Config->get_verify_remove_last_capture()) {
5102 prompt = _("Do you really want to destroy the last capture?"
5103 "\n(This is destructive and cannot be undone)");
5105 choices.push_back (_("No, do nothing."));
5106 choices.push_back (_("Yes, destroy it."));
5108 Choice prompter (_("Destroy last capture"), prompt, choices);
5110 if (prompter.run () == 1) {
5111 _session->remove_last_capture ();
5112 _regions->redisplay ();
5116 _session->remove_last_capture();
5117 _regions->redisplay ();
5122 Editor::normalize_region ()
5128 RegionSelection rs = get_regions_from_selection_and_entered ();
5134 NormalizeDialog dialog (rs.size() > 1);
5136 if (dialog.run () != RESPONSE_ACCEPT) {
5140 CursorContext::Handle cursor_ctx = CursorContext::create(*this, _cursors->wait);
5143 /* XXX: should really only count audio regions here */
5144 int const regions = rs.size ();
5146 /* Make a list of the selected audio regions' maximum amplitudes, and also
5147 obtain the maximum amplitude of them all.
5149 list<double> max_amps;
5150 list<double> rms_vals;
5153 bool use_rms = dialog.constrain_rms ();
5155 for (RegionSelection::const_iterator i = rs.begin(); i != rs.end(); ++i) {
5156 AudioRegionView const * arv = dynamic_cast<AudioRegionView const *> (*i);
5160 dialog.descend (1.0 / regions);
5161 double const a = arv->audio_region()->maximum_amplitude (&dialog);
5163 double r = arv->audio_region()->rms (&dialog);
5164 max_rms = max (max_rms, r);
5165 rms_vals.push_back (r);
5169 /* the user cancelled the operation */
5173 max_amps.push_back (a);
5174 max_amp = max (max_amp, a);
5178 list<double>::const_iterator a = max_amps.begin ();
5179 list<double>::const_iterator l = rms_vals.begin ();
5180 bool in_command = false;
5182 for (RegionSelection::iterator r = rs.begin(); r != rs.end(); ++r) {
5183 AudioRegionView* const arv = dynamic_cast<AudioRegionView*> (*r);
5188 arv->region()->clear_changes ();
5190 double amp = dialog.normalize_individually() ? *a : max_amp;
5191 double target = dialog.target_peak (); // dB
5194 double const amp_rms = dialog.normalize_individually() ? *l : max_rms;
5195 const double t_rms = dialog.target_rms ();
5196 const gain_t c_peak = dB_to_coefficient (target);
5197 const gain_t c_rms = dB_to_coefficient (t_rms);
5198 if ((amp_rms / c_rms) > (amp / c_peak)) {
5204 arv->audio_region()->normalize (amp, target);
5207 begin_reversible_command (_("normalize"));
5210 _session->add_command (new StatefulDiffCommand (arv->region()));
5217 commit_reversible_command ();
5223 Editor::reset_region_scale_amplitude ()
5229 RegionSelection rs = get_regions_from_selection_and_entered ();
5235 bool in_command = false;
5237 for (RegionSelection::iterator r = rs.begin(); r != rs.end(); ++r) {
5238 AudioRegionView* const arv = dynamic_cast<AudioRegionView*>(*r);
5241 arv->region()->clear_changes ();
5242 arv->audio_region()->set_scale_amplitude (1.0f);
5245 begin_reversible_command ("reset gain");
5248 _session->add_command (new StatefulDiffCommand (arv->region()));
5252 commit_reversible_command ();
5257 Editor::adjust_region_gain (bool up)
5259 RegionSelection rs = get_regions_from_selection_and_entered ();
5261 if (!_session || rs.empty()) {
5265 bool in_command = false;
5267 for (RegionSelection::iterator r = rs.begin(); r != rs.end(); ++r) {
5268 AudioRegionView* const arv = dynamic_cast<AudioRegionView*>(*r);
5273 arv->region()->clear_changes ();
5275 double dB = accurate_coefficient_to_dB (arv->audio_region()->scale_amplitude ());
5283 arv->audio_region()->set_scale_amplitude (dB_to_coefficient (dB));
5286 begin_reversible_command ("adjust region gain");
5289 _session->add_command (new StatefulDiffCommand (arv->region()));
5293 commit_reversible_command ();
5298 Editor::reset_region_gain ()
5300 RegionSelection rs = get_regions_from_selection_and_entered ();
5302 if (!_session || rs.empty()) {
5306 bool in_command = false;
5308 for (RegionSelection::iterator r = rs.begin(); r != rs.end(); ++r) {
5309 AudioRegionView* const arv = dynamic_cast<AudioRegionView*>(*r);
5314 arv->region()->clear_changes ();
5316 arv->audio_region()->set_scale_amplitude (1.0f);
5319 begin_reversible_command ("reset region gain");
5322 _session->add_command (new StatefulDiffCommand (arv->region()));
5326 commit_reversible_command ();
5331 Editor::reverse_region ()
5337 Reverse rev (*_session);
5338 apply_filter (rev, _("reverse regions"));
5342 Editor::strip_region_silence ()
5348 RegionSelection rs = get_regions_from_selection_and_entered ();
5354 std::list<RegionView*> audio_only;
5356 for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
5357 AudioRegionView* const arv = dynamic_cast<AudioRegionView*> (*i);
5359 audio_only.push_back (arv);
5363 assert (!audio_only.empty());
5365 StripSilenceDialog d (_session, audio_only);
5366 int const r = d.run ();
5370 if (r == Gtk::RESPONSE_OK) {
5371 ARDOUR::AudioIntervalMap silences;
5372 d.silences (silences);
5373 StripSilence s (*_session, silences, d.fade_length());
5375 apply_filter (s, _("strip silence"), &d);
5380 Editor::apply_midi_note_edit_op_to_region (MidiOperator& op, MidiRegionView& mrv)
5382 Evoral::Sequence<Temporal::Beats>::Notes selected;
5383 mrv.selection_as_notelist (selected, true);
5385 vector<Evoral::Sequence<Temporal::Beats>::Notes> v;
5386 v.push_back (selected);
5388 Temporal::Beats pos_beats = Temporal::Beats (mrv.midi_region()->beat()) - mrv.midi_region()->start_beats();
5390 return op (mrv.midi_region()->model(), pos_beats, v);
5394 Editor::apply_midi_note_edit_op (MidiOperator& op, const RegionSelection& rs)
5400 bool in_command = false;
5402 for (RegionSelection::const_iterator r = rs.begin(); r != rs.end(); ) {
5403 RegionSelection::const_iterator tmp = r;
5406 MidiRegionView* const mrv = dynamic_cast<MidiRegionView*> (*r);
5409 Command* cmd = apply_midi_note_edit_op_to_region (op, *mrv);
5412 begin_reversible_command (op.name ());
5416 _session->add_command (cmd);
5424 commit_reversible_command ();
5425 _session->set_dirty ();
5430 Editor::fork_region ()
5432 RegionSelection rs = get_regions_from_selection_and_entered ();
5438 CursorContext::Handle cursor_ctx = CursorContext::create(*this, _cursors->wait);
5439 bool in_command = false;
5443 for (RegionSelection::iterator r = rs.begin(); r != rs.end(); ) {
5444 RegionSelection::iterator tmp = r;
5447 MidiRegionView* const mrv = dynamic_cast<MidiRegionView*>(*r);
5451 boost::shared_ptr<Playlist> playlist = mrv->region()->playlist();
5452 boost::shared_ptr<MidiSource> new_source = _session->create_midi_source_by_stealing_name (mrv->midi_view()->track());
5453 boost::shared_ptr<MidiRegion> newregion = mrv->midi_region()->clone (new_source);
5456 begin_reversible_command (_("Fork Region(s)"));
5459 playlist->clear_changes ();
5460 playlist->replace_region (mrv->region(), newregion, mrv->region()->position());
5461 _session->add_command(new StatefulDiffCommand (playlist));
5463 error << string_compose (_("Could not unlink %1"), mrv->region()->name()) << endmsg;
5471 commit_reversible_command ();
5476 Editor::quantize_region ()
5479 quantize_regions(get_regions_from_selection_and_entered ());
5484 Editor::quantize_regions (const RegionSelection& rs)
5486 if (rs.n_midi_regions() == 0) {
5490 if (!quantize_dialog) {
5491 quantize_dialog = new QuantizeDialog (*this);
5494 if (quantize_dialog->is_mapped()) {
5495 /* in progress already */
5499 quantize_dialog->present ();
5500 const int r = quantize_dialog->run ();
5501 quantize_dialog->hide ();
5503 if (r == Gtk::RESPONSE_OK) {
5504 Quantize quant (quantize_dialog->snap_start(),
5505 quantize_dialog->snap_end(),
5506 quantize_dialog->start_grid_size(),
5507 quantize_dialog->end_grid_size(),
5508 quantize_dialog->strength(),
5509 quantize_dialog->swing(),
5510 quantize_dialog->threshold());
5512 apply_midi_note_edit_op (quant, rs);
5517 Editor::legatize_region (bool shrink_only)
5520 legatize_regions(get_regions_from_selection_and_entered (), shrink_only);
5525 Editor::legatize_regions (const RegionSelection& rs, bool shrink_only)
5527 if (rs.n_midi_regions() == 0) {
5531 Legatize legatize(shrink_only);
5532 apply_midi_note_edit_op (legatize, rs);
5536 Editor::transform_region ()
5539 transform_regions(get_regions_from_selection_and_entered ());
5544 Editor::transform_regions (const RegionSelection& rs)
5546 if (rs.n_midi_regions() == 0) {
5553 const int r = td.run();
5556 if (r == Gtk::RESPONSE_OK) {
5557 Transform transform(td.get());
5558 apply_midi_note_edit_op(transform, rs);
5563 Editor::transpose_region ()
5566 transpose_regions(get_regions_from_selection_and_entered ());
5571 Editor::transpose_regions (const RegionSelection& rs)
5573 if (rs.n_midi_regions() == 0) {
5578 int const r = d.run ();
5580 if (r == RESPONSE_ACCEPT) {
5581 Transpose transpose(d.semitones ());
5582 apply_midi_note_edit_op (transpose, rs);
5587 Editor::insert_patch_change (bool from_context)
5589 RegionSelection rs = get_regions_from_selection_and_entered ();
5595 const samplepos_t p = get_preferred_edit_position (EDIT_IGNORE_NONE, from_context);
5597 /* XXX: bit of a hack; use the MIDNAM from the first selected region;
5598 there may be more than one, but the PatchChangeDialog can only offer
5599 one set of patch menus.
5601 MidiRegionView* first = dynamic_cast<MidiRegionView*> (rs.front ());
5603 Evoral::PatchChange<Temporal::Beats> empty (Temporal::Beats(), 0, 0, 0);
5604 PatchChangeDialog d (0, _session, empty, first->instrument_info(), Gtk::Stock::ADD);
5606 if (d.run() == RESPONSE_CANCEL) {
5610 for (RegionSelection::iterator i = rs.begin (); i != rs.end(); ++i) {
5611 MidiRegionView* const mrv = dynamic_cast<MidiRegionView*> (*i);
5613 if (p >= mrv->region()->first_sample() && p <= mrv->region()->last_sample()) {
5614 mrv->add_patch_change (p - mrv->region()->position(), d.patch ());
5621 Editor::apply_filter (Filter& filter, string command, ProgressReporter* progress)
5623 RegionSelection rs = get_regions_from_selection_and_entered ();
5629 CursorContext::Handle cursor_ctx = CursorContext::create(*this, _cursors->wait);
5630 bool in_command = false;
5635 int const N = rs.size ();
5637 for (RegionSelection::iterator r = rs.begin(); r != rs.end(); ) {
5638 RegionSelection::iterator tmp = r;
5641 AudioRegionView* const arv = dynamic_cast<AudioRegionView*>(*r);
5643 boost::shared_ptr<Playlist> playlist = arv->region()->playlist();
5646 progress->descend (1.0 / N);
5649 if (arv->audio_region()->apply (filter, progress) == 0) {
5651 playlist->clear_changes ();
5652 playlist->clear_owned_changes ();
5655 begin_reversible_command (command);
5659 if (filter.results.empty ()) {
5661 /* no regions returned; remove the old one */
5662 playlist->remove_region (arv->region ());
5666 std::vector<boost::shared_ptr<Region> >::iterator res = filter.results.begin ();
5668 /* first region replaces the old one */
5669 playlist->replace_region (arv->region(), *res, (*res)->position());
5673 while (res != filter.results.end()) {
5674 playlist->add_region (*res, (*res)->position());
5680 /* We might have removed regions, which alters other regions' layering_index,
5681 so we need to do a recursive diff here.
5683 vector<Command*> cmds;
5684 playlist->rdiff (cmds);
5685 _session->add_commands (cmds);
5687 _session->add_command(new StatefulDiffCommand (playlist));
5691 progress->ascend ();
5700 commit_reversible_command ();
5705 Editor::external_edit_region ()
5711 Editor::reset_region_gain_envelopes ()
5713 RegionSelection rs = get_regions_from_selection_and_entered ();
5715 if (!_session || rs.empty()) {
5719 bool in_command = false;
5721 for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
5722 AudioRegionView* const arv = dynamic_cast<AudioRegionView*>(*i);
5724 boost::shared_ptr<AutomationList> alist (arv->audio_region()->envelope());
5725 XMLNode& before (alist->get_state());
5727 arv->audio_region()->set_default_envelope ();
5730 begin_reversible_command (_("reset region gain"));
5733 _session->add_command (new MementoCommand<AutomationList>(*arv->audio_region()->envelope().get(), &before, &alist->get_state()));
5738 commit_reversible_command ();
5743 Editor::set_region_gain_visibility (RegionView* rv)
5745 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (rv);
5747 arv->update_envelope_visibility();
5752 Editor::set_gain_envelope_visibility ()
5758 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
5759 AudioTimeAxisView* v = dynamic_cast<AudioTimeAxisView*>(*i);
5761 v->audio_view()->foreach_regionview (sigc::mem_fun (this, &Editor::set_region_gain_visibility));
5767 Editor::toggle_gain_envelope_active ()
5769 if (_ignore_region_action) {
5773 RegionSelection rs = get_regions_from_selection_and_entered ();
5775 if (!_session || rs.empty()) {
5779 bool in_command = false;
5781 for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
5782 AudioRegionView* const arv = dynamic_cast<AudioRegionView*>(*i);
5784 arv->region()->clear_changes ();
5785 arv->audio_region()->set_envelope_active (!arv->audio_region()->envelope_active());
5788 begin_reversible_command (_("region gain envelope active"));
5791 _session->add_command (new StatefulDiffCommand (arv->region()));
5796 commit_reversible_command ();
5801 Editor::toggle_region_lock ()
5803 if (_ignore_region_action) {
5807 RegionSelection rs = get_regions_from_selection_and_entered ();
5809 if (!_session || rs.empty()) {
5813 begin_reversible_command (_("toggle region lock"));
5815 for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
5816 (*i)->region()->clear_changes ();
5817 (*i)->region()->set_locked (!(*i)->region()->locked());
5818 _session->add_command (new StatefulDiffCommand ((*i)->region()));
5821 commit_reversible_command ();
5825 Editor::toggle_region_video_lock ()
5827 if (_ignore_region_action) {
5831 RegionSelection rs = get_regions_from_selection_and_entered ();
5833 if (!_session || rs.empty()) {
5837 begin_reversible_command (_("Toggle Video Lock"));
5839 for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
5840 (*i)->region()->clear_changes ();
5841 (*i)->region()->set_video_locked (!(*i)->region()->video_locked());
5842 _session->add_command (new StatefulDiffCommand ((*i)->region()));
5845 commit_reversible_command ();
5849 Editor::toggle_region_lock_style ()
5851 if (_ignore_region_action) {
5855 RegionSelection rs = get_regions_from_selection_and_entered ();
5857 if (!_session || rs.empty()) {
5861 Glib::RefPtr<ToggleAction> a = Glib::RefPtr<ToggleAction>::cast_dynamic (_region_actions->get_action("toggle-region-lock-style"));
5862 vector<Widget*> proxies = a->get_proxies();
5863 Gtk::CheckMenuItem* cmi = dynamic_cast<Gtk::CheckMenuItem*> (proxies.front());
5867 begin_reversible_command (_("toggle region lock style"));
5869 for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
5870 (*i)->region()->clear_changes ();
5871 PositionLockStyle const ns = ((*i)->region()->position_lock_style() == AudioTime && !cmi->get_inconsistent()) ? MusicTime : AudioTime;
5872 (*i)->region()->set_position_lock_style (ns);
5873 _session->add_command (new StatefulDiffCommand ((*i)->region()));
5876 commit_reversible_command ();
5880 Editor::toggle_opaque_region ()
5882 if (_ignore_region_action) {
5886 RegionSelection rs = get_regions_from_selection_and_entered ();
5888 if (!_session || rs.empty()) {
5892 begin_reversible_command (_("change region opacity"));
5894 for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
5895 (*i)->region()->clear_changes ();
5896 (*i)->region()->set_opaque (!(*i)->region()->opaque());
5897 _session->add_command (new StatefulDiffCommand ((*i)->region()));
5900 commit_reversible_command ();
5904 Editor::toggle_record_enable ()
5906 bool new_state = false;
5908 for (TrackSelection::iterator i = selection->tracks.begin(); i != selection->tracks.end(); ++i) {
5909 RouteTimeAxisView *rtav = dynamic_cast<RouteTimeAxisView *>(*i);
5912 if (!rtav->is_track())
5916 new_state = !rtav->track()->rec_enable_control()->get_value();
5920 rtav->track()->rec_enable_control()->set_value (new_state, Controllable::UseGroup);
5925 tracklist_to_stripables (TrackViewList list)
5929 for (TrackSelection::iterator i = list.begin(); i != list.end(); ++i) {
5930 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> ((*i));
5932 if (rtv && rtv->is_track()) {
5933 ret.push_back (rtv->track());
5941 Editor::play_solo_selection (bool restart)
5943 //note: session::solo_selection takes care of invalidating the region playlist
5945 if ((!selection->tracks.empty()) && selection->time.length() > 0) { //a range is selected; solo the tracks and roll
5947 StripableList sl = tracklist_to_stripables (selection->tracks);
5948 _session->solo_selection (sl, true);
5951 samplepos_t start = selection->time.start();
5952 samplepos_t end = selection->time.end_sample();
5953 _session->request_bounded_roll (start, end);
5955 } else if (! selection->tracks.empty()) { //no range is selected, but tracks are selected; solo the tracks and roll
5956 StripableList sl = tracklist_to_stripables (selection->tracks);
5957 _session->solo_selection (sl, true);
5958 _session->request_cancel_play_range();
5959 transition_to_rolling (true);
5961 } else if (! selection->regions.empty()) { //solo any tracks with selected regions, and roll
5962 StripableList sl = tracklist_to_stripables (get_tracks_for_range_action());
5963 _session->solo_selection (sl, true);
5964 _session->request_cancel_play_range();
5965 transition_to_rolling (true);
5967 _session->request_cancel_play_range();
5968 transition_to_rolling (true); //no selection. just roll.
5973 Editor::toggle_solo ()
5975 bool new_state = false;
5977 boost::shared_ptr<ControlList> cl (new ControlList);
5979 for (TrackSelection::iterator i = selection->tracks.begin(); i != selection->tracks.end(); ++i) {
5980 StripableTimeAxisView *stav = dynamic_cast<StripableTimeAxisView *>(*i);
5982 if (!stav || !stav->stripable()->solo_control()) {
5987 new_state = !stav->stripable()->solo_control()->soloed ();
5991 cl->push_back (stav->stripable()->solo_control());
5994 _session->set_controls (cl, new_state ? 1.0 : 0.0, Controllable::UseGroup);
5998 Editor::toggle_mute ()
6000 bool new_state = false;
6002 boost::shared_ptr<ControlList> cl (new ControlList);
6004 for (TrackSelection::iterator i = selection->tracks.begin(); i != selection->tracks.end(); ++i) {
6005 StripableTimeAxisView *stav = dynamic_cast<StripableTimeAxisView *>(*i);
6007 if (!stav || !stav->stripable()->mute_control()) {
6012 new_state = !stav->stripable()->mute_control()->muted();
6016 cl->push_back (stav->stripable()->mute_control());
6019 _session->set_controls (cl, new_state, Controllable::UseGroup);
6023 Editor::toggle_solo_isolate ()
6029 Editor::fade_range ()
6031 TrackViewList ts = selection->tracks.filter_to_unique_playlists ();
6033 begin_reversible_command (_("fade range"));
6035 for (TrackViewList::iterator i = ts.begin(); i != ts.end(); ++i) {
6036 (*i)->fade_range (selection->time);
6039 commit_reversible_command ();
6044 Editor::set_fade_length (bool in)
6046 RegionSelection rs = get_regions_from_selection_and_entered ();
6052 /* we need a region to measure the offset from the start */
6054 RegionView* rv = rs.front ();
6056 samplepos_t pos = get_preferred_edit_position();
6060 if (pos > rv->region()->last_sample() || pos < rv->region()->first_sample()) {
6061 /* edit point is outside the relevant region */
6066 if (pos <= rv->region()->position()) {
6070 len = pos - rv->region()->position();
6071 cmd = _("set fade in length");
6073 if (pos >= rv->region()->last_sample()) {
6077 len = rv->region()->last_sample() - pos;
6078 cmd = _("set fade out length");
6081 bool in_command = false;
6083 for (RegionSelection::iterator x = rs.begin(); x != rs.end(); ++x) {
6084 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (*x);
6090 boost::shared_ptr<AutomationList> alist;
6092 alist = tmp->audio_region()->fade_in();
6094 alist = tmp->audio_region()->fade_out();
6097 XMLNode &before = alist->get_state();
6100 tmp->audio_region()->set_fade_in_length (len);
6101 tmp->audio_region()->set_fade_in_active (true);
6103 tmp->audio_region()->set_fade_out_length (len);
6104 tmp->audio_region()->set_fade_out_active (true);
6108 begin_reversible_command (cmd);
6111 XMLNode &after = alist->get_state();
6112 _session->add_command(new MementoCommand<AutomationList>(*alist, &before, &after));
6116 commit_reversible_command ();
6121 Editor::set_fade_in_shape (FadeShape shape)
6123 RegionSelection rs = get_regions_from_selection_and_entered ();
6128 bool in_command = false;
6130 for (RegionSelection::iterator x = rs.begin(); x != rs.end(); ++x) {
6131 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (*x);
6137 boost::shared_ptr<AutomationList> alist = tmp->audio_region()->fade_in();
6138 XMLNode &before = alist->get_state();
6140 tmp->audio_region()->set_fade_in_shape (shape);
6143 begin_reversible_command (_("set fade in shape"));
6146 XMLNode &after = alist->get_state();
6147 _session->add_command(new MementoCommand<AutomationList>(*alist.get(), &before, &after));
6151 commit_reversible_command ();
6156 Editor::set_fade_out_shape (FadeShape shape)
6158 RegionSelection rs = get_regions_from_selection_and_entered ();
6163 bool in_command = false;
6165 for (RegionSelection::iterator x = rs.begin(); x != rs.end(); ++x) {
6166 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (*x);
6172 boost::shared_ptr<AutomationList> alist = tmp->audio_region()->fade_out();
6173 XMLNode &before = alist->get_state();
6175 tmp->audio_region()->set_fade_out_shape (shape);
6178 begin_reversible_command (_("set fade out shape"));
6181 XMLNode &after = alist->get_state();
6182 _session->add_command(new MementoCommand<AutomationList>(*alist.get(), &before, &after));
6186 commit_reversible_command ();
6191 Editor::set_fade_in_active (bool yn)
6193 RegionSelection rs = get_regions_from_selection_and_entered ();
6198 bool in_command = false;
6200 for (RegionSelection::iterator x = rs.begin(); x != rs.end(); ++x) {
6201 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (*x);
6208 boost::shared_ptr<AudioRegion> ar (tmp->audio_region());
6210 ar->clear_changes ();
6211 ar->set_fade_in_active (yn);
6214 begin_reversible_command (_("set fade in active"));
6217 _session->add_command (new StatefulDiffCommand (ar));
6221 commit_reversible_command ();
6226 Editor::set_fade_out_active (bool yn)
6228 RegionSelection rs = get_regions_from_selection_and_entered ();
6233 bool in_command = false;
6235 for (RegionSelection::iterator x = rs.begin(); x != rs.end(); ++x) {
6236 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (*x);
6242 boost::shared_ptr<AudioRegion> ar (tmp->audio_region());
6244 ar->clear_changes ();
6245 ar->set_fade_out_active (yn);
6248 begin_reversible_command (_("set fade out active"));
6251 _session->add_command(new StatefulDiffCommand (ar));
6255 commit_reversible_command ();
6260 Editor::toggle_region_fades (int dir)
6262 if (_ignore_region_action) {
6266 boost::shared_ptr<AudioRegion> ar;
6269 RegionSelection rs = get_regions_from_selection_and_entered ();
6275 RegionSelection::iterator i;
6276 for (i = rs.begin(); i != rs.end(); ++i) {
6277 if ((ar = boost::dynamic_pointer_cast<AudioRegion>((*i)->region())) != 0) {
6279 yn = ar->fade_out_active ();
6281 yn = ar->fade_in_active ();
6287 if (i == rs.end()) {
6291 /* XXX should this undo-able? */
6292 bool in_command = false;
6294 for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
6295 if ((ar = boost::dynamic_pointer_cast<AudioRegion>((*i)->region())) == 0) {
6298 ar->clear_changes ();
6300 if (dir == 1 || dir == 0) {
6301 ar->set_fade_in_active (!yn);
6304 if (dir == -1 || dir == 0) {
6305 ar->set_fade_out_active (!yn);
6308 begin_reversible_command (_("toggle fade active"));
6311 _session->add_command(new StatefulDiffCommand (ar));
6315 commit_reversible_command ();
6320 /** Update region fade visibility after its configuration has been changed */
6322 Editor::update_region_fade_visibility ()
6324 bool _fade_visibility = _session->config.get_show_region_fades ();
6326 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
6327 AudioTimeAxisView* v = dynamic_cast<AudioTimeAxisView*>(*i);
6329 if (_fade_visibility) {
6330 v->audio_view()->show_all_fades ();
6332 v->audio_view()->hide_all_fades ();
6339 Editor::set_edit_point ()
6342 MusicSample where (0, 0);
6344 if (!mouse_sample (where.sample, ignored)) {
6350 if (selection->markers.empty()) {
6352 mouse_add_new_marker (where.sample);
6357 Location* loc = find_location_from_marker (selection->markers.front(), ignored);
6360 loc->move_to (where.sample, where.division);
6366 Editor::set_playhead_cursor ()
6368 if (entered_marker) {
6369 _session->request_locate (entered_marker->position(), _session->transport_rolling());
6371 MusicSample where (0, 0);
6374 if (!mouse_sample (where.sample, ignored)) {
6381 _session->request_locate (where.sample, _session->transport_rolling());
6385 //not sure what this was for; remove it for now.
6386 // if (UIConfiguration::instance().get_follow_edits() && (!_session || !_session->config.get_external_sync())) {
6387 // cancel_time_selection();
6393 Editor::split_region ()
6395 if (_dragging_playhead) {
6397 } else if (_drags->active ()) {
6398 /*any other kind of drag, bail out so we avoid Undo snafu*/
6402 //if a range is selected, separate it
6403 if (!selection->time.empty()) {
6404 separate_regions_between (selection->time);
6408 //if no range was selected, try to find some regions to split
6409 if (current_mouse_mode() == MouseObject || current_mouse_mode() == MouseRange ) { //don't try this for Internal Edit, Stretch, Draw, etc.
6411 RegionSelection rs = get_regions_from_selection_and_edit_point ();
6412 const samplepos_t pos = get_preferred_edit_position();
6413 const int32_t division = get_grid_music_divisions (0);
6414 MusicSample where (pos, division);
6420 split_regions_at (where, rs);
6426 Editor::select_next_stripable (bool routes_only)
6428 _session->selection().select_next_stripable (false, routes_only);
6432 Editor::select_prev_stripable (bool routes_only)
6434 _session->selection().select_prev_stripable (false, routes_only);
6438 Editor::set_loop_from_selection (bool play)
6440 if (_session == 0) {
6444 samplepos_t start, end;
6445 if (!get_selection_extents (start, end))
6448 set_loop_range (start, end, _("set loop range from selection"));
6451 _session->request_play_loop (true, true);
6456 Editor::set_loop_from_region (bool play)
6458 samplepos_t start, end;
6459 if (!get_selection_extents (start, end))
6462 set_loop_range (start, end, _("set loop range from region"));
6465 _session->request_locate (start, true);
6466 _session->request_play_loop (true);
6471 Editor::set_punch_from_selection ()
6473 if (_session == 0) {
6477 samplepos_t start, end;
6478 if (!get_selection_extents (start, end))
6481 set_punch_range (start, end, _("set punch range from selection"));
6485 Editor::set_auto_punch_range ()
6487 // auto punch in/out button from a single button
6488 // If Punch In is unset, set punch range from playhead to end, enable punch in
6489 // If Punch In is set, the next punch sets Punch Out, unless the playhead has been
6490 // rewound beyond the Punch In marker, in which case that marker will be moved back
6491 // to the current playhead position.
6492 // If punch out is set, it clears the punch range and Punch In/Out buttons
6494 if (_session == 0) {
6498 Location* tpl = transport_punch_location();
6499 samplepos_t now = playhead_cursor->current_sample();
6500 samplepos_t begin = now;
6501 samplepos_t end = _session->current_end_sample();
6503 if (!_session->config.get_punch_in()) {
6504 // First Press - set punch in and create range from here to eternity
6505 set_punch_range (begin, end, _("Auto Punch In"));
6506 _session->config.set_punch_in(true);
6507 } else if (tpl && !_session->config.get_punch_out()) {
6508 // Second press - update end range marker and set punch_out
6509 if (now < tpl->start()) {
6510 // playhead has been rewound - move start back and pretend nothing happened
6512 set_punch_range (begin, end, _("Auto Punch In/Out"));
6514 // normal case for 2nd press - set the punch out
6515 end = playhead_cursor->current_sample ();
6516 set_punch_range (tpl->start(), now, _("Auto Punch In/Out"));
6517 _session->config.set_punch_out(true);
6520 if (_session->config.get_punch_out()) {
6521 _session->config.set_punch_out(false);
6524 if (_session->config.get_punch_in()) {
6525 _session->config.set_punch_in(false);
6530 // third press - unset punch in/out and remove range
6531 _session->locations()->remove(tpl);
6538 Editor::set_session_extents_from_selection ()
6540 if (_session == 0) {
6544 samplepos_t start, end;
6545 if (!get_selection_extents (start, end))
6549 if ((loc = _session->locations()->session_range_location()) == 0) {
6550 _session->set_session_extents (start, end); // this will create a new session range; no need for UNDO
6552 XMLNode &before = loc->get_state();
6554 _session->set_session_extents (start, end);
6556 XMLNode &after = loc->get_state();
6558 begin_reversible_command (_("set session start/end from selection"));
6560 _session->add_command (new MementoCommand<Location>(*loc, &before, &after));
6562 commit_reversible_command ();
6565 _session->set_end_is_free (false);
6569 Editor::set_punch_start_from_edit_point ()
6573 MusicSample start (0, 0);
6574 samplepos_t end = max_samplepos;
6576 //use the existing punch end, if any
6577 Location* tpl = transport_punch_location();
6582 if ((_edit_point == EditAtPlayhead) && _session->transport_rolling()) {
6583 start.sample = _session->audible_sample();
6585 start.sample = get_preferred_edit_position();
6588 //if there's not already a sensible selection endpoint, go "forever"
6589 if (start.sample > end) {
6590 end = max_samplepos;
6593 set_punch_range (start.sample, end, _("set punch start from EP"));
6599 Editor::set_punch_end_from_edit_point ()
6603 samplepos_t start = 0;
6604 MusicSample end (max_samplepos, 0);
6606 //use the existing punch start, if any
6607 Location* tpl = transport_punch_location();
6609 start = tpl->start();
6612 if ((_edit_point == EditAtPlayhead) && _session->transport_rolling()) {
6613 end.sample = _session->audible_sample();
6615 end.sample = get_preferred_edit_position();
6618 set_punch_range (start, end.sample, _("set punch end from EP"));
6624 Editor::set_loop_start_from_edit_point ()
6628 MusicSample start (0, 0);
6629 samplepos_t end = max_samplepos;
6631 //use the existing loop end, if any
6632 Location* tpl = transport_loop_location();
6637 if ((_edit_point == EditAtPlayhead) && _session->transport_rolling()) {
6638 start.sample = _session->audible_sample();
6640 start.sample = get_preferred_edit_position();
6643 //if there's not already a sensible selection endpoint, go "forever"
6644 if (start.sample > end) {
6645 end = max_samplepos;
6648 set_loop_range (start.sample, end, _("set loop start from EP"));
6654 Editor::set_loop_end_from_edit_point ()
6658 samplepos_t start = 0;
6659 MusicSample end (max_samplepos, 0);
6661 //use the existing loop start, if any
6662 Location* tpl = transport_loop_location();
6664 start = tpl->start();
6667 if ((_edit_point == EditAtPlayhead) && _session->transport_rolling()) {
6668 end.sample = _session->audible_sample();
6670 end.sample = get_preferred_edit_position();
6673 set_loop_range (start, end.sample, _("set loop end from EP"));
6678 Editor::set_punch_from_region ()
6680 samplepos_t start, end;
6681 if (!get_selection_extents (start, end))
6684 set_punch_range (start, end, _("set punch range from region"));
6688 Editor::pitch_shift_region ()
6690 RegionSelection rs = get_regions_from_selection_and_entered ();
6692 RegionSelection audio_rs;
6693 for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
6694 if (dynamic_cast<AudioRegionView*> (*i)) {
6695 audio_rs.push_back (*i);
6699 if (audio_rs.empty()) {
6703 pitch_shift (audio_rs, 1.2);
6707 Editor::set_tempo_from_region ()
6709 RegionSelection rs = get_regions_from_selection_and_entered ();
6711 if (!_session || rs.empty()) {
6715 RegionView* rv = rs.front();
6717 define_one_bar (rv->region()->position(), rv->region()->last_sample() + 1);
6721 Editor::use_range_as_bar ()
6723 samplepos_t start, end;
6724 if (get_edit_op_range (start, end)) {
6725 define_one_bar (start, end);
6730 Editor::define_one_bar (samplepos_t start, samplepos_t end)
6732 samplepos_t length = end - start;
6734 const Meter& m (_session->tempo_map().meter_at_sample (start));
6736 /* length = 1 bar */
6738 /* We're going to deliver a constant tempo here,
6739 so we can use samples per beat to determine length.
6740 now we want samples per beat.
6741 we have samples per bar, and beats per bar, so ...
6744 /* XXXX METER MATH */
6746 double samples_per_beat = length / m.divisions_per_bar();
6748 /* beats per minute = */
6750 double beats_per_minute = (_session->sample_rate() * 60.0) / samples_per_beat;
6752 /* now decide whether to:
6754 (a) set global tempo
6755 (b) add a new tempo marker
6759 const TempoSection& t (_session->tempo_map().tempo_section_at_sample (start));
6761 bool do_global = false;
6763 if ((_session->tempo_map().n_tempos() == 1) && (_session->tempo_map().n_meters() == 1)) {
6765 /* only 1 tempo & 1 meter: ask if the user wants to set the tempo
6766 at the start, or create a new marker
6769 vector<string> options;
6770 options.push_back (_("Cancel"));
6771 options.push_back (_("Add new marker"));
6772 options.push_back (_("Set global tempo"));
6775 _("Define one bar"),
6776 _("Do you want to set the global tempo or add a new tempo marker?"),
6780 c.set_default_response (2);
6796 /* more than 1 tempo and/or meter section already, go ahead do the "usual":
6797 if the marker is at the region starter, change it, otherwise add
6802 begin_reversible_command (_("set tempo from region"));
6803 XMLNode& before (_session->tempo_map().get_state());
6806 _session->tempo_map().change_initial_tempo (beats_per_minute, t.note_type(), t.end_note_types_per_minute());
6807 } else if (t.sample() == start) {
6808 _session->tempo_map().change_existing_tempo_at (start, beats_per_minute, t.note_type(), t.end_note_types_per_minute());
6810 /* constant tempo */
6811 const Tempo tempo (beats_per_minute, t.note_type());
6812 _session->tempo_map().add_tempo (tempo, 0.0, start, AudioTime);
6815 XMLNode& after (_session->tempo_map().get_state());
6817 _session->add_command (new MementoCommand<TempoMap>(_session->tempo_map(), &before, &after));
6818 commit_reversible_command ();
6822 Editor::split_region_at_transients ()
6824 AnalysisFeatureList positions;
6826 RegionSelection rs = get_regions_from_selection_and_entered ();
6828 if (!_session || rs.empty()) {
6832 begin_reversible_command (_("split regions"));
6834 for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ) {
6836 RegionSelection::iterator tmp;
6841 boost::shared_ptr<AudioRegion> ar = boost::dynamic_pointer_cast<AudioRegion> ((*i)->region());
6844 ar->transients (positions);
6845 split_region_at_points ((*i)->region(), positions, true);
6852 commit_reversible_command ();
6857 Editor::split_region_at_points (boost::shared_ptr<Region> r, AnalysisFeatureList& positions, bool can_ferret, bool select_new)
6859 bool use_rhythmic_rodent = false;
6861 boost::shared_ptr<Playlist> pl = r->playlist();
6863 list<boost::shared_ptr<Region> > new_regions;
6869 if (positions.empty()) {
6873 if (positions.size() > 20 && can_ferret) {
6874 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);
6875 MessageDialog msg (msgstr,
6878 Gtk::BUTTONS_OK_CANCEL);
6881 msg.add_button (_("Call for the Ferret!"), RESPONSE_APPLY);
6882 msg.set_secondary_text (_("Press OK to continue with this split operation\nor ask the Ferret dialog to tune the analysis"));
6884 msg.set_secondary_text (_("Press OK to continue with this split operation"));
6887 msg.set_title (_("Excessive split?"));
6890 int response = msg.run();
6896 case RESPONSE_APPLY:
6897 use_rhythmic_rodent = true;
6904 if (use_rhythmic_rodent) {
6905 show_rhythm_ferret ();
6909 AnalysisFeatureList::const_iterator x;
6911 pl->clear_changes ();
6912 pl->clear_owned_changes ();
6914 x = positions.begin();
6916 if (x == positions.end()) {
6921 pl->remove_region (r);
6923 samplepos_t pos = 0;
6925 samplepos_t rstart = r->first_sample ();
6926 samplepos_t rend = r->last_sample ();
6928 while (x != positions.end()) {
6930 /* deal with positons that are out of scope of present region bounds */
6931 if (*x <= rstart || *x > rend) {
6936 /* file start = original start + how far we from the initial position ? */
6938 samplepos_t file_start = r->start() + pos;
6940 /* length = next position - current position */
6942 samplepos_t len = (*x) - pos - rstart;
6944 /* XXX we do we really want to allow even single-sample regions?
6945 * shouldn't we have some kind of lower limit on region size?
6954 if (RegionFactory::region_name (new_name, r->name())) {
6958 /* do NOT announce new regions 1 by one, just wait till they are all done */
6962 plist.add (ARDOUR::Properties::start, file_start);
6963 plist.add (ARDOUR::Properties::length, len);
6964 plist.add (ARDOUR::Properties::name, new_name);
6965 plist.add (ARDOUR::Properties::layer, 0);
6966 // TODO set transients_offset
6968 boost::shared_ptr<Region> nr = RegionFactory::create (r->sources(), plist, false);
6969 /* because we set annouce to false, manually add the new region to the
6972 RegionFactory::map_add (nr);
6974 pl->add_region (nr, rstart + pos);
6977 new_regions.push_front(nr);
6986 RegionFactory::region_name (new_name, r->name());
6988 /* Add the final region */
6991 plist.add (ARDOUR::Properties::start, r->start() + pos);
6992 plist.add (ARDOUR::Properties::length, r->last_sample() - (r->position() + pos) + 1);
6993 plist.add (ARDOUR::Properties::name, new_name);
6994 plist.add (ARDOUR::Properties::layer, 0);
6996 boost::shared_ptr<Region> nr = RegionFactory::create (r->sources(), plist, false);
6997 /* because we set annouce to false, manually add the new region to the
7000 RegionFactory::map_add (nr);
7001 pl->add_region (nr, r->position() + pos);
7004 new_regions.push_front(nr);
7009 /* We might have removed regions, which alters other regions' layering_index,
7010 so we need to do a recursive diff here.
7012 vector<Command*> cmds;
7014 _session->add_commands (cmds);
7016 _session->add_command (new StatefulDiffCommand (pl));
7020 for (list<boost::shared_ptr<Region> >::iterator i = new_regions.begin(); i != new_regions.end(); ++i){
7021 set_selected_regionview_from_region_list ((*i), Selection::Add);
7027 Editor::place_transient()
7033 RegionSelection rs = get_regions_from_selection_and_edit_point ();
7039 samplepos_t where = get_preferred_edit_position();
7041 begin_reversible_command (_("place transient"));
7043 for (RegionSelection::iterator r = rs.begin(); r != rs.end(); ++r) {
7044 (*r)->region()->add_transient(where);
7047 commit_reversible_command ();
7051 Editor::remove_transient(ArdourCanvas::Item* item)
7057 ArdourCanvas::Line* _line = reinterpret_cast<ArdourCanvas::Line*> (item);
7060 AudioRegionView* _arv = reinterpret_cast<AudioRegionView*> (item->get_data ("regionview"));
7061 _arv->remove_transient (*(float*) _line->get_data ("position"));
7065 Editor::snap_regions_to_grid ()
7067 list <boost::shared_ptr<Playlist > > used_playlists;
7069 RegionSelection rs = get_regions_from_selection_and_entered ();
7071 if (!_session || rs.empty()) {
7075 begin_reversible_command (_("snap regions to grid"));
7077 for (RegionSelection::iterator r = rs.begin(); r != rs.end(); ++r) {
7079 boost::shared_ptr<Playlist> pl = (*r)->region()->playlist();
7081 if (!pl->frozen()) {
7082 /* we haven't seen this playlist before */
7084 /* remember used playlists so we can thaw them later */
7085 used_playlists.push_back(pl);
7088 (*r)->region()->clear_changes ();
7090 MusicSample start ((*r)->region()->first_sample (), 0);
7091 snap_to (start, RoundNearest, SnapToGrid_Unscaled, true);
7092 (*r)->region()->set_position (start.sample, start.division);
7093 _session->add_command(new StatefulDiffCommand ((*r)->region()));
7096 while (used_playlists.size() > 0) {
7097 list <boost::shared_ptr<Playlist > >::iterator i = used_playlists.begin();
7099 used_playlists.pop_front();
7102 commit_reversible_command ();
7106 Editor::close_region_gaps ()
7108 list <boost::shared_ptr<Playlist > > used_playlists;
7110 RegionSelection rs = get_regions_from_selection_and_entered ();
7112 if (!_session || rs.empty()) {
7116 Dialog dialog (_("Close Region Gaps"));
7119 table.set_spacings (12);
7120 table.set_border_width (12);
7121 Label* l = manage (left_aligned_label (_("Crossfade length")));
7122 table.attach (*l, 0, 1, 0, 1);
7124 SpinButton spin_crossfade (1, 0);
7125 spin_crossfade.set_range (0, 15);
7126 spin_crossfade.set_increments (1, 1);
7127 spin_crossfade.set_value (5);
7128 table.attach (spin_crossfade, 1, 2, 0, 1);
7130 table.attach (*manage (new Label (_("ms"))), 2, 3, 0, 1);
7132 l = manage (left_aligned_label (_("Pull-back length")));
7133 table.attach (*l, 0, 1, 1, 2);
7135 SpinButton spin_pullback (1, 0);
7136 spin_pullback.set_range (0, 100);
7137 spin_pullback.set_increments (1, 1);
7138 spin_pullback.set_value(30);
7139 table.attach (spin_pullback, 1, 2, 1, 2);
7141 table.attach (*manage (new Label (_("ms"))), 2, 3, 1, 2);
7143 dialog.get_vbox()->pack_start (table);
7144 dialog.add_button (Stock::CANCEL, RESPONSE_CANCEL);
7145 dialog.add_button (_("Ok"), RESPONSE_ACCEPT);
7148 if (dialog.run () == RESPONSE_CANCEL) {
7152 samplepos_t crossfade_len = spin_crossfade.get_value();
7153 samplepos_t pull_back_samples = spin_pullback.get_value();
7155 crossfade_len = lrintf (crossfade_len * _session->sample_rate()/1000);
7156 pull_back_samples = lrintf (pull_back_samples * _session->sample_rate()/1000);
7158 /* Iterate over the region list and make adjacent regions overlap by crossfade_len_ms */
7160 begin_reversible_command (_("close region gaps"));
7163 boost::shared_ptr<Region> last_region;
7165 rs.sort_by_position_and_track();
7167 for (RegionSelection::iterator r = rs.begin(); r != rs.end(); ++r) {
7169 boost::shared_ptr<Playlist> pl = (*r)->region()->playlist();
7171 if (!pl->frozen()) {
7172 /* we haven't seen this playlist before */
7174 /* remember used playlists so we can thaw them later */
7175 used_playlists.push_back(pl);
7179 samplepos_t position = (*r)->region()->position();
7181 if (idx == 0 || position < last_region->position()){
7182 last_region = (*r)->region();
7187 (*r)->region()->clear_changes ();
7188 (*r)->region()->trim_front((position - pull_back_samples));
7190 last_region->clear_changes ();
7191 last_region->trim_end ((position - pull_back_samples + crossfade_len));
7193 _session->add_command (new StatefulDiffCommand ((*r)->region()));
7194 _session->add_command (new StatefulDiffCommand (last_region));
7196 last_region = (*r)->region();
7200 while (used_playlists.size() > 0) {
7201 list <boost::shared_ptr<Playlist > >::iterator i = used_playlists.begin();
7203 used_playlists.pop_front();
7206 commit_reversible_command ();
7210 Editor::tab_to_transient (bool forward)
7212 AnalysisFeatureList positions;
7214 RegionSelection rs = get_regions_from_selection_and_entered ();
7220 samplepos_t pos = _session->audible_sample ();
7222 if (!selection->tracks.empty()) {
7224 /* don't waste time searching for transients in duplicate playlists.
7227 TrackViewList ts = selection->tracks.filter_to_unique_playlists ();
7229 for (TrackViewList::iterator t = ts.begin(); t != ts.end(); ++t) {
7231 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (*t);
7234 boost::shared_ptr<Track> tr = rtv->track();
7236 boost::shared_ptr<Playlist> pl = tr->playlist ();
7238 samplepos_t result = pl->find_next_transient (pos, forward ? 1 : -1);
7241 positions.push_back (result);
7254 for (RegionSelection::iterator r = rs.begin(); r != rs.end(); ++r) {
7255 (*r)->region()->get_transients (positions);
7259 TransientDetector::cleanup_transients (positions, _session->sample_rate(), 3.0);
7262 AnalysisFeatureList::iterator x;
7264 for (x = positions.begin(); x != positions.end(); ++x) {
7270 if (x != positions.end ()) {
7271 _session->request_locate (*x);
7275 AnalysisFeatureList::reverse_iterator x;
7277 for (x = positions.rbegin(); x != positions.rend(); ++x) {
7283 if (x != positions.rend ()) {
7284 _session->request_locate (*x);
7290 Editor::playhead_forward_to_grid ()
7296 MusicSample pos (playhead_cursor->current_sample (), 0);
7298 if ( _grid_type == GridTypeNone) {
7299 if (pos.sample < max_samplepos - current_page_samples()*0.1) {
7300 pos.sample += current_page_samples()*0.1;
7301 _session->request_locate (pos.sample);
7303 _session->request_locate (0);
7307 if (pos.sample < max_samplepos - 1) {
7309 pos = snap_to_grid (pos, RoundUpAlways, SnapToGrid_Scaled);
7310 _session->request_locate (pos.sample);
7315 /* keep PH visible in window */
7316 if (pos.sample > (_leftmost_sample + current_page_samples() *0.9)) {
7317 reset_x_origin (pos.sample - (current_page_samples()*0.9));
7323 Editor::playhead_backward_to_grid ()
7329 MusicSample pos (playhead_cursor->current_sample (), 0);
7331 if ( _grid_type == GridTypeNone) {
7332 if ( pos.sample > current_page_samples()*0.1 ) {
7333 pos.sample -= current_page_samples()*0.1;
7334 _session->request_locate (pos.sample);
7336 _session->request_locate (0);
7340 if (pos.sample > 2) {
7342 pos = snap_to_grid (pos, RoundDownAlways, SnapToGrid_Scaled);
7345 //handle the case where we are rolling, and we're less than one-half second past the mark, we want to go to the prior mark...
7346 //also see: jump_backward_to_mark
7347 if (_session->transport_rolling()) {
7348 if ((playhead_cursor->current_sample() - pos.sample) < _session->sample_rate()/2) {
7349 pos = snap_to_grid (pos, RoundDownAlways, SnapToGrid_Scaled);
7353 _session->request_locate (pos.sample, _session->transport_rolling());
7356 /* keep PH visible in window */
7357 if (pos.sample < (_leftmost_sample + current_page_samples() *0.1)) {
7358 reset_x_origin (pos.sample - (current_page_samples()*0.1));
7363 Editor::set_track_height (Height h)
7365 TrackSelection& ts (selection->tracks);
7367 for (TrackSelection::iterator x = ts.begin(); x != ts.end(); ++x) {
7368 (*x)->set_height_enum (h);
7373 Editor::toggle_tracks_active ()
7375 TrackSelection& ts (selection->tracks);
7377 bool target = false;
7383 for (TrackSelection::iterator x = ts.begin(); x != ts.end(); ++x) {
7384 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*>(*x);
7388 target = !rtv->_route->active();
7391 rtv->_route->set_active (target, this);
7397 Editor::remove_tracks ()
7399 /* this will delete GUI objects that may be the subject of an event
7400 handler in which this method is called. Defer actual deletion to the
7401 next idle callback, when all event handling is finished.
7403 Glib::signal_idle().connect (sigc::mem_fun (*this, &Editor::idle_remove_tracks));
7407 Editor::idle_remove_tracks ()
7409 Session::StateProtector sp (_session);
7411 return false; /* do not call again */
7415 Editor::_remove_tracks ()
7417 TrackSelection& ts (selection->tracks);
7423 vector<string> choices;
7428 const char* trackstr;
7431 vector<boost::shared_ptr<Route> > routes;
7432 vector<boost::shared_ptr<VCA> > vcas;
7433 bool special_bus = false;
7435 for (TrackSelection::iterator x = ts.begin(); x != ts.end(); ++x) {
7436 VCATimeAxisView* vtv = dynamic_cast<VCATimeAxisView*> (*x);
7438 vcas.push_back (vtv->vca());
7442 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (*x);
7446 if (rtv->is_track()) {
7451 routes.push_back (rtv->_route);
7453 if (rtv->route()->is_master() || rtv->route()->is_monitor()) {
7458 if (special_bus && !Config->get_allow_special_bus_removal()) {
7459 MessageDialog msg (_("That would be bad news ...."),
7463 msg.set_secondary_text (string_compose (_(
7464 "Removing the master or monitor bus is such a bad idea\n\
7465 that %1 is not going to allow it.\n\
7467 If you really want to do this sort of thing\n\
7468 edit your ardour.rc file to set the\n\
7469 \"allow-special-bus-removal\" option to be \"yes\""), PROGRAM_NAME));
7476 if (ntracks + nbusses + nvcas == 0) {
7482 trackstr = P_("track", "tracks", ntracks);
7483 busstr = P_("bus", "busses", nbusses);
7484 vcastr = P_("VCA", "VCAs", nvcas);
7486 if (ntracks > 0 && nbusses > 0 && nvcas > 0) {
7487 title = _("Remove various strips");
7488 prompt = string_compose (_("Do you really want to remove %1 %2, %3 %4 and %5 %6?"),
7489 ntracks, trackstr, nbusses, busstr, nvcas, vcastr);
7491 else if (ntracks > 0 && nbusses > 0) {
7492 title = string_compose (_("Remove %1 and %2"), trackstr, busstr);
7493 prompt = string_compose (_("Do you really want to remove %1 %2 and %3 %4?"),
7494 ntracks, trackstr, nbusses, busstr);
7496 else if (ntracks > 0 && nvcas > 0) {
7497 title = string_compose (_("Remove %1 and %2"), trackstr, vcastr);
7498 prompt = string_compose (_("Do you really want to remove %1 %2 and %3 %4?"),
7499 ntracks, trackstr, nvcas, vcastr);
7501 else if (nbusses > 0 && nvcas > 0) {
7502 title = string_compose (_("Remove %1 and %2"), busstr, vcastr);
7503 prompt = string_compose (_("Do you really want to remove %1 %2 and %3 %4?"),
7504 nbusses, busstr, nvcas, vcastr);
7506 else if (ntracks > 0) {
7507 title = string_compose (_("Remove %1"), trackstr);
7508 prompt = string_compose (_("Do you really want to remove %1 %2?"),
7511 else if (nbusses > 0) {
7512 title = string_compose (_("Remove %1"), busstr);
7513 prompt = string_compose (_("Do you really want to remove %1 %2?"),
7516 else if (nvcas > 0) {
7517 title = string_compose (_("Remove %1"), vcastr);
7518 prompt = string_compose (_("Do you really want to remove %1 %2?"),
7526 prompt += "\n" + string_compose ("(You may also lose the playlists associated with the %1)", trackstr) + "\n";
7529 prompt += "\n" + string(_("This action cannot be undone, and the session file will be overwritten!"));
7531 choices.push_back (_("No, do nothing."));
7532 if (ntracks + nbusses + nvcas > 1) {
7533 choices.push_back (_("Yes, remove them."));
7535 choices.push_back (_("Yes, remove it."));
7538 Choice prompter (title, prompt, choices);
7540 if (prompter.run () != 1) {
7544 if (current_mixer_strip && routes.size () > 1 && std::find (routes.begin(), routes.end(), current_mixer_strip->route()) != routes.end ()) {
7545 /* Route deletion calls Editor::timeaxisview_deleted() iteratively (for each deleted
7546 * route). If the deleted route is currently displayed in the Editor-Mixer (highly
7547 * likely because deletion requires selection) this will call
7548 * Editor::set_selected_mixer_strip () which is expensive (MixerStrip::set_route()).
7549 * It's likewise likely that the route that has just been displayed in the
7550 * Editor-Mixer will be next in line for deletion.
7552 * So simply switch to the master-bus (if present)
7554 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
7555 if ((*i)->stripable ()->is_master ()) {
7556 set_selected_mixer_strip (*(*i));
7563 PresentationInfo::ChangeSuspender cs;
7564 DisplaySuspender ds;
7566 boost::shared_ptr<RouteList> rl (new RouteList);
7567 for (vector<boost::shared_ptr<Route> >::iterator x = routes.begin(); x != routes.end(); ++x) {
7570 _session->remove_routes (rl);
7572 for (vector<boost::shared_ptr<VCA> >::iterator x = vcas.begin(); x != vcas.end(); ++x) {
7573 _session->vca_manager().remove_vca (*x);
7577 /* TrackSelection and RouteList leave scope,
7578 * destructors are called,
7579 * diskstream drops references, save_state is called (again for every track)
7584 Editor::do_insert_time ()
7586 if (selection->tracks.empty()) {
7587 MessageDialog msg (_("You must first select some tracks to Insert Time."),
7588 true, MESSAGE_INFO, BUTTONS_OK, true);
7589 msg.set_position (WIN_POS_MOUSE);
7594 if (Config->get_edit_mode() == Lock) {
7595 MessageDialog msg (_("You cannot insert time in Lock Edit mode."),
7596 true, MESSAGE_INFO, BUTTONS_OK, true);
7597 msg.set_position (WIN_POS_MOUSE);
7602 InsertRemoveTimeDialog d (*this);
7603 int response = d.run ();
7605 if (response != RESPONSE_OK) {
7609 if (d.distance() == 0) {
7616 d.intersected_region_action (),
7620 d.move_glued_markers(),
7621 d.move_locked_markers(),
7627 Editor::insert_time (
7628 samplepos_t pos, samplecnt_t samples, InsertTimeOption opt,
7629 bool all_playlists, bool ignore_music_glue, bool markers_too, bool glued_markers_too, bool locked_markers_too, bool tempo_too
7633 if (Config->get_edit_mode() == Lock) {
7636 bool in_command = false;
7638 TrackViewList ts = selection->tracks.filter_to_unique_playlists ();
7640 for (TrackViewList::iterator x = ts.begin(); x != ts.end(); ++x) {
7644 /* don't operate on any playlist more than once, which could
7645 * happen if "all playlists" is enabled, but there is more
7646 * than 1 track using playlists "from" a given track.
7649 set<boost::shared_ptr<Playlist> > pl;
7651 if (all_playlists) {
7652 RouteTimeAxisView* rtav = dynamic_cast<RouteTimeAxisView*> (*x);
7653 if (rtav && rtav->track ()) {
7654 vector<boost::shared_ptr<Playlist> > all = _session->playlists->playlists_for_track (rtav->track ());
7655 for (vector<boost::shared_ptr<Playlist> >::iterator p = all.begin(); p != all.end(); ++p) {
7660 if ((*x)->playlist ()) {
7661 pl.insert ((*x)->playlist ());
7665 for (set<boost::shared_ptr<Playlist> >::iterator i = pl.begin(); i != pl.end(); ++i) {
7667 (*i)->clear_changes ();
7668 (*i)->clear_owned_changes ();
7671 begin_reversible_command (_("insert time"));
7675 if (opt == SplitIntersected) {
7676 /* non musical split */
7677 (*i)->split (MusicSample (pos, 0));
7680 (*i)->shift (pos, samples, (opt == MoveIntersected), ignore_music_glue);
7682 vector<Command*> cmds;
7684 _session->add_commands (cmds);
7686 _session->add_command (new StatefulDiffCommand (*i));
7690 RouteTimeAxisView* rtav = dynamic_cast<RouteTimeAxisView*> (*x);
7693 begin_reversible_command (_("insert time"));
7696 rtav->route ()->shift (pos, samples);
7703 const int32_t divisions = get_grid_music_divisions (0);
7704 XMLNode& before (_session->locations()->get_state());
7705 Locations::LocationList copy (_session->locations()->list());
7707 for (Locations::LocationList::iterator i = copy.begin(); i != copy.end(); ++i) {
7709 Locations::LocationList::const_iterator tmp;
7711 if ((*i)->position_lock_style() == AudioTime || glued_markers_too) {
7712 bool const was_locked = (*i)->locked ();
7713 if (locked_markers_too) {
7717 if ((*i)->start() >= pos) {
7718 // move end first, in case we're moving by more than the length of the range
7719 if (!(*i)->is_mark()) {
7720 (*i)->set_end ((*i)->end() + samples, false, true, divisions);
7722 (*i)->set_start ((*i)->start() + samples, false, true, divisions);
7734 begin_reversible_command (_("insert time"));
7737 XMLNode& after (_session->locations()->get_state());
7738 _session->add_command (new MementoCommand<Locations>(*_session->locations(), &before, &after));
7744 begin_reversible_command (_("insert time"));
7747 XMLNode& before (_session->tempo_map().get_state());
7748 _session->tempo_map().insert_time (pos, samples);
7749 XMLNode& after (_session->tempo_map().get_state());
7750 _session->add_command (new MementoCommand<TempoMap>(_session->tempo_map(), &before, &after));
7754 commit_reversible_command ();
7759 Editor::do_remove_time ()
7761 if (selection->tracks.empty()) {
7762 MessageDialog msg (_("You must first select some tracks to Remove Time."),
7763 true, MESSAGE_INFO, BUTTONS_OK, true);
7764 msg.set_position (WIN_POS_MOUSE);
7769 if (Config->get_edit_mode() == Lock) {
7770 MessageDialog msg (_("You cannot remove time in Lock Edit mode."),
7771 true, MESSAGE_INFO, BUTTONS_OK, true);
7772 msg.set_position (WIN_POS_MOUSE);
7777 InsertRemoveTimeDialog d (*this, true);
7779 int response = d.run ();
7781 if (response != RESPONSE_OK) {
7785 samplecnt_t distance = d.distance();
7787 if (distance == 0) {
7797 d.move_glued_markers(),
7798 d.move_locked_markers(),
7804 Editor::remove_time (samplepos_t pos, samplecnt_t samples, InsertTimeOption opt,
7805 bool ignore_music_glue, bool markers_too, bool glued_markers_too, bool locked_markers_too, bool tempo_too)
7807 if (Config->get_edit_mode() == Lock) {
7808 error << (_("Cannot insert or delete time when in Lock edit.")) << endmsg;
7811 bool in_command = false;
7813 for (TrackSelection::iterator x = selection->tracks.begin(); x != selection->tracks.end(); ++x) {
7815 boost::shared_ptr<Playlist> pl = (*x)->playlist();
7819 XMLNode &before = pl->get_state();
7822 begin_reversible_command (_("remove time"));
7826 std::list<AudioRange> rl;
7827 AudioRange ar(pos, pos+samples, 0);
7830 pl->shift (pos, -samples, true, ignore_music_glue);
7832 XMLNode &after = pl->get_state();
7834 _session->add_command (new MementoCommand<Playlist> (*pl, &before, &after));
7838 RouteTimeAxisView* rtav = dynamic_cast<RouteTimeAxisView*> (*x);
7841 begin_reversible_command (_("remove time"));
7844 rtav->route ()->shift (pos, -samples);
7848 const int32_t divisions = get_grid_music_divisions (0);
7849 std::list<Location*> loc_kill_list;
7854 XMLNode& before (_session->locations()->get_state());
7855 Locations::LocationList copy (_session->locations()->list());
7857 for (Locations::LocationList::iterator i = copy.begin(); i != copy.end(); ++i) {
7858 if ((*i)->position_lock_style() == AudioTime || glued_markers_too) {
7860 bool const was_locked = (*i)->locked ();
7861 if (locked_markers_too) {
7865 if (!(*i)->is_mark()) { // it's a range; have to handle both start and end
7866 if ((*i)->end() >= pos
7867 && (*i)->end() < pos+samples
7868 && (*i)->start() >= pos
7869 && (*i)->end() < pos+samples) { // range is completely enclosed; kill it
7871 loc_kill_list.push_back(*i);
7872 } else { // only start or end is included, try to do the right thing
7873 // move start before moving end, to avoid trying to move the end to before the start
7874 // if we're removing more time than the length of the range
7875 if ((*i)->start() >= pos && (*i)->start() < pos+samples) {
7876 // start is within cut
7877 (*i)->set_start (pos, false, true,divisions); // bring the start marker to the beginning of the cut
7879 } else if ((*i)->start() >= pos+samples) {
7880 // start (and thus entire range) lies beyond end of cut
7881 (*i)->set_start ((*i)->start() - samples, false, true, divisions); // slip the start marker back
7884 if ((*i)->end() >= pos && (*i)->end() < pos+samples) {
7885 // end is inside cut
7886 (*i)->set_end (pos, false, true, divisions); // bring the end to the cut
7888 } else if ((*i)->end() >= pos+samples) {
7889 // end is beyond end of cut
7890 (*i)->set_end ((*i)->end() - samples, false, true, divisions); // slip the end marker back
7895 } else if ((*i)->start() >= pos && (*i)->start() < pos+samples) {
7896 loc_kill_list.push_back(*i);
7898 } else if ((*i)->start() >= pos) {
7899 (*i)->set_start ((*i)->start() -samples, false, true, divisions);
7909 for (list<Location*>::iterator i = loc_kill_list.begin(); i != loc_kill_list.end(); ++i) {
7910 _session->locations()->remove (*i);
7915 begin_reversible_command (_("remove time"));
7918 XMLNode& after (_session->locations()->get_state());
7919 _session->add_command (new MementoCommand<Locations>(*_session->locations(), &before, &after));
7924 XMLNode& before (_session->tempo_map().get_state());
7926 if (_session->tempo_map().remove_time (pos, samples)) {
7928 begin_reversible_command (_("remove time"));
7931 XMLNode& after (_session->tempo_map().get_state());
7932 _session->add_command (new MementoCommand<TempoMap>(_session->tempo_map(), &before, &after));
7937 commit_reversible_command ();
7942 Editor::fit_selection ()
7944 if (!selection->tracks.empty()) {
7945 fit_tracks (selection->tracks);
7949 /* no selected tracks - use tracks with selected regions */
7951 if (!selection->regions.empty()) {
7952 for (RegionSelection::iterator r = selection->regions.begin(); r != selection->regions.end(); ++r) {
7953 tvl.push_back (&(*r)->get_time_axis_view ());
7959 } else if (internal_editing()) {
7960 /* no selected tracks, or regions, but in internal edit mode, so follow the mouse and use
7963 if (entered_track) {
7964 tvl.push_back (entered_track);
7972 Editor::fit_tracks (TrackViewList & tracks)
7974 if (tracks.empty()) {
7978 uint32_t child_heights = 0;
7979 int visible_tracks = 0;
7981 for (TrackSelection::iterator t = tracks.begin(); t != tracks.end(); ++t) {
7983 if (!(*t)->marked_for_display()) {
7987 child_heights += (*t)->effective_height() - (*t)->current_height();
7991 /* compute the per-track height from:
7993 * total canvas visible height
7994 * - height that will be taken by visible children of selected tracks
7995 * - height of the ruler/hscroll area
7997 uint32_t h = (uint32_t) floor ((trackviews_height() - child_heights) / visible_tracks);
7998 double first_y_pos = DBL_MAX;
8000 if (h < TimeAxisView::preset_height (HeightSmall)) {
8001 MessageDialog msg (_("There are too many tracks to fit in the current window"));
8002 /* too small to be displayed */
8006 undo_visual_stack.push_back (current_visual_state (true));
8007 PBD::Unwinder<bool> nsv (no_save_visual, true);
8009 /* build a list of all tracks, including children */
8012 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
8014 TimeAxisView::Children c = (*i)->get_child_list ();
8015 for (TimeAxisView::Children::iterator j = c.begin(); j != c.end(); ++j) {
8016 all.push_back (j->get());
8021 // find selection range.
8022 // if someone knows how to user TrackViewList::iterator for this
8024 int selected_top = -1;
8025 int selected_bottom = -1;
8027 for (TrackViewList::iterator t = all.begin(); t != all.end(); ++t, ++i) {
8028 if ((*t)->marked_for_display ()) {
8029 if (tracks.contains(*t)) {
8030 if (selected_top == -1) {
8033 selected_bottom = i;
8039 for (TrackViewList::iterator t = all.begin(); t != all.end(); ++t, ++i) {
8040 if ((*t)->marked_for_display ()) {
8041 if (tracks.contains(*t)) {
8042 (*t)->set_height (h);
8043 first_y_pos = std::min ((*t)->y_position (), first_y_pos);
8045 if (i > selected_top && i < selected_bottom) {
8046 hide_track_in_display (*t);
8053 set the controls_layout height now, because waiting for its size
8054 request signal handler will cause the vertical adjustment setting to fail
8057 controls_layout.property_height () = _full_canvas_height;
8058 vertical_adjustment.set_value (first_y_pos);
8060 redo_visual_stack.push_back (current_visual_state (true));
8062 visible_tracks_selector.set_text (_("Sel"));
8066 Editor::save_visual_state (uint32_t n)
8068 while (visual_states.size() <= n) {
8069 visual_states.push_back (0);
8072 if (visual_states[n] != 0) {
8073 delete visual_states[n];
8076 visual_states[n] = current_visual_state (true);
8081 Editor::goto_visual_state (uint32_t n)
8083 if (visual_states.size() <= n) {
8087 if (visual_states[n] == 0) {
8091 use_visual_state (*visual_states[n]);
8095 Editor::start_visual_state_op (uint32_t n)
8097 save_visual_state (n);
8099 PopUp* pup = new PopUp (WIN_POS_MOUSE, 1000, true);
8101 snprintf (buf, sizeof (buf), _("Saved view %u"), n+1);
8102 pup->set_text (buf);
8107 Editor::cancel_visual_state_op (uint32_t n)
8109 goto_visual_state (n);
8113 Editor::toggle_region_mute ()
8115 if (_ignore_region_action) {
8119 RegionSelection rs = get_regions_from_selection_and_entered ();
8125 if (rs.size() > 1) {
8126 begin_reversible_command (_("mute regions"));
8128 begin_reversible_command (_("mute region"));
8131 for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
8133 (*i)->region()->playlist()->clear_changes ();
8134 (*i)->region()->set_muted (!(*i)->region()->muted ());
8135 _session->add_command (new StatefulDiffCommand ((*i)->region()));
8139 commit_reversible_command ();
8143 Editor::combine_regions ()
8145 /* foreach track with selected regions, take all selected regions
8146 and join them into a new region containing the subregions (as a
8150 typedef set<RouteTimeAxisView*> RTVS;
8153 if (selection->regions.empty()) {
8157 for (RegionSelection::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
8158 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*>(&(*i)->get_time_axis_view());
8161 tracks.insert (rtv);
8165 begin_reversible_command (_("combine regions"));
8167 vector<RegionView*> new_selection;
8169 for (RTVS::iterator i = tracks.begin(); i != tracks.end(); ++i) {
8172 if ((rv = (*i)->combine_regions ()) != 0) {
8173 new_selection.push_back (rv);
8177 selection->clear_regions ();
8178 for (vector<RegionView*>::iterator i = new_selection.begin(); i != new_selection.end(); ++i) {
8179 selection->add (*i);
8182 commit_reversible_command ();
8186 Editor::uncombine_regions ()
8188 typedef set<RouteTimeAxisView*> RTVS;
8191 if (selection->regions.empty()) {
8195 for (RegionSelection::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
8196 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*>(&(*i)->get_time_axis_view());
8199 tracks.insert (rtv);
8203 begin_reversible_command (_("uncombine regions"));
8205 for (RTVS::iterator i = tracks.begin(); i != tracks.end(); ++i) {
8206 (*i)->uncombine_regions ();
8209 commit_reversible_command ();
8213 Editor::toggle_midi_input_active (bool flip_others)
8216 boost::shared_ptr<RouteList> rl (new RouteList);
8218 for (TrackSelection::iterator i = selection->tracks.begin(); i != selection->tracks.end(); ++i) {
8219 RouteTimeAxisView *rtav = dynamic_cast<RouteTimeAxisView *>(*i);
8225 boost::shared_ptr<MidiTrack> mt = rtav->midi_track();
8228 rl->push_back (rtav->route());
8229 onoff = !mt->input_active();
8233 _session->set_exclusive_input_active (rl, onoff, flip_others);
8236 static bool ok_fine (GdkEventAny*) { return true; }
8242 lock_dialog = new Gtk::Dialog (string_compose (_("%1: Locked"), PROGRAM_NAME), true);
8244 Gtk::Image* padlock = manage (new Gtk::Image (ARDOUR_UI_UTILS::get_icon ("padlock_closed")));
8245 lock_dialog->get_vbox()->pack_start (*padlock);
8246 lock_dialog->signal_delete_event ().connect (sigc::ptr_fun (ok_fine));
8248 ArdourButton* b = manage (new ArdourButton);
8249 b->set_name ("lock button");
8250 b->set_text (_("Click to unlock"));
8251 b->signal_clicked.connect (sigc::mem_fun (*this, &Editor::unlock));
8252 lock_dialog->get_vbox()->pack_start (*b);
8254 lock_dialog->get_vbox()->show_all ();
8255 lock_dialog->set_size_request (200, 200);
8258 delete _main_menu_disabler;
8259 _main_menu_disabler = new MainMenuDisabler;
8261 lock_dialog->present ();
8263 lock_dialog->get_window()->set_decorations (Gdk::WMDecoration (0));
8269 lock_dialog->hide ();
8271 delete _main_menu_disabler;
8272 _main_menu_disabler = 0;
8274 if (UIConfiguration::instance().get_lock_gui_after_seconds()) {
8275 start_lock_event_timing ();
8280 Editor::bring_in_callback (Gtk::Label* label, uint32_t n, uint32_t total, string name)
8282 Gtkmm2ext::UI::instance()->call_slot (invalidator (*this), boost::bind (&Editor::update_bring_in_message, this, label, n, total, name));
8286 Editor::update_bring_in_message (Gtk::Label* label, uint32_t n, uint32_t total, string name)
8288 Timers::TimerSuspender t;
8289 label->set_text (string_compose ("Copying %1, %2 of %3", name, n, total));
8290 Gtkmm2ext::UI::instance()->flush_pending (1);
8294 Editor::bring_all_sources_into_session ()
8301 ArdourDialog w (_("Moving embedded files into session folder"));
8302 w.get_vbox()->pack_start (msg);
8305 /* flush all pending GUI events because we're about to start copying
8309 Timers::TimerSuspender t;
8310 Gtkmm2ext::UI::instance()->flush_pending (3);
8314 _session->bring_all_sources_into_session (boost::bind (&Editor::bring_in_callback, this, &msg, _1, _2, _3));