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/transpose.h"
67 #include "ardour/vca_manager.h"
69 #include "canvas/canvas.h"
72 #include "ardour_ui.h"
73 #include "audio_region_view.h"
74 #include "audio_streamview.h"
75 #include "audio_time_axis.h"
76 #include "automation_region_view.h"
77 #include "automation_time_axis.h"
78 #include "control_point.h"
82 #include "editor_cursors.h"
83 #include "editor_drag.h"
84 #include "editor_regions.h"
85 #include "editor_routes.h"
86 #include "gui_thread.h"
87 #include "insert_remove_time_dialog.h"
88 #include "interthread_progress_window.h"
89 #include "item_counts.h"
91 #include "midi_region_view.h"
93 #include "mixer_strip.h"
94 #include "mouse_cursors.h"
95 #include "normalize_dialog.h"
97 #include "paste_context.h"
98 #include "patch_change_dialog.h"
99 #include "quantize_dialog.h"
100 #include "region_gain_line.h"
101 #include "rgb_macros.h"
102 #include "route_time_axis.h"
103 #include "selection.h"
104 #include "selection_templates.h"
105 #include "streamview.h"
106 #include "strip_silence_dialog.h"
107 #include "time_axis_view.h"
109 #include "transpose_dialog.h"
110 #include "transform_dialog.h"
111 #include "ui_config.h"
112 #include "vca_time_axis.h"
114 #include "pbd/i18n.h"
117 using namespace ARDOUR;
120 using namespace Gtkmm2ext;
121 using namespace ArdourWidgets;
122 using namespace Editing;
123 using Gtkmm2ext::Keyboard;
125 /***********************************************************************
127 ***********************************************************************/
130 Editor::undo (uint32_t n)
132 if (_session && _session->actively_recording()) {
133 /* no undo allowed while recording. Session will check also,
134 but we don't even want to get to that.
139 if (_drags->active ()) {
145 if (_session->undo_depth() == 0) {
146 undo_action->set_sensitive(false);
148 redo_action->set_sensitive(true);
149 begin_selection_op_history ();
154 Editor::redo (uint32_t n)
156 if (_session && _session->actively_recording()) {
157 /* no redo allowed while recording. Session will check also,
158 but we don't even want to get to that.
163 if (_drags->active ()) {
169 if (_session->redo_depth() == 0) {
170 redo_action->set_sensitive(false);
172 undo_action->set_sensitive(true);
173 begin_selection_op_history ();
178 Editor::split_regions_at (MusicSample where, RegionSelection& regions)
182 RegionSelection pre_selected_regions = selection->regions;
183 bool working_on_selection = !pre_selected_regions.empty();
185 list<boost::shared_ptr<Playlist> > used_playlists;
186 list<RouteTimeAxisView*> used_trackviews;
188 if (regions.empty()) {
192 begin_reversible_command (_("split"));
195 if (regions.size() == 1) {
196 /* TODO: if splitting a single region, and snap-to is using
197 region boundaries, mabye we shouldn't pay attention to them? */
200 EditorFreeze(); /* Emit Signal */
203 for (RegionSelection::iterator a = regions.begin(); a != regions.end(); ) {
205 RegionSelection::iterator tmp;
207 /* XXX this test needs to be more complicated, to make sure we really
208 have something to split.
211 if (!(*a)->region()->covers (where.sample)) {
219 boost::shared_ptr<Playlist> pl = (*a)->region()->playlist();
227 /* we haven't seen this playlist before */
229 /* remember used playlists so we can thaw them later */
230 used_playlists.push_back(pl);
232 TimeAxisView& tv = (*a)->get_time_axis_view();
233 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (&tv);
235 used_trackviews.push_back (rtv);
242 pl->clear_changes ();
243 pl->split_region ((*a)->region(), where);
244 _session->add_command (new StatefulDiffCommand (pl));
250 latest_regionviews.clear ();
252 vector<sigc::connection> region_added_connections;
254 for (list<RouteTimeAxisView*>::iterator i = used_trackviews.begin(); i != used_trackviews.end(); ++i) {
255 region_added_connections.push_back ((*i)->view()->RegionViewAdded.connect (sigc::mem_fun(*this, &Editor::collect_new_region_view)));
258 while (used_playlists.size() > 0) {
259 list <boost::shared_ptr<Playlist > >::iterator i = used_playlists.begin();
261 used_playlists.pop_front();
264 for (vector<sigc::connection>::iterator c = region_added_connections.begin(); c != region_added_connections.end(); ++c) {
269 EditorThaw(); /* Emit Signal */
272 if (working_on_selection) {
273 // IFF we were working on selected regions, try to reinstate the other region selections that existed before the freeze/thaw.
275 RegionSelectionAfterSplit rsas = Config->get_region_selection_after_split();
276 /* There are three classes of regions that we might want selected after
277 splitting selected regions:
278 - regions selected before the split operation, and unaffected by it
279 - newly-created regions before the split
280 - newly-created regions after the split
283 if (rsas & Existing) {
284 // region selections that existed before the split.
285 selection->add (pre_selected_regions);
288 for (RegionSelection::iterator ri = latest_regionviews.begin(); ri != latest_regionviews.end(); ri++) {
289 if ((*ri)->region()->position() < where.sample) {
290 // new regions created before the split
291 if (rsas & NewlyCreatedLeft) {
292 selection->add (*ri);
295 // new regions created after the split
296 if (rsas & NewlyCreatedRight) {
297 selection->add (*ri);
303 commit_reversible_command ();
306 /** Move one extreme of the current range selection. If more than one range is selected,
307 * the start of the earliest range or the end of the latest range is moved.
309 * @param move_end true to move the end of the current range selection, false to move
311 * @param next true to move the extreme to the next region boundary, false to move to
315 Editor::move_range_selection_start_or_end_to_region_boundary (bool move_end, bool next)
317 if (selection->time.start() == selection->time.end_sample()) {
321 samplepos_t start = selection->time.start ();
322 samplepos_t end = selection->time.end_sample ();
324 /* the position of the thing we may move */
325 samplepos_t pos = move_end ? end : start;
326 int dir = next ? 1 : -1;
328 /* so we don't find the current region again */
329 if (dir > 0 || pos > 0) {
333 samplepos_t const target = get_region_boundary (pos, dir, true, false);
348 begin_reversible_selection_op (_("alter selection"));
349 selection->set_preserving_all_ranges (start, end);
350 commit_reversible_selection_op ();
354 Editor::nudge_forward_release (GdkEventButton* ev)
356 if (ev->state & Keyboard::PrimaryModifier) {
357 nudge_forward (false, true);
359 nudge_forward (false, false);
365 Editor::nudge_backward_release (GdkEventButton* ev)
367 if (ev->state & Keyboard::PrimaryModifier) {
368 nudge_backward (false, true);
370 nudge_backward (false, false);
377 Editor::nudge_forward (bool next, bool force_playhead)
379 samplepos_t distance;
380 samplepos_t next_distance;
386 RegionSelection rs = get_regions_from_selection_and_entered ();
388 if (!force_playhead && !rs.empty()) {
390 begin_reversible_command (_("nudge regions forward"));
392 for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
393 boost::shared_ptr<Region> r ((*i)->region());
395 distance = get_nudge_distance (r->position(), next_distance);
398 distance = next_distance;
402 r->set_position (r->position() + distance);
403 _session->add_command (new StatefulDiffCommand (r));
406 commit_reversible_command ();
409 } else if (!force_playhead && !selection->markers.empty()) {
412 bool in_command = false;
413 const int32_t divisions = get_grid_music_divisions (0);
415 for (MarkerSelection::iterator i = selection->markers.begin(); i != selection->markers.end(); ++i) {
417 Location* loc = find_location_from_marker ((*i), is_start);
421 XMLNode& before (loc->get_state());
424 distance = get_nudge_distance (loc->start(), next_distance);
426 distance = next_distance;
428 if (max_samplepos - distance > loc->start() + loc->length()) {
429 loc->set_start (loc->start() + distance, false, true, divisions);
431 loc->set_start (max_samplepos - loc->length(), false, true, divisions);
434 distance = get_nudge_distance (loc->end(), next_distance);
436 distance = next_distance;
438 if (max_samplepos - distance > loc->end()) {
439 loc->set_end (loc->end() + distance, false, true, divisions);
441 loc->set_end (max_samplepos, false, true, divisions);
443 if (loc->is_session_range()) {
444 _session->set_end_is_free (false);
448 begin_reversible_command (_("nudge location forward"));
451 XMLNode& after (loc->get_state());
452 _session->add_command (new MementoCommand<Location>(*loc, &before, &after));
457 commit_reversible_command ();
460 distance = get_nudge_distance (playhead_cursor->current_sample (), next_distance);
461 _session->request_locate (playhead_cursor->current_sample () + distance);
466 Editor::nudge_backward (bool next, bool force_playhead)
468 samplepos_t distance;
469 samplepos_t next_distance;
475 RegionSelection rs = get_regions_from_selection_and_entered ();
477 if (!force_playhead && !rs.empty()) {
479 begin_reversible_command (_("nudge regions backward"));
481 for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
482 boost::shared_ptr<Region> r ((*i)->region());
484 distance = get_nudge_distance (r->position(), next_distance);
487 distance = next_distance;
492 if (r->position() > distance) {
493 r->set_position (r->position() - distance);
497 _session->add_command (new StatefulDiffCommand (r));
500 commit_reversible_command ();
502 } else if (!force_playhead && !selection->markers.empty()) {
505 bool in_command = false;
507 for (MarkerSelection::iterator i = selection->markers.begin(); i != selection->markers.end(); ++i) {
509 Location* loc = find_location_from_marker ((*i), is_start);
513 XMLNode& before (loc->get_state());
516 distance = get_nudge_distance (loc->start(), next_distance);
518 distance = next_distance;
520 if (distance < loc->start()) {
521 loc->set_start (loc->start() - distance, false, true, get_grid_music_divisions(0));
523 loc->set_start (0, false, true, get_grid_music_divisions(0));
526 distance = get_nudge_distance (loc->end(), next_distance);
529 distance = next_distance;
532 if (distance < loc->end() - loc->length()) {
533 loc->set_end (loc->end() - distance, false, true, get_grid_music_divisions(0));
535 loc->set_end (loc->length(), false, true, get_grid_music_divisions(0));
537 if (loc->is_session_range()) {
538 _session->set_end_is_free (false);
542 begin_reversible_command (_("nudge location forward"));
545 XMLNode& after (loc->get_state());
546 _session->add_command (new MementoCommand<Location>(*loc, &before, &after));
550 commit_reversible_command ();
555 distance = get_nudge_distance (playhead_cursor->current_sample (), next_distance);
557 if (playhead_cursor->current_sample () > distance) {
558 _session->request_locate (playhead_cursor->current_sample () - distance);
560 _session->goto_start();
566 Editor::nudge_forward_capture_offset ()
568 RegionSelection rs = get_regions_from_selection_and_entered ();
570 if (!_session || rs.empty()) {
574 begin_reversible_command (_("nudge forward"));
576 samplepos_t const distance = _session->worst_output_latency();
578 for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
579 boost::shared_ptr<Region> r ((*i)->region());
582 r->set_position (r->position() + distance);
583 _session->add_command(new StatefulDiffCommand (r));
586 commit_reversible_command ();
590 Editor::nudge_backward_capture_offset ()
592 RegionSelection rs = get_regions_from_selection_and_entered ();
594 if (!_session || rs.empty()) {
598 begin_reversible_command (_("nudge backward"));
600 samplepos_t const distance = _session->worst_output_latency();
602 for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
603 boost::shared_ptr<Region> r ((*i)->region());
607 if (r->position() > distance) {
608 r->set_position (r->position() - distance);
612 _session->add_command(new StatefulDiffCommand (r));
615 commit_reversible_command ();
618 struct RegionSelectionPositionSorter {
619 bool operator() (RegionView* a, RegionView* b) {
620 return a->region()->position() < b->region()->position();
625 Editor::sequence_regions ()
628 samplepos_t r_end_prev;
636 RegionSelection rs = get_regions_from_selection_and_entered ();
637 rs.sort(RegionSelectionPositionSorter());
641 bool in_command = false;
643 for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
644 boost::shared_ptr<Region> r ((*i)->region());
652 if(r->position_locked())
659 r->set_position(r_end_prev);
663 begin_reversible_command (_("sequence regions"));
666 _session->add_command (new StatefulDiffCommand (r));
668 r_end=r->position() + r->length();
674 commit_reversible_command ();
683 Editor::move_to_start ()
685 _session->goto_start ();
689 Editor::move_to_end ()
692 _session->request_locate (_session->current_end_sample());
696 Editor::build_region_boundary_cache ()
699 /* TODO: maybe set a timer so we don't recalutate when lots of changes are coming in */
700 /* TODO: maybe somehow defer this until session is fully loaded. */
702 if (!_region_boundary_cache_dirty)
706 vector<RegionPoint> interesting_points;
707 boost::shared_ptr<Region> r;
708 TrackViewList tracks;
711 region_boundary_cache.clear ();
717 bool maybe_first_sample = false;
719 if (UIConfiguration::instance().get_snap_to_region_start()) {
720 interesting_points.push_back (Start);
721 maybe_first_sample = true;
724 if (UIConfiguration::instance().get_snap_to_region_end()) {
725 interesting_points.push_back (End);
728 if (UIConfiguration::instance().get_snap_to_region_sync()) {
729 interesting_points.push_back (SyncPoint);
732 /* if no snap selections are set, boundary cache should be left empty */
733 if ( interesting_points.empty() ) {
734 _region_boundary_cache_dirty = false;
738 TimeAxisView *ontrack = 0;
741 tlist = track_views.filter_to_unique_playlists ();
743 if (maybe_first_sample) {
744 TrackViewList::const_iterator i;
745 for (i = tlist.begin(); i != tlist.end(); ++i) {
746 boost::shared_ptr<Playlist> pl = (*i)->playlist();
747 if (pl && pl->count_regions_at (0)) {
748 region_boundary_cache.push_back (0);
754 //allow regions to snap to the video start (if any) as if it were a "region"
755 if (ARDOUR_UI::instance()->video_timeline) {
756 region_boundary_cache.push_back (ARDOUR_UI::instance()->video_timeline->get_video_start_offset());
759 std::pair<samplepos_t, samplepos_t> ext = session_gui_extents (false);
760 samplepos_t session_end = ext.second;
762 while (pos < session_end && !at_end) {
765 samplepos_t lpos = session_end;
767 for (vector<RegionPoint>::iterator p = interesting_points.begin(); p != interesting_points.end(); ++p) {
769 if ((r = find_next_region (pos, *p, 1, tlist, &ontrack)) == 0) {
770 if (*p == interesting_points.back()) {
773 /* move to next point type */
779 rpos = r->first_sample();
783 rpos = r->last_sample();
787 rpos = r->sync_position ();
798 /* prevent duplicates, but we don't use set<> because we want to be able
802 vector<samplepos_t>::iterator ri;
804 for (ri = region_boundary_cache.begin(); ri != region_boundary_cache.end(); ++ri) {
810 if (ri == region_boundary_cache.end()) {
811 region_boundary_cache.push_back (rpos);
818 /* finally sort to be sure that the order is correct */
820 sort (region_boundary_cache.begin(), region_boundary_cache.end());
822 _region_boundary_cache_dirty = false;
825 boost::shared_ptr<Region>
826 Editor::find_next_region (samplepos_t sample, RegionPoint point, int32_t dir, TrackViewList& tracks, TimeAxisView **ontrack)
828 TrackViewList::iterator i;
829 samplepos_t closest = max_samplepos;
830 boost::shared_ptr<Region> ret;
831 samplepos_t rpos = 0;
833 samplepos_t track_sample;
835 for (i = tracks.begin(); i != tracks.end(); ++i) {
837 samplecnt_t distance;
838 boost::shared_ptr<Region> r;
840 track_sample = sample;
842 if ((r = (*i)->find_next_region (track_sample, point, dir)) == 0) {
848 rpos = r->first_sample ();
852 rpos = r->last_sample ();
856 rpos = r->sync_position ();
861 distance = rpos - sample;
863 distance = sample - rpos;
866 if (distance < closest) {
878 Editor::find_next_region_boundary (samplepos_t pos, int32_t dir, const TrackViewList& tracks)
880 samplecnt_t distance = max_samplepos;
881 samplepos_t current_nearest = -1;
883 for (TrackViewList::const_iterator i = tracks.begin(); i != tracks.end(); ++i) {
884 samplepos_t contender;
887 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (*i);
893 if ((contender = rtv->find_next_region_boundary (pos, dir)) < 0) {
897 d = ::llabs (pos - contender);
900 current_nearest = contender;
905 return current_nearest;
909 Editor::get_region_boundary (samplepos_t pos, int32_t dir, bool with_selection, bool only_onscreen)
914 if (with_selection && Config->get_region_boundaries_from_selected_tracks()) {
916 if (!selection->tracks.empty()) {
918 target = find_next_region_boundary (pos, dir, selection->tracks);
922 if (only_onscreen || Config->get_region_boundaries_from_onscreen_tracks()) {
923 get_onscreen_tracks (tvl);
924 target = find_next_region_boundary (pos, dir, tvl);
926 target = find_next_region_boundary (pos, dir, track_views);
932 if (only_onscreen || Config->get_region_boundaries_from_onscreen_tracks()) {
933 get_onscreen_tracks (tvl);
934 target = find_next_region_boundary (pos, dir, tvl);
936 target = find_next_region_boundary (pos, dir, track_views);
944 Editor::cursor_to_region_boundary (bool with_selection, int32_t dir)
946 samplepos_t pos = playhead_cursor->current_sample ();
953 // so we don't find the current region again..
954 if (dir > 0 || pos > 0) {
958 if ((target = get_region_boundary (pos, dir, with_selection, false)) < 0) {
962 _session->request_locate (target);
966 Editor::cursor_to_next_region_boundary (bool with_selection)
968 cursor_to_region_boundary (with_selection, 1);
972 Editor::cursor_to_previous_region_boundary (bool with_selection)
974 cursor_to_region_boundary (with_selection, -1);
978 Editor::cursor_to_region_point (EditorCursor* cursor, RegionPoint point, int32_t dir)
980 boost::shared_ptr<Region> r;
981 samplepos_t pos = cursor->current_sample ();
987 TimeAxisView *ontrack = 0;
989 // so we don't find the current region again..
993 if (!selection->tracks.empty()) {
995 r = find_next_region (pos, point, dir, selection->tracks, &ontrack);
997 } else if (clicked_axisview) {
1000 t.push_back (clicked_axisview);
1002 r = find_next_region (pos, point, dir, t, &ontrack);
1006 r = find_next_region (pos, point, dir, track_views, &ontrack);
1015 pos = r->first_sample ();
1019 pos = r->last_sample ();
1023 pos = r->sync_position ();
1027 if (cursor == playhead_cursor) {
1028 _session->request_locate (pos);
1030 cursor->set_position (pos);
1035 Editor::cursor_to_next_region_point (EditorCursor* cursor, RegionPoint point)
1037 cursor_to_region_point (cursor, point, 1);
1041 Editor::cursor_to_previous_region_point (EditorCursor* cursor, RegionPoint point)
1043 cursor_to_region_point (cursor, point, -1);
1047 Editor::cursor_to_selection_start (EditorCursor *cursor)
1049 samplepos_t pos = 0;
1051 switch (mouse_mode) {
1053 if (!selection->regions.empty()) {
1054 pos = selection->regions.start();
1059 if (!selection->time.empty()) {
1060 pos = selection->time.start ();
1068 if (cursor == playhead_cursor) {
1069 _session->request_locate (pos);
1071 cursor->set_position (pos);
1076 Editor::cursor_to_selection_end (EditorCursor *cursor)
1078 samplepos_t pos = 0;
1080 switch (mouse_mode) {
1082 if (!selection->regions.empty()) {
1083 pos = selection->regions.end_sample();
1088 if (!selection->time.empty()) {
1089 pos = selection->time.end_sample ();
1097 if (cursor == playhead_cursor) {
1098 _session->request_locate (pos);
1100 cursor->set_position (pos);
1105 Editor::selected_marker_to_region_boundary (bool with_selection, int32_t dir)
1115 if (selection->markers.empty()) {
1119 if (!mouse_sample (mouse, ignored)) {
1123 add_location_mark (mouse);
1126 if ((loc = find_location_from_marker (selection->markers.front(), ignored)) == 0) {
1130 samplepos_t pos = loc->start();
1132 // so we don't find the current region again..
1133 if (dir > 0 || pos > 0) {
1137 if ((target = get_region_boundary (pos, dir, with_selection, false)) < 0) {
1141 loc->move_to (target, 0);
1145 Editor::selected_marker_to_next_region_boundary (bool with_selection)
1147 selected_marker_to_region_boundary (with_selection, 1);
1151 Editor::selected_marker_to_previous_region_boundary (bool with_selection)
1153 selected_marker_to_region_boundary (with_selection, -1);
1157 Editor::selected_marker_to_region_point (RegionPoint point, int32_t dir)
1159 boost::shared_ptr<Region> r;
1164 if (!_session || selection->markers.empty()) {
1168 if ((loc = find_location_from_marker (selection->markers.front(), ignored)) == 0) {
1172 TimeAxisView *ontrack = 0;
1176 // so we don't find the current region again..
1180 if (!selection->tracks.empty()) {
1182 r = find_next_region (pos, point, dir, selection->tracks, &ontrack);
1186 r = find_next_region (pos, point, dir, track_views, &ontrack);
1195 pos = r->first_sample ();
1199 pos = r->last_sample ();
1203 pos = r->adjust_to_sync (r->first_sample());
1207 loc->move_to (pos, 0);
1211 Editor::selected_marker_to_next_region_point (RegionPoint point)
1213 selected_marker_to_region_point (point, 1);
1217 Editor::selected_marker_to_previous_region_point (RegionPoint point)
1219 selected_marker_to_region_point (point, -1);
1223 Editor::selected_marker_to_selection_start ()
1225 samplepos_t pos = 0;
1229 if (!_session || selection->markers.empty()) {
1233 if ((loc = find_location_from_marker (selection->markers.front(), ignored)) == 0) {
1237 switch (mouse_mode) {
1239 if (!selection->regions.empty()) {
1240 pos = selection->regions.start();
1245 if (!selection->time.empty()) {
1246 pos = selection->time.start ();
1254 loc->move_to (pos, 0);
1258 Editor::selected_marker_to_selection_end ()
1260 samplepos_t pos = 0;
1264 if (!_session || selection->markers.empty()) {
1268 if ((loc = find_location_from_marker (selection->markers.front(), ignored)) == 0) {
1272 switch (mouse_mode) {
1274 if (!selection->regions.empty()) {
1275 pos = selection->regions.end_sample();
1280 if (!selection->time.empty()) {
1281 pos = selection->time.end_sample ();
1289 loc->move_to (pos, 0);
1293 Editor::scroll_playhead (bool forward)
1295 samplepos_t pos = playhead_cursor->current_sample ();
1296 samplecnt_t delta = (samplecnt_t) floor (current_page_samples() / 0.8);
1299 if (pos == max_samplepos) {
1303 if (pos < max_samplepos - delta) {
1306 pos = max_samplepos;
1322 _session->request_locate (pos);
1326 Editor::cursor_align (bool playhead_to_edit)
1332 if (playhead_to_edit) {
1334 if (selection->markers.empty()) {
1338 _session->request_locate (selection->markers.front()->position(), _session->transport_rolling());
1341 const int32_t divisions = get_grid_music_divisions (0);
1342 /* move selected markers to playhead */
1344 for (MarkerSelection::iterator i = selection->markers.begin(); i != selection->markers.end(); ++i) {
1347 Location* loc = find_location_from_marker (*i, ignored);
1349 if (loc->is_mark()) {
1350 loc->set_start (playhead_cursor->current_sample (), false, true, divisions);
1352 loc->set (playhead_cursor->current_sample (),
1353 playhead_cursor->current_sample () + loc->length(), true, divisions);
1360 Editor::scroll_backward (float pages)
1362 samplepos_t const one_page = (samplepos_t) rint (_visible_canvas_width * samples_per_pixel);
1363 samplepos_t const cnt = (samplepos_t) floor (pages * one_page);
1366 if (_leftmost_sample < cnt) {
1369 sample = _leftmost_sample - cnt;
1372 reset_x_origin (sample);
1376 Editor::scroll_forward (float pages)
1378 samplepos_t const one_page = (samplepos_t) rint (_visible_canvas_width * samples_per_pixel);
1379 samplepos_t const cnt = (samplepos_t) floor (pages * one_page);
1382 if (max_samplepos - cnt < _leftmost_sample) {
1383 sample = max_samplepos - cnt;
1385 sample = _leftmost_sample + cnt;
1388 reset_x_origin (sample);
1392 Editor::scroll_tracks_down ()
1394 double vert_value = vertical_adjustment.get_value() + vertical_adjustment.get_page_size();
1395 if (vert_value > vertical_adjustment.get_upper() - _visible_canvas_height) {
1396 vert_value = vertical_adjustment.get_upper() - _visible_canvas_height;
1399 vertical_adjustment.set_value (vert_value);
1403 Editor::scroll_tracks_up ()
1405 vertical_adjustment.set_value (vertical_adjustment.get_value() - vertical_adjustment.get_page_size());
1409 Editor::scroll_tracks_down_line ()
1411 double vert_value = vertical_adjustment.get_value() + 60;
1413 if (vert_value > vertical_adjustment.get_upper() - _visible_canvas_height) {
1414 vert_value = vertical_adjustment.get_upper() - _visible_canvas_height;
1417 vertical_adjustment.set_value (vert_value);
1421 Editor::scroll_tracks_up_line ()
1423 reset_y_origin (vertical_adjustment.get_value() - 60);
1427 Editor::select_topmost_track ()
1429 const double top_of_trackviews = vertical_adjustment.get_value();
1430 for (TrackViewList::iterator t = track_views.begin(); t != track_views.end(); ++t) {
1431 if ((*t)->hidden()) {
1434 std::pair<TimeAxisView*,double> res = (*t)->covers_y_position (top_of_trackviews);
1436 selection->set (*t);
1443 Editor::scroll_down_one_track (bool skip_child_views)
1445 TrackViewList::reverse_iterator next = track_views.rend();
1446 const double top_of_trackviews = vertical_adjustment.get_value();
1448 for (TrackViewList::reverse_iterator t = track_views.rbegin(); t != track_views.rend(); ++t) {
1449 if ((*t)->hidden()) {
1453 /* If this is the upper-most visible trackview, we want to display
1454 * the one above it (next)
1456 * Note that covers_y_position() is recursive and includes child views
1458 std::pair<TimeAxisView*,double> res = (*t)->covers_y_position (top_of_trackviews);
1461 if (skip_child_views) {
1464 /* automation lane (one level, non-recursive)
1466 * - if no automation lane exists -> move to next tack
1467 * - if the first (here: bottom-most) matches -> move to next tack
1468 * - if no y-axis match is found -> the current track is at the top
1469 * -> move to last (here: top-most) automation lane
1471 TimeAxisView::Children kids = (*t)->get_child_list();
1472 TimeAxisView::Children::reverse_iterator nkid = kids.rend();
1474 for (TimeAxisView::Children::reverse_iterator ci = kids.rbegin(); ci != kids.rend(); ++ci) {
1475 if ((*ci)->hidden()) {
1479 std::pair<TimeAxisView*,double> dev;
1480 dev = (*ci)->covers_y_position (top_of_trackviews);
1482 /* some automation lane is currently at the top */
1483 if (ci == kids.rbegin()) {
1484 /* first (bottom-most) autmation lane is at the top.
1485 * -> move to next track
1494 if (nkid != kids.rend()) {
1495 ensure_time_axis_view_is_visible (**nkid, true);
1503 /* move to the track below the first one that covers the */
1505 if (next != track_views.rend()) {
1506 ensure_time_axis_view_is_visible (**next, true);
1514 Editor::scroll_up_one_track (bool skip_child_views)
1516 TrackViewList::iterator prev = track_views.end();
1517 double top_of_trackviews = vertical_adjustment.get_value ();
1519 for (TrackViewList::iterator t = track_views.begin(); t != track_views.end(); ++t) {
1521 if ((*t)->hidden()) {
1525 /* find the trackview at the top of the trackview group
1527 * Note that covers_y_position() is recursive and includes child views
1529 std::pair<TimeAxisView*,double> res = (*t)->covers_y_position (top_of_trackviews);
1532 if (skip_child_views) {
1535 /* automation lane (one level, non-recursive)
1537 * - if no automation lane exists -> move to prev tack
1538 * - if no y-axis match is found -> the current track is at the top -> move to prev track
1539 * (actually last automation lane of previous track, see below)
1540 * - if first (top-most) lane is at the top -> move to this track
1541 * - else move up one lane
1543 TimeAxisView::Children kids = (*t)->get_child_list();
1544 TimeAxisView::Children::iterator pkid = kids.end();
1546 for (TimeAxisView::Children::iterator ci = kids.begin(); ci != kids.end(); ++ci) {
1547 if ((*ci)->hidden()) {
1551 std::pair<TimeAxisView*,double> dev;
1552 dev = (*ci)->covers_y_position (top_of_trackviews);
1554 /* some automation lane is currently at the top */
1555 if (ci == kids.begin()) {
1556 /* first (top-most) autmation lane is at the top.
1557 * jump directly to this track's top
1559 ensure_time_axis_view_is_visible (**t, true);
1562 else if (pkid != kids.end()) {
1563 /* some other automation lane is at the top.
1564 * move up to prev automation lane.
1566 ensure_time_axis_view_is_visible (**pkid, true);
1569 assert(0); // not reached
1580 if (prev != track_views.end()) {
1581 // move to bottom-most automation-lane of the previous track
1582 TimeAxisView::Children kids = (*prev)->get_child_list();
1583 TimeAxisView::Children::reverse_iterator pkid = kids.rend();
1584 if (!skip_child_views) {
1585 // find the last visible lane
1586 for (TimeAxisView::Children::reverse_iterator ci = kids.rbegin(); ci != kids.rend(); ++ci) {
1587 if (!(*ci)->hidden()) {
1593 if (pkid != kids.rend()) {
1594 ensure_time_axis_view_is_visible (**pkid, true);
1596 ensure_time_axis_view_is_visible (**prev, true);
1605 Editor::scroll_left_step ()
1607 samplepos_t xdelta = (current_page_samples() / 8);
1609 if (_leftmost_sample > xdelta) {
1610 reset_x_origin (_leftmost_sample - xdelta);
1618 Editor::scroll_right_step ()
1620 samplepos_t xdelta = (current_page_samples() / 8);
1622 if (max_samplepos - xdelta > _leftmost_sample) {
1623 reset_x_origin (_leftmost_sample + xdelta);
1625 reset_x_origin (max_samplepos - current_page_samples());
1630 Editor::scroll_left_half_page ()
1632 samplepos_t xdelta = (current_page_samples() / 2);
1633 if (_leftmost_sample > xdelta) {
1634 reset_x_origin (_leftmost_sample - xdelta);
1641 Editor::scroll_right_half_page ()
1643 samplepos_t xdelta = (current_page_samples() / 2);
1644 if (max_samplepos - xdelta > _leftmost_sample) {
1645 reset_x_origin (_leftmost_sample + xdelta);
1647 reset_x_origin (max_samplepos - current_page_samples());
1654 Editor::tav_zoom_step (bool coarser)
1656 DisplaySuspender ds;
1660 if (selection->tracks.empty()) {
1663 ts = &selection->tracks;
1666 for (TrackViewList::iterator i = ts->begin(); i != ts->end(); ++i) {
1667 TimeAxisView *tv = (static_cast<TimeAxisView*>(*i));
1668 tv->step_height (coarser);
1673 Editor::tav_zoom_smooth (bool coarser, bool force_all)
1675 DisplaySuspender ds;
1679 if (selection->tracks.empty() || force_all) {
1682 ts = &selection->tracks;
1685 for (TrackViewList::iterator i = ts->begin(); i != ts->end(); ++i) {
1686 TimeAxisView *tv = (static_cast<TimeAxisView*>(*i));
1687 uint32_t h = tv->current_height ();
1692 if (h >= TimeAxisView::preset_height (HeightSmall)) {
1697 tv->set_height (h + 5);
1703 Editor::temporal_zoom_step_mouse_focus_scale (bool zoom_out, double scale)
1705 Editing::ZoomFocus temp_focus = zoom_focus;
1706 zoom_focus = Editing::ZoomFocusMouse;
1707 temporal_zoom_step_scale (zoom_out, scale);
1708 zoom_focus = temp_focus;
1712 Editor::temporal_zoom_step_mouse_focus (bool zoom_out)
1714 temporal_zoom_step_mouse_focus_scale (zoom_out, 2.0);
1718 Editor::temporal_zoom_step (bool zoom_out)
1720 temporal_zoom_step_scale (zoom_out, 2.0);
1724 Editor::temporal_zoom_step_scale (bool zoom_out, double scale)
1726 ENSURE_GUI_THREAD (*this, &Editor::temporal_zoom_step, zoom_out, scale)
1728 samplecnt_t nspp = samples_per_pixel;
1732 if (nspp == samples_per_pixel) {
1737 if (nspp == samples_per_pixel) {
1742 //zoom-behavior-tweaks
1743 //limit our maximum zoom to the session gui extents value
1744 std::pair<samplepos_t, samplepos_t> ext = session_gui_extents();
1745 samplecnt_t session_extents_pp = (ext.second - ext.first) / _visible_canvas_width;
1746 if (nspp > session_extents_pp)
1747 nspp = session_extents_pp;
1749 temporal_zoom (nspp);
1753 Editor::temporal_zoom (samplecnt_t fpp)
1759 samplepos_t current_page = current_page_samples();
1760 samplepos_t current_leftmost = _leftmost_sample;
1761 samplepos_t current_rightmost;
1762 samplepos_t current_center;
1763 samplepos_t new_page_size;
1764 samplepos_t half_page_size;
1765 samplepos_t leftmost_after_zoom = 0;
1767 bool in_track_canvas;
1768 bool use_mouse_sample = true;
1772 if (fpp == samples_per_pixel) {
1776 // Imposing an arbitrary limit to zoom out as too much zoom out produces
1777 // segfaults for lack of memory. If somebody decides this is not high enough I
1778 // believe it can be raisen to higher values but some limit must be in place.
1780 // This constant represents 1 day @ 48kHz on a 1600 pixel wide display
1781 // all of which is used for the editor track displays. The whole day
1782 // would be 4147200000 samples, so 2592000 samples per pixel.
1784 nfpp = min (fpp, (samplecnt_t) 2592000);
1785 nfpp = max ((samplecnt_t) 1, nfpp);
1787 new_page_size = (samplepos_t) floor (_visible_canvas_width * nfpp);
1788 half_page_size = new_page_size / 2;
1790 switch (zoom_focus) {
1792 leftmost_after_zoom = current_leftmost;
1795 case ZoomFocusRight:
1796 current_rightmost = _leftmost_sample + current_page;
1797 if (current_rightmost < new_page_size) {
1798 leftmost_after_zoom = 0;
1800 leftmost_after_zoom = current_rightmost - new_page_size;
1804 case ZoomFocusCenter:
1805 current_center = current_leftmost + (current_page/2);
1806 if (current_center < half_page_size) {
1807 leftmost_after_zoom = 0;
1809 leftmost_after_zoom = current_center - half_page_size;
1813 case ZoomFocusPlayhead:
1814 /* centre playhead */
1815 l = playhead_cursor->current_sample () - (new_page_size * 0.5);
1818 leftmost_after_zoom = 0;
1819 } else if (l > max_samplepos) {
1820 leftmost_after_zoom = max_samplepos - new_page_size;
1822 leftmost_after_zoom = (samplepos_t) l;
1826 case ZoomFocusMouse:
1827 /* try to keep the mouse over the same point in the display */
1829 if (_drags->active()) {
1830 where = _drags->current_pointer_sample ();
1831 } else if (!mouse_sample (where, in_track_canvas)) {
1832 use_mouse_sample = false;
1835 if (use_mouse_sample) {
1836 l = - ((new_page_size * ((where - current_leftmost)/(double)current_page)) - where);
1839 leftmost_after_zoom = 0;
1840 } else if (l > max_samplepos) {
1841 leftmost_after_zoom = max_samplepos - new_page_size;
1843 leftmost_after_zoom = (samplepos_t) l;
1846 /* use playhead instead */
1847 where = playhead_cursor->current_sample ();
1849 if (where < half_page_size) {
1850 leftmost_after_zoom = 0;
1852 leftmost_after_zoom = where - half_page_size;
1858 /* try to keep the edit point in the same place */
1859 where = get_preferred_edit_position ();
1863 double l = - ((new_page_size * ((where - current_leftmost)/(double)current_page)) - where);
1866 leftmost_after_zoom = 0;
1867 } else if (l > max_samplepos) {
1868 leftmost_after_zoom = max_samplepos - new_page_size;
1870 leftmost_after_zoom = (samplepos_t) l;
1874 /* edit point not defined */
1881 // leftmost_after_zoom = min (leftmost_after_zoom, _session->current_end_sample());
1883 reposition_and_zoom (leftmost_after_zoom, nfpp);
1887 Editor::calc_extra_zoom_edges(samplepos_t &start, samplepos_t &end)
1889 /* this func helps make sure we leave a little space
1890 at each end of the editor so that the zoom doesn't fit the region
1891 precisely to the screen.
1894 GdkScreen* screen = gdk_screen_get_default ();
1895 const gint pixwidth = gdk_screen_get_width (screen);
1896 const gint mmwidth = gdk_screen_get_width_mm (screen);
1897 const double pix_per_mm = (double) pixwidth/ (double) mmwidth;
1898 const double one_centimeter_in_pixels = pix_per_mm * 10.0;
1900 const samplepos_t range = end - start;
1901 const samplecnt_t new_fpp = (samplecnt_t) ceil ((double) range / (double) _visible_canvas_width);
1902 const samplepos_t extra_samples = (samplepos_t) floor (one_centimeter_in_pixels * new_fpp);
1904 if (start > extra_samples) {
1905 start -= extra_samples;
1910 if (max_samplepos - extra_samples > end) {
1911 end += extra_samples;
1913 end = max_samplepos;
1918 Editor::get_selection_extents (samplepos_t &start, samplepos_t &end) const
1920 start = max_samplepos;
1924 //ToDo: if notes are selected, set extents to that selection
1926 //ToDo: if control points are selected, set extents to that selection
1928 if (!selection->regions.empty()) {
1929 RegionSelection rs = get_regions_from_selection_and_entered ();
1931 for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
1933 if ((*i)->region()->position() < start) {
1934 start = (*i)->region()->position();
1937 if ((*i)->region()->last_sample() + 1 > end) {
1938 end = (*i)->region()->last_sample() + 1;
1942 } else if (!selection->time.empty()) {
1943 start = selection->time.start();
1944 end = selection->time.end_sample();
1946 ret = false; //no selection found
1949 if ((start == 0 && end == 0) || end < start) {
1958 Editor::temporal_zoom_selection (Editing::ZoomAxis axes)
1960 if (!selection) return;
1962 if (selection->regions.empty() && selection->time.empty()) {
1963 if (axes == Horizontal || axes == Both) {
1964 temporal_zoom_step(true);
1966 if (axes == Vertical || axes == Both) {
1967 if (!track_views.empty()) {
1971 //implicit hack: by extending the top & bottom check outside the current view limits, we include the trackviews immediately above & below what is visible
1972 const double top = vertical_adjustment.get_value() - 10;
1973 const double btm = top + _visible_canvas_height + 10;
1975 for (TrackViewList::iterator iter = track_views.begin(); iter != track_views.end(); ++iter) {
1976 if ((*iter)->covered_by_y_range (top, btm)) {
1977 tvl.push_back(*iter);
1987 //ToDo: if notes are selected, zoom to that
1989 //ToDo: if control points are selected, zoom to that
1991 if (axes == Horizontal || axes == Both) {
1993 samplepos_t start, end;
1994 if (get_selection_extents (start, end)) {
1995 calc_extra_zoom_edges (start, end);
1996 temporal_zoom_by_sample (start, end);
2000 if (axes == Vertical || axes == Both) {
2004 //normally, we don't do anything "automatic" to the user's selection.
2005 //but in this case, we will clear the selection after a zoom-to-selection.
2010 Editor::temporal_zoom_session ()
2012 ENSURE_GUI_THREAD (*this, &Editor::temporal_zoom_session)
2015 samplecnt_t start = _session->current_start_sample();
2016 samplecnt_t end = _session->current_end_sample();
2018 if (_session->actively_recording ()) {
2019 samplepos_t cur = playhead_cursor->current_sample ();
2021 /* recording beyond the end marker; zoom out
2022 * by 5 seconds more so that if 'follow
2023 * playhead' is active we don't immediately
2026 end = cur + _session->sample_rate() * 5;
2030 if ((start == 0 && end == 0) || end < start) {
2034 calc_extra_zoom_edges(start, end);
2036 temporal_zoom_by_sample (start, end);
2041 Editor::temporal_zoom_extents ()
2043 ENSURE_GUI_THREAD (*this, &Editor::temporal_zoom_extents)
2046 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
2048 samplecnt_t start = ext.first;
2049 samplecnt_t end = ext.second;
2051 if (_session->actively_recording ()) {
2052 samplepos_t cur = playhead_cursor->current_sample ();
2054 /* recording beyond the end marker; zoom out
2055 * by 5 seconds more so that if 'follow
2056 * playhead' is active we don't immediately
2059 end = cur + _session->sample_rate() * 5;
2063 if ((start == 0 && end == 0) || end < start) {
2067 calc_extra_zoom_edges(start, end);
2069 temporal_zoom_by_sample (start, end);
2074 Editor::temporal_zoom_by_sample (samplepos_t start, samplepos_t end)
2076 if (!_session) return;
2078 if ((start == 0 && end == 0) || end < start) {
2082 samplepos_t range = end - start;
2084 const samplecnt_t new_fpp = (samplecnt_t) ceil ((double) range / (double) _visible_canvas_width);
2086 samplepos_t new_page = range;
2087 samplepos_t middle = (samplepos_t) floor ((double) start + ((double) range / 2.0f));
2088 samplepos_t new_leftmost = (samplepos_t) floor ((double) middle - ((double) new_page / 2.0f));
2090 if (new_leftmost > middle) {
2094 if (new_leftmost < 0) {
2098 reposition_and_zoom (new_leftmost, new_fpp);
2102 Editor::temporal_zoom_to_sample (bool coarser, samplepos_t sample)
2108 samplecnt_t range_before = sample - _leftmost_sample;
2109 samplecnt_t new_spp;
2112 if (samples_per_pixel <= 1) {
2115 new_spp = samples_per_pixel + (samples_per_pixel/2);
2117 range_before += range_before/2;
2119 if (samples_per_pixel >= 1) {
2120 new_spp = samples_per_pixel - (samples_per_pixel/2);
2122 /* could bail out here since we cannot zoom any finer,
2123 but leave that to the equality test below
2125 new_spp = samples_per_pixel;
2128 range_before -= range_before/2;
2131 if (new_spp == samples_per_pixel) {
2135 /* zoom focus is automatically taken as @param sample when this
2139 samplepos_t new_leftmost = sample - (samplepos_t)range_before;
2141 if (new_leftmost > sample) {
2145 if (new_leftmost < 0) {
2149 reposition_and_zoom (new_leftmost, new_spp);
2154 Editor::choose_new_marker_name(string &name) {
2156 if (!UIConfiguration::instance().get_name_new_markers()) {
2157 /* don't prompt user for a new name */
2161 Prompter dialog (true);
2163 dialog.set_prompt (_("New Name:"));
2165 dialog.set_title (_("New Location Marker"));
2167 dialog.set_name ("MarkNameWindow");
2168 dialog.set_size_request (250, -1);
2169 dialog.set_position (Gtk::WIN_POS_MOUSE);
2171 dialog.add_button (Stock::OK, RESPONSE_ACCEPT);
2172 dialog.set_initial_text (name);
2176 switch (dialog.run ()) {
2177 case RESPONSE_ACCEPT:
2183 dialog.get_result(name);
2190 Editor::add_location_from_selection ()
2194 if (selection->time.empty()) {
2198 if (_session == 0 || clicked_axisview == 0) {
2202 samplepos_t start = selection->time[clicked_selection].start;
2203 samplepos_t end = selection->time[clicked_selection].end;
2205 _session->locations()->next_available_name(rangename,"selection");
2206 Location *location = new Location (*_session, start, end, rangename, Location::IsRangeMarker, get_grid_music_divisions(0));
2208 begin_reversible_command (_("add marker"));
2210 XMLNode &before = _session->locations()->get_state();
2211 _session->locations()->add (location, true);
2212 XMLNode &after = _session->locations()->get_state();
2213 _session->add_command(new MementoCommand<Locations>(*(_session->locations()), &before, &after));
2215 commit_reversible_command ();
2219 Editor::add_location_mark (samplepos_t where)
2223 select_new_marker = true;
2225 _session->locations()->next_available_name(markername,"mark");
2226 if (!choose_new_marker_name(markername)) {
2229 Location *location = new Location (*_session, where, where, markername, Location::IsMark, get_grid_music_divisions (0));
2230 begin_reversible_command (_("add marker"));
2232 XMLNode &before = _session->locations()->get_state();
2233 _session->locations()->add (location, true);
2234 XMLNode &after = _session->locations()->get_state();
2235 _session->add_command(new MementoCommand<Locations>(*(_session->locations()), &before, &after));
2237 commit_reversible_command ();
2241 Editor::set_session_start_from_playhead ()
2247 if ((loc = _session->locations()->session_range_location()) == 0) { //should never happen
2248 _session->set_session_extents (_session->audible_sample(), _session->audible_sample());
2250 XMLNode &before = loc->get_state();
2252 _session->set_session_extents (_session->audible_sample(), loc->end());
2254 XMLNode &after = loc->get_state();
2256 begin_reversible_command (_("Set session start"));
2258 _session->add_command (new MementoCommand<Location>(*loc, &before, &after));
2260 commit_reversible_command ();
2265 Editor::set_session_end_from_playhead ()
2271 if ((loc = _session->locations()->session_range_location()) == 0) { //should never happen
2272 _session->set_session_extents (_session->audible_sample(), _session->audible_sample());
2274 XMLNode &before = loc->get_state();
2276 _session->set_session_extents (loc->start(), _session->audible_sample());
2278 XMLNode &after = loc->get_state();
2280 begin_reversible_command (_("Set session start"));
2282 _session->add_command (new MementoCommand<Location>(*loc, &before, &after));
2284 commit_reversible_command ();
2287 _session->set_end_is_free (false);
2292 Editor::toggle_location_at_playhead_cursor ()
2294 if (!do_remove_location_at_playhead_cursor())
2296 add_location_from_playhead_cursor();
2301 Editor::add_location_from_playhead_cursor ()
2303 add_location_mark (_session->audible_sample());
2307 Editor::do_remove_location_at_playhead_cursor ()
2309 bool removed = false;
2312 XMLNode &before = _session->locations()->get_state();
2314 //find location(s) at this time
2315 Locations::LocationList locs;
2316 _session->locations()->find_all_between (_session->audible_sample(), _session->audible_sample()+1, locs, Location::Flags(0));
2317 for (Locations::LocationList::iterator i = locs.begin(); i != locs.end(); ++i) {
2318 if ((*i)->is_mark()) {
2319 _session->locations()->remove (*i);
2326 begin_reversible_command (_("remove marker"));
2327 XMLNode &after = _session->locations()->get_state();
2328 _session->add_command(new MementoCommand<Locations>(*(_session->locations()), &before, &after));
2329 commit_reversible_command ();
2336 Editor::remove_location_at_playhead_cursor ()
2338 do_remove_location_at_playhead_cursor ();
2341 /** Add a range marker around each selected region */
2343 Editor::add_locations_from_region ()
2345 RegionSelection rs = get_regions_from_selection_and_entered ();
2350 bool commit = false;
2352 XMLNode &before = _session->locations()->get_state();
2354 for (RegionSelection::iterator i = rs.begin (); i != rs.end (); ++i) {
2356 boost::shared_ptr<Region> region = (*i)->region ();
2358 Location *location = new Location (*_session, region->position(), region->last_sample(), region->name(), Location::IsRangeMarker, 0);
2360 _session->locations()->add (location, true);
2365 begin_reversible_command (selection->regions.size () > 1 ? _("add markers") : _("add marker"));
2366 XMLNode &after = _session->locations()->get_state();
2367 _session->add_command (new MementoCommand<Locations>(*(_session->locations()), &before, &after));
2368 commit_reversible_command ();
2372 /** Add a single range marker around all selected regions */
2374 Editor::add_location_from_region ()
2376 RegionSelection rs = get_regions_from_selection_and_entered ();
2382 XMLNode &before = _session->locations()->get_state();
2386 if (rs.size() > 1) {
2387 _session->locations()->next_available_name(markername, "regions");
2389 RegionView* rv = *(rs.begin());
2390 boost::shared_ptr<Region> region = rv->region();
2391 markername = region->name();
2394 if (!choose_new_marker_name(markername)) {
2398 // single range spanning all selected
2399 Location *location = new Location (*_session, selection->regions.start(), selection->regions.end_sample(), markername, Location::IsRangeMarker, 0);
2400 _session->locations()->add (location, true);
2402 begin_reversible_command (_("add marker"));
2403 XMLNode &after = _session->locations()->get_state();
2404 _session->add_command (new MementoCommand<Locations>(*(_session->locations()), &before, &after));
2405 commit_reversible_command ();
2411 Editor::jump_forward_to_mark ()
2417 samplepos_t pos = _session->locations()->first_mark_after (playhead_cursor->current_sample());
2423 _session->request_locate (pos, _session->transport_rolling());
2427 Editor::jump_backward_to_mark ()
2433 samplepos_t pos = _session->locations()->first_mark_before (playhead_cursor->current_sample());
2435 //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...
2436 if (_session->transport_rolling()) {
2437 if ((playhead_cursor->current_sample() - pos) < _session->sample_rate()/2) {
2438 samplepos_t prior = _session->locations()->first_mark_before (pos);
2447 _session->request_locate (pos, _session->transport_rolling());
2453 samplepos_t const pos = _session->audible_sample ();
2456 _session->locations()->next_available_name (markername, "mark");
2458 if (!choose_new_marker_name (markername)) {
2462 _session->locations()->add (new Location (*_session, pos, 0, markername, Location::IsMark, 0), true);
2466 Editor::clear_markers ()
2469 begin_reversible_command (_("clear markers"));
2471 XMLNode &before = _session->locations()->get_state();
2472 _session->locations()->clear_markers ();
2473 XMLNode &after = _session->locations()->get_state();
2474 _session->add_command(new MementoCommand<Locations>(*(_session->locations()), &before, &after));
2476 commit_reversible_command ();
2481 Editor::clear_ranges ()
2484 begin_reversible_command (_("clear ranges"));
2486 XMLNode &before = _session->locations()->get_state();
2488 _session->locations()->clear_ranges ();
2490 XMLNode &after = _session->locations()->get_state();
2491 _session->add_command(new MementoCommand<Locations>(*(_session->locations()), &before, &after));
2493 commit_reversible_command ();
2498 Editor::clear_locations ()
2500 begin_reversible_command (_("clear locations"));
2502 XMLNode &before = _session->locations()->get_state();
2503 _session->locations()->clear ();
2504 XMLNode &after = _session->locations()->get_state();
2505 _session->add_command(new MementoCommand<Locations>(*(_session->locations()), &before, &after));
2507 commit_reversible_command ();
2511 Editor::unhide_markers ()
2513 for (LocationMarkerMap::iterator i = location_markers.begin(); i != location_markers.end(); ++i) {
2514 Location *l = (*i).first;
2515 if (l->is_hidden() && l->is_mark()) {
2516 l->set_hidden(false, this);
2522 Editor::unhide_ranges ()
2524 for (LocationMarkerMap::iterator i = location_markers.begin(); i != location_markers.end(); ++i) {
2525 Location *l = (*i).first;
2526 if (l->is_hidden() && l->is_range_marker()) {
2527 l->set_hidden(false, this);
2532 /* INSERT/REPLACE */
2535 Editor::insert_region_list_selection (float times)
2537 RouteTimeAxisView *tv = 0;
2538 boost::shared_ptr<Playlist> playlist;
2540 if (clicked_routeview != 0) {
2541 tv = clicked_routeview;
2542 } else if (!selection->tracks.empty()) {
2543 if ((tv = dynamic_cast<RouteTimeAxisView*>(selection->tracks.front())) == 0) {
2546 } else if (entered_track != 0) {
2547 if ((tv = dynamic_cast<RouteTimeAxisView*>(entered_track)) == 0) {
2554 if ((playlist = tv->playlist()) == 0) {
2558 boost::shared_ptr<Region> region = _regions->get_single_selection ();
2563 begin_reversible_command (_("insert region"));
2564 playlist->clear_changes ();
2565 playlist->add_region ((RegionFactory::create (region, true)), get_preferred_edit_position(), times);
2566 if (Config->get_edit_mode() == Ripple)
2567 playlist->ripple (get_preferred_edit_position(), region->length() * times, boost::shared_ptr<Region>());
2569 _session->add_command(new StatefulDiffCommand (playlist));
2570 commit_reversible_command ();
2573 /* BUILT-IN EFFECTS */
2576 Editor::reverse_selection ()
2581 /* GAIN ENVELOPE EDITING */
2584 Editor::edit_envelope ()
2591 Editor::transition_to_rolling (bool fwd)
2597 if (_session->config.get_external_sync()) {
2598 switch (Config->get_sync_source()) {
2602 /* transport controlled by the master */
2607 if (_session->is_auditioning()) {
2608 _session->cancel_audition ();
2612 _session->request_transport_speed (fwd ? 1.0f : -1.0f);
2616 Editor::play_from_start ()
2618 _session->request_locate (_session->current_start_sample(), true);
2622 Editor::play_from_edit_point ()
2624 _session->request_locate (get_preferred_edit_position(), true);
2628 Editor::play_from_edit_point_and_return ()
2630 samplepos_t start_sample;
2631 samplepos_t return_sample;
2633 start_sample = get_preferred_edit_position (EDIT_IGNORE_PHEAD);
2635 if (_session->transport_rolling()) {
2636 _session->request_locate (start_sample, false);
2640 /* don't reset the return sample if its already set */
2642 if ((return_sample = _session->requested_return_sample()) < 0) {
2643 return_sample = _session->audible_sample();
2646 if (start_sample >= 0) {
2647 _session->request_roll_at_and_return (start_sample, return_sample);
2652 Editor::play_selection ()
2654 samplepos_t start, end;
2655 if (!get_selection_extents (start, end))
2658 AudioRange ar (start, end, 0);
2659 list<AudioRange> lar;
2662 _session->request_play_range (&lar, true);
2667 Editor::maybe_locate_with_edit_preroll (samplepos_t location)
2669 if (_session->transport_rolling() || !UIConfiguration::instance().get_follow_edits() || _session->config.get_external_sync())
2672 location -= _session->preroll_samples (location);
2674 //don't try to locate before the beginning of time
2679 //if follow_playhead is on, keep the playhead on the screen
2680 if (_follow_playhead)
2681 if (location < _leftmost_sample)
2682 location = _leftmost_sample;
2684 _session->request_locate (location);
2688 Editor::play_with_preroll ()
2690 samplepos_t start, end;
2691 if (UIConfiguration::instance().get_follow_edits() && get_selection_extents (start, end)) {
2692 const samplepos_t preroll = _session->preroll_samples (start);
2694 samplepos_t ret = start;
2696 if (start > preroll) {
2697 start = start - preroll;
2700 end = end + preroll; //"post-roll"
2702 AudioRange ar (start, end, 0);
2703 list<AudioRange> lar;
2706 _session->request_play_range (&lar, true);
2707 _session->set_requested_return_sample (ret); //force auto-return to return to range start, without the preroll
2709 samplepos_t ph = playhead_cursor->current_sample ();
2710 const samplepos_t preroll = _session->preroll_samples (ph);
2713 start = ph - preroll;
2717 _session->request_locate (start, true);
2718 _session->set_requested_return_sample (ph); //force auto-return to return to playhead location, without the preroll
2723 Editor::rec_with_preroll ()
2725 samplepos_t ph = playhead_cursor->current_sample ();
2726 samplepos_t preroll = _session->preroll_samples (ph);
2727 _session->request_preroll_record_trim (ph, preroll);
2731 Editor::rec_with_count_in ()
2733 _session->request_count_in_record ();
2737 Editor::play_location (Location& location)
2739 if (location.start() <= location.end()) {
2743 _session->request_bounded_roll (location.start(), location.end());
2747 Editor::loop_location (Location& location)
2749 if (location.start() <= location.end()) {
2755 if ((tll = transport_loop_location()) != 0) {
2756 tll->set (location.start(), location.end());
2758 // enable looping, reposition and start rolling
2759 _session->request_locate (tll->start(), true);
2760 _session->request_play_loop (true);
2765 Editor::do_layer_operation (LayerOperation op)
2767 if (selection->regions.empty ()) {
2771 bool const multiple = selection->regions.size() > 1;
2775 begin_reversible_command (_("raise regions"));
2777 begin_reversible_command (_("raise region"));
2783 begin_reversible_command (_("raise regions to top"));
2785 begin_reversible_command (_("raise region to top"));
2791 begin_reversible_command (_("lower regions"));
2793 begin_reversible_command (_("lower region"));
2799 begin_reversible_command (_("lower regions to bottom"));
2801 begin_reversible_command (_("lower region"));
2806 set<boost::shared_ptr<Playlist> > playlists = selection->regions.playlists ();
2807 for (set<boost::shared_ptr<Playlist> >::iterator i = playlists.begin(); i != playlists.end(); ++i) {
2808 (*i)->clear_owned_changes ();
2811 for (RegionSelection::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
2812 boost::shared_ptr<Region> r = (*i)->region ();
2824 r->lower_to_bottom ();
2828 for (set<boost::shared_ptr<Playlist> >::iterator i = playlists.begin(); i != playlists.end(); ++i) {
2829 vector<Command*> cmds;
2831 _session->add_commands (cmds);
2834 commit_reversible_command ();
2838 Editor::raise_region ()
2840 do_layer_operation (Raise);
2844 Editor::raise_region_to_top ()
2846 do_layer_operation (RaiseToTop);
2850 Editor::lower_region ()
2852 do_layer_operation (Lower);
2856 Editor::lower_region_to_bottom ()
2858 do_layer_operation (LowerToBottom);
2861 /** Show the region editor for the selected regions */
2863 Editor::show_region_properties ()
2865 selection->foreach_regionview (&RegionView::show_region_editor);
2868 /** Show the midi list editor for the selected MIDI regions */
2870 Editor::show_midi_list_editor ()
2872 selection->foreach_midi_regionview (&MidiRegionView::show_list_editor);
2876 Editor::rename_region ()
2878 RegionSelection rs = get_regions_from_selection_and_entered ();
2884 ArdourDialog d (_("Rename Region"), true, false);
2886 Label label (_("New name:"));
2889 hbox.set_spacing (6);
2890 hbox.pack_start (label, false, false);
2891 hbox.pack_start (entry, true, true);
2893 d.get_vbox()->set_border_width (12);
2894 d.get_vbox()->pack_start (hbox, false, false);
2896 d.add_button(Gtk::Stock::CANCEL, Gtk::RESPONSE_CANCEL);
2897 d.add_button(Gtk::Stock::OK, Gtk::RESPONSE_OK);
2899 d.set_size_request (300, -1);
2901 entry.set_text (rs.front()->region()->name());
2902 entry.select_region (0, -1);
2904 entry.signal_activate().connect (sigc::bind (sigc::mem_fun (d, &Dialog::response), RESPONSE_OK));
2910 int const ret = d.run();
2914 if (ret != RESPONSE_OK) {
2918 std::string str = entry.get_text();
2919 strip_whitespace_edges (str);
2921 rs.front()->region()->set_name (str);
2922 _regions->redisplay ();
2926 /** Start an audition of the first selected region */
2928 Editor::play_edit_range ()
2930 samplepos_t start, end;
2932 if (get_edit_op_range (start, end)) {
2933 _session->request_bounded_roll (start, end);
2938 Editor::play_selected_region ()
2940 samplepos_t start = max_samplepos;
2941 samplepos_t end = 0;
2943 RegionSelection rs = get_regions_from_selection_and_entered ();
2949 for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
2950 if ((*i)->region()->position() < start) {
2951 start = (*i)->region()->position();
2953 if ((*i)->region()->last_sample() + 1 > end) {
2954 end = (*i)->region()->last_sample() + 1;
2958 _session->request_bounded_roll (start, end);
2962 Editor::audition_playlist_region_standalone (boost::shared_ptr<Region> region)
2964 _session->audition_region (region);
2968 Editor::region_from_selection ()
2970 if (clicked_axisview == 0) {
2974 if (selection->time.empty()) {
2978 samplepos_t start = selection->time[clicked_selection].start;
2979 samplepos_t end = selection->time[clicked_selection].end;
2981 TrackViewList tracks = get_tracks_for_range_action ();
2983 samplepos_t selection_cnt = end - start + 1;
2985 for (TrackSelection::iterator i = tracks.begin(); i != tracks.end(); ++i) {
2986 boost::shared_ptr<Region> current;
2987 boost::shared_ptr<Playlist> pl;
2988 samplepos_t internal_start;
2991 if ((pl = (*i)->playlist()) == 0) {
2995 if ((current = pl->top_region_at (start)) == 0) {
2999 internal_start = start - current->position();
3000 RegionFactory::region_name (new_name, current->name(), true);
3004 plist.add (ARDOUR::Properties::start, current->start() + internal_start);
3005 plist.add (ARDOUR::Properties::length, selection_cnt);
3006 plist.add (ARDOUR::Properties::name, new_name);
3007 plist.add (ARDOUR::Properties::layer, 0);
3009 boost::shared_ptr<Region> region (RegionFactory::create (current, plist));
3014 Editor::create_region_from_selection (vector<boost::shared_ptr<Region> >& new_regions)
3016 if (selection->time.empty() || selection->tracks.empty()) {
3020 samplepos_t start, end;
3021 if (clicked_selection) {
3022 start = selection->time[clicked_selection].start;
3023 end = selection->time[clicked_selection].end;
3025 start = selection->time.start();
3026 end = selection->time.end_sample();
3029 TrackViewList ts = selection->tracks.filter_to_unique_playlists ();
3030 sort_track_selection (ts);
3032 for (TrackSelection::iterator i = ts.begin(); i != ts.end(); ++i) {
3033 boost::shared_ptr<Region> current;
3034 boost::shared_ptr<Playlist> playlist;
3035 samplepos_t internal_start;
3038 if ((playlist = (*i)->playlist()) == 0) {
3042 if ((current = playlist->top_region_at(start)) == 0) {
3046 internal_start = start - current->position();
3047 RegionFactory::region_name (new_name, current->name(), true);
3051 plist.add (ARDOUR::Properties::start, current->start() + internal_start);
3052 plist.add (ARDOUR::Properties::length, end - start + 1);
3053 plist.add (ARDOUR::Properties::name, new_name);
3055 new_regions.push_back (RegionFactory::create (current, plist));
3060 Editor::split_multichannel_region ()
3062 RegionSelection rs = get_regions_from_selection_and_entered ();
3068 vector< boost::shared_ptr<Region> > v;
3070 for (list<RegionView*>::iterator x = rs.begin(); x != rs.end(); ++x) {
3071 (*x)->region()->separate_by_channel (v);
3076 Editor::new_region_from_selection ()
3078 region_from_selection ();
3079 cancel_selection ();
3083 add_if_covered (RegionView* rv, const AudioRange* ar, RegionSelection* rs)
3085 switch (rv->region()->coverage (ar->start, ar->end - 1)) {
3086 // n.b. -1 because AudioRange::end is one past the end, but coverage expects inclusive ranges
3087 case Evoral::OverlapNone:
3095 * - selected tracks, or if there are none...
3096 * - tracks containing selected regions, or if there are none...
3101 Editor::get_tracks_for_range_action () const
3105 if (selection->tracks.empty()) {
3107 /* use tracks with selected regions */
3109 RegionSelection rs = selection->regions;
3111 for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
3112 TimeAxisView* tv = &(*i)->get_time_axis_view();
3114 if (!t.contains (tv)) {
3120 /* no regions and no tracks: use all tracks */
3126 t = selection->tracks;
3129 return t.filter_to_unique_playlists();
3133 Editor::separate_regions_between (const TimeSelection& ts)
3135 bool in_command = false;
3136 boost::shared_ptr<Playlist> playlist;
3137 RegionSelection new_selection;
3139 TrackViewList tmptracks = get_tracks_for_range_action ();
3140 sort_track_selection (tmptracks);
3142 for (TrackSelection::iterator i = tmptracks.begin(); i != tmptracks.end(); ++i) {
3144 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> ((*i));
3150 if (!rtv->is_track()) {
3154 /* no edits to destructive tracks */
3156 if (rtv->track()->destructive()) {
3160 if ((playlist = rtv->playlist()) != 0) {
3162 playlist->clear_changes ();
3164 /* XXX need to consider musical time selections here at some point */
3166 for (list<AudioRange>::const_iterator t = ts.begin(); t != ts.end(); ++t) {
3168 sigc::connection c = rtv->view()->RegionViewAdded.connect (
3169 sigc::mem_fun(*this, &Editor::collect_new_region_view));
3171 latest_regionviews.clear ();
3173 playlist->partition ((*t).start, (*t).end, false);
3177 if (!latest_regionviews.empty()) {
3179 rtv->view()->foreach_regionview (sigc::bind (
3180 sigc::ptr_fun (add_if_covered),
3181 &(*t), &new_selection));
3184 begin_reversible_command (_("separate"));
3188 /* pick up changes to existing regions */
3190 vector<Command*> cmds;
3191 playlist->rdiff (cmds);
3192 _session->add_commands (cmds);
3194 /* pick up changes to the playlist itself (adds/removes)
3197 _session->add_command(new StatefulDiffCommand (playlist));
3204 // selection->set (new_selection);
3206 commit_reversible_command ();
3210 struct PlaylistState {
3211 boost::shared_ptr<Playlist> playlist;
3215 /** Take tracks from get_tracks_for_range_action and cut any regions
3216 * on those tracks so that the tracks are empty over the time
3220 Editor::separate_region_from_selection ()
3222 /* preferentially use *all* ranges in the time selection if we're in range mode
3223 to allow discontiguous operation, since get_edit_op_range() currently
3224 returns a single range.
3227 if (!selection->time.empty()) {
3229 separate_regions_between (selection->time);
3236 if (get_edit_op_range (start, end)) {
3238 AudioRange ar (start, end, 1);
3242 separate_regions_between (ts);
3248 Editor::separate_region_from_punch ()
3250 Location* loc = _session->locations()->auto_punch_location();
3252 separate_regions_using_location (*loc);
3257 Editor::separate_region_from_loop ()
3259 Location* loc = _session->locations()->auto_loop_location();
3261 separate_regions_using_location (*loc);
3266 Editor::separate_regions_using_location (Location& loc)
3268 if (loc.is_mark()) {
3272 AudioRange ar (loc.start(), loc.end(), 1);
3277 separate_regions_between (ts);
3280 /** Separate regions under the selected region */
3282 Editor::separate_under_selected_regions ()
3284 vector<PlaylistState> playlists;
3288 rs = get_regions_from_selection_and_entered();
3290 if (!_session || rs.empty()) {
3294 begin_reversible_command (_("separate region under"));
3296 list<boost::shared_ptr<Region> > regions_to_remove;
3298 for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
3299 // we can't just remove the region(s) in this loop because
3300 // this removes them from the RegionSelection, and they thus
3301 // disappear from underneath the iterator, and the ++i above
3302 // SEGVs in a puzzling fashion.
3304 // so, first iterate over the regions to be removed from rs and
3305 // add them to the regions_to_remove list, and then
3306 // iterate over the list to actually remove them.
3308 regions_to_remove.push_back ((*i)->region());
3311 for (list<boost::shared_ptr<Region> >::iterator rl = regions_to_remove.begin(); rl != regions_to_remove.end(); ++rl) {
3313 boost::shared_ptr<Playlist> playlist = (*rl)->playlist();
3316 // is this check necessary?
3320 vector<PlaylistState>::iterator i;
3322 //only take state if this is a new playlist.
3323 for (i = playlists.begin(); i != playlists.end(); ++i) {
3324 if ((*i).playlist == playlist) {
3329 if (i == playlists.end()) {
3331 PlaylistState before;
3332 before.playlist = playlist;
3333 before.before = &playlist->get_state();
3334 playlist->clear_changes ();
3335 playlist->freeze ();
3336 playlists.push_back(before);
3339 //Partition on the region bounds
3340 playlist->partition ((*rl)->first_sample() - 1, (*rl)->last_sample() + 1, true);
3342 //Re-add region that was just removed due to the partition operation
3343 playlist->add_region ((*rl), (*rl)->first_sample());
3346 vector<PlaylistState>::iterator pl;
3348 for (pl = playlists.begin(); pl != playlists.end(); ++pl) {
3349 (*pl).playlist->thaw ();
3350 _session->add_command(new MementoCommand<Playlist>(*(*pl).playlist, (*pl).before, &(*pl).playlist->get_state()));
3353 commit_reversible_command ();
3357 Editor::crop_region_to_selection ()
3359 if (!selection->time.empty()) {
3361 begin_reversible_command (_("Crop Regions to Time Selection"));
3362 for (std::list<AudioRange>::iterator i = selection->time.begin(); i != selection->time.end(); ++i) {
3363 crop_region_to ((*i).start, (*i).end);
3365 commit_reversible_command();
3371 if (get_edit_op_range (start, end)) {
3372 begin_reversible_command (_("Crop Regions to Edit Range"));
3374 crop_region_to (start, end);
3376 commit_reversible_command();
3383 Editor::crop_region_to (samplepos_t start, samplepos_t end)
3385 vector<boost::shared_ptr<Playlist> > playlists;
3386 boost::shared_ptr<Playlist> playlist;
3389 if (selection->tracks.empty()) {
3390 ts = track_views.filter_to_unique_playlists();
3392 ts = selection->tracks.filter_to_unique_playlists ();
3395 sort_track_selection (ts);
3397 for (TrackSelection::iterator i = ts.begin(); i != ts.end(); ++i) {
3399 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> ((*i));
3405 boost::shared_ptr<Track> t = rtv->track();
3407 if (t != 0 && ! t->destructive()) {
3409 if ((playlist = rtv->playlist()) != 0) {
3410 playlists.push_back (playlist);
3415 if (playlists.empty()) {
3420 samplepos_t new_start;
3421 samplepos_t new_end;
3422 samplecnt_t new_length;
3424 for (vector<boost::shared_ptr<Playlist> >::iterator i = playlists.begin(); i != playlists.end(); ++i) {
3426 /* Only the top regions at start and end have to be cropped */
3427 boost::shared_ptr<Region> region_at_start = (*i)->top_region_at(start);
3428 boost::shared_ptr<Region> region_at_end = (*i)->top_region_at(end);
3430 vector<boost::shared_ptr<Region> > regions;
3432 if (region_at_start != 0) {
3433 regions.push_back (region_at_start);
3435 if (region_at_end != 0) {
3436 regions.push_back (region_at_end);
3439 /* now adjust lengths */
3440 for (vector<boost::shared_ptr<Region> >::iterator i = regions.begin(); i != regions.end(); ++i) {
3442 pos = (*i)->position();
3443 new_start = max (start, pos);
3444 if (max_samplepos - pos > (*i)->length()) {
3445 new_end = pos + (*i)->length() - 1;
3447 new_end = max_samplepos;
3449 new_end = min (end, new_end);
3450 new_length = new_end - new_start + 1;
3452 (*i)->clear_changes ();
3453 (*i)->trim_to (new_start, new_length);
3454 _session->add_command (new StatefulDiffCommand (*i));
3460 Editor::region_fill_track ()
3462 boost::shared_ptr<Playlist> playlist;
3463 RegionSelection regions = get_regions_from_selection_and_entered ();
3464 RegionSelection foo;
3466 samplepos_t const end = _session->current_end_sample ();
3468 if (regions.empty () || regions.end_sample () + 1 >= end) {
3472 samplepos_t const start_sample = regions.start ();
3473 samplepos_t const end_sample = regions.end_sample ();
3474 samplecnt_t const gap = end_sample - start_sample + 1;
3476 begin_reversible_command (Operations::region_fill);
3478 selection->clear_regions ();
3480 for (RegionSelection::iterator i = regions.begin(); i != regions.end(); ++i) {
3482 boost::shared_ptr<Region> r ((*i)->region());
3484 TimeAxisView& tv = (*i)->get_time_axis_view();
3485 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (&tv);
3486 latest_regionviews.clear ();
3487 sigc::connection c = rtv->view()->RegionViewAdded.connect (sigc::mem_fun(*this, &Editor::collect_new_region_view));
3489 samplepos_t const position = end_sample + (r->first_sample() - start_sample + 1);
3490 playlist = (*i)->region()->playlist();
3491 playlist->clear_changes ();
3492 playlist->duplicate_until (r, position, gap, end);
3493 _session->add_command(new StatefulDiffCommand (playlist));
3497 foo.insert (foo.end(), latest_regionviews.begin(), latest_regionviews.end());
3501 selection->set (foo);
3504 commit_reversible_command ();
3508 Editor::set_region_sync_position ()
3510 set_sync_point (get_preferred_edit_position (), get_regions_from_selection_and_edit_point ());
3514 Editor::set_sync_point (samplepos_t where, const RegionSelection& rs)
3516 bool in_command = false;
3518 for (RegionSelection::const_iterator r = rs.begin(); r != rs.end(); ++r) {
3520 if (!(*r)->region()->covers (where)) {
3524 boost::shared_ptr<Region> region ((*r)->region());
3527 begin_reversible_command (_("set sync point"));
3531 region->clear_changes ();
3532 region->set_sync_position (where);
3533 _session->add_command(new StatefulDiffCommand (region));
3537 commit_reversible_command ();
3541 /** Remove the sync positions of the selection */
3543 Editor::remove_region_sync ()
3545 RegionSelection rs = get_regions_from_selection_and_entered ();
3551 begin_reversible_command (_("remove region sync"));
3553 for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
3555 (*i)->region()->clear_changes ();
3556 (*i)->region()->clear_sync_position ();
3557 _session->add_command(new StatefulDiffCommand ((*i)->region()));
3560 commit_reversible_command ();
3564 Editor::naturalize_region ()
3566 RegionSelection rs = get_regions_from_selection_and_entered ();
3572 if (rs.size() > 1) {
3573 begin_reversible_command (_("move regions to original position"));
3575 begin_reversible_command (_("move region to original position"));
3578 for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
3579 (*i)->region()->clear_changes ();
3580 (*i)->region()->move_to_natural_position ();
3581 _session->add_command (new StatefulDiffCommand ((*i)->region()));
3584 commit_reversible_command ();
3588 Editor::align_regions (RegionPoint what)
3590 RegionSelection const rs = get_regions_from_selection_and_edit_point ();
3596 begin_reversible_command (_("align selection"));
3598 samplepos_t const position = get_preferred_edit_position ();
3600 for (RegionSelection::const_iterator i = rs.begin(); i != rs.end(); ++i) {
3601 align_region_internal ((*i)->region(), what, position);
3604 commit_reversible_command ();
3607 struct RegionSortByTime {
3608 bool operator() (const RegionView* a, const RegionView* b) {
3609 return a->region()->position() < b->region()->position();
3614 Editor::align_regions_relative (RegionPoint point)
3616 RegionSelection const rs = get_regions_from_selection_and_edit_point ();
3622 samplepos_t const position = get_preferred_edit_position ();
3624 samplepos_t distance = 0;
3625 samplepos_t pos = 0;
3628 list<RegionView*> sorted;
3629 rs.by_position (sorted);
3631 boost::shared_ptr<Region> r ((*sorted.begin())->region());
3636 if (position > r->position()) {
3637 distance = position - r->position();
3639 distance = r->position() - position;
3645 if (position > r->last_sample()) {
3646 distance = position - r->last_sample();
3647 pos = r->position() + distance;
3649 distance = r->last_sample() - position;
3650 pos = r->position() - distance;
3656 pos = r->adjust_to_sync (position);
3657 if (pos > r->position()) {
3658 distance = pos - r->position();
3660 distance = r->position() - pos;
3666 if (pos == r->position()) {
3670 begin_reversible_command (_("align selection (relative)"));
3672 /* move first one specially */
3674 r->clear_changes ();
3675 r->set_position (pos);
3676 _session->add_command(new StatefulDiffCommand (r));
3678 /* move rest by the same amount */
3682 for (list<RegionView*>::iterator i = sorted.begin(); i != sorted.end(); ++i) {
3684 boost::shared_ptr<Region> region ((*i)->region());
3686 region->clear_changes ();
3689 region->set_position (region->position() + distance);
3691 region->set_position (region->position() - distance);
3694 _session->add_command(new StatefulDiffCommand (region));
3698 commit_reversible_command ();
3702 Editor::align_region (boost::shared_ptr<Region> region, RegionPoint point, samplepos_t position)
3704 begin_reversible_command (_("align region"));
3705 align_region_internal (region, point, position);
3706 commit_reversible_command ();
3710 Editor::align_region_internal (boost::shared_ptr<Region> region, RegionPoint point, samplepos_t position)
3712 region->clear_changes ();
3716 region->set_position (region->adjust_to_sync (position));
3720 if (position > region->length()) {
3721 region->set_position (position - region->length());
3726 region->set_position (position);
3730 _session->add_command(new StatefulDiffCommand (region));
3734 Editor::trim_region_front ()
3740 Editor::trim_region_back ()
3742 trim_region (false);
3746 Editor::trim_region (bool front)
3748 samplepos_t where = get_preferred_edit_position();
3749 RegionSelection rs = get_regions_from_selection_and_edit_point ();
3755 begin_reversible_command (front ? _("trim front") : _("trim back"));
3757 for (list<RegionView*>::const_iterator i = rs.by_layer().begin(); i != rs.by_layer().end(); ++i) {
3758 if (!(*i)->region()->locked()) {
3760 (*i)->region()->clear_changes ();
3763 (*i)->region()->trim_front (where);
3765 (*i)->region()->trim_end (where);
3768 _session->add_command (new StatefulDiffCommand ((*i)->region()));
3772 commit_reversible_command ();
3775 /** Trim the end of the selected regions to the position of the edit cursor */
3777 Editor::trim_region_to_loop ()
3779 Location* loc = _session->locations()->auto_loop_location();
3783 trim_region_to_location (*loc, _("trim to loop"));
3787 Editor::trim_region_to_punch ()
3789 Location* loc = _session->locations()->auto_punch_location();
3793 trim_region_to_location (*loc, _("trim to punch"));
3797 Editor::trim_region_to_location (const Location& loc, const char* str)
3799 RegionSelection rs = get_regions_from_selection_and_entered ();
3800 bool in_command = false;
3802 for (RegionSelection::iterator x = rs.begin(); x != rs.end(); ++x) {
3803 RegionView* rv = (*x);
3805 /* require region to span proposed trim */
3806 switch (rv->region()->coverage (loc.start(), loc.end())) {
3807 case Evoral::OverlapInternal:
3813 RouteTimeAxisView* tav = dynamic_cast<RouteTimeAxisView*> (&rv->get_time_axis_view());
3821 start = loc.start();
3824 rv->region()->clear_changes ();
3825 rv->region()->trim_to (start, (end - start));
3828 begin_reversible_command (str);
3831 _session->add_command(new StatefulDiffCommand (rv->region()));
3835 commit_reversible_command ();
3840 Editor::trim_region_to_previous_region_end ()
3842 return trim_to_region(false);
3846 Editor::trim_region_to_next_region_start ()
3848 return trim_to_region(true);
3852 Editor::trim_to_region(bool forward)
3854 RegionSelection rs = get_regions_from_selection_and_entered ();
3855 bool in_command = false;
3857 boost::shared_ptr<Region> next_region;
3859 for (RegionSelection::iterator x = rs.begin(); x != rs.end(); ++x) {
3861 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (*x);
3867 AudioTimeAxisView* atav = dynamic_cast<AudioTimeAxisView*> (&arv->get_time_axis_view());
3873 boost::shared_ptr<Region> region = arv->region();
3874 boost::shared_ptr<Playlist> playlist (region->playlist());
3876 region->clear_changes ();
3880 next_region = playlist->find_next_region (region->first_sample(), Start, 1);
3886 region->trim_end (next_region->first_sample() - 1);
3887 arv->region_changed (PropertyChange (ARDOUR::Properties::length));
3891 next_region = playlist->find_next_region (region->first_sample(), Start, 0);
3897 region->trim_front (next_region->last_sample() + 1);
3898 arv->region_changed (ARDOUR::bounds_change);
3902 begin_reversible_command (_("trim to region"));
3905 _session->add_command(new StatefulDiffCommand (region));
3909 commit_reversible_command ();
3914 Editor::unfreeze_route ()
3916 if (clicked_routeview == 0 || !clicked_routeview->is_track()) {
3920 clicked_routeview->track()->unfreeze ();
3924 Editor::_freeze_thread (void* arg)
3926 return static_cast<Editor*>(arg)->freeze_thread ();
3930 Editor::freeze_thread ()
3932 /* create event pool because we may need to talk to the session */
3933 SessionEvent::create_per_thread_pool ("freeze events", 64);
3934 /* create per-thread buffers for process() tree to use */
3935 clicked_routeview->audio_track()->freeze_me (*current_interthread_info);
3936 current_interthread_info->done = true;
3941 Editor::freeze_route ()
3947 /* stop transport before we start. this is important */
3949 _session->request_transport_speed (0.0);
3951 /* wait for just a little while, because the above call is asynchronous */
3953 Glib::usleep (250000);
3955 if (clicked_routeview == 0 || !clicked_routeview->is_audio_track()) {
3959 if (!clicked_routeview->track()->bounceable (clicked_routeview->track()->main_outs(), true)) {
3961 _("This track/bus cannot be frozen because the signal adds or loses channels before reaching the outputs.\n"
3962 "This is typically caused by plugins that generate stereo output from mono input or vice versa.")
3964 d.set_title (_("Cannot freeze"));
3969 if (clicked_routeview->track()->has_external_redirects()) {
3970 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"
3971 "Freezing will only process the signal as far as the first send/insert/return."),
3972 clicked_routeview->track()->name()), true, MESSAGE_INFO, BUTTONS_NONE, true);
3974 d.add_button (_("Freeze anyway"), Gtk::RESPONSE_OK);
3975 d.add_button (_("Don't freeze"), Gtk::RESPONSE_CANCEL);
3976 d.set_title (_("Freeze Limits"));
3978 int response = d.run ();
3981 case Gtk::RESPONSE_CANCEL:
3988 InterThreadInfo itt;
3989 current_interthread_info = &itt;
3991 InterthreadProgressWindow ipw (current_interthread_info, _("Freeze"), _("Cancel Freeze"));
3993 pthread_create_and_store (X_("freezer"), &itt.thread, _freeze_thread, this);
3995 CursorContext::Handle cursor_ctx = CursorContext::create(*this, _cursors->wait);
3997 while (!itt.done && !itt.cancel) {
3998 gtk_main_iteration ();
4001 pthread_join (itt.thread, 0);
4002 current_interthread_info = 0;
4006 Editor::bounce_range_selection (bool replace, bool enable_processing)
4008 if (selection->time.empty()) {
4012 TrackSelection views = selection->tracks;
4014 for (TrackViewList::iterator i = views.begin(); i != views.end(); ++i) {
4016 if (enable_processing) {
4018 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (*i);
4020 if (rtv && rtv->track() && replace && enable_processing && !rtv->track()->bounceable (rtv->track()->main_outs(), false)) {
4022 _("You can't perform this operation because the processing of the signal "
4023 "will cause one or more of the tracks to end up with a region with more channels than this track has inputs.\n\n"
4024 "You can do this without processing, which is a different operation.")
4026 d.set_title (_("Cannot bounce"));
4033 samplepos_t start = selection->time[clicked_selection].start;
4034 samplepos_t end = selection->time[clicked_selection].end;
4035 samplepos_t cnt = end - start + 1;
4036 bool in_command = false;
4038 for (TrackViewList::iterator i = views.begin(); i != views.end(); ++i) {
4040 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (*i);
4046 boost::shared_ptr<Playlist> playlist;
4048 if ((playlist = rtv->playlist()) == 0) {
4052 InterThreadInfo itt;
4054 playlist->clear_changes ();
4055 playlist->clear_owned_changes ();
4057 boost::shared_ptr<Region> r;
4059 if (enable_processing) {
4060 r = rtv->track()->bounce_range (start, start+cnt, itt, rtv->track()->main_outs(), false);
4062 r = rtv->track()->bounce_range (start, start+cnt, itt, boost::shared_ptr<Processor>(), false);
4070 list<AudioRange> ranges;
4071 ranges.push_back (AudioRange (start, start+cnt, 0));
4072 playlist->cut (ranges); // discard result
4073 playlist->add_region (r, start);
4077 begin_reversible_command (_("bounce range"));
4080 vector<Command*> cmds;
4081 playlist->rdiff (cmds);
4082 _session->add_commands (cmds);
4084 _session->add_command (new StatefulDiffCommand (playlist));
4088 commit_reversible_command ();
4092 /** Delete selected regions, automation points or a time range */
4096 //special case: if the user is pointing in the editor/mixer strip, they may be trying to delete a plugin.
4097 //we need this because the editor-mixer strip is in the editor window, so it doesn't get the bindings from the mix window
4098 bool deleted = false;
4099 if (current_mixer_strip && current_mixer_strip == MixerStrip::entered_mixer_strip())
4100 deleted = current_mixer_strip->delete_processors ();
4106 /** Cut selected regions, automation points or a time range */
4113 /** Copy selected regions, automation points or a time range */
4121 /** @return true if a Cut, Copy or Clear is possible */
4123 Editor::can_cut_copy () const
4125 if (!selection->time.empty() || !selection->regions.empty() || !selection->points.empty())
4132 /** Cut, copy or clear selected regions, automation points or a time range.
4133 * @param op Operation (Delete, Cut, Copy or Clear)
4136 Editor::cut_copy (CutCopyOp op)
4138 /* only cancel selection if cut/copy is successful.*/
4144 opname = _("delete");
4153 opname = _("clear");
4157 /* if we're deleting something, and the mouse is still pressed,
4158 the thing we started a drag for will be gone when we release
4159 the mouse button(s). avoid this. see part 2 at the end of
4163 if (op == Delete || op == Cut || op == Clear) {
4164 if (_drags->active ()) {
4169 if (op != Delete) { //"Delete" doesn't change copy/paste buf
4170 cut_buffer->clear ();
4173 if (entered_marker) {
4175 /* cut/delete op while pointing at a marker */
4178 Location* loc = find_location_from_marker (entered_marker, ignored);
4180 if (_session && loc) {
4181 entered_marker = NULL;
4182 Glib::signal_idle().connect (sigc::bind (sigc::mem_fun(*this, &Editor::really_remove_marker), loc));
4189 switch (mouse_mode) {
4192 begin_reversible_command (opname + ' ' + X_("MIDI"));
4194 commit_reversible_command ();
4200 bool did_edit = false;
4202 if (!selection->regions.empty() || !selection->points.empty()) {
4203 begin_reversible_command (opname + ' ' + _("objects"));
4206 if (!selection->regions.empty()) {
4207 cut_copy_regions (op, selection->regions);
4209 if (op == Cut || op == Delete) {
4210 selection->clear_regions ();
4214 if (!selection->points.empty()) {
4215 cut_copy_points (op);
4217 if (op == Cut || op == Delete) {
4218 selection->clear_points ();
4221 } else if (selection->time.empty()) {
4222 samplepos_t start, end;
4223 /* no time selection, see if we can get an edit range
4226 if (get_edit_op_range (start, end)) {
4227 selection->set (start, end);
4229 } else if (!selection->time.empty()) {
4230 begin_reversible_command (opname + ' ' + _("range"));
4233 cut_copy_ranges (op);
4235 if (op == Cut || op == Delete) {
4236 selection->clear_time ();
4241 /* reset repeated paste state */
4243 last_paste_pos = -1;
4244 commit_reversible_command ();
4247 if (op == Delete || op == Cut || op == Clear) {
4253 struct AutomationRecord {
4254 AutomationRecord () : state (0) , line(NULL) {}
4255 AutomationRecord (XMLNode* s, const AutomationLine* l) : state (s) , line (l) {}
4257 XMLNode* state; ///< state before any operation
4258 const AutomationLine* line; ///< line this came from
4259 boost::shared_ptr<Evoral::ControlList> copy; ///< copied events for the cut buffer
4262 struct PointsSelectionPositionSorter {
4263 bool operator() (ControlPoint* a, ControlPoint* b) {
4264 return (*(a->model()))->when < (*(b->model()))->when;
4268 /** Cut, copy or clear selected automation points.
4269 * @param op Operation (Cut, Copy or Clear)
4272 Editor::cut_copy_points (Editing::CutCopyOp op, Temporal::Beats earliest, bool midi)
4274 if (selection->points.empty ()) {
4278 /* XXX: not ideal, as there may be more than one track involved in the point selection */
4279 _last_cut_copy_source_track = &selection->points.front()->line().trackview;
4281 /* Keep a record of the AutomationLists that we end up using in this operation */
4282 typedef std::map<boost::shared_ptr<AutomationList>, AutomationRecord> Lists;
4285 /* user could select points in any order */
4286 selection->points.sort(PointsSelectionPositionSorter ());
4288 /* Go through all selected points, making an AutomationRecord for each distinct AutomationList */
4289 for (PointSelection::iterator sel_point = selection->points.begin(); sel_point != selection->points.end(); ++sel_point) {
4290 const AutomationLine& line = (*sel_point)->line();
4291 const boost::shared_ptr<AutomationList> al = line.the_list();
4292 if (lists.find (al) == lists.end ()) {
4293 /* We haven't seen this list yet, so make a record for it. This includes
4294 taking a copy of its current state, in case this is needed for undo later.
4296 lists[al] = AutomationRecord (&al->get_state (), &line);
4300 if (op == Cut || op == Copy) {
4301 /* This operation will involve putting things in the cut buffer, so create an empty
4302 ControlList for each of our source lists to put the cut buffer data in.
4304 for (Lists::iterator i = lists.begin(); i != lists.end(); ++i) {
4305 i->second.copy = i->first->create (i->first->parameter (), i->first->descriptor());
4308 /* Add all selected points to the relevant copy ControlLists */
4309 MusicSample start (std::numeric_limits<samplepos_t>::max(), 0);
4310 for (PointSelection::iterator sel_point = selection->points.begin(); sel_point != selection->points.end(); ++sel_point) {
4311 boost::shared_ptr<AutomationList> al = (*sel_point)->line().the_list();
4312 AutomationList::const_iterator ctrl_evt = (*sel_point)->model ();
4314 lists[al].copy->fast_simple_add ((*ctrl_evt)->when, (*ctrl_evt)->value);
4316 /* Update earliest MIDI start time in beats */
4317 earliest = std::min(earliest, Temporal::Beats((*ctrl_evt)->when));
4319 /* Update earliest session start time in samples */
4320 start.sample = std::min(start.sample, (*sel_point)->line().session_position(ctrl_evt));
4324 /* Snap start time backwards, so copy/paste is snap aligned. */
4326 if (earliest == std::numeric_limits<Temporal::Beats>::max()) {
4327 earliest = Temporal::Beats(); // Weird... don't offset
4329 earliest.round_down_to_beat();
4331 if (start.sample == std::numeric_limits<double>::max()) {
4332 start.sample = 0; // Weird... don't offset
4334 snap_to(start, RoundDownMaybe);
4337 const double line_offset = midi ? earliest.to_double() : start.sample;
4338 for (Lists::iterator i = lists.begin(); i != lists.end(); ++i) {
4339 /* Correct this copy list so that it is relative to the earliest
4340 start time, so relative ordering between points is preserved
4341 when copying from several lists and the paste starts at the
4342 earliest copied piece of data. */
4343 boost::shared_ptr<Evoral::ControlList> &al_cpy = i->second.copy;
4344 for (AutomationList::iterator ctrl_evt = al_cpy->begin(); ctrl_evt != al_cpy->end(); ++ctrl_evt) {
4345 (*ctrl_evt)->when -= line_offset;
4348 /* And add it to the cut buffer */
4349 cut_buffer->add (al_cpy);
4353 if (op == Delete || op == Cut) {
4354 /* This operation needs to remove things from the main AutomationList, so do that now */
4356 for (Lists::iterator i = lists.begin(); i != lists.end(); ++i) {
4357 i->first->freeze ();
4360 /* Remove each selected point from its AutomationList */
4361 for (PointSelection::iterator sel_point = selection->points.begin(); sel_point != selection->points.end(); ++sel_point) {
4362 AutomationLine& line = (*sel_point)->line ();
4363 boost::shared_ptr<AutomationList> al = line.the_list();
4367 if (dynamic_cast<AudioRegionGainLine*> (&line)) {
4368 /* removing of first and last gain point in region gain lines is prohibited*/
4369 if (line.is_last_point (*(*sel_point)) || line.is_first_point (*(*sel_point))) {
4375 al->erase ((*sel_point)->model ());
4379 /* Thaw the lists and add undo records for them */
4380 for (Lists::iterator i = lists.begin(); i != lists.end(); ++i) {
4381 boost::shared_ptr<AutomationList> al = i->first;
4383 _session->add_command (new MementoCommand<AutomationList> (*al.get(), i->second.state, &(al->get_state ())));
4388 /** Cut, copy or clear selected automation points.
4389 * @param op Operation (Cut, Copy or Clear)
4392 Editor::cut_copy_midi (CutCopyOp op)
4394 Temporal::Beats earliest = std::numeric_limits<Temporal::Beats>::max();
4395 for (MidiRegionSelection::iterator i = selection->midi_regions.begin(); i != selection->midi_regions.end(); ++i) {
4396 MidiRegionView* mrv = dynamic_cast<MidiRegionView*>(*i);
4398 if (!mrv->selection().empty()) {
4399 earliest = std::min(earliest, (*mrv->selection().begin())->note()->time());
4401 mrv->cut_copy_clear (op);
4403 /* XXX: not ideal, as there may be more than one track involved in the selection */
4404 _last_cut_copy_source_track = &mrv->get_time_axis_view();
4408 if (!selection->points.empty()) {
4409 cut_copy_points (op, earliest, true);
4410 if (op == Cut || op == Delete) {
4411 selection->clear_points ();
4416 struct lt_playlist {
4417 bool operator () (const PlaylistState& a, const PlaylistState& b) {
4418 return a.playlist < b.playlist;
4422 struct PlaylistMapping {
4424 boost::shared_ptr<Playlist> pl;
4426 PlaylistMapping (TimeAxisView* tvp) : tv (tvp) {}
4429 /** Remove `clicked_regionview' */
4431 Editor::remove_clicked_region ()
4433 if (clicked_routeview == 0 || clicked_regionview == 0) {
4437 begin_reversible_command (_("remove region"));
4439 boost::shared_ptr<Playlist> playlist = clicked_routeview->playlist();
4441 playlist->clear_changes ();
4442 playlist->clear_owned_changes ();
4443 playlist->remove_region (clicked_regionview->region());
4444 if (Config->get_edit_mode() == Ripple)
4445 playlist->ripple (clicked_regionview->region()->position(), -clicked_regionview->region()->length(), boost::shared_ptr<Region>());
4447 /* We might have removed regions, which alters other regions' layering_index,
4448 so we need to do a recursive diff here.
4450 vector<Command*> cmds;
4451 playlist->rdiff (cmds);
4452 _session->add_commands (cmds);
4454 _session->add_command(new StatefulDiffCommand (playlist));
4455 commit_reversible_command ();
4459 /** Remove the selected regions */
4461 Editor::remove_selected_regions ()
4463 RegionSelection rs = get_regions_from_selection_and_entered ();
4465 if (!_session || rs.empty()) {
4469 list<boost::shared_ptr<Region> > regions_to_remove;
4471 for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
4472 // we can't just remove the region(s) in this loop because
4473 // this removes them from the RegionSelection, and they thus
4474 // disappear from underneath the iterator, and the ++i above
4475 // SEGVs in a puzzling fashion.
4477 // so, first iterate over the regions to be removed from rs and
4478 // add them to the regions_to_remove list, and then
4479 // iterate over the list to actually remove them.
4481 regions_to_remove.push_back ((*i)->region());
4484 vector<boost::shared_ptr<Playlist> > playlists;
4486 for (list<boost::shared_ptr<Region> >::iterator rl = regions_to_remove.begin(); rl != regions_to_remove.end(); ++rl) {
4488 boost::shared_ptr<Playlist> playlist = (*rl)->playlist();
4491 // is this check necessary?
4495 /* get_regions_from_selection_and_entered() guarantees that
4496 the playlists involved are unique, so there is no need
4500 playlists.push_back (playlist);
4502 playlist->clear_changes ();
4503 playlist->clear_owned_changes ();
4504 playlist->freeze ();
4505 playlist->remove_region (*rl);
4506 if (Config->get_edit_mode() == Ripple)
4507 playlist->ripple ((*rl)->position(), -(*rl)->length(), boost::shared_ptr<Region>());
4511 vector<boost::shared_ptr<Playlist> >::iterator pl;
4512 bool in_command = false;
4514 for (pl = playlists.begin(); pl != playlists.end(); ++pl) {
4517 /* We might have removed regions, which alters other regions' layering_index,
4518 so we need to do a recursive diff here.
4522 begin_reversible_command (_("remove region"));
4525 vector<Command*> cmds;
4526 (*pl)->rdiff (cmds);
4527 _session->add_commands (cmds);
4529 _session->add_command(new StatefulDiffCommand (*pl));
4533 commit_reversible_command ();
4537 /** Cut, copy or clear selected regions.
4538 * @param op Operation (Cut, Copy or Clear)
4541 Editor::cut_copy_regions (CutCopyOp op, RegionSelection& rs)
4543 /* we can't use a std::map here because the ordering is important, and we can't trivially sort
4544 a map when we want ordered access to both elements. i think.
4547 vector<PlaylistMapping> pmap;
4549 samplepos_t first_position = max_samplepos;
4551 typedef set<boost::shared_ptr<Playlist> > FreezeList;
4552 FreezeList freezelist;
4554 /* get ordering correct before we cut/copy */
4556 rs.sort_by_position_and_track ();
4558 for (RegionSelection::iterator x = rs.begin(); x != rs.end(); ++x) {
4560 first_position = min ((samplepos_t) (*x)->region()->position(), first_position);
4562 if (op == Cut || op == Clear || op == Delete) {
4563 boost::shared_ptr<Playlist> pl = (*x)->region()->playlist();
4566 FreezeList::iterator fl;
4568 // only take state if this is a new playlist.
4569 for (fl = freezelist.begin(); fl != freezelist.end(); ++fl) {
4575 if (fl == freezelist.end()) {
4576 pl->clear_changes();
4577 pl->clear_owned_changes ();
4579 freezelist.insert (pl);
4584 TimeAxisView* tv = &(*x)->get_time_axis_view();
4585 vector<PlaylistMapping>::iterator z;
4587 for (z = pmap.begin(); z != pmap.end(); ++z) {
4588 if ((*z).tv == tv) {
4593 if (z == pmap.end()) {
4594 pmap.push_back (PlaylistMapping (tv));
4598 for (RegionSelection::iterator x = rs.begin(); x != rs.end(); ) {
4600 boost::shared_ptr<Playlist> pl = (*x)->region()->playlist();
4603 /* region not yet associated with a playlist (e.g. unfinished
4610 TimeAxisView& tv = (*x)->get_time_axis_view();
4611 boost::shared_ptr<Playlist> npl;
4612 RegionSelection::iterator tmp;
4619 vector<PlaylistMapping>::iterator z;
4621 for (z = pmap.begin(); z != pmap.end(); ++z) {
4622 if ((*z).tv == &tv) {
4627 assert (z != pmap.end());
4630 npl = PlaylistFactory::create (pl->data_type(), *_session, "cutlist", true);
4638 boost::shared_ptr<Region> r = (*x)->region();
4639 boost::shared_ptr<Region> _xx;
4645 pl->remove_region (r);
4646 if (Config->get_edit_mode() == Ripple)
4647 pl->ripple (r->position(), -r->length(), boost::shared_ptr<Region>());
4651 _xx = RegionFactory::create (r);
4652 npl->add_region (_xx, r->position() - first_position);
4653 pl->remove_region (r);
4654 if (Config->get_edit_mode() == Ripple)
4655 pl->ripple (r->position(), -r->length(), boost::shared_ptr<Region>());
4659 /* copy region before adding, so we're not putting same object into two different playlists */
4660 npl->add_region (RegionFactory::create (r), r->position() - first_position);
4664 pl->remove_region (r);
4665 if (Config->get_edit_mode() == Ripple)
4666 pl->ripple (r->position(), -r->length(), boost::shared_ptr<Region>());
4675 list<boost::shared_ptr<Playlist> > foo;
4677 /* the pmap is in the same order as the tracks in which selected regions occurred */
4679 for (vector<PlaylistMapping>::iterator i = pmap.begin(); i != pmap.end(); ++i) {
4682 foo.push_back ((*i).pl);
4687 cut_buffer->set (foo);
4691 _last_cut_copy_source_track = 0;
4693 _last_cut_copy_source_track = pmap.front().tv;
4697 for (FreezeList::iterator pl = freezelist.begin(); pl != freezelist.end(); ++pl) {
4700 /* We might have removed regions, which alters other regions' layering_index,
4701 so we need to do a recursive diff here.
4703 vector<Command*> cmds;
4704 (*pl)->rdiff (cmds);
4705 _session->add_commands (cmds);
4707 _session->add_command (new StatefulDiffCommand (*pl));
4712 Editor::cut_copy_ranges (CutCopyOp op)
4714 TrackViewList ts = selection->tracks.filter_to_unique_playlists ();
4716 /* Sort the track selection now, so that it if is used, the playlists
4717 selected by the calls below to cut_copy_clear are in the order that
4718 their tracks appear in the editor. This makes things like paste
4719 of ranges work properly.
4722 sort_track_selection (ts);
4725 if (!entered_track) {
4728 ts.push_back (entered_track);
4731 for (TrackViewList::iterator i = ts.begin(); i != ts.end(); ++i) {
4732 (*i)->cut_copy_clear (*selection, op);
4737 Editor::paste (float times, bool from_context)
4739 DEBUG_TRACE (DEBUG::CutNPaste, "paste to preferred edit pos\n");
4740 MusicSample where (get_preferred_edit_position (EDIT_IGNORE_NONE, from_context), 0);
4741 paste_internal (where.sample, times, 0);
4745 Editor::mouse_paste ()
4747 MusicSample where (0, 0);
4749 if (!mouse_sample (where.sample, ignored)) {
4754 paste_internal (where.sample, 1, where.division);
4758 Editor::paste_internal (samplepos_t position, float times, const int32_t sub_num)
4760 DEBUG_TRACE (DEBUG::CutNPaste, string_compose ("apparent paste position is %1\n", position));
4762 if (cut_buffer->empty(internal_editing())) {
4766 if (position == max_samplepos) {
4767 position = get_preferred_edit_position();
4768 DEBUG_TRACE (DEBUG::CutNPaste, string_compose ("preferred edit position is %1\n", position));
4771 if (position != last_paste_pos) {
4772 /* paste in new location, reset repeated paste state */
4774 last_paste_pos = position;
4777 /* get everything in the correct order */
4780 if (!selection->tracks.empty()) {
4781 /* If there is a track selection, paste into exactly those tracks and
4782 * only those tracks. This allows the user to be explicit and override
4783 * the below "do the reasonable thing" logic. */
4784 ts = selection->tracks.filter_to_unique_playlists ();
4785 sort_track_selection (ts);
4787 /* Figure out which track to base the paste at. */
4788 TimeAxisView* base_track = NULL;
4789 if (_edit_point == Editing::EditAtMouse && entered_track) {
4790 /* With the mouse edit point, paste onto the track under the mouse. */
4791 base_track = entered_track;
4792 } else if (_edit_point == Editing::EditAtMouse && entered_regionview) {
4793 /* With the mouse edit point, paste onto the track of the region under the mouse. */
4794 base_track = &entered_regionview->get_time_axis_view();
4795 } else if (_last_cut_copy_source_track) {
4796 /* Paste to the track that the cut/copy came from (see mantis #333). */
4797 base_track = _last_cut_copy_source_track;
4799 /* This is "impossible" since we've copied... well, do nothing. */
4803 /* Walk up to parent if necessary, so base track is a route. */
4804 while (base_track->get_parent()) {
4805 base_track = base_track->get_parent();
4808 /* Add base track and all tracks below it. The paste logic will select
4809 the appropriate object types from the cut buffer in relative order. */
4810 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
4811 if ((*i)->order() >= base_track->order()) {
4816 /* Sort tracks so the nth track of type T will pick the nth object of type T. */
4817 sort_track_selection (ts);
4819 /* Add automation children of each track in order, for pasting several lines. */
4820 for (TrackViewList::iterator i = ts.begin(); i != ts.end();) {
4821 /* Add any automation children for pasting several lines */
4822 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*>(*i++);
4827 typedef RouteTimeAxisView::AutomationTracks ATracks;
4828 const ATracks& atracks = rtv->automation_tracks();
4829 for (ATracks::const_iterator a = atracks.begin(); a != atracks.end(); ++a) {
4830 i = ts.insert(i, a->second.get());
4835 /* We now have a list of trackviews starting at base_track, including
4836 automation children, in the order shown in the editor, e.g. R1,
4837 R1.A1, R1.A2, R2, R2.A1, ... */
4840 begin_reversible_command (Operations::paste);
4842 if (ts.size() == 1 && cut_buffer->lines.size() == 1 &&
4843 dynamic_cast<AutomationTimeAxisView*>(ts.front())) {
4844 /* Only one line copied, and one automation track selected. Do a
4845 "greedy" paste from one automation type to another. */
4847 PasteContext ctx(paste_count, times, ItemCounts(), true);
4848 ts.front()->paste (position, *cut_buffer, ctx, sub_num);
4852 /* Paste into tracks */
4854 PasteContext ctx(paste_count, times, ItemCounts(), false);
4855 for (TrackViewList::iterator i = ts.begin(); i != ts.end(); ++i) {
4856 (*i)->paste (position, *cut_buffer, ctx, sub_num);
4862 commit_reversible_command ();
4866 Editor::duplicate_regions (float times)
4868 RegionSelection rs (get_regions_from_selection_and_entered());
4869 duplicate_some_regions (rs, times);
4873 Editor::duplicate_some_regions (RegionSelection& regions, float times)
4875 if (regions.empty ()) {
4879 boost::shared_ptr<Playlist> playlist;
4880 std::set<boost::shared_ptr<Playlist> > playlists; // list of unique playlists affected by duplication
4881 RegionSelection sel = regions; // clear (below) may clear the argument list if its the current region selection
4882 RegionSelection foo;
4884 samplepos_t const start_sample = regions.start ();
4885 samplepos_t const end_sample = regions.end_sample ();
4886 samplecnt_t const span = end_sample - start_sample + 1;
4888 begin_reversible_command (Operations::duplicate_region);
4890 selection->clear_regions ();
4892 /* ripple first so that we don't move the duplicates that will be added */
4894 if (Config->get_edit_mode() == Ripple) {
4896 /* convert RegionSelection into RegionList so that we can pass it to ripple and exclude the regions we will duplicate */
4900 for (RegionSelection::iterator i = sel.begin(); i != sel.end(); ++i) {
4901 exclude.push_back ((*i)->region());
4902 playlist = (*i)->region()->playlist();
4903 if (playlists.insert (playlist).second) {
4904 /* successfully inserted into set, so it's the first time we've seen this playlist */
4905 playlist->clear_changes ();
4909 for (set<boost::shared_ptr<Playlist> >::iterator p = playlists.begin(); p != playlists.end(); ++p) {
4910 (*p)->ripple (start_sample, span * times, &exclude);
4914 for (RegionSelection::iterator i = sel.begin(); i != sel.end(); ++i) {
4916 boost::shared_ptr<Region> r ((*i)->region());
4918 TimeAxisView& tv = (*i)->get_time_axis_view();
4919 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (&tv);
4920 latest_regionviews.clear ();
4921 sigc::connection c = rtv->view()->RegionViewAdded.connect (sigc::mem_fun(*this, &Editor::collect_new_region_view));
4923 samplepos_t const position = end_sample + (r->first_sample() - start_sample + 1);
4924 playlist = (*i)->region()->playlist();
4926 if (Config->get_edit_mode() != Ripple) {
4927 if (playlists.insert (playlist).second) {
4928 playlist->clear_changes ();
4932 playlist->duplicate (r, position, span, times);
4936 foo.insert (foo.end(), latest_regionviews.begin(), latest_regionviews.end());
4939 for (set<boost::shared_ptr<Playlist> >::iterator p = playlists.begin(); p != playlists.end(); ++p) {
4940 _session->add_command (new StatefulDiffCommand (*p));
4941 vector<Command*> cmds;
4943 _session->add_commands (cmds);
4947 selection->set (foo);
4950 commit_reversible_command ();
4954 Editor::duplicate_selection (float times)
4956 if (selection->time.empty() || selection->tracks.empty()) {
4960 boost::shared_ptr<Playlist> playlist;
4962 TrackViewList ts = selection->tracks.filter_to_unique_playlists ();
4964 bool in_command = false;
4966 for (TrackViewList::iterator i = ts.begin(); i != ts.end(); ++i) {
4967 if ((playlist = (*i)->playlist()) == 0) {
4970 playlist->clear_changes ();
4972 if (clicked_selection) {
4973 playlist->duplicate_range (selection->time[clicked_selection], times);
4975 playlist->duplicate_ranges (selection->time, times);
4979 begin_reversible_command (_("duplicate range selection"));
4982 _session->add_command (new StatefulDiffCommand (playlist));
4987 if (times == 1.0f) {
4988 // now "move" range selection to after the current range selection
4989 samplecnt_t distance = 0;
4991 if (clicked_selection) {
4993 selection->time[clicked_selection].end - selection->time[clicked_selection].start;
4995 distance = selection->time.end_sample () - selection->time.start ();
4998 selection->move_time (distance);
5000 commit_reversible_command ();
5004 /** Reset all selected points to the relevant default value */
5006 Editor::reset_point_selection ()
5008 for (PointSelection::iterator i = selection->points.begin(); i != selection->points.end(); ++i) {
5009 ARDOUR::AutomationList::iterator j = (*i)->model ();
5010 (*j)->value = (*i)->line().the_list()->descriptor ().normal;
5015 Editor::center_playhead ()
5017 float const page = _visible_canvas_width * samples_per_pixel;
5018 center_screen_internal (playhead_cursor->current_sample (), page);
5022 Editor::center_edit_point ()
5024 float const page = _visible_canvas_width * samples_per_pixel;
5025 center_screen_internal (get_preferred_edit_position(), page);
5028 /** Caller must begin and commit a reversible command */
5030 Editor::clear_playlist (boost::shared_ptr<Playlist> playlist)
5032 playlist->clear_changes ();
5034 _session->add_command (new StatefulDiffCommand (playlist));
5038 Editor::nudge_track (bool use_edit, bool forwards)
5040 boost::shared_ptr<Playlist> playlist;
5041 samplepos_t distance;
5042 samplepos_t next_distance;
5046 start = get_preferred_edit_position();
5051 if ((distance = get_nudge_distance (start, next_distance)) == 0) {
5055 if (selection->tracks.empty()) {
5059 TrackViewList ts = selection->tracks.filter_to_unique_playlists ();
5060 bool in_command = false;
5062 for (TrackViewList::iterator i = ts.begin(); i != ts.end(); ++i) {
5064 if ((playlist = (*i)->playlist()) == 0) {
5068 playlist->clear_changes ();
5069 playlist->clear_owned_changes ();
5071 playlist->nudge_after (start, distance, forwards);
5074 begin_reversible_command (_("nudge track"));
5077 vector<Command*> cmds;
5079 playlist->rdiff (cmds);
5080 _session->add_commands (cmds);
5082 _session->add_command (new StatefulDiffCommand (playlist));
5086 commit_reversible_command ();
5091 Editor::remove_last_capture ()
5093 vector<string> choices;
5100 if (Config->get_verify_remove_last_capture()) {
5101 prompt = _("Do you really want to destroy the last capture?"
5102 "\n(This is destructive and cannot be undone)");
5104 choices.push_back (_("No, do nothing."));
5105 choices.push_back (_("Yes, destroy it."));
5107 Choice prompter (_("Destroy last capture"), prompt, choices);
5109 if (prompter.run () == 1) {
5110 _session->remove_last_capture ();
5111 _regions->redisplay ();
5115 _session->remove_last_capture();
5116 _regions->redisplay ();
5121 Editor::normalize_region ()
5127 RegionSelection rs = get_regions_from_selection_and_entered ();
5133 NormalizeDialog dialog (rs.size() > 1);
5135 if (dialog.run () != RESPONSE_ACCEPT) {
5139 CursorContext::Handle cursor_ctx = CursorContext::create(*this, _cursors->wait);
5142 /* XXX: should really only count audio regions here */
5143 int const regions = rs.size ();
5145 /* Make a list of the selected audio regions' maximum amplitudes, and also
5146 obtain the maximum amplitude of them all.
5148 list<double> max_amps;
5149 list<double> rms_vals;
5152 bool use_rms = dialog.constrain_rms ();
5154 for (RegionSelection::const_iterator i = rs.begin(); i != rs.end(); ++i) {
5155 AudioRegionView const * arv = dynamic_cast<AudioRegionView const *> (*i);
5159 dialog.descend (1.0 / regions);
5160 double const a = arv->audio_region()->maximum_amplitude (&dialog);
5162 double r = arv->audio_region()->rms (&dialog);
5163 max_rms = max (max_rms, r);
5164 rms_vals.push_back (r);
5168 /* the user cancelled the operation */
5172 max_amps.push_back (a);
5173 max_amp = max (max_amp, a);
5177 list<double>::const_iterator a = max_amps.begin ();
5178 list<double>::const_iterator l = rms_vals.begin ();
5179 bool in_command = false;
5181 for (RegionSelection::iterator r = rs.begin(); r != rs.end(); ++r) {
5182 AudioRegionView* const arv = dynamic_cast<AudioRegionView*> (*r);
5187 arv->region()->clear_changes ();
5189 double amp = dialog.normalize_individually() ? *a : max_amp;
5190 double target = dialog.target_peak (); // dB
5193 double const amp_rms = dialog.normalize_individually() ? *l : max_rms;
5194 const double t_rms = dialog.target_rms ();
5195 const gain_t c_peak = dB_to_coefficient (target);
5196 const gain_t c_rms = dB_to_coefficient (t_rms);
5197 if ((amp_rms / c_rms) > (amp / c_peak)) {
5203 arv->audio_region()->normalize (amp, target);
5206 begin_reversible_command (_("normalize"));
5209 _session->add_command (new StatefulDiffCommand (arv->region()));
5216 commit_reversible_command ();
5222 Editor::reset_region_scale_amplitude ()
5228 RegionSelection rs = get_regions_from_selection_and_entered ();
5234 bool in_command = false;
5236 for (RegionSelection::iterator r = rs.begin(); r != rs.end(); ++r) {
5237 AudioRegionView* const arv = dynamic_cast<AudioRegionView*>(*r);
5240 arv->region()->clear_changes ();
5241 arv->audio_region()->set_scale_amplitude (1.0f);
5244 begin_reversible_command ("reset gain");
5247 _session->add_command (new StatefulDiffCommand (arv->region()));
5251 commit_reversible_command ();
5256 Editor::adjust_region_gain (bool up)
5258 RegionSelection rs = get_regions_from_selection_and_entered ();
5260 if (!_session || rs.empty()) {
5264 bool in_command = false;
5266 for (RegionSelection::iterator r = rs.begin(); r != rs.end(); ++r) {
5267 AudioRegionView* const arv = dynamic_cast<AudioRegionView*>(*r);
5272 arv->region()->clear_changes ();
5274 double dB = accurate_coefficient_to_dB (arv->audio_region()->scale_amplitude ());
5282 arv->audio_region()->set_scale_amplitude (dB_to_coefficient (dB));
5285 begin_reversible_command ("adjust region gain");
5288 _session->add_command (new StatefulDiffCommand (arv->region()));
5292 commit_reversible_command ();
5297 Editor::reset_region_gain ()
5299 RegionSelection rs = get_regions_from_selection_and_entered ();
5301 if (!_session || rs.empty()) {
5305 bool in_command = false;
5307 for (RegionSelection::iterator r = rs.begin(); r != rs.end(); ++r) {
5308 AudioRegionView* const arv = dynamic_cast<AudioRegionView*>(*r);
5313 arv->region()->clear_changes ();
5315 arv->audio_region()->set_scale_amplitude (1.0f);
5318 begin_reversible_command ("reset region gain");
5321 _session->add_command (new StatefulDiffCommand (arv->region()));
5325 commit_reversible_command ();
5330 Editor::reverse_region ()
5336 Reverse rev (*_session);
5337 apply_filter (rev, _("reverse regions"));
5341 Editor::strip_region_silence ()
5347 RegionSelection rs = get_regions_from_selection_and_entered ();
5353 std::list<RegionView*> audio_only;
5355 for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
5356 AudioRegionView* const arv = dynamic_cast<AudioRegionView*> (*i);
5358 audio_only.push_back (arv);
5362 assert (!audio_only.empty());
5364 StripSilenceDialog d (_session, audio_only);
5365 int const r = d.run ();
5369 if (r == Gtk::RESPONSE_OK) {
5370 ARDOUR::AudioIntervalMap silences;
5371 d.silences (silences);
5372 StripSilence s (*_session, silences, d.fade_length());
5374 apply_filter (s, _("strip silence"), &d);
5379 Editor::apply_midi_note_edit_op_to_region (MidiOperator& op, MidiRegionView& mrv)
5381 Evoral::Sequence<Temporal::Beats>::Notes selected;
5382 mrv.selection_as_notelist (selected, true);
5384 vector<Evoral::Sequence<Temporal::Beats>::Notes> v;
5385 v.push_back (selected);
5387 Temporal::Beats pos_beats = Temporal::Beats (mrv.midi_region()->beat()) - mrv.midi_region()->start_beats();
5389 return op (mrv.midi_region()->model(), pos_beats, v);
5393 Editor::apply_midi_note_edit_op (MidiOperator& op, const RegionSelection& rs)
5399 bool in_command = false;
5401 for (RegionSelection::const_iterator r = rs.begin(); r != rs.end(); ) {
5402 RegionSelection::const_iterator tmp = r;
5405 MidiRegionView* const mrv = dynamic_cast<MidiRegionView*> (*r);
5408 Command* cmd = apply_midi_note_edit_op_to_region (op, *mrv);
5411 begin_reversible_command (op.name ());
5415 _session->add_command (cmd);
5423 commit_reversible_command ();
5424 _session->set_dirty ();
5429 Editor::fork_region ()
5431 RegionSelection rs = get_regions_from_selection_and_entered ();
5437 CursorContext::Handle cursor_ctx = CursorContext::create(*this, _cursors->wait);
5438 bool in_command = false;
5442 for (RegionSelection::iterator r = rs.begin(); r != rs.end(); ) {
5443 RegionSelection::iterator tmp = r;
5446 MidiRegionView* const mrv = dynamic_cast<MidiRegionView*>(*r);
5450 boost::shared_ptr<Playlist> playlist = mrv->region()->playlist();
5451 boost::shared_ptr<MidiSource> new_source = _session->create_midi_source_by_stealing_name (mrv->midi_view()->track());
5452 boost::shared_ptr<MidiRegion> newregion = mrv->midi_region()->clone (new_source);
5455 begin_reversible_command (_("Fork Region(s)"));
5458 playlist->clear_changes ();
5459 playlist->replace_region (mrv->region(), newregion, mrv->region()->position());
5460 _session->add_command(new StatefulDiffCommand (playlist));
5462 error << string_compose (_("Could not unlink %1"), mrv->region()->name()) << endmsg;
5470 commit_reversible_command ();
5475 Editor::quantize_region ()
5478 quantize_regions(get_regions_from_selection_and_entered ());
5483 Editor::quantize_regions (const RegionSelection& rs)
5485 if (rs.n_midi_regions() == 0) {
5489 if (!quantize_dialog) {
5490 quantize_dialog = new QuantizeDialog (*this);
5493 if (quantize_dialog->is_mapped()) {
5494 /* in progress already */
5498 quantize_dialog->present ();
5499 const int r = quantize_dialog->run ();
5500 quantize_dialog->hide ();
5502 if (r == Gtk::RESPONSE_OK) {
5503 Quantize quant (quantize_dialog->snap_start(),
5504 quantize_dialog->snap_end(),
5505 quantize_dialog->start_grid_size(),
5506 quantize_dialog->end_grid_size(),
5507 quantize_dialog->strength(),
5508 quantize_dialog->swing(),
5509 quantize_dialog->threshold());
5511 apply_midi_note_edit_op (quant, rs);
5516 Editor::legatize_region (bool shrink_only)
5519 legatize_regions(get_regions_from_selection_and_entered (), shrink_only);
5524 Editor::legatize_regions (const RegionSelection& rs, bool shrink_only)
5526 if (rs.n_midi_regions() == 0) {
5530 Legatize legatize(shrink_only);
5531 apply_midi_note_edit_op (legatize, rs);
5535 Editor::transform_region ()
5538 transform_regions(get_regions_from_selection_and_entered ());
5543 Editor::transform_regions (const RegionSelection& rs)
5545 if (rs.n_midi_regions() == 0) {
5552 const int r = td.run();
5555 if (r == Gtk::RESPONSE_OK) {
5556 Transform transform(td.get());
5557 apply_midi_note_edit_op(transform, rs);
5562 Editor::transpose_region ()
5565 transpose_regions(get_regions_from_selection_and_entered ());
5570 Editor::transpose_regions (const RegionSelection& rs)
5572 if (rs.n_midi_regions() == 0) {
5577 int const r = d.run ();
5579 if (r == RESPONSE_ACCEPT) {
5580 Transpose transpose(d.semitones ());
5581 apply_midi_note_edit_op (transpose, rs);
5586 Editor::insert_patch_change (bool from_context)
5588 RegionSelection rs = get_regions_from_selection_and_entered ();
5594 const samplepos_t p = get_preferred_edit_position (EDIT_IGNORE_NONE, from_context);
5596 /* XXX: bit of a hack; use the MIDNAM from the first selected region;
5597 there may be more than one, but the PatchChangeDialog can only offer
5598 one set of patch menus.
5600 MidiRegionView* first = dynamic_cast<MidiRegionView*> (rs.front ());
5602 Evoral::PatchChange<Temporal::Beats> empty (Temporal::Beats(), 0, 0, 0);
5603 PatchChangeDialog d (0, _session, empty, first->instrument_info(), Gtk::Stock::ADD);
5605 if (d.run() == RESPONSE_CANCEL) {
5609 for (RegionSelection::iterator i = rs.begin (); i != rs.end(); ++i) {
5610 MidiRegionView* const mrv = dynamic_cast<MidiRegionView*> (*i);
5612 if (p >= mrv->region()->first_sample() && p <= mrv->region()->last_sample()) {
5613 mrv->add_patch_change (p - mrv->region()->position(), d.patch ());
5620 Editor::apply_filter (Filter& filter, string command, ProgressReporter* progress)
5622 RegionSelection rs = get_regions_from_selection_and_entered ();
5628 CursorContext::Handle cursor_ctx = CursorContext::create(*this, _cursors->wait);
5629 bool in_command = false;
5634 int const N = rs.size ();
5636 for (RegionSelection::iterator r = rs.begin(); r != rs.end(); ) {
5637 RegionSelection::iterator tmp = r;
5640 AudioRegionView* const arv = dynamic_cast<AudioRegionView*>(*r);
5642 boost::shared_ptr<Playlist> playlist = arv->region()->playlist();
5645 progress->descend (1.0 / N);
5648 if (arv->audio_region()->apply (filter, progress) == 0) {
5650 playlist->clear_changes ();
5651 playlist->clear_owned_changes ();
5654 begin_reversible_command (command);
5658 if (filter.results.empty ()) {
5660 /* no regions returned; remove the old one */
5661 playlist->remove_region (arv->region ());
5665 std::vector<boost::shared_ptr<Region> >::iterator res = filter.results.begin ();
5667 /* first region replaces the old one */
5668 playlist->replace_region (arv->region(), *res, (*res)->position());
5672 while (res != filter.results.end()) {
5673 playlist->add_region (*res, (*res)->position());
5679 /* We might have removed regions, which alters other regions' layering_index,
5680 so we need to do a recursive diff here.
5682 vector<Command*> cmds;
5683 playlist->rdiff (cmds);
5684 _session->add_commands (cmds);
5686 _session->add_command(new StatefulDiffCommand (playlist));
5690 progress->ascend ();
5699 commit_reversible_command ();
5704 Editor::external_edit_region ()
5710 Editor::reset_region_gain_envelopes ()
5712 RegionSelection rs = get_regions_from_selection_and_entered ();
5714 if (!_session || rs.empty()) {
5718 bool in_command = false;
5720 for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
5721 AudioRegionView* const arv = dynamic_cast<AudioRegionView*>(*i);
5723 boost::shared_ptr<AutomationList> alist (arv->audio_region()->envelope());
5724 XMLNode& before (alist->get_state());
5726 arv->audio_region()->set_default_envelope ();
5729 begin_reversible_command (_("reset region gain"));
5732 _session->add_command (new MementoCommand<AutomationList>(*arv->audio_region()->envelope().get(), &before, &alist->get_state()));
5737 commit_reversible_command ();
5742 Editor::set_region_gain_visibility (RegionView* rv)
5744 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (rv);
5746 arv->update_envelope_visibility();
5751 Editor::set_gain_envelope_visibility ()
5757 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
5758 AudioTimeAxisView* v = dynamic_cast<AudioTimeAxisView*>(*i);
5760 v->audio_view()->foreach_regionview (sigc::mem_fun (this, &Editor::set_region_gain_visibility));
5766 Editor::toggle_gain_envelope_active ()
5768 if (_ignore_region_action) {
5772 RegionSelection rs = get_regions_from_selection_and_entered ();
5774 if (!_session || rs.empty()) {
5778 bool in_command = false;
5780 for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
5781 AudioRegionView* const arv = dynamic_cast<AudioRegionView*>(*i);
5783 arv->region()->clear_changes ();
5784 arv->audio_region()->set_envelope_active (!arv->audio_region()->envelope_active());
5787 begin_reversible_command (_("region gain envelope active"));
5790 _session->add_command (new StatefulDiffCommand (arv->region()));
5795 commit_reversible_command ();
5800 Editor::toggle_region_lock ()
5802 if (_ignore_region_action) {
5806 RegionSelection rs = get_regions_from_selection_and_entered ();
5808 if (!_session || rs.empty()) {
5812 begin_reversible_command (_("toggle region lock"));
5814 for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
5815 (*i)->region()->clear_changes ();
5816 (*i)->region()->set_locked (!(*i)->region()->locked());
5817 _session->add_command (new StatefulDiffCommand ((*i)->region()));
5820 commit_reversible_command ();
5824 Editor::toggle_region_video_lock ()
5826 if (_ignore_region_action) {
5830 RegionSelection rs = get_regions_from_selection_and_entered ();
5832 if (!_session || rs.empty()) {
5836 begin_reversible_command (_("Toggle Video Lock"));
5838 for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
5839 (*i)->region()->clear_changes ();
5840 (*i)->region()->set_video_locked (!(*i)->region()->video_locked());
5841 _session->add_command (new StatefulDiffCommand ((*i)->region()));
5844 commit_reversible_command ();
5848 Editor::toggle_region_lock_style ()
5850 if (_ignore_region_action) {
5854 RegionSelection rs = get_regions_from_selection_and_entered ();
5856 if (!_session || rs.empty()) {
5860 Glib::RefPtr<ToggleAction> a = Glib::RefPtr<ToggleAction>::cast_dynamic (_region_actions->get_action("toggle-region-lock-style"));
5861 vector<Widget*> proxies = a->get_proxies();
5862 Gtk::CheckMenuItem* cmi = dynamic_cast<Gtk::CheckMenuItem*> (proxies.front());
5866 begin_reversible_command (_("toggle region lock style"));
5868 for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
5869 (*i)->region()->clear_changes ();
5870 PositionLockStyle const ns = ((*i)->region()->position_lock_style() == AudioTime && !cmi->get_inconsistent()) ? MusicTime : AudioTime;
5871 (*i)->region()->set_position_lock_style (ns);
5872 _session->add_command (new StatefulDiffCommand ((*i)->region()));
5875 commit_reversible_command ();
5879 Editor::toggle_opaque_region ()
5881 if (_ignore_region_action) {
5885 RegionSelection rs = get_regions_from_selection_and_entered ();
5887 if (!_session || rs.empty()) {
5891 begin_reversible_command (_("change region opacity"));
5893 for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
5894 (*i)->region()->clear_changes ();
5895 (*i)->region()->set_opaque (!(*i)->region()->opaque());
5896 _session->add_command (new StatefulDiffCommand ((*i)->region()));
5899 commit_reversible_command ();
5903 Editor::toggle_record_enable ()
5905 bool new_state = false;
5907 for (TrackSelection::iterator i = selection->tracks.begin(); i != selection->tracks.end(); ++i) {
5908 RouteTimeAxisView *rtav = dynamic_cast<RouteTimeAxisView *>(*i);
5911 if (!rtav->is_track())
5915 new_state = !rtav->track()->rec_enable_control()->get_value();
5919 rtav->track()->rec_enable_control()->set_value (new_state, Controllable::UseGroup);
5924 tracklist_to_stripables (TrackViewList list)
5928 for (TrackSelection::iterator i = list.begin(); i != list.end(); ++i) {
5929 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> ((*i));
5931 if (rtv && rtv->is_track()) {
5932 ret.push_back (rtv->track());
5940 Editor::play_solo_selection (bool restart)
5942 //note: session::solo_selection takes care of invalidating the region playlist
5944 if ((!selection->tracks.empty()) && selection->time.length() > 0) { //a range is selected; solo the tracks and roll
5946 StripableList sl = tracklist_to_stripables (selection->tracks);
5947 _session->solo_selection (sl, true);
5950 samplepos_t start = selection->time.start();
5951 samplepos_t end = selection->time.end_sample();
5952 _session->request_bounded_roll (start, end);
5954 } else if (! selection->tracks.empty()) { //no range is selected, but tracks are selected; solo the tracks and roll
5955 StripableList sl = tracklist_to_stripables (selection->tracks);
5956 _session->solo_selection (sl, true);
5957 _session->request_cancel_play_range();
5958 transition_to_rolling (true);
5960 } else if (! selection->regions.empty()) { //solo any tracks with selected regions, and roll
5961 StripableList sl = tracklist_to_stripables (get_tracks_for_range_action());
5962 _session->solo_selection (sl, true);
5963 _session->request_cancel_play_range();
5964 transition_to_rolling (true);
5966 _session->request_cancel_play_range();
5967 transition_to_rolling (true); //no selection. just roll.
5972 Editor::toggle_solo ()
5974 bool new_state = false;
5976 boost::shared_ptr<ControlList> cl (new ControlList);
5978 for (TrackSelection::iterator i = selection->tracks.begin(); i != selection->tracks.end(); ++i) {
5979 StripableTimeAxisView *stav = dynamic_cast<StripableTimeAxisView *>(*i);
5981 if (!stav || !stav->stripable()->solo_control()) {
5986 new_state = !stav->stripable()->solo_control()->soloed ();
5990 cl->push_back (stav->stripable()->solo_control());
5993 _session->set_controls (cl, new_state ? 1.0 : 0.0, Controllable::UseGroup);
5997 Editor::toggle_mute ()
5999 bool new_state = false;
6001 boost::shared_ptr<ControlList> cl (new ControlList);
6003 for (TrackSelection::iterator i = selection->tracks.begin(); i != selection->tracks.end(); ++i) {
6004 StripableTimeAxisView *stav = dynamic_cast<StripableTimeAxisView *>(*i);
6006 if (!stav || !stav->stripable()->mute_control()) {
6011 new_state = !stav->stripable()->mute_control()->muted();
6015 cl->push_back (stav->stripable()->mute_control());
6018 _session->set_controls (cl, new_state, Controllable::UseGroup);
6022 Editor::toggle_solo_isolate ()
6028 Editor::fade_range ()
6030 TrackViewList ts = selection->tracks.filter_to_unique_playlists ();
6032 begin_reversible_command (_("fade range"));
6034 for (TrackViewList::iterator i = ts.begin(); i != ts.end(); ++i) {
6035 (*i)->fade_range (selection->time);
6038 commit_reversible_command ();
6043 Editor::set_fade_length (bool in)
6045 RegionSelection rs = get_regions_from_selection_and_entered ();
6051 /* we need a region to measure the offset from the start */
6053 RegionView* rv = rs.front ();
6055 samplepos_t pos = get_preferred_edit_position();
6059 if (pos > rv->region()->last_sample() || pos < rv->region()->first_sample()) {
6060 /* edit point is outside the relevant region */
6065 if (pos <= rv->region()->position()) {
6069 len = pos - rv->region()->position();
6070 cmd = _("set fade in length");
6072 if (pos >= rv->region()->last_sample()) {
6076 len = rv->region()->last_sample() - pos;
6077 cmd = _("set fade out length");
6080 bool in_command = false;
6082 for (RegionSelection::iterator x = rs.begin(); x != rs.end(); ++x) {
6083 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (*x);
6089 boost::shared_ptr<AutomationList> alist;
6091 alist = tmp->audio_region()->fade_in();
6093 alist = tmp->audio_region()->fade_out();
6096 XMLNode &before = alist->get_state();
6099 tmp->audio_region()->set_fade_in_length (len);
6100 tmp->audio_region()->set_fade_in_active (true);
6102 tmp->audio_region()->set_fade_out_length (len);
6103 tmp->audio_region()->set_fade_out_active (true);
6107 begin_reversible_command (cmd);
6110 XMLNode &after = alist->get_state();
6111 _session->add_command(new MementoCommand<AutomationList>(*alist, &before, &after));
6115 commit_reversible_command ();
6120 Editor::set_fade_in_shape (FadeShape shape)
6122 RegionSelection rs = get_regions_from_selection_and_entered ();
6127 bool in_command = false;
6129 for (RegionSelection::iterator x = rs.begin(); x != rs.end(); ++x) {
6130 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (*x);
6136 boost::shared_ptr<AutomationList> alist = tmp->audio_region()->fade_in();
6137 XMLNode &before = alist->get_state();
6139 tmp->audio_region()->set_fade_in_shape (shape);
6142 begin_reversible_command (_("set fade in shape"));
6145 XMLNode &after = alist->get_state();
6146 _session->add_command(new MementoCommand<AutomationList>(*alist.get(), &before, &after));
6150 commit_reversible_command ();
6155 Editor::set_fade_out_shape (FadeShape shape)
6157 RegionSelection rs = get_regions_from_selection_and_entered ();
6162 bool in_command = false;
6164 for (RegionSelection::iterator x = rs.begin(); x != rs.end(); ++x) {
6165 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (*x);
6171 boost::shared_ptr<AutomationList> alist = tmp->audio_region()->fade_out();
6172 XMLNode &before = alist->get_state();
6174 tmp->audio_region()->set_fade_out_shape (shape);
6177 begin_reversible_command (_("set fade out shape"));
6180 XMLNode &after = alist->get_state();
6181 _session->add_command(new MementoCommand<AutomationList>(*alist.get(), &before, &after));
6185 commit_reversible_command ();
6190 Editor::set_fade_in_active (bool yn)
6192 RegionSelection rs = get_regions_from_selection_and_entered ();
6197 bool in_command = false;
6199 for (RegionSelection::iterator x = rs.begin(); x != rs.end(); ++x) {
6200 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (*x);
6207 boost::shared_ptr<AudioRegion> ar (tmp->audio_region());
6209 ar->clear_changes ();
6210 ar->set_fade_in_active (yn);
6213 begin_reversible_command (_("set fade in active"));
6216 _session->add_command (new StatefulDiffCommand (ar));
6220 commit_reversible_command ();
6225 Editor::set_fade_out_active (bool yn)
6227 RegionSelection rs = get_regions_from_selection_and_entered ();
6232 bool in_command = false;
6234 for (RegionSelection::iterator x = rs.begin(); x != rs.end(); ++x) {
6235 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (*x);
6241 boost::shared_ptr<AudioRegion> ar (tmp->audio_region());
6243 ar->clear_changes ();
6244 ar->set_fade_out_active (yn);
6247 begin_reversible_command (_("set fade out active"));
6250 _session->add_command(new StatefulDiffCommand (ar));
6254 commit_reversible_command ();
6259 Editor::toggle_region_fades (int dir)
6261 if (_ignore_region_action) {
6265 boost::shared_ptr<AudioRegion> ar;
6268 RegionSelection rs = get_regions_from_selection_and_entered ();
6274 RegionSelection::iterator i;
6275 for (i = rs.begin(); i != rs.end(); ++i) {
6276 if ((ar = boost::dynamic_pointer_cast<AudioRegion>((*i)->region())) != 0) {
6278 yn = ar->fade_out_active ();
6280 yn = ar->fade_in_active ();
6286 if (i == rs.end()) {
6290 /* XXX should this undo-able? */
6291 bool in_command = false;
6293 for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
6294 if ((ar = boost::dynamic_pointer_cast<AudioRegion>((*i)->region())) == 0) {
6297 ar->clear_changes ();
6299 if (dir == 1 || dir == 0) {
6300 ar->set_fade_in_active (!yn);
6303 if (dir == -1 || dir == 0) {
6304 ar->set_fade_out_active (!yn);
6307 begin_reversible_command (_("toggle fade active"));
6310 _session->add_command(new StatefulDiffCommand (ar));
6314 commit_reversible_command ();
6319 /** Update region fade visibility after its configuration has been changed */
6321 Editor::update_region_fade_visibility ()
6323 bool _fade_visibility = _session->config.get_show_region_fades ();
6325 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
6326 AudioTimeAxisView* v = dynamic_cast<AudioTimeAxisView*>(*i);
6328 if (_fade_visibility) {
6329 v->audio_view()->show_all_fades ();
6331 v->audio_view()->hide_all_fades ();
6338 Editor::set_edit_point ()
6341 MusicSample where (0, 0);
6343 if (!mouse_sample (where.sample, ignored)) {
6349 if (selection->markers.empty()) {
6351 mouse_add_new_marker (where.sample);
6356 Location* loc = find_location_from_marker (selection->markers.front(), ignored);
6359 loc->move_to (where.sample, where.division);
6365 Editor::set_playhead_cursor ()
6367 if (entered_marker) {
6368 _session->request_locate (entered_marker->position(), _session->transport_rolling());
6370 MusicSample where (0, 0);
6373 if (!mouse_sample (where.sample, ignored)) {
6380 _session->request_locate (where.sample, _session->transport_rolling());
6384 //not sure what this was for; remove it for now.
6385 // if (UIConfiguration::instance().get_follow_edits() && (!_session || !_session->config.get_external_sync())) {
6386 // cancel_time_selection();
6392 Editor::split_region ()
6394 if (_dragging_playhead) {
6396 } else if (_drags->active ()) {
6397 /*any other kind of drag, bail out so we avoid Undo snafu*/
6401 //if a range is selected, separate it
6402 if (!selection->time.empty()) {
6403 separate_regions_between (selection->time);
6407 //if no range was selected, try to find some regions to split
6408 if (current_mouse_mode() == MouseObject || current_mouse_mode() == MouseRange ) { //don't try this for Internal Edit, Stretch, Draw, etc.
6410 RegionSelection rs = get_regions_from_selection_and_edit_point ();
6411 const samplepos_t pos = get_preferred_edit_position();
6412 const int32_t division = get_grid_music_divisions (0);
6413 MusicSample where (pos, division);
6419 split_regions_at (where, rs);
6425 Editor::select_next_stripable (bool routes_only)
6427 _session->selection().select_next_stripable (false, routes_only);
6431 Editor::select_prev_stripable (bool routes_only)
6433 _session->selection().select_prev_stripable (false, routes_only);
6437 Editor::set_loop_from_selection (bool play)
6439 if (_session == 0) {
6443 samplepos_t start, end;
6444 if (!get_selection_extents (start, end))
6447 set_loop_range (start, end, _("set loop range from selection"));
6450 _session->request_play_loop (true, true);
6455 Editor::set_loop_from_region (bool play)
6457 samplepos_t start, end;
6458 if (!get_selection_extents (start, end))
6461 set_loop_range (start, end, _("set loop range from region"));
6464 _session->request_locate (start, true);
6465 _session->request_play_loop (true);
6470 Editor::set_punch_from_selection ()
6472 if (_session == 0) {
6476 samplepos_t start, end;
6477 if (!get_selection_extents (start, end))
6480 set_punch_range (start, end, _("set punch range from selection"));
6484 Editor::set_auto_punch_range ()
6486 // auto punch in/out button from a single button
6487 // If Punch In is unset, set punch range from playhead to end, enable punch in
6488 // If Punch In is set, the next punch sets Punch Out, unless the playhead has been
6489 // rewound beyond the Punch In marker, in which case that marker will be moved back
6490 // to the current playhead position.
6491 // If punch out is set, it clears the punch range and Punch In/Out buttons
6493 if (_session == 0) {
6497 Location* tpl = transport_punch_location();
6498 samplepos_t now = playhead_cursor->current_sample();
6499 samplepos_t begin = now;
6500 samplepos_t end = _session->current_end_sample();
6502 if (!_session->config.get_punch_in()) {
6503 // First Press - set punch in and create range from here to eternity
6504 set_punch_range (begin, end, _("Auto Punch In"));
6505 _session->config.set_punch_in(true);
6506 } else if (tpl && !_session->config.get_punch_out()) {
6507 // Second press - update end range marker and set punch_out
6508 if (now < tpl->start()) {
6509 // playhead has been rewound - move start back and pretend nothing happened
6511 set_punch_range (begin, end, _("Auto Punch In/Out"));
6513 // normal case for 2nd press - set the punch out
6514 end = playhead_cursor->current_sample ();
6515 set_punch_range (tpl->start(), now, _("Auto Punch In/Out"));
6516 _session->config.set_punch_out(true);
6519 if (_session->config.get_punch_out()) {
6520 _session->config.set_punch_out(false);
6523 if (_session->config.get_punch_in()) {
6524 _session->config.set_punch_in(false);
6529 // third press - unset punch in/out and remove range
6530 _session->locations()->remove(tpl);
6537 Editor::set_session_extents_from_selection ()
6539 if (_session == 0) {
6543 samplepos_t start, end;
6544 if (!get_selection_extents (start, end))
6548 if ((loc = _session->locations()->session_range_location()) == 0) {
6549 _session->set_session_extents (start, end); // this will create a new session range; no need for UNDO
6551 XMLNode &before = loc->get_state();
6553 _session->set_session_extents (start, end);
6555 XMLNode &after = loc->get_state();
6557 begin_reversible_command (_("set session start/end from selection"));
6559 _session->add_command (new MementoCommand<Location>(*loc, &before, &after));
6561 commit_reversible_command ();
6564 _session->set_end_is_free (false);
6568 Editor::set_punch_start_from_edit_point ()
6572 MusicSample start (0, 0);
6573 samplepos_t end = max_samplepos;
6575 //use the existing punch end, if any
6576 Location* tpl = transport_punch_location();
6581 if ((_edit_point == EditAtPlayhead) && _session->transport_rolling()) {
6582 start.sample = _session->audible_sample();
6584 start.sample = get_preferred_edit_position();
6587 //if there's not already a sensible selection endpoint, go "forever"
6588 if (start.sample > end) {
6589 end = max_samplepos;
6592 set_punch_range (start.sample, end, _("set punch start from EP"));
6598 Editor::set_punch_end_from_edit_point ()
6602 samplepos_t start = 0;
6603 MusicSample end (max_samplepos, 0);
6605 //use the existing punch start, if any
6606 Location* tpl = transport_punch_location();
6608 start = tpl->start();
6611 if ((_edit_point == EditAtPlayhead) && _session->transport_rolling()) {
6612 end.sample = _session->audible_sample();
6614 end.sample = get_preferred_edit_position();
6617 set_punch_range (start, end.sample, _("set punch end from EP"));
6623 Editor::set_loop_start_from_edit_point ()
6627 MusicSample start (0, 0);
6628 samplepos_t end = max_samplepos;
6630 //use the existing loop end, if any
6631 Location* tpl = transport_loop_location();
6636 if ((_edit_point == EditAtPlayhead) && _session->transport_rolling()) {
6637 start.sample = _session->audible_sample();
6639 start.sample = get_preferred_edit_position();
6642 //if there's not already a sensible selection endpoint, go "forever"
6643 if (start.sample > end) {
6644 end = max_samplepos;
6647 set_loop_range (start.sample, end, _("set loop start from EP"));
6653 Editor::set_loop_end_from_edit_point ()
6657 samplepos_t start = 0;
6658 MusicSample end (max_samplepos, 0);
6660 //use the existing loop start, if any
6661 Location* tpl = transport_loop_location();
6663 start = tpl->start();
6666 if ((_edit_point == EditAtPlayhead) && _session->transport_rolling()) {
6667 end.sample = _session->audible_sample();
6669 end.sample = get_preferred_edit_position();
6672 set_loop_range (start, end.sample, _("set loop end from EP"));
6677 Editor::set_punch_from_region ()
6679 samplepos_t start, end;
6680 if (!get_selection_extents (start, end))
6683 set_punch_range (start, end, _("set punch range from region"));
6687 Editor::pitch_shift_region ()
6689 RegionSelection rs = get_regions_from_selection_and_entered ();
6691 RegionSelection audio_rs;
6692 for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
6693 if (dynamic_cast<AudioRegionView*> (*i)) {
6694 audio_rs.push_back (*i);
6698 if (audio_rs.empty()) {
6702 pitch_shift (audio_rs, 1.2);
6706 Editor::set_tempo_from_region ()
6708 RegionSelection rs = get_regions_from_selection_and_entered ();
6710 if (!_session || rs.empty()) {
6714 RegionView* rv = rs.front();
6716 define_one_bar (rv->region()->position(), rv->region()->last_sample() + 1);
6720 Editor::use_range_as_bar ()
6722 samplepos_t start, end;
6723 if (get_edit_op_range (start, end)) {
6724 define_one_bar (start, end);
6729 Editor::define_one_bar (samplepos_t start, samplepos_t end)
6731 samplepos_t length = end - start;
6733 const Meter& m (_session->tempo_map().meter_at_sample (start));
6735 /* length = 1 bar */
6737 /* We're going to deliver a constant tempo here,
6738 so we can use samples per beat to determine length.
6739 now we want samples per beat.
6740 we have samples per bar, and beats per bar, so ...
6743 /* XXXX METER MATH */
6745 double samples_per_beat = length / m.divisions_per_bar();
6747 /* beats per minute = */
6749 double beats_per_minute = (_session->sample_rate() * 60.0) / samples_per_beat;
6751 /* now decide whether to:
6753 (a) set global tempo
6754 (b) add a new tempo marker
6758 const TempoSection& t (_session->tempo_map().tempo_section_at_sample (start));
6760 bool do_global = false;
6762 if ((_session->tempo_map().n_tempos() == 1) && (_session->tempo_map().n_meters() == 1)) {
6764 /* only 1 tempo & 1 meter: ask if the user wants to set the tempo
6765 at the start, or create a new marker
6768 vector<string> options;
6769 options.push_back (_("Cancel"));
6770 options.push_back (_("Add new marker"));
6771 options.push_back (_("Set global tempo"));
6774 _("Define one bar"),
6775 _("Do you want to set the global tempo or add a new tempo marker?"),
6779 c.set_default_response (2);
6795 /* more than 1 tempo and/or meter section already, go ahead do the "usual":
6796 if the marker is at the region starter, change it, otherwise add
6801 begin_reversible_command (_("set tempo from region"));
6802 XMLNode& before (_session->tempo_map().get_state());
6805 _session->tempo_map().change_initial_tempo (beats_per_minute, t.note_type(), t.end_note_types_per_minute());
6806 } else if (t.sample() == start) {
6807 _session->tempo_map().change_existing_tempo_at (start, beats_per_minute, t.note_type(), t.end_note_types_per_minute());
6809 /* constant tempo */
6810 const Tempo tempo (beats_per_minute, t.note_type());
6811 _session->tempo_map().add_tempo (tempo, 0.0, start, AudioTime);
6814 XMLNode& after (_session->tempo_map().get_state());
6816 _session->add_command (new MementoCommand<TempoMap>(_session->tempo_map(), &before, &after));
6817 commit_reversible_command ();
6821 Editor::split_region_at_transients ()
6823 AnalysisFeatureList positions;
6825 RegionSelection rs = get_regions_from_selection_and_entered ();
6827 if (!_session || rs.empty()) {
6831 begin_reversible_command (_("split regions"));
6833 for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ) {
6835 RegionSelection::iterator tmp;
6840 boost::shared_ptr<AudioRegion> ar = boost::dynamic_pointer_cast<AudioRegion> ((*i)->region());
6843 ar->transients (positions);
6844 split_region_at_points ((*i)->region(), positions, true);
6851 commit_reversible_command ();
6856 Editor::split_region_at_points (boost::shared_ptr<Region> r, AnalysisFeatureList& positions, bool can_ferret, bool select_new)
6858 bool use_rhythmic_rodent = false;
6860 boost::shared_ptr<Playlist> pl = r->playlist();
6862 list<boost::shared_ptr<Region> > new_regions;
6868 if (positions.empty()) {
6872 if (positions.size() > 20 && can_ferret) {
6873 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);
6874 MessageDialog msg (msgstr,
6877 Gtk::BUTTONS_OK_CANCEL);
6880 msg.add_button (_("Call for the Ferret!"), RESPONSE_APPLY);
6881 msg.set_secondary_text (_("Press OK to continue with this split operation\nor ask the Ferret dialog to tune the analysis"));
6883 msg.set_secondary_text (_("Press OK to continue with this split operation"));
6886 msg.set_title (_("Excessive split?"));
6889 int response = msg.run();
6895 case RESPONSE_APPLY:
6896 use_rhythmic_rodent = true;
6903 if (use_rhythmic_rodent) {
6904 show_rhythm_ferret ();
6908 AnalysisFeatureList::const_iterator x;
6910 pl->clear_changes ();
6911 pl->clear_owned_changes ();
6913 x = positions.begin();
6915 if (x == positions.end()) {
6920 pl->remove_region (r);
6922 samplepos_t pos = 0;
6924 samplepos_t rstart = r->first_sample ();
6925 samplepos_t rend = r->last_sample ();
6927 while (x != positions.end()) {
6929 /* deal with positons that are out of scope of present region bounds */
6930 if (*x <= rstart || *x > rend) {
6935 /* file start = original start + how far we from the initial position ? */
6937 samplepos_t file_start = r->start() + pos;
6939 /* length = next position - current position */
6941 samplepos_t len = (*x) - pos - rstart;
6943 /* XXX we do we really want to allow even single-sample regions?
6944 * shouldn't we have some kind of lower limit on region size?
6953 if (RegionFactory::region_name (new_name, r->name())) {
6957 /* do NOT announce new regions 1 by one, just wait till they are all done */
6961 plist.add (ARDOUR::Properties::start, file_start);
6962 plist.add (ARDOUR::Properties::length, len);
6963 plist.add (ARDOUR::Properties::name, new_name);
6964 plist.add (ARDOUR::Properties::layer, 0);
6965 // TODO set transients_offset
6967 boost::shared_ptr<Region> nr = RegionFactory::create (r->sources(), plist, false);
6968 /* because we set annouce to false, manually add the new region to the
6971 RegionFactory::map_add (nr);
6973 pl->add_region (nr, rstart + pos);
6976 new_regions.push_front(nr);
6985 RegionFactory::region_name (new_name, r->name());
6987 /* Add the final region */
6990 plist.add (ARDOUR::Properties::start, r->start() + pos);
6991 plist.add (ARDOUR::Properties::length, r->last_sample() - (r->position() + pos) + 1);
6992 plist.add (ARDOUR::Properties::name, new_name);
6993 plist.add (ARDOUR::Properties::layer, 0);
6995 boost::shared_ptr<Region> nr = RegionFactory::create (r->sources(), plist, false);
6996 /* because we set annouce to false, manually add the new region to the
6999 RegionFactory::map_add (nr);
7000 pl->add_region (nr, r->position() + pos);
7003 new_regions.push_front(nr);
7008 /* We might have removed regions, which alters other regions' layering_index,
7009 so we need to do a recursive diff here.
7011 vector<Command*> cmds;
7013 _session->add_commands (cmds);
7015 _session->add_command (new StatefulDiffCommand (pl));
7019 for (list<boost::shared_ptr<Region> >::iterator i = new_regions.begin(); i != new_regions.end(); ++i){
7020 set_selected_regionview_from_region_list ((*i), Selection::Add);
7026 Editor::place_transient()
7032 RegionSelection rs = get_regions_from_selection_and_edit_point ();
7038 samplepos_t where = get_preferred_edit_position();
7040 begin_reversible_command (_("place transient"));
7042 for (RegionSelection::iterator r = rs.begin(); r != rs.end(); ++r) {
7043 (*r)->region()->add_transient(where);
7046 commit_reversible_command ();
7050 Editor::remove_transient(ArdourCanvas::Item* item)
7056 ArdourCanvas::Line* _line = reinterpret_cast<ArdourCanvas::Line*> (item);
7059 AudioRegionView* _arv = reinterpret_cast<AudioRegionView*> (item->get_data ("regionview"));
7060 _arv->remove_transient (*(float*) _line->get_data ("position"));
7064 Editor::snap_regions_to_grid ()
7066 list <boost::shared_ptr<Playlist > > used_playlists;
7068 RegionSelection rs = get_regions_from_selection_and_entered ();
7070 if (!_session || rs.empty()) {
7074 begin_reversible_command (_("snap regions to grid"));
7076 for (RegionSelection::iterator r = rs.begin(); r != rs.end(); ++r) {
7078 boost::shared_ptr<Playlist> pl = (*r)->region()->playlist();
7080 if (!pl->frozen()) {
7081 /* we haven't seen this playlist before */
7083 /* remember used playlists so we can thaw them later */
7084 used_playlists.push_back(pl);
7087 (*r)->region()->clear_changes ();
7089 MusicSample start ((*r)->region()->first_sample (), 0);
7090 snap_to (start, RoundNearest, SnapToGrid_Unscaled, true);
7091 (*r)->region()->set_position (start.sample, start.division);
7092 _session->add_command(new StatefulDiffCommand ((*r)->region()));
7095 while (used_playlists.size() > 0) {
7096 list <boost::shared_ptr<Playlist > >::iterator i = used_playlists.begin();
7098 used_playlists.pop_front();
7101 commit_reversible_command ();
7105 Editor::close_region_gaps ()
7107 list <boost::shared_ptr<Playlist > > used_playlists;
7109 RegionSelection rs = get_regions_from_selection_and_entered ();
7111 if (!_session || rs.empty()) {
7115 Dialog dialog (_("Close Region Gaps"));
7118 table.set_spacings (12);
7119 table.set_border_width (12);
7120 Label* l = manage (left_aligned_label (_("Crossfade length")));
7121 table.attach (*l, 0, 1, 0, 1);
7123 SpinButton spin_crossfade (1, 0);
7124 spin_crossfade.set_range (0, 15);
7125 spin_crossfade.set_increments (1, 1);
7126 spin_crossfade.set_value (5);
7127 table.attach (spin_crossfade, 1, 2, 0, 1);
7129 table.attach (*manage (new Label (_("ms"))), 2, 3, 0, 1);
7131 l = manage (left_aligned_label (_("Pull-back length")));
7132 table.attach (*l, 0, 1, 1, 2);
7134 SpinButton spin_pullback (1, 0);
7135 spin_pullback.set_range (0, 100);
7136 spin_pullback.set_increments (1, 1);
7137 spin_pullback.set_value(30);
7138 table.attach (spin_pullback, 1, 2, 1, 2);
7140 table.attach (*manage (new Label (_("ms"))), 2, 3, 1, 2);
7142 dialog.get_vbox()->pack_start (table);
7143 dialog.add_button (Stock::CANCEL, RESPONSE_CANCEL);
7144 dialog.add_button (_("Ok"), RESPONSE_ACCEPT);
7147 if (dialog.run () == RESPONSE_CANCEL) {
7151 samplepos_t crossfade_len = spin_crossfade.get_value();
7152 samplepos_t pull_back_samples = spin_pullback.get_value();
7154 crossfade_len = lrintf (crossfade_len * _session->sample_rate()/1000);
7155 pull_back_samples = lrintf (pull_back_samples * _session->sample_rate()/1000);
7157 /* Iterate over the region list and make adjacent regions overlap by crossfade_len_ms */
7159 begin_reversible_command (_("close region gaps"));
7162 boost::shared_ptr<Region> last_region;
7164 rs.sort_by_position_and_track();
7166 for (RegionSelection::iterator r = rs.begin(); r != rs.end(); ++r) {
7168 boost::shared_ptr<Playlist> pl = (*r)->region()->playlist();
7170 if (!pl->frozen()) {
7171 /* we haven't seen this playlist before */
7173 /* remember used playlists so we can thaw them later */
7174 used_playlists.push_back(pl);
7178 samplepos_t position = (*r)->region()->position();
7180 if (idx == 0 || position < last_region->position()){
7181 last_region = (*r)->region();
7186 (*r)->region()->clear_changes ();
7187 (*r)->region()->trim_front((position - pull_back_samples));
7189 last_region->clear_changes ();
7190 last_region->trim_end ((position - pull_back_samples + crossfade_len));
7192 _session->add_command (new StatefulDiffCommand ((*r)->region()));
7193 _session->add_command (new StatefulDiffCommand (last_region));
7195 last_region = (*r)->region();
7199 while (used_playlists.size() > 0) {
7200 list <boost::shared_ptr<Playlist > >::iterator i = used_playlists.begin();
7202 used_playlists.pop_front();
7205 commit_reversible_command ();
7209 Editor::tab_to_transient (bool forward)
7211 AnalysisFeatureList positions;
7213 RegionSelection rs = get_regions_from_selection_and_entered ();
7219 samplepos_t pos = _session->audible_sample ();
7221 if (!selection->tracks.empty()) {
7223 /* don't waste time searching for transients in duplicate playlists.
7226 TrackViewList ts = selection->tracks.filter_to_unique_playlists ();
7228 for (TrackViewList::iterator t = ts.begin(); t != ts.end(); ++t) {
7230 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (*t);
7233 boost::shared_ptr<Track> tr = rtv->track();
7235 boost::shared_ptr<Playlist> pl = tr->playlist ();
7237 samplepos_t result = pl->find_next_transient (pos, forward ? 1 : -1);
7240 positions.push_back (result);
7253 for (RegionSelection::iterator r = rs.begin(); r != rs.end(); ++r) {
7254 (*r)->region()->get_transients (positions);
7258 TransientDetector::cleanup_transients (positions, _session->sample_rate(), 3.0);
7261 AnalysisFeatureList::iterator x;
7263 for (x = positions.begin(); x != positions.end(); ++x) {
7269 if (x != positions.end ()) {
7270 _session->request_locate (*x);
7274 AnalysisFeatureList::reverse_iterator x;
7276 for (x = positions.rbegin(); x != positions.rend(); ++x) {
7282 if (x != positions.rend ()) {
7283 _session->request_locate (*x);
7289 Editor::playhead_forward_to_grid ()
7295 MusicSample pos (playhead_cursor->current_sample (), 0);
7297 if ( _grid_type == GridTypeNone) {
7298 if (pos.sample < max_samplepos - current_page_samples()*0.1) {
7299 pos.sample += current_page_samples()*0.1;
7300 _session->request_locate (pos.sample);
7302 _session->request_locate (0);
7306 if (pos.sample < max_samplepos - 1) {
7308 snap_to_internal (pos, RoundUpAlways, SnapToGrid_Scaled, true);
7309 _session->request_locate (pos.sample);
7314 /* keep PH visible in window */
7315 if (pos.sample > (_leftmost_sample + current_page_samples() *0.9)) {
7316 reset_x_origin (pos.sample - (current_page_samples()*0.9));
7322 Editor::playhead_backward_to_grid ()
7328 MusicSample pos (playhead_cursor->current_sample (), 0);
7330 if ( _grid_type == GridTypeNone) {
7331 if ( pos.sample > current_page_samples()*0.1 ) {
7332 pos.sample -= current_page_samples()*0.1;
7333 _session->request_locate (pos.sample);
7335 _session->request_locate (0);
7339 if (pos.sample > 2) {
7341 snap_to_internal (pos, RoundDownAlways, SnapToGrid_Scaled, true);
7344 //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...
7345 //also see: jump_backward_to_mark
7346 if (_session->transport_rolling()) {
7347 if ((playhead_cursor->current_sample() - pos.sample) < _session->sample_rate()/2) {
7348 snap_to_internal (pos, RoundDownAlways, SnapToGrid_Scaled, true);
7352 _session->request_locate (pos.sample, _session->transport_rolling());
7355 /* keep PH visible in window */
7356 if (pos.sample < (_leftmost_sample + current_page_samples() *0.1)) {
7357 reset_x_origin (pos.sample - (current_page_samples()*0.1));
7362 Editor::set_track_height (Height h)
7364 TrackSelection& ts (selection->tracks);
7366 for (TrackSelection::iterator x = ts.begin(); x != ts.end(); ++x) {
7367 (*x)->set_height_enum (h);
7372 Editor::toggle_tracks_active ()
7374 TrackSelection& ts (selection->tracks);
7376 bool target = false;
7382 for (TrackSelection::iterator x = ts.begin(); x != ts.end(); ++x) {
7383 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*>(*x);
7387 target = !rtv->_route->active();
7390 rtv->_route->set_active (target, this);
7396 Editor::remove_tracks ()
7398 /* this will delete GUI objects that may be the subject of an event
7399 handler in which this method is called. Defer actual deletion to the
7400 next idle callback, when all event handling is finished.
7402 Glib::signal_idle().connect (sigc::mem_fun (*this, &Editor::idle_remove_tracks));
7406 Editor::idle_remove_tracks ()
7408 Session::StateProtector sp (_session);
7410 return false; /* do not call again */
7414 Editor::_remove_tracks ()
7416 TrackSelection& ts (selection->tracks);
7422 vector<string> choices;
7427 const char* trackstr;
7430 vector<boost::shared_ptr<Route> > routes;
7431 vector<boost::shared_ptr<VCA> > vcas;
7432 bool special_bus = false;
7434 for (TrackSelection::iterator x = ts.begin(); x != ts.end(); ++x) {
7435 VCATimeAxisView* vtv = dynamic_cast<VCATimeAxisView*> (*x);
7437 vcas.push_back (vtv->vca());
7441 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (*x);
7445 if (rtv->is_track()) {
7450 routes.push_back (rtv->_route);
7452 if (rtv->route()->is_master() || rtv->route()->is_monitor()) {
7457 if (special_bus && !Config->get_allow_special_bus_removal()) {
7458 MessageDialog msg (_("That would be bad news ...."),
7462 msg.set_secondary_text (string_compose (_(
7463 "Removing the master or monitor bus is such a bad idea\n\
7464 that %1 is not going to allow it.\n\
7466 If you really want to do this sort of thing\n\
7467 edit your ardour.rc file to set the\n\
7468 \"allow-special-bus-removal\" option to be \"yes\""), PROGRAM_NAME));
7475 if (ntracks + nbusses + nvcas == 0) {
7481 trackstr = P_("track", "tracks", ntracks);
7482 busstr = P_("bus", "busses", nbusses);
7483 vcastr = P_("VCA", "VCAs", nvcas);
7485 if (ntracks > 0 && nbusses > 0 && nvcas > 0) {
7486 title = _("Remove various strips");
7487 prompt = string_compose (_("Do you really want to remove %1 %2, %3 %4 and %5 %6?"),
7488 ntracks, trackstr, nbusses, busstr, nvcas, vcastr);
7490 else if (ntracks > 0 && nbusses > 0) {
7491 title = string_compose (_("Remove %1 and %2"), trackstr, busstr);
7492 prompt = string_compose (_("Do you really want to remove %1 %2 and %3 %4?"),
7493 ntracks, trackstr, nbusses, busstr);
7495 else if (ntracks > 0 && nvcas > 0) {
7496 title = string_compose (_("Remove %1 and %2"), trackstr, vcastr);
7497 prompt = string_compose (_("Do you really want to remove %1 %2 and %3 %4?"),
7498 ntracks, trackstr, nvcas, vcastr);
7500 else if (nbusses > 0 && nvcas > 0) {
7501 title = string_compose (_("Remove %1 and %2"), busstr, vcastr);
7502 prompt = string_compose (_("Do you really want to remove %1 %2 and %3 %4?"),
7503 nbusses, busstr, nvcas, vcastr);
7505 else if (ntracks > 0) {
7506 title = string_compose (_("Remove %1"), trackstr);
7507 prompt = string_compose (_("Do you really want to remove %1 %2?"),
7510 else if (nbusses > 0) {
7511 title = string_compose (_("Remove %1"), busstr);
7512 prompt = string_compose (_("Do you really want to remove %1 %2?"),
7515 else if (nvcas > 0) {
7516 title = string_compose (_("Remove %1"), vcastr);
7517 prompt = string_compose (_("Do you really want to remove %1 %2?"),
7525 prompt += "\n" + string_compose ("(You may also lose the playlists associated with the %1)", trackstr) + "\n";
7528 prompt += "\n" + string(_("This action cannot be undone, and the session file will be overwritten!"));
7530 choices.push_back (_("No, do nothing."));
7531 if (ntracks + nbusses + nvcas > 1) {
7532 choices.push_back (_("Yes, remove them."));
7534 choices.push_back (_("Yes, remove it."));
7537 Choice prompter (title, prompt, choices);
7539 if (prompter.run () != 1) {
7543 if (current_mixer_strip && routes.size () > 1 && std::find (routes.begin(), routes.end(), current_mixer_strip->route()) != routes.end ()) {
7544 /* Route deletion calls Editor::timeaxisview_deleted() iteratively (for each deleted
7545 * route). If the deleted route is currently displayed in the Editor-Mixer (highly
7546 * likely because deletion requires selection) this will call
7547 * Editor::set_selected_mixer_strip () which is expensive (MixerStrip::set_route()).
7548 * It's likewise likely that the route that has just been displayed in the
7549 * Editor-Mixer will be next in line for deletion.
7551 * So simply switch to the master-bus (if present)
7553 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
7554 if ((*i)->stripable ()->is_master ()) {
7555 set_selected_mixer_strip (*(*i));
7562 PresentationInfo::ChangeSuspender cs;
7563 DisplaySuspender ds;
7565 boost::shared_ptr<RouteList> rl (new RouteList);
7566 for (vector<boost::shared_ptr<Route> >::iterator x = routes.begin(); x != routes.end(); ++x) {
7569 _session->remove_routes (rl);
7571 for (vector<boost::shared_ptr<VCA> >::iterator x = vcas.begin(); x != vcas.end(); ++x) {
7572 _session->vca_manager().remove_vca (*x);
7576 /* TrackSelection and RouteList leave scope,
7577 * destructors are called,
7578 * diskstream drops references, save_state is called (again for every track)
7583 Editor::do_insert_time ()
7585 if (selection->tracks.empty()) {
7586 MessageDialog msg (_("You must first select some tracks to Insert Time."),
7587 true, MESSAGE_INFO, BUTTONS_OK, true);
7588 msg.set_position (WIN_POS_MOUSE);
7593 if (Config->get_edit_mode() == Lock) {
7594 MessageDialog msg (_("You cannot insert time in Lock Edit mode."),
7595 true, MESSAGE_INFO, BUTTONS_OK, true);
7596 msg.set_position (WIN_POS_MOUSE);
7601 InsertRemoveTimeDialog d (*this);
7602 int response = d.run ();
7604 if (response != RESPONSE_OK) {
7608 if (d.distance() == 0) {
7615 d.intersected_region_action (),
7619 d.move_glued_markers(),
7620 d.move_locked_markers(),
7626 Editor::insert_time (
7627 samplepos_t pos, samplecnt_t samples, InsertTimeOption opt,
7628 bool all_playlists, bool ignore_music_glue, bool markers_too, bool glued_markers_too, bool locked_markers_too, bool tempo_too
7632 if (Config->get_edit_mode() == Lock) {
7635 bool in_command = false;
7637 TrackViewList ts = selection->tracks.filter_to_unique_playlists ();
7639 for (TrackViewList::iterator x = ts.begin(); x != ts.end(); ++x) {
7643 /* don't operate on any playlist more than once, which could
7644 * happen if "all playlists" is enabled, but there is more
7645 * than 1 track using playlists "from" a given track.
7648 set<boost::shared_ptr<Playlist> > pl;
7650 if (all_playlists) {
7651 RouteTimeAxisView* rtav = dynamic_cast<RouteTimeAxisView*> (*x);
7652 if (rtav && rtav->track ()) {
7653 vector<boost::shared_ptr<Playlist> > all = _session->playlists->playlists_for_track (rtav->track ());
7654 for (vector<boost::shared_ptr<Playlist> >::iterator p = all.begin(); p != all.end(); ++p) {
7659 if ((*x)->playlist ()) {
7660 pl.insert ((*x)->playlist ());
7664 for (set<boost::shared_ptr<Playlist> >::iterator i = pl.begin(); i != pl.end(); ++i) {
7666 (*i)->clear_changes ();
7667 (*i)->clear_owned_changes ();
7670 begin_reversible_command (_("insert time"));
7674 if (opt == SplitIntersected) {
7675 /* non musical split */
7676 (*i)->split (MusicSample (pos, 0));
7679 (*i)->shift (pos, samples, (opt == MoveIntersected), ignore_music_glue);
7681 vector<Command*> cmds;
7683 _session->add_commands (cmds);
7685 _session->add_command (new StatefulDiffCommand (*i));
7689 RouteTimeAxisView* rtav = dynamic_cast<RouteTimeAxisView*> (*x);
7692 begin_reversible_command (_("insert time"));
7695 rtav->route ()->shift (pos, samples);
7702 const int32_t divisions = get_grid_music_divisions (0);
7703 XMLNode& before (_session->locations()->get_state());
7704 Locations::LocationList copy (_session->locations()->list());
7706 for (Locations::LocationList::iterator i = copy.begin(); i != copy.end(); ++i) {
7708 Locations::LocationList::const_iterator tmp;
7710 if ((*i)->position_lock_style() == AudioTime || glued_markers_too) {
7711 bool const was_locked = (*i)->locked ();
7712 if (locked_markers_too) {
7716 if ((*i)->start() >= pos) {
7717 // move end first, in case we're moving by more than the length of the range
7718 if (!(*i)->is_mark()) {
7719 (*i)->set_end ((*i)->end() + samples, false, true, divisions);
7721 (*i)->set_start ((*i)->start() + samples, false, true, divisions);
7733 begin_reversible_command (_("insert time"));
7736 XMLNode& after (_session->locations()->get_state());
7737 _session->add_command (new MementoCommand<Locations>(*_session->locations(), &before, &after));
7743 begin_reversible_command (_("insert time"));
7746 XMLNode& before (_session->tempo_map().get_state());
7747 _session->tempo_map().insert_time (pos, samples);
7748 XMLNode& after (_session->tempo_map().get_state());
7749 _session->add_command (new MementoCommand<TempoMap>(_session->tempo_map(), &before, &after));
7753 commit_reversible_command ();
7758 Editor::do_remove_time ()
7760 if (selection->tracks.empty()) {
7761 MessageDialog msg (_("You must first select some tracks to Remove Time."),
7762 true, MESSAGE_INFO, BUTTONS_OK, true);
7763 msg.set_position (WIN_POS_MOUSE);
7768 if (Config->get_edit_mode() == Lock) {
7769 MessageDialog msg (_("You cannot remove time in Lock Edit mode."),
7770 true, MESSAGE_INFO, BUTTONS_OK, true);
7771 msg.set_position (WIN_POS_MOUSE);
7776 InsertRemoveTimeDialog d (*this, true);
7778 int response = d.run ();
7780 if (response != RESPONSE_OK) {
7784 samplecnt_t distance = d.distance();
7786 if (distance == 0) {
7796 d.move_glued_markers(),
7797 d.move_locked_markers(),
7803 Editor::remove_time (samplepos_t pos, samplecnt_t samples, InsertTimeOption opt,
7804 bool ignore_music_glue, bool markers_too, bool glued_markers_too, bool locked_markers_too, bool tempo_too)
7806 if (Config->get_edit_mode() == Lock) {
7807 error << (_("Cannot insert or delete time when in Lock edit.")) << endmsg;
7810 bool in_command = false;
7812 for (TrackSelection::iterator x = selection->tracks.begin(); x != selection->tracks.end(); ++x) {
7814 boost::shared_ptr<Playlist> pl = (*x)->playlist();
7818 XMLNode &before = pl->get_state();
7821 begin_reversible_command (_("remove time"));
7825 std::list<AudioRange> rl;
7826 AudioRange ar(pos, pos+samples, 0);
7829 pl->shift (pos, -samples, true, ignore_music_glue);
7831 XMLNode &after = pl->get_state();
7833 _session->add_command (new MementoCommand<Playlist> (*pl, &before, &after));
7837 RouteTimeAxisView* rtav = dynamic_cast<RouteTimeAxisView*> (*x);
7840 begin_reversible_command (_("remove time"));
7843 rtav->route ()->shift (pos, -samples);
7847 const int32_t divisions = get_grid_music_divisions (0);
7848 std::list<Location*> loc_kill_list;
7853 XMLNode& before (_session->locations()->get_state());
7854 Locations::LocationList copy (_session->locations()->list());
7856 for (Locations::LocationList::iterator i = copy.begin(); i != copy.end(); ++i) {
7857 if ((*i)->position_lock_style() == AudioTime || glued_markers_too) {
7859 bool const was_locked = (*i)->locked ();
7860 if (locked_markers_too) {
7864 if (!(*i)->is_mark()) { // it's a range; have to handle both start and end
7865 if ((*i)->end() >= pos
7866 && (*i)->end() < pos+samples
7867 && (*i)->start() >= pos
7868 && (*i)->end() < pos+samples) { // range is completely enclosed; kill it
7870 loc_kill_list.push_back(*i);
7871 } else { // only start or end is included, try to do the right thing
7872 // move start before moving end, to avoid trying to move the end to before the start
7873 // if we're removing more time than the length of the range
7874 if ((*i)->start() >= pos && (*i)->start() < pos+samples) {
7875 // start is within cut
7876 (*i)->set_start (pos, false, true,divisions); // bring the start marker to the beginning of the cut
7878 } else if ((*i)->start() >= pos+samples) {
7879 // start (and thus entire range) lies beyond end of cut
7880 (*i)->set_start ((*i)->start() - samples, false, true, divisions); // slip the start marker back
7883 if ((*i)->end() >= pos && (*i)->end() < pos+samples) {
7884 // end is inside cut
7885 (*i)->set_end (pos, false, true, divisions); // bring the end to the cut
7887 } else if ((*i)->end() >= pos+samples) {
7888 // end is beyond end of cut
7889 (*i)->set_end ((*i)->end() - samples, false, true, divisions); // slip the end marker back
7894 } else if ((*i)->start() >= pos && (*i)->start() < pos+samples) {
7895 loc_kill_list.push_back(*i);
7897 } else if ((*i)->start() >= pos) {
7898 (*i)->set_start ((*i)->start() -samples, false, true, divisions);
7908 for (list<Location*>::iterator i = loc_kill_list.begin(); i != loc_kill_list.end(); ++i) {
7909 _session->locations()->remove (*i);
7914 begin_reversible_command (_("remove time"));
7917 XMLNode& after (_session->locations()->get_state());
7918 _session->add_command (new MementoCommand<Locations>(*_session->locations(), &before, &after));
7923 XMLNode& before (_session->tempo_map().get_state());
7925 if (_session->tempo_map().remove_time (pos, samples)) {
7927 begin_reversible_command (_("remove time"));
7930 XMLNode& after (_session->tempo_map().get_state());
7931 _session->add_command (new MementoCommand<TempoMap>(_session->tempo_map(), &before, &after));
7936 commit_reversible_command ();
7941 Editor::fit_selection ()
7943 if (!selection->tracks.empty()) {
7944 fit_tracks (selection->tracks);
7948 /* no selected tracks - use tracks with selected regions */
7950 if (!selection->regions.empty()) {
7951 for (RegionSelection::iterator r = selection->regions.begin(); r != selection->regions.end(); ++r) {
7952 tvl.push_back (&(*r)->get_time_axis_view ());
7958 } else if (internal_editing()) {
7959 /* no selected tracks, or regions, but in internal edit mode, so follow the mouse and use
7962 if (entered_track) {
7963 tvl.push_back (entered_track);
7971 Editor::fit_tracks (TrackViewList & tracks)
7973 if (tracks.empty()) {
7977 uint32_t child_heights = 0;
7978 int visible_tracks = 0;
7980 for (TrackSelection::iterator t = tracks.begin(); t != tracks.end(); ++t) {
7982 if (!(*t)->marked_for_display()) {
7986 child_heights += (*t)->effective_height() - (*t)->current_height();
7990 /* compute the per-track height from:
7992 * total canvas visible height
7993 * - height that will be taken by visible children of selected tracks
7994 * - height of the ruler/hscroll area
7996 uint32_t h = (uint32_t) floor ((trackviews_height() - child_heights) / visible_tracks);
7997 double first_y_pos = DBL_MAX;
7999 if (h < TimeAxisView::preset_height (HeightSmall)) {
8000 MessageDialog msg (_("There are too many tracks to fit in the current window"));
8001 /* too small to be displayed */
8005 undo_visual_stack.push_back (current_visual_state (true));
8006 PBD::Unwinder<bool> nsv (no_save_visual, true);
8008 /* build a list of all tracks, including children */
8011 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
8013 TimeAxisView::Children c = (*i)->get_child_list ();
8014 for (TimeAxisView::Children::iterator j = c.begin(); j != c.end(); ++j) {
8015 all.push_back (j->get());
8020 // find selection range.
8021 // if someone knows how to user TrackViewList::iterator for this
8023 int selected_top = -1;
8024 int selected_bottom = -1;
8026 for (TrackViewList::iterator t = all.begin(); t != all.end(); ++t, ++i) {
8027 if ((*t)->marked_for_display ()) {
8028 if (tracks.contains(*t)) {
8029 if (selected_top == -1) {
8032 selected_bottom = i;
8038 for (TrackViewList::iterator t = all.begin(); t != all.end(); ++t, ++i) {
8039 if ((*t)->marked_for_display ()) {
8040 if (tracks.contains(*t)) {
8041 (*t)->set_height (h);
8042 first_y_pos = std::min ((*t)->y_position (), first_y_pos);
8044 if (i > selected_top && i < selected_bottom) {
8045 hide_track_in_display (*t);
8052 set the controls_layout height now, because waiting for its size
8053 request signal handler will cause the vertical adjustment setting to fail
8056 controls_layout.property_height () = _full_canvas_height;
8057 vertical_adjustment.set_value (first_y_pos);
8059 redo_visual_stack.push_back (current_visual_state (true));
8061 visible_tracks_selector.set_text (_("Sel"));
8065 Editor::save_visual_state (uint32_t n)
8067 while (visual_states.size() <= n) {
8068 visual_states.push_back (0);
8071 if (visual_states[n] != 0) {
8072 delete visual_states[n];
8075 visual_states[n] = current_visual_state (true);
8080 Editor::goto_visual_state (uint32_t n)
8082 if (visual_states.size() <= n) {
8086 if (visual_states[n] == 0) {
8090 use_visual_state (*visual_states[n]);
8094 Editor::start_visual_state_op (uint32_t n)
8096 save_visual_state (n);
8098 PopUp* pup = new PopUp (WIN_POS_MOUSE, 1000, true);
8100 snprintf (buf, sizeof (buf), _("Saved view %u"), n+1);
8101 pup->set_text (buf);
8106 Editor::cancel_visual_state_op (uint32_t n)
8108 goto_visual_state (n);
8112 Editor::toggle_region_mute ()
8114 if (_ignore_region_action) {
8118 RegionSelection rs = get_regions_from_selection_and_entered ();
8124 if (rs.size() > 1) {
8125 begin_reversible_command (_("mute regions"));
8127 begin_reversible_command (_("mute region"));
8130 for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
8132 (*i)->region()->playlist()->clear_changes ();
8133 (*i)->region()->set_muted (!(*i)->region()->muted ());
8134 _session->add_command (new StatefulDiffCommand ((*i)->region()));
8138 commit_reversible_command ();
8142 Editor::combine_regions ()
8144 /* foreach track with selected regions, take all selected regions
8145 and join them into a new region containing the subregions (as a
8149 typedef set<RouteTimeAxisView*> RTVS;
8152 if (selection->regions.empty()) {
8156 for (RegionSelection::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
8157 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*>(&(*i)->get_time_axis_view());
8160 tracks.insert (rtv);
8164 begin_reversible_command (_("combine regions"));
8166 vector<RegionView*> new_selection;
8168 for (RTVS::iterator i = tracks.begin(); i != tracks.end(); ++i) {
8171 if ((rv = (*i)->combine_regions ()) != 0) {
8172 new_selection.push_back (rv);
8176 selection->clear_regions ();
8177 for (vector<RegionView*>::iterator i = new_selection.begin(); i != new_selection.end(); ++i) {
8178 selection->add (*i);
8181 commit_reversible_command ();
8185 Editor::uncombine_regions ()
8187 typedef set<RouteTimeAxisView*> RTVS;
8190 if (selection->regions.empty()) {
8194 for (RegionSelection::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
8195 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*>(&(*i)->get_time_axis_view());
8198 tracks.insert (rtv);
8202 begin_reversible_command (_("uncombine regions"));
8204 for (RTVS::iterator i = tracks.begin(); i != tracks.end(); ++i) {
8205 (*i)->uncombine_regions ();
8208 commit_reversible_command ();
8212 Editor::toggle_midi_input_active (bool flip_others)
8215 boost::shared_ptr<RouteList> rl (new RouteList);
8217 for (TrackSelection::iterator i = selection->tracks.begin(); i != selection->tracks.end(); ++i) {
8218 RouteTimeAxisView *rtav = dynamic_cast<RouteTimeAxisView *>(*i);
8224 boost::shared_ptr<MidiTrack> mt = rtav->midi_track();
8227 rl->push_back (rtav->route());
8228 onoff = !mt->input_active();
8232 _session->set_exclusive_input_active (rl, onoff, flip_others);
8235 static bool ok_fine (GdkEventAny*) { return true; }
8241 lock_dialog = new Gtk::Dialog (string_compose (_("%1: Locked"), PROGRAM_NAME), true);
8243 Gtk::Image* padlock = manage (new Gtk::Image (ARDOUR_UI_UTILS::get_icon ("padlock_closed")));
8244 lock_dialog->get_vbox()->pack_start (*padlock);
8245 lock_dialog->signal_delete_event ().connect (sigc::ptr_fun (ok_fine));
8247 ArdourButton* b = manage (new ArdourButton);
8248 b->set_name ("lock button");
8249 b->set_text (_("Click to unlock"));
8250 b->signal_clicked.connect (sigc::mem_fun (*this, &Editor::unlock));
8251 lock_dialog->get_vbox()->pack_start (*b);
8253 lock_dialog->get_vbox()->show_all ();
8254 lock_dialog->set_size_request (200, 200);
8257 delete _main_menu_disabler;
8258 _main_menu_disabler = new MainMenuDisabler;
8260 lock_dialog->present ();
8262 lock_dialog->get_window()->set_decorations (Gdk::WMDecoration (0));
8268 lock_dialog->hide ();
8270 delete _main_menu_disabler;
8271 _main_menu_disabler = 0;
8273 if (UIConfiguration::instance().get_lock_gui_after_seconds()) {
8274 start_lock_event_timing ();
8279 Editor::bring_in_callback (Gtk::Label* label, uint32_t n, uint32_t total, string name)
8281 Gtkmm2ext::UI::instance()->call_slot (invalidator (*this), boost::bind (&Editor::update_bring_in_message, this, label, n, total, name));
8285 Editor::update_bring_in_message (Gtk::Label* label, uint32_t n, uint32_t total, string name)
8287 Timers::TimerSuspender t;
8288 label->set_text (string_compose ("Copying %1, %2 of %3", name, n, total));
8289 Gtkmm2ext::UI::instance()->flush_pending (1);
8293 Editor::bring_all_sources_into_session ()
8300 ArdourDialog w (_("Moving embedded files into session folder"));
8301 w.get_vbox()->pack_start (msg);
8304 /* flush all pending GUI events because we're about to start copying
8308 Timers::TimerSuspender t;
8309 Gtkmm2ext::UI::instance()->flush_pending (3);
8313 _session->bring_all_sources_into_session (boost::bind (&Editor::bring_in_callback, this, &msg, _1, _2, _3));