2 * Copyright (C) 2005-2006 Taybin Rutkin <taybin@taybin.com>
3 * Copyright (C) 2005-2009 Sampo Savolainen <v2@iki.fi>
4 * Copyright (C) 2005-2018 Paul Davis <paul@linuxaudiosystems.com>
5 * Copyright (C) 2005 Karsten Wiese <fzuuzf@googlemail.com>
6 * Copyright (C) 2006-2015 David Robillard <d@drobilla.net>
7 * Copyright (C) 2007-2012 Carl Hetherington <carl@carlh.net>
8 * Copyright (C) 2007-2017 Tim Mayberry <mojofunk@gmail.com>
9 * Copyright (C) 2013-2016 Colin Fletcher <colin.m.fletcher@googlemail.com>
10 * Copyright (C) 2013-2017 John Emmas <john@creativepost.co.uk>
11 * Copyright (C) 2013-2017 Nick Mainsbridge <mainsbridge@gmail.com>
12 * Copyright (C) 2013-2019 Robin Gareus <robin@gareus.org>
13 * Copyright (C) 2014-2019 Ben Loftis <ben@harrisonconsoles.com>
14 * Copyright (C) 2015 André Nusser <andre.nusser@googlemail.com>
16 * This program is free software; you can redistribute it and/or modify
17 * it under the terms of the GNU General Public License as published by
18 * the Free Software Foundation; either version 2 of the License, or
19 * (at your option) any later version.
21 * This program is distributed in the hope that it will be useful,
22 * but WITHOUT ANY WARRANTY; without even the implied warranty of
23 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
24 * GNU General Public License for more details.
26 * You should have received a copy of the GNU General Public License along
27 * with this program; if not, write to the Free Software Foundation, Inc.,
28 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
31 /* Note: public Editor methods are documented in public_editor.h */
42 #include <gtkmm/messagedialog.h>
44 #include "pbd/error.h"
45 #include "pbd/basename.h"
46 #include "pbd/pthread_utils.h"
47 #include "pbd/memento_command.h"
48 #include "pbd/unwind.h"
49 #include "pbd/whitespace.h"
50 #include "pbd/stateful_diff_command.h"
52 #include "gtkmm2ext/utils.h"
54 #include "widgets/choice.h"
55 #include "widgets/popup.h"
56 #include "widgets/prompter.h"
58 #include "ardour/audio_track.h"
59 #include "ardour/audioregion.h"
60 #include "ardour/boost_debug.h"
61 #include "ardour/dB.h"
62 #include "ardour/location.h"
63 #include "ardour/midi_region.h"
64 #include "ardour/midi_track.h"
65 #include "ardour/operations.h"
66 #include "ardour/playlist_factory.h"
67 #include "ardour/profile.h"
68 #include "ardour/quantize.h"
69 #include "ardour/legatize.h"
70 #include "ardour/region_factory.h"
71 #include "ardour/reverse.h"
72 #include "ardour/selection.h"
73 #include "ardour/session.h"
74 #include "ardour/session_playlists.h"
75 #include "ardour/source.h"
76 #include "ardour/strip_silence.h"
77 #include "ardour/transient_detector.h"
78 #include "ardour/transport_master_manager.h"
79 #include "ardour/transpose.h"
80 #include "ardour/vca_manager.h"
82 #include "canvas/canvas.h"
85 #include "ardour_ui.h"
86 #include "audio_region_view.h"
87 #include "audio_streamview.h"
88 #include "audio_time_axis.h"
89 #include "automation_region_view.h"
90 #include "automation_time_axis.h"
91 #include "control_point.h"
95 #include "editor_cursors.h"
96 #include "editor_drag.h"
97 #include "editor_regions.h"
98 #include "editor_sources.h"
99 #include "editor_routes.h"
100 #include "gui_thread.h"
101 #include "insert_remove_time_dialog.h"
102 #include "interthread_progress_window.h"
103 #include "item_counts.h"
104 #include "keyboard.h"
105 #include "midi_region_view.h"
106 #include "mixer_ui.h"
107 #include "mixer_strip.h"
108 #include "mouse_cursors.h"
109 #include "normalize_dialog.h"
111 #include "paste_context.h"
112 #include "patch_change_dialog.h"
113 #include "quantize_dialog.h"
114 #include "region_gain_line.h"
115 #include "rgb_macros.h"
116 #include "route_time_axis.h"
117 #include "selection.h"
118 #include "selection_templates.h"
119 #include "streamview.h"
120 #include "strip_silence_dialog.h"
121 #include "time_axis_view.h"
123 #include "transpose_dialog.h"
124 #include "transform_dialog.h"
125 #include "ui_config.h"
127 #include "vca_time_axis.h"
129 #include "pbd/i18n.h"
132 using namespace ARDOUR;
135 using namespace Gtkmm2ext;
136 using namespace ArdourWidgets;
137 using namespace Editing;
138 using Gtkmm2ext::Keyboard;
140 /***********************************************************************
142 ***********************************************************************/
145 Editor::undo (uint32_t n)
147 if (_session && _session->actively_recording()) {
148 /* no undo allowed while recording. Session will check also,
149 but we don't even want to get to that.
154 if (_drags->active ()) {
160 if (_session->undo_depth() == 0) {
161 undo_action->set_sensitive(false);
163 redo_action->set_sensitive(true);
164 begin_selection_op_history ();
169 Editor::redo (uint32_t n)
171 if (_session && _session->actively_recording()) {
172 /* no redo allowed while recording. Session will check also,
173 but we don't even want to get to that.
178 if (_drags->active ()) {
184 if (_session->redo_depth() == 0) {
185 redo_action->set_sensitive(false);
187 undo_action->set_sensitive(true);
188 begin_selection_op_history ();
193 Editor::split_regions_at (MusicSample where, RegionSelection& regions)
197 list<boost::shared_ptr<Playlist> > used_playlists;
198 list<RouteTimeAxisView*> used_trackviews;
200 if (regions.empty()) {
204 begin_reversible_command (_("split"));
207 if (regions.size() == 1) {
208 /* TODO: if splitting a single region, and snap-to is using
209 region boundaries, mabye we shouldn't pay attention to them? */
212 EditorFreeze(); /* Emit Signal */
215 for (RegionSelection::iterator a = regions.begin(); a != regions.end(); ) {
217 RegionSelection::iterator tmp;
219 /* XXX this test needs to be more complicated, to make sure we really
220 have something to split.
223 if (!(*a)->region()->covers (where.sample)) {
231 boost::shared_ptr<Playlist> pl = (*a)->region()->playlist();
239 /* we haven't seen this playlist before */
241 /* remember used playlists so we can thaw them later */
242 used_playlists.push_back(pl);
244 TimeAxisView& tv = (*a)->get_time_axis_view();
245 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (&tv);
247 used_trackviews.push_back (rtv);
254 pl->clear_changes ();
255 pl->split_region ((*a)->region(), where);
256 _session->add_command (new StatefulDiffCommand (pl));
262 latest_regionviews.clear ();
264 vector<sigc::connection> region_added_connections;
266 for (list<RouteTimeAxisView*>::iterator i = used_trackviews.begin(); i != used_trackviews.end(); ++i) {
267 region_added_connections.push_back ((*i)->view()->RegionViewAdded.connect (sigc::mem_fun(*this, &Editor::collect_new_region_view)));
270 while (used_playlists.size() > 0) {
271 list <boost::shared_ptr<Playlist > >::iterator i = used_playlists.begin();
273 used_playlists.pop_front();
276 for (vector<sigc::connection>::iterator c = region_added_connections.begin(); c != region_added_connections.end(); ++c) {
281 EditorThaw(); /* Emit Signal */
284 RegionSelectionAfterSplit rsas = Config->get_region_selection_after_split();
286 //if the user has "Clear Selection" as their post-split behavior, then clear the selection
287 if (!latest_regionviews.empty() && (rsas == None)) {
288 selection->clear_objects();
289 selection->clear_time();
290 //but leave track selection intact
293 //if the user doesn't want to preserve the "Existing" selection, then clear the selection
294 if (!(rsas & Existing)) {
295 selection->clear_objects();
296 selection->clear_time();
299 //if the user wants newly-created regions to be selected, then select them:
300 if (mouse_mode == MouseObject) {
301 for (RegionSelection::iterator ri = latest_regionviews.begin(); ri != latest_regionviews.end(); ri++) {
302 if ((*ri)->region()->position() < where.sample) {
303 // new regions created before the split
304 if (rsas & NewlyCreatedLeft) {
305 selection->add (*ri);
308 // new regions created after the split
309 if (rsas & NewlyCreatedRight) {
310 selection->add (*ri);
316 commit_reversible_command ();
319 /** Move one extreme of the current range selection. If more than one range is selected,
320 * the start of the earliest range or the end of the latest range is moved.
322 * @param move_end true to move the end of the current range selection, false to move
324 * @param next true to move the extreme to the next region boundary, false to move to
328 Editor::move_range_selection_start_or_end_to_region_boundary (bool move_end, bool next)
330 if (selection->time.start() == selection->time.end_sample()) {
334 samplepos_t start = selection->time.start ();
335 samplepos_t end = selection->time.end_sample ();
337 /* the position of the thing we may move */
338 samplepos_t pos = move_end ? end : start;
339 int dir = next ? 1 : -1;
341 /* so we don't find the current region again */
342 if (dir > 0 || pos > 0) {
346 samplepos_t const target = get_region_boundary (pos, dir, true, false);
361 begin_reversible_selection_op (_("alter selection"));
362 selection->set_preserving_all_ranges (start, end);
363 commit_reversible_selection_op ();
367 Editor::nudge_forward_release (GdkEventButton* ev)
369 if (ev->state & Keyboard::PrimaryModifier) {
370 nudge_forward (false, true);
372 nudge_forward (false, false);
378 Editor::nudge_backward_release (GdkEventButton* ev)
380 if (ev->state & Keyboard::PrimaryModifier) {
381 nudge_backward (false, true);
383 nudge_backward (false, false);
390 Editor::nudge_forward (bool next, bool force_playhead)
392 samplepos_t distance;
393 samplepos_t next_distance;
399 RegionSelection rs = get_regions_from_selection_and_entered ();
401 if (!force_playhead && !rs.empty()) {
403 begin_reversible_command (_("nudge regions forward"));
405 for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
406 boost::shared_ptr<Region> r ((*i)->region());
408 distance = get_nudge_distance (r->position(), next_distance);
411 distance = next_distance;
415 r->set_position (r->position() + distance);
416 _session->add_command (new StatefulDiffCommand (r));
419 commit_reversible_command ();
422 } else if (!force_playhead && !selection->markers.empty()) {
425 bool in_command = false;
426 const int32_t divisions = get_grid_music_divisions (0);
428 for (MarkerSelection::iterator i = selection->markers.begin(); i != selection->markers.end(); ++i) {
430 Location* loc = find_location_from_marker ((*i), is_start);
434 XMLNode& before (loc->get_state());
437 distance = get_nudge_distance (loc->start(), next_distance);
439 distance = next_distance;
441 if (max_samplepos - distance > loc->start() + loc->length()) {
442 loc->set_start (loc->start() + distance, false, true, divisions);
444 loc->set_start (max_samplepos - loc->length(), false, true, divisions);
447 distance = get_nudge_distance (loc->end(), next_distance);
449 distance = next_distance;
451 if (max_samplepos - distance > loc->end()) {
452 loc->set_end (loc->end() + distance, false, true, divisions);
454 loc->set_end (max_samplepos, false, true, divisions);
456 if (loc->is_session_range()) {
457 _session->set_session_range_is_free (false);
461 begin_reversible_command (_("nudge location forward"));
464 XMLNode& after (loc->get_state());
465 _session->add_command (new MementoCommand<Location>(*loc, &before, &after));
470 commit_reversible_command ();
473 distance = get_nudge_distance (playhead_cursor->current_sample (), next_distance);
474 _session->request_locate (playhead_cursor->current_sample () + distance);
479 Editor::nudge_backward (bool next, bool force_playhead)
481 samplepos_t distance;
482 samplepos_t next_distance;
488 RegionSelection rs = get_regions_from_selection_and_entered ();
490 if (!force_playhead && !rs.empty()) {
492 begin_reversible_command (_("nudge regions backward"));
494 for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
495 boost::shared_ptr<Region> r ((*i)->region());
497 distance = get_nudge_distance (r->position(), next_distance);
500 distance = next_distance;
505 if (r->position() > distance) {
506 r->set_position (r->position() - distance);
510 _session->add_command (new StatefulDiffCommand (r));
513 commit_reversible_command ();
515 } else if (!force_playhead && !selection->markers.empty()) {
518 bool in_command = false;
520 for (MarkerSelection::iterator i = selection->markers.begin(); i != selection->markers.end(); ++i) {
522 Location* loc = find_location_from_marker ((*i), is_start);
526 XMLNode& before (loc->get_state());
529 distance = get_nudge_distance (loc->start(), next_distance);
531 distance = next_distance;
533 if (distance < loc->start()) {
534 loc->set_start (loc->start() - distance, false, true, get_grid_music_divisions(0));
536 loc->set_start (0, false, true, get_grid_music_divisions(0));
539 distance = get_nudge_distance (loc->end(), next_distance);
542 distance = next_distance;
545 if (distance < loc->end() - loc->length()) {
546 loc->set_end (loc->end() - distance, false, true, get_grid_music_divisions(0));
548 loc->set_end (loc->length(), false, true, get_grid_music_divisions(0));
550 if (loc->is_session_range()) {
551 _session->set_session_range_is_free (false);
555 begin_reversible_command (_("nudge location forward"));
558 XMLNode& after (loc->get_state());
559 _session->add_command (new MementoCommand<Location>(*loc, &before, &after));
563 commit_reversible_command ();
568 distance = get_nudge_distance (playhead_cursor->current_sample (), next_distance);
570 if (playhead_cursor->current_sample () > distance) {
571 _session->request_locate (playhead_cursor->current_sample () - distance);
573 _session->goto_start();
579 Editor::nudge_forward_capture_offset ()
581 RegionSelection rs = get_regions_from_selection_and_entered ();
583 if (!_session || rs.empty()) {
587 begin_reversible_command (_("nudge forward"));
589 samplepos_t const distance = _session->worst_output_latency();
591 for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
592 boost::shared_ptr<Region> r ((*i)->region());
595 r->set_position (r->position() + distance);
596 _session->add_command(new StatefulDiffCommand (r));
599 commit_reversible_command ();
603 Editor::nudge_backward_capture_offset ()
605 RegionSelection rs = get_regions_from_selection_and_entered ();
607 if (!_session || rs.empty()) {
611 begin_reversible_command (_("nudge backward"));
613 samplepos_t const distance = _session->worst_output_latency();
615 for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
616 boost::shared_ptr<Region> r ((*i)->region());
620 if (r->position() > distance) {
621 r->set_position (r->position() - distance);
625 _session->add_command(new StatefulDiffCommand (r));
628 commit_reversible_command ();
631 struct RegionSelectionPositionSorter {
632 bool operator() (RegionView* a, RegionView* b) {
633 return a->region()->position() < b->region()->position();
638 Editor::sequence_regions ()
641 samplepos_t r_end_prev;
649 RegionSelection rs = get_regions_from_selection_and_entered ();
650 rs.sort(RegionSelectionPositionSorter());
654 bool in_command = false;
656 for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
657 boost::shared_ptr<Region> r ((*i)->region());
665 if(r->position_locked())
672 r->set_position(r_end_prev);
676 begin_reversible_command (_("sequence regions"));
679 _session->add_command (new StatefulDiffCommand (r));
681 r_end=r->position() + r->length();
687 commit_reversible_command ();
696 Editor::move_to_start ()
698 _session->goto_start ();
702 Editor::move_to_end ()
705 _session->request_locate (_session->current_end_sample());
709 Editor::build_region_boundary_cache ()
712 /* TODO: maybe set a timer so we don't recalutate when lots of changes are coming in */
713 /* TODO: maybe somehow defer this until session is fully loaded. */
715 if (!_region_boundary_cache_dirty)
719 vector<RegionPoint> interesting_points;
720 boost::shared_ptr<Region> r;
721 TrackViewList tracks;
724 region_boundary_cache.clear ();
730 bool maybe_first_sample = false;
732 if (UIConfiguration::instance().get_snap_to_region_start()) {
733 interesting_points.push_back (Start);
734 maybe_first_sample = true;
737 if (UIConfiguration::instance().get_snap_to_region_end()) {
738 interesting_points.push_back (End);
741 if (UIConfiguration::instance().get_snap_to_region_sync()) {
742 interesting_points.push_back (SyncPoint);
745 /* if no snap selections are set, boundary cache should be left empty */
746 if ( interesting_points.empty() ) {
747 _region_boundary_cache_dirty = false;
751 TimeAxisView *ontrack = 0;
754 tlist = track_views.filter_to_unique_playlists ();
756 if (maybe_first_sample) {
757 TrackViewList::const_iterator i;
758 for (i = tlist.begin(); i != tlist.end(); ++i) {
759 boost::shared_ptr<Playlist> pl = (*i)->playlist();
760 if (pl && pl->count_regions_at (0)) {
761 region_boundary_cache.push_back (0);
767 //allow regions to snap to the video start (if any) as if it were a "region"
768 if (ARDOUR_UI::instance()->video_timeline) {
769 region_boundary_cache.push_back (ARDOUR_UI::instance()->video_timeline->get_video_start_offset());
772 std::pair<samplepos_t, samplepos_t> ext = session_gui_extents (false);
773 samplepos_t session_end = ext.second;
775 while (pos < session_end && !at_end) {
778 samplepos_t lpos = session_end;
780 for (vector<RegionPoint>::iterator p = interesting_points.begin(); p != interesting_points.end(); ++p) {
782 if ((r = find_next_region (pos, *p, 1, tlist, &ontrack)) == 0) {
783 if (*p == interesting_points.back()) {
786 /* move to next point type */
792 rpos = r->first_sample();
796 rpos = r->last_sample();
800 rpos = r->sync_position ();
811 /* prevent duplicates, but we don't use set<> because we want to be able
815 vector<samplepos_t>::iterator ri;
817 for (ri = region_boundary_cache.begin(); ri != region_boundary_cache.end(); ++ri) {
823 if (ri == region_boundary_cache.end()) {
824 region_boundary_cache.push_back (rpos);
831 /* finally sort to be sure that the order is correct */
833 sort (region_boundary_cache.begin(), region_boundary_cache.end());
835 _region_boundary_cache_dirty = false;
838 boost::shared_ptr<Region>
839 Editor::find_next_region (samplepos_t sample, RegionPoint point, int32_t dir, TrackViewList& tracks, TimeAxisView **ontrack)
841 TrackViewList::iterator i;
842 samplepos_t closest = max_samplepos;
843 boost::shared_ptr<Region> ret;
844 samplepos_t rpos = 0;
846 samplepos_t track_sample;
848 for (i = tracks.begin(); i != tracks.end(); ++i) {
850 samplecnt_t distance;
851 boost::shared_ptr<Region> r;
853 track_sample = sample;
855 if ((r = (*i)->find_next_region (track_sample, point, dir)) == 0) {
861 rpos = r->first_sample ();
865 rpos = r->last_sample ();
869 rpos = r->sync_position ();
874 distance = rpos - sample;
876 distance = sample - rpos;
879 if (distance < closest) {
891 Editor::find_next_region_boundary (samplepos_t pos, int32_t dir, const TrackViewList& tracks)
893 samplecnt_t distance = max_samplepos;
894 samplepos_t current_nearest = -1;
896 for (TrackViewList::const_iterator i = tracks.begin(); i != tracks.end(); ++i) {
897 samplepos_t contender;
900 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (*i);
906 if ((contender = rtv->find_next_region_boundary (pos, dir)) < 0) {
910 d = ::llabs (pos - contender);
913 current_nearest = contender;
918 return current_nearest;
922 Editor::get_region_boundary (samplepos_t pos, int32_t dir, bool with_selection, bool only_onscreen)
927 if (with_selection && Config->get_region_boundaries_from_selected_tracks()) {
929 if (!selection->tracks.empty()) {
931 target = find_next_region_boundary (pos, dir, selection->tracks);
935 if (only_onscreen || Config->get_region_boundaries_from_onscreen_tracks()) {
936 get_onscreen_tracks (tvl);
937 target = find_next_region_boundary (pos, dir, tvl);
939 target = find_next_region_boundary (pos, dir, track_views);
945 if (only_onscreen || Config->get_region_boundaries_from_onscreen_tracks()) {
946 get_onscreen_tracks (tvl);
947 target = find_next_region_boundary (pos, dir, tvl);
949 target = find_next_region_boundary (pos, dir, track_views);
957 Editor::cursor_to_region_boundary (bool with_selection, int32_t dir)
959 samplepos_t pos = playhead_cursor->current_sample ();
966 // so we don't find the current region again..
967 if (dir > 0 || pos > 0) {
971 if ((target = get_region_boundary (pos, dir, with_selection, false)) < 0) {
975 _session->request_locate (target);
979 Editor::cursor_to_next_region_boundary (bool with_selection)
981 cursor_to_region_boundary (with_selection, 1);
985 Editor::cursor_to_previous_region_boundary (bool with_selection)
987 cursor_to_region_boundary (with_selection, -1);
991 Editor::cursor_to_region_point (EditorCursor* cursor, RegionPoint point, int32_t dir)
993 boost::shared_ptr<Region> r;
994 samplepos_t pos = cursor->current_sample ();
1000 TimeAxisView *ontrack = 0;
1002 // so we don't find the current region again..
1006 if (!selection->tracks.empty()) {
1008 r = find_next_region (pos, point, dir, selection->tracks, &ontrack);
1010 } else if (clicked_axisview) {
1013 t.push_back (clicked_axisview);
1015 r = find_next_region (pos, point, dir, t, &ontrack);
1019 r = find_next_region (pos, point, dir, track_views, &ontrack);
1028 pos = r->first_sample ();
1032 pos = r->last_sample ();
1036 pos = r->sync_position ();
1040 if (cursor == playhead_cursor) {
1041 _session->request_locate (pos);
1043 cursor->set_position (pos);
1048 Editor::cursor_to_next_region_point (EditorCursor* cursor, RegionPoint point)
1050 cursor_to_region_point (cursor, point, 1);
1054 Editor::cursor_to_previous_region_point (EditorCursor* cursor, RegionPoint point)
1056 cursor_to_region_point (cursor, point, -1);
1060 Editor::cursor_to_selection_start (EditorCursor *cursor)
1062 samplepos_t pos = 0;
1064 switch (mouse_mode) {
1066 if (!selection->regions.empty()) {
1067 pos = selection->regions.start();
1072 if (!selection->time.empty()) {
1073 pos = selection->time.start ();
1081 if (cursor == playhead_cursor) {
1082 _session->request_locate (pos);
1084 cursor->set_position (pos);
1089 Editor::cursor_to_selection_end (EditorCursor *cursor)
1091 samplepos_t pos = 0;
1093 switch (mouse_mode) {
1095 if (!selection->regions.empty()) {
1096 pos = selection->regions.end_sample();
1101 if (!selection->time.empty()) {
1102 pos = selection->time.end_sample ();
1110 if (cursor == playhead_cursor) {
1111 _session->request_locate (pos);
1113 cursor->set_position (pos);
1118 Editor::selected_marker_to_region_boundary (bool with_selection, int32_t dir)
1128 if (selection->markers.empty()) {
1132 if (!mouse_sample (mouse, ignored)) {
1136 add_location_mark (mouse);
1139 if ((loc = find_location_from_marker (selection->markers.front(), ignored)) == 0) {
1143 samplepos_t pos = loc->start();
1145 // so we don't find the current region again..
1146 if (dir > 0 || pos > 0) {
1150 if ((target = get_region_boundary (pos, dir, with_selection, false)) < 0) {
1154 loc->move_to (target, 0);
1158 Editor::selected_marker_to_next_region_boundary (bool with_selection)
1160 selected_marker_to_region_boundary (with_selection, 1);
1164 Editor::selected_marker_to_previous_region_boundary (bool with_selection)
1166 selected_marker_to_region_boundary (with_selection, -1);
1170 Editor::selected_marker_to_region_point (RegionPoint point, int32_t dir)
1172 boost::shared_ptr<Region> r;
1177 if (!_session || selection->markers.empty()) {
1181 if ((loc = find_location_from_marker (selection->markers.front(), ignored)) == 0) {
1185 TimeAxisView *ontrack = 0;
1189 // so we don't find the current region again..
1193 if (!selection->tracks.empty()) {
1195 r = find_next_region (pos, point, dir, selection->tracks, &ontrack);
1199 r = find_next_region (pos, point, dir, track_views, &ontrack);
1208 pos = r->first_sample ();
1212 pos = r->last_sample ();
1216 pos = r->adjust_to_sync (r->first_sample());
1220 loc->move_to (pos, 0);
1224 Editor::selected_marker_to_next_region_point (RegionPoint point)
1226 selected_marker_to_region_point (point, 1);
1230 Editor::selected_marker_to_previous_region_point (RegionPoint point)
1232 selected_marker_to_region_point (point, -1);
1236 Editor::selected_marker_to_selection_start ()
1238 samplepos_t pos = 0;
1242 if (!_session || selection->markers.empty()) {
1246 if ((loc = find_location_from_marker (selection->markers.front(), ignored)) == 0) {
1250 switch (mouse_mode) {
1252 if (!selection->regions.empty()) {
1253 pos = selection->regions.start();
1258 if (!selection->time.empty()) {
1259 pos = selection->time.start ();
1267 loc->move_to (pos, 0);
1271 Editor::selected_marker_to_selection_end ()
1273 samplepos_t pos = 0;
1277 if (!_session || selection->markers.empty()) {
1281 if ((loc = find_location_from_marker (selection->markers.front(), ignored)) == 0) {
1285 switch (mouse_mode) {
1287 if (!selection->regions.empty()) {
1288 pos = selection->regions.end_sample();
1293 if (!selection->time.empty()) {
1294 pos = selection->time.end_sample ();
1302 loc->move_to (pos, 0);
1306 Editor::scroll_playhead (bool forward)
1308 samplepos_t pos = playhead_cursor->current_sample ();
1309 samplecnt_t delta = (samplecnt_t) floor (current_page_samples() / 0.8);
1312 if (pos == max_samplepos) {
1316 if (pos < max_samplepos - delta) {
1319 pos = max_samplepos;
1335 _session->request_locate (pos);
1339 Editor::cursor_align (bool playhead_to_edit)
1345 if (playhead_to_edit) {
1347 if (selection->markers.empty()) {
1351 _session->request_locate (selection->markers.front()->position(), _session->transport_rolling());
1354 const int32_t divisions = get_grid_music_divisions (0);
1355 /* move selected markers to playhead */
1357 for (MarkerSelection::iterator i = selection->markers.begin(); i != selection->markers.end(); ++i) {
1360 Location* loc = find_location_from_marker (*i, ignored);
1362 if (loc->is_mark()) {
1363 loc->set_start (playhead_cursor->current_sample (), false, true, divisions);
1365 loc->set (playhead_cursor->current_sample (),
1366 playhead_cursor->current_sample () + loc->length(), true, divisions);
1373 Editor::scroll_backward (float pages)
1375 samplepos_t const one_page = (samplepos_t) rint (_visible_canvas_width * samples_per_pixel);
1376 samplepos_t const cnt = (samplepos_t) floor (pages * one_page);
1379 if (_leftmost_sample < cnt) {
1382 sample = _leftmost_sample - cnt;
1385 reset_x_origin (sample);
1389 Editor::scroll_forward (float pages)
1391 samplepos_t const one_page = (samplepos_t) rint (_visible_canvas_width * samples_per_pixel);
1392 samplepos_t const cnt = (samplepos_t) floor (pages * one_page);
1395 if (max_samplepos - cnt < _leftmost_sample) {
1396 sample = max_samplepos - cnt;
1398 sample = _leftmost_sample + cnt;
1401 reset_x_origin (sample);
1405 Editor::scroll_tracks_down ()
1407 double vert_value = vertical_adjustment.get_value() + vertical_adjustment.get_page_size();
1408 if (vert_value > vertical_adjustment.get_upper() - _visible_canvas_height) {
1409 vert_value = vertical_adjustment.get_upper() - _visible_canvas_height;
1412 vertical_adjustment.set_value (vert_value);
1416 Editor::scroll_tracks_up ()
1418 vertical_adjustment.set_value (vertical_adjustment.get_value() - vertical_adjustment.get_page_size());
1422 Editor::scroll_tracks_down_line ()
1424 double vert_value = vertical_adjustment.get_value() + 60;
1426 if (vert_value > vertical_adjustment.get_upper() - _visible_canvas_height) {
1427 vert_value = vertical_adjustment.get_upper() - _visible_canvas_height;
1430 vertical_adjustment.set_value (vert_value);
1434 Editor::scroll_tracks_up_line ()
1436 reset_y_origin (vertical_adjustment.get_value() - 60);
1440 Editor::select_topmost_track ()
1442 const double top_of_trackviews = vertical_adjustment.get_value();
1443 for (TrackViewList::iterator t = track_views.begin(); t != track_views.end(); ++t) {
1444 if ((*t)->hidden()) {
1447 std::pair<TimeAxisView*,double> res = (*t)->covers_y_position (top_of_trackviews);
1449 selection->set (*t);
1456 Editor::scroll_down_one_track (bool skip_child_views)
1458 TrackViewList::reverse_iterator next = track_views.rend();
1459 const double top_of_trackviews = vertical_adjustment.get_value();
1461 for (TrackViewList::reverse_iterator t = track_views.rbegin(); t != track_views.rend(); ++t) {
1462 if ((*t)->hidden()) {
1466 /* If this is the upper-most visible trackview, we want to display
1467 * the one above it (next)
1469 * Note that covers_y_position() is recursive and includes child views
1471 std::pair<TimeAxisView*,double> res = (*t)->covers_y_position (top_of_trackviews);
1474 if (skip_child_views) {
1477 /* automation lane (one level, non-recursive)
1479 * - if no automation lane exists -> move to next tack
1480 * - if the first (here: bottom-most) matches -> move to next tack
1481 * - if no y-axis match is found -> the current track is at the top
1482 * -> move to last (here: top-most) automation lane
1484 TimeAxisView::Children kids = (*t)->get_child_list();
1485 TimeAxisView::Children::reverse_iterator nkid = kids.rend();
1487 for (TimeAxisView::Children::reverse_iterator ci = kids.rbegin(); ci != kids.rend(); ++ci) {
1488 if ((*ci)->hidden()) {
1492 std::pair<TimeAxisView*,double> dev;
1493 dev = (*ci)->covers_y_position (top_of_trackviews);
1495 /* some automation lane is currently at the top */
1496 if (ci == kids.rbegin()) {
1497 /* first (bottom-most) autmation lane is at the top.
1498 * -> move to next track
1507 if (nkid != kids.rend()) {
1508 ensure_time_axis_view_is_visible (**nkid, true);
1516 /* move to the track below the first one that covers the */
1518 if (next != track_views.rend()) {
1519 ensure_time_axis_view_is_visible (**next, true);
1527 Editor::scroll_up_one_track (bool skip_child_views)
1529 TrackViewList::iterator prev = track_views.end();
1530 double top_of_trackviews = vertical_adjustment.get_value ();
1532 for (TrackViewList::iterator t = track_views.begin(); t != track_views.end(); ++t) {
1534 if ((*t)->hidden()) {
1538 /* find the trackview at the top of the trackview group
1540 * Note that covers_y_position() is recursive and includes child views
1542 std::pair<TimeAxisView*,double> res = (*t)->covers_y_position (top_of_trackviews);
1545 if (skip_child_views) {
1548 /* automation lane (one level, non-recursive)
1550 * - if no automation lane exists -> move to prev tack
1551 * - if no y-axis match is found -> the current track is at the top -> move to prev track
1552 * (actually last automation lane of previous track, see below)
1553 * - if first (top-most) lane is at the top -> move to this track
1554 * - else move up one lane
1556 TimeAxisView::Children kids = (*t)->get_child_list();
1557 TimeAxisView::Children::iterator pkid = kids.end();
1559 for (TimeAxisView::Children::iterator ci = kids.begin(); ci != kids.end(); ++ci) {
1560 if ((*ci)->hidden()) {
1564 std::pair<TimeAxisView*,double> dev;
1565 dev = (*ci)->covers_y_position (top_of_trackviews);
1567 /* some automation lane is currently at the top */
1568 if (ci == kids.begin()) {
1569 /* first (top-most) autmation lane is at the top.
1570 * jump directly to this track's top
1572 ensure_time_axis_view_is_visible (**t, true);
1575 else if (pkid != kids.end()) {
1576 /* some other automation lane is at the top.
1577 * move up to prev automation lane.
1579 ensure_time_axis_view_is_visible (**pkid, true);
1582 assert(0); // not reached
1593 if (prev != track_views.end()) {
1594 // move to bottom-most automation-lane of the previous track
1595 TimeAxisView::Children kids = (*prev)->get_child_list();
1596 TimeAxisView::Children::reverse_iterator pkid = kids.rend();
1597 if (!skip_child_views) {
1598 // find the last visible lane
1599 for (TimeAxisView::Children::reverse_iterator ci = kids.rbegin(); ci != kids.rend(); ++ci) {
1600 if (!(*ci)->hidden()) {
1606 if (pkid != kids.rend()) {
1607 ensure_time_axis_view_is_visible (**pkid, true);
1609 ensure_time_axis_view_is_visible (**prev, true);
1618 Editor::scroll_left_step ()
1620 samplepos_t xdelta = (current_page_samples() / 8);
1622 if (_leftmost_sample > xdelta) {
1623 reset_x_origin (_leftmost_sample - xdelta);
1631 Editor::scroll_right_step ()
1633 samplepos_t xdelta = (current_page_samples() / 8);
1635 if (max_samplepos - xdelta > _leftmost_sample) {
1636 reset_x_origin (_leftmost_sample + xdelta);
1638 reset_x_origin (max_samplepos - current_page_samples());
1643 Editor::scroll_left_half_page ()
1645 samplepos_t xdelta = (current_page_samples() / 2);
1646 if (_leftmost_sample > xdelta) {
1647 reset_x_origin (_leftmost_sample - xdelta);
1654 Editor::scroll_right_half_page ()
1656 samplepos_t xdelta = (current_page_samples() / 2);
1657 if (max_samplepos - xdelta > _leftmost_sample) {
1658 reset_x_origin (_leftmost_sample + xdelta);
1660 reset_x_origin (max_samplepos - current_page_samples());
1667 Editor::tav_zoom_step (bool coarser)
1669 DisplaySuspender ds;
1673 if (selection->tracks.empty()) {
1676 ts = &selection->tracks;
1679 for (TrackViewList::iterator i = ts->begin(); i != ts->end(); ++i) {
1680 TimeAxisView *tv = (static_cast<TimeAxisView*>(*i));
1681 tv->step_height (coarser);
1686 Editor::tav_zoom_smooth (bool coarser, bool force_all)
1688 DisplaySuspender ds;
1692 if (selection->tracks.empty() || force_all) {
1695 ts = &selection->tracks;
1698 for (TrackViewList::iterator i = ts->begin(); i != ts->end(); ++i) {
1699 TimeAxisView *tv = (static_cast<TimeAxisView*>(*i));
1700 uint32_t h = tv->current_height ();
1705 if (h >= TimeAxisView::preset_height (HeightSmall)) {
1710 tv->set_height (h + 5);
1716 Editor::temporal_zoom_step_mouse_focus_scale (bool zoom_out, double scale)
1718 Editing::ZoomFocus temp_focus = zoom_focus;
1719 zoom_focus = Editing::ZoomFocusMouse;
1720 temporal_zoom_step_scale (zoom_out, scale);
1721 zoom_focus = temp_focus;
1725 Editor::temporal_zoom_step_mouse_focus (bool zoom_out)
1727 temporal_zoom_step_mouse_focus_scale (zoom_out, 2.0);
1731 Editor::temporal_zoom_step (bool zoom_out)
1733 temporal_zoom_step_scale (zoom_out, 2.0);
1737 Editor::temporal_zoom_step_scale (bool zoom_out, double scale)
1739 ENSURE_GUI_THREAD (*this, &Editor::temporal_zoom_step, zoom_out, scale)
1741 samplecnt_t nspp = samples_per_pixel;
1745 if (nspp == samples_per_pixel) {
1750 if (nspp == samples_per_pixel) {
1755 //zoom-behavior-tweaks
1756 //limit our maximum zoom to the session gui extents value
1757 std::pair<samplepos_t, samplepos_t> ext = session_gui_extents();
1758 samplecnt_t session_extents_pp = (ext.second - ext.first) / _visible_canvas_width;
1759 if (nspp > session_extents_pp)
1760 nspp = session_extents_pp;
1762 temporal_zoom (nspp);
1766 Editor::temporal_zoom (samplecnt_t fpp)
1772 samplepos_t current_page = current_page_samples();
1773 samplepos_t current_leftmost = _leftmost_sample;
1774 samplepos_t current_rightmost;
1775 samplepos_t current_center;
1776 samplepos_t new_page_size;
1777 samplepos_t half_page_size;
1778 samplepos_t leftmost_after_zoom = 0;
1780 bool in_track_canvas;
1781 bool use_mouse_sample = true;
1785 if (fpp == samples_per_pixel) {
1789 // Imposing an arbitrary limit to zoom out as too much zoom out produces
1790 // segfaults for lack of memory. If somebody decides this is not high enough I
1791 // believe it can be raisen to higher values but some limit must be in place.
1793 // This constant represents 1 day @ 48kHz on a 1600 pixel wide display
1794 // all of which is used for the editor track displays. The whole day
1795 // would be 4147200000 samples, so 2592000 samples per pixel.
1797 nfpp = min (fpp, (samplecnt_t) 2592000);
1798 nfpp = max ((samplecnt_t) 1, nfpp);
1800 new_page_size = (samplepos_t) floor (_visible_canvas_width * nfpp);
1801 half_page_size = new_page_size / 2;
1803 switch (zoom_focus) {
1805 leftmost_after_zoom = current_leftmost;
1808 case ZoomFocusRight:
1809 current_rightmost = _leftmost_sample + current_page;
1810 if (current_rightmost < new_page_size) {
1811 leftmost_after_zoom = 0;
1813 leftmost_after_zoom = current_rightmost - new_page_size;
1817 case ZoomFocusCenter:
1818 current_center = current_leftmost + (current_page/2);
1819 if (current_center < half_page_size) {
1820 leftmost_after_zoom = 0;
1822 leftmost_after_zoom = current_center - half_page_size;
1826 case ZoomFocusPlayhead:
1827 /* centre playhead */
1828 l = playhead_cursor->current_sample () - (new_page_size * 0.5);
1831 leftmost_after_zoom = 0;
1832 } else if (l > max_samplepos) {
1833 leftmost_after_zoom = max_samplepos - new_page_size;
1835 leftmost_after_zoom = (samplepos_t) l;
1839 case ZoomFocusMouse:
1840 /* try to keep the mouse over the same point in the display */
1842 if (_drags->active()) {
1843 where = _drags->current_pointer_sample ();
1844 } else if (!mouse_sample (where, in_track_canvas)) {
1845 use_mouse_sample = false;
1848 if (use_mouse_sample) {
1849 l = - ((new_page_size * ((where - current_leftmost)/(double)current_page)) - where);
1852 leftmost_after_zoom = 0;
1853 } else if (l > max_samplepos) {
1854 leftmost_after_zoom = max_samplepos - new_page_size;
1856 leftmost_after_zoom = (samplepos_t) l;
1859 /* use playhead instead */
1860 where = playhead_cursor->current_sample ();
1862 if (where < half_page_size) {
1863 leftmost_after_zoom = 0;
1865 leftmost_after_zoom = where - half_page_size;
1871 /* try to keep the edit point in the same place */
1872 where = get_preferred_edit_position ();
1876 double l = - ((new_page_size * ((where - current_leftmost)/(double)current_page)) - where);
1879 leftmost_after_zoom = 0;
1880 } else if (l > max_samplepos) {
1881 leftmost_after_zoom = max_samplepos - new_page_size;
1883 leftmost_after_zoom = (samplepos_t) l;
1887 /* edit point not defined */
1894 // leftmost_after_zoom = min (leftmost_after_zoom, _session->current_end_sample());
1896 reposition_and_zoom (leftmost_after_zoom, nfpp);
1900 Editor::calc_extra_zoom_edges(samplepos_t &start, samplepos_t &end)
1902 /* this func helps make sure we leave a little space
1903 at each end of the editor so that the zoom doesn't fit the region
1904 precisely to the screen.
1907 GdkScreen* screen = gdk_screen_get_default ();
1908 const gint pixwidth = gdk_screen_get_width (screen);
1909 const gint mmwidth = gdk_screen_get_width_mm (screen);
1910 const double pix_per_mm = (double) pixwidth/ (double) mmwidth;
1911 const double one_centimeter_in_pixels = pix_per_mm * 10.0;
1913 const samplepos_t range = end - start;
1914 const samplecnt_t new_fpp = (samplecnt_t) ceil ((double) range / (double) _visible_canvas_width);
1915 const samplepos_t extra_samples = (samplepos_t) floor (one_centimeter_in_pixels * new_fpp);
1917 if (start > extra_samples) {
1918 start -= extra_samples;
1923 if (max_samplepos - extra_samples > end) {
1924 end += extra_samples;
1926 end = max_samplepos;
1931 Editor::get_selection_extents (samplepos_t &start, samplepos_t &end) const
1933 start = max_samplepos;
1937 //ToDo: if notes are selected, set extents to that selection
1939 //ToDo: if control points are selected, set extents to that selection
1941 if (!selection->regions.empty()) {
1942 RegionSelection rs = get_regions_from_selection_and_entered ();
1944 for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
1946 if ((*i)->region()->position() < start) {
1947 start = (*i)->region()->position();
1950 if ((*i)->region()->last_sample() + 1 > end) {
1951 end = (*i)->region()->last_sample() + 1;
1955 } else if (!selection->time.empty()) {
1956 start = selection->time.start();
1957 end = selection->time.end_sample();
1959 ret = false; //no selection found
1962 if ((start == 0 && end == 0) || end < start) {
1971 Editor::temporal_zoom_selection (Editing::ZoomAxis axes)
1973 if (!selection) return;
1975 if (selection->regions.empty() && selection->time.empty()) {
1976 if (axes == Horizontal || axes == Both) {
1977 temporal_zoom_step(true);
1979 if (axes == Vertical || axes == Both) {
1980 if (!track_views.empty()) {
1984 //implicit hack: by extending the top & bottom check outside the current view limits, we include the trackviews immediately above & below what is visible
1985 const double top = vertical_adjustment.get_value() - 10;
1986 const double btm = top + _visible_canvas_height + 10;
1988 for (TrackViewList::iterator iter = track_views.begin(); iter != track_views.end(); ++iter) {
1989 if ((*iter)->covered_by_y_range (top, btm)) {
1990 tvl.push_back(*iter);
2000 //ToDo: if notes are selected, zoom to that
2002 //ToDo: if control points are selected, zoom to that
2004 if (axes == Horizontal || axes == Both) {
2006 samplepos_t start, end;
2007 if (get_selection_extents (start, end)) {
2008 calc_extra_zoom_edges (start, end);
2009 temporal_zoom_by_sample (start, end);
2013 if (axes == Vertical || axes == Both) {
2017 //normally, we don't do anything "automatic" to the user's selection.
2018 //but in this case, we will clear the selection after a zoom-to-selection.
2023 Editor::temporal_zoom_session ()
2025 ENSURE_GUI_THREAD (*this, &Editor::temporal_zoom_session)
2028 samplecnt_t start = _session->current_start_sample();
2029 samplecnt_t end = _session->current_end_sample();
2031 if (_session->actively_recording ()) {
2032 samplepos_t cur = playhead_cursor->current_sample ();
2034 /* recording beyond the end marker; zoom out
2035 * by 5 seconds more so that if 'follow
2036 * playhead' is active we don't immediately
2039 end = cur + _session->sample_rate() * 5;
2043 if ((start == 0 && end == 0) || end < start) {
2047 calc_extra_zoom_edges(start, end);
2049 temporal_zoom_by_sample (start, end);
2054 Editor::temporal_zoom_extents ()
2056 ENSURE_GUI_THREAD (*this, &Editor::temporal_zoom_extents)
2059 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
2061 samplecnt_t start = ext.first;
2062 samplecnt_t end = ext.second;
2064 if (_session->actively_recording ()) {
2065 samplepos_t cur = playhead_cursor->current_sample ();
2067 /* recording beyond the end marker; zoom out
2068 * by 5 seconds more so that if 'follow
2069 * playhead' is active we don't immediately
2072 end = cur + _session->sample_rate() * 5;
2076 if ((start == 0 && end == 0) || end < start) {
2080 calc_extra_zoom_edges(start, end);
2082 temporal_zoom_by_sample (start, end);
2087 Editor::temporal_zoom_by_sample (samplepos_t start, samplepos_t end)
2089 if (!_session) return;
2091 if ((start == 0 && end == 0) || end < start) {
2095 samplepos_t range = end - start;
2097 const samplecnt_t new_fpp = (samplecnt_t) ceil ((double) range / (double) _visible_canvas_width);
2099 samplepos_t new_page = range;
2100 samplepos_t middle = (samplepos_t) floor ((double) start + ((double) range / 2.0f));
2101 samplepos_t new_leftmost = (samplepos_t) floor ((double) middle - ((double) new_page / 2.0f));
2103 if (new_leftmost > middle) {
2107 if (new_leftmost < 0) {
2111 reposition_and_zoom (new_leftmost, new_fpp);
2115 Editor::temporal_zoom_to_sample (bool coarser, samplepos_t sample)
2121 samplecnt_t range_before = sample - _leftmost_sample;
2122 samplecnt_t new_spp;
2125 if (samples_per_pixel <= 1) {
2128 new_spp = samples_per_pixel + (samples_per_pixel/2);
2130 range_before += range_before/2;
2132 if (samples_per_pixel >= 1) {
2133 new_spp = samples_per_pixel - (samples_per_pixel/2);
2135 /* could bail out here since we cannot zoom any finer,
2136 but leave that to the equality test below
2138 new_spp = samples_per_pixel;
2141 range_before -= range_before/2;
2144 if (new_spp == samples_per_pixel) {
2148 /* zoom focus is automatically taken as @param sample when this
2152 samplepos_t new_leftmost = sample - (samplepos_t)range_before;
2154 if (new_leftmost > sample) {
2158 if (new_leftmost < 0) {
2162 reposition_and_zoom (new_leftmost, new_spp);
2167 Editor::choose_new_marker_name(string &name, bool is_range) {
2169 if (!UIConfiguration::instance().get_name_new_markers()) {
2170 /* don't prompt user for a new name */
2174 Prompter dialog (true);
2176 dialog.set_prompt (_("New Name:"));
2179 dialog.set_title(_("New Range"));
2181 dialog.set_title (_("New Location Marker"));
2184 dialog.set_name ("MarkNameWindow");
2185 dialog.set_size_request (250, -1);
2186 dialog.set_position (Gtk::WIN_POS_MOUSE);
2188 dialog.add_button (Stock::OK, RESPONSE_ACCEPT);
2189 dialog.set_initial_text (name);
2193 switch (dialog.run ()) {
2194 case RESPONSE_ACCEPT:
2200 dialog.get_result(name);
2207 Editor::add_location_from_selection ()
2211 if (selection->time.empty()) {
2215 if (_session == 0 || clicked_axisview == 0) {
2219 samplepos_t start = selection->time[clicked_selection].start;
2220 samplepos_t end = selection->time[clicked_selection].end;
2222 _session->locations()->next_available_name(rangename,"selection");
2223 if (!choose_new_marker_name(rangename, true)) {
2226 Location *location = new Location (*_session, start, end, rangename, Location::IsRangeMarker, get_grid_music_divisions(0));
2228 begin_reversible_command (_("add marker"));
2230 XMLNode &before = _session->locations()->get_state();
2231 _session->locations()->add (location, true);
2232 XMLNode &after = _session->locations()->get_state();
2233 _session->add_command(new MementoCommand<Locations>(*(_session->locations()), &before, &after));
2235 commit_reversible_command ();
2239 Editor::add_location_mark (samplepos_t where)
2243 select_new_marker = true;
2245 _session->locations()->next_available_name(markername,"mark");
2246 if (!choose_new_marker_name(markername)) {
2249 Location *location = new Location (*_session, where, where, markername, Location::IsMark, get_grid_music_divisions (0));
2250 begin_reversible_command (_("add marker"));
2252 XMLNode &before = _session->locations()->get_state();
2253 _session->locations()->add (location, true);
2254 XMLNode &after = _session->locations()->get_state();
2255 _session->add_command(new MementoCommand<Locations>(*(_session->locations()), &before, &after));
2257 commit_reversible_command ();
2261 Editor::set_session_start_from_playhead ()
2267 if ((loc = _session->locations()->session_range_location()) == 0) { //should never happen
2268 _session->set_session_extents (_session->audible_sample(), _session->audible_sample());
2270 XMLNode &before = loc->get_state();
2272 _session->set_session_extents (_session->audible_sample(), loc->end());
2274 XMLNode &after = loc->get_state();
2276 begin_reversible_command (_("Set session start"));
2278 _session->add_command (new MementoCommand<Location>(*loc, &before, &after));
2280 commit_reversible_command ();
2283 _session->set_session_range_is_free (false);
2287 Editor::set_session_end_from_playhead ()
2293 if ((loc = _session->locations()->session_range_location()) == 0) { //should never happen
2294 _session->set_session_extents (_session->audible_sample(), _session->audible_sample());
2296 XMLNode &before = loc->get_state();
2298 _session->set_session_extents (loc->start(), _session->audible_sample());
2300 XMLNode &after = loc->get_state();
2302 begin_reversible_command (_("Set session start"));
2304 _session->add_command (new MementoCommand<Location>(*loc, &before, &after));
2306 commit_reversible_command ();
2309 _session->set_session_range_is_free (false);
2314 Editor::toggle_location_at_playhead_cursor ()
2316 if (!do_remove_location_at_playhead_cursor())
2318 add_location_from_playhead_cursor();
2323 Editor::add_location_from_playhead_cursor ()
2325 add_location_mark (_session->audible_sample());
2329 Editor::do_remove_location_at_playhead_cursor ()
2331 bool removed = false;
2334 XMLNode &before = _session->locations()->get_state();
2336 //find location(s) at this time
2337 Locations::LocationList locs;
2338 _session->locations()->find_all_between (_session->audible_sample(), _session->audible_sample()+1, locs, Location::Flags(0));
2339 for (Locations::LocationList::iterator i = locs.begin(); i != locs.end(); ++i) {
2340 if ((*i)->is_mark()) {
2341 _session->locations()->remove (*i);
2348 begin_reversible_command (_("remove marker"));
2349 XMLNode &after = _session->locations()->get_state();
2350 _session->add_command(new MementoCommand<Locations>(*(_session->locations()), &before, &after));
2351 commit_reversible_command ();
2358 Editor::remove_location_at_playhead_cursor ()
2360 do_remove_location_at_playhead_cursor ();
2363 /** Add a range marker around each selected region */
2365 Editor::add_locations_from_region ()
2367 RegionSelection rs = get_regions_from_selection_and_entered ();
2372 bool commit = false;
2374 XMLNode &before = _session->locations()->get_state();
2376 for (RegionSelection::iterator i = rs.begin (); i != rs.end (); ++i) {
2378 boost::shared_ptr<Region> region = (*i)->region ();
2380 Location *location = new Location (*_session, region->position(), region->last_sample(), region->name(), Location::IsRangeMarker, 0);
2382 _session->locations()->add (location, true);
2387 begin_reversible_command (selection->regions.size () > 1 ? _("add markers") : _("add marker"));
2388 XMLNode &after = _session->locations()->get_state();
2389 _session->add_command (new MementoCommand<Locations>(*(_session->locations()), &before, &after));
2390 commit_reversible_command ();
2394 /** Add a single range marker around all selected regions */
2396 Editor::add_location_from_region ()
2398 RegionSelection rs = get_regions_from_selection_and_entered ();
2404 XMLNode &before = _session->locations()->get_state();
2408 if (rs.size() > 1) {
2409 _session->locations()->next_available_name(markername, "regions");
2411 RegionView* rv = *(rs.begin());
2412 boost::shared_ptr<Region> region = rv->region();
2413 markername = region->name();
2416 if (!choose_new_marker_name(markername)) {
2420 // single range spanning all selected
2421 Location *location = new Location (*_session, selection->regions.start(), selection->regions.end_sample(), markername, Location::IsRangeMarker, 0);
2422 _session->locations()->add (location, true);
2424 begin_reversible_command (_("add marker"));
2425 XMLNode &after = _session->locations()->get_state();
2426 _session->add_command (new MementoCommand<Locations>(*(_session->locations()), &before, &after));
2427 commit_reversible_command ();
2433 Editor::jump_forward_to_mark ()
2439 samplepos_t pos = _session->locations()->first_mark_after (playhead_cursor->current_sample());
2445 _session->request_locate (pos, _session->transport_rolling());
2449 Editor::jump_backward_to_mark ()
2455 samplepos_t pos = _session->locations()->first_mark_before (playhead_cursor->current_sample());
2457 //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...
2458 if (_session->transport_rolling()) {
2459 if ((playhead_cursor->current_sample() - pos) < _session->sample_rate()/2) {
2460 samplepos_t prior = _session->locations()->first_mark_before (pos);
2469 _session->request_locate (pos, _session->transport_rolling());
2475 samplepos_t const pos = _session->audible_sample ();
2478 _session->locations()->next_available_name (markername, "mark");
2480 if (!choose_new_marker_name (markername)) {
2484 _session->locations()->add (new Location (*_session, pos, 0, markername, Location::IsMark, 0), true);
2488 Editor::clear_markers ()
2491 begin_reversible_command (_("clear markers"));
2493 XMLNode &before = _session->locations()->get_state();
2494 _session->locations()->clear_markers ();
2495 XMLNode &after = _session->locations()->get_state();
2496 _session->add_command(new MementoCommand<Locations>(*(_session->locations()), &before, &after));
2498 commit_reversible_command ();
2503 Editor::clear_ranges ()
2506 begin_reversible_command (_("clear ranges"));
2508 XMLNode &before = _session->locations()->get_state();
2510 _session->locations()->clear_ranges ();
2512 XMLNode &after = _session->locations()->get_state();
2513 _session->add_command(new MementoCommand<Locations>(*(_session->locations()), &before, &after));
2515 commit_reversible_command ();
2520 Editor::clear_locations ()
2522 begin_reversible_command (_("clear locations"));
2524 XMLNode &before = _session->locations()->get_state();
2525 _session->locations()->clear ();
2526 XMLNode &after = _session->locations()->get_state();
2527 _session->add_command(new MementoCommand<Locations>(*(_session->locations()), &before, &after));
2529 commit_reversible_command ();
2533 Editor::unhide_markers ()
2535 for (LocationMarkerMap::iterator i = location_markers.begin(); i != location_markers.end(); ++i) {
2536 Location *l = (*i).first;
2537 if (l->is_hidden() && l->is_mark()) {
2538 l->set_hidden(false, this);
2544 Editor::unhide_ranges ()
2546 for (LocationMarkerMap::iterator i = location_markers.begin(); i != location_markers.end(); ++i) {
2547 Location *l = (*i).first;
2548 if (l->is_hidden() && l->is_range_marker()) {
2549 l->set_hidden(false, this);
2554 /* INSERT/REPLACE */
2557 Editor::insert_source_list_selection (float times)
2559 RouteTimeAxisView *tv = 0;
2560 boost::shared_ptr<Playlist> playlist;
2562 if (clicked_routeview != 0) {
2563 tv = clicked_routeview;
2564 } else if (!selection->tracks.empty()) {
2565 if ((tv = dynamic_cast<RouteTimeAxisView*>(selection->tracks.front())) == 0) {
2568 } else if (entered_track != 0) {
2569 if ((tv = dynamic_cast<RouteTimeAxisView*>(entered_track)) == 0) {
2576 if ((playlist = tv->playlist()) == 0) {
2580 boost::shared_ptr<Region> region = _sources->get_single_selection ();
2585 begin_reversible_command (_("insert region"));
2586 playlist->clear_changes ();
2587 playlist->add_region ((RegionFactory::create (region, true)), get_preferred_edit_position(), times);
2588 if (Config->get_edit_mode() == Ripple)
2589 playlist->ripple (get_preferred_edit_position(), region->length() * times, boost::shared_ptr<Region>());
2591 _session->add_command(new StatefulDiffCommand (playlist));
2592 commit_reversible_command ();
2595 /* BUILT-IN EFFECTS */
2598 Editor::reverse_selection ()
2603 /* GAIN ENVELOPE EDITING */
2606 Editor::edit_envelope ()
2613 Editor::transition_to_rolling (bool fwd)
2619 if (_session->config.get_external_sync()) {
2620 switch (TransportMasterManager::instance().current()->type()) {
2624 /* transport controlled by the master */
2629 if (_session->is_auditioning()) {
2630 _session->cancel_audition ();
2634 _session->request_transport_speed (fwd ? 1.0f : -1.0f);
2638 Editor::play_from_start ()
2640 _session->request_locate (_session->current_start_sample(), true);
2644 Editor::play_from_edit_point ()
2646 _session->request_locate (get_preferred_edit_position(), true);
2650 Editor::play_from_edit_point_and_return ()
2652 samplepos_t start_sample;
2653 samplepos_t return_sample;
2655 start_sample = get_preferred_edit_position (EDIT_IGNORE_PHEAD);
2657 if (_session->transport_rolling()) {
2658 _session->request_locate (start_sample, false);
2662 /* don't reset the return sample if its already set */
2664 if ((return_sample = _session->requested_return_sample()) < 0) {
2665 return_sample = _session->audible_sample();
2668 if (start_sample >= 0) {
2669 _session->request_roll_at_and_return (start_sample, return_sample);
2674 Editor::play_selection ()
2676 samplepos_t start, end;
2677 if (!get_selection_extents (start, end))
2680 AudioRange ar (start, end, 0);
2681 list<AudioRange> lar;
2684 _session->request_play_range (&lar, true);
2689 Editor::maybe_locate_with_edit_preroll (samplepos_t location)
2691 if (_session->transport_rolling() || !UIConfiguration::instance().get_follow_edits() || _session->config.get_external_sync())
2694 location -= _session->preroll_samples (location);
2696 //don't try to locate before the beginning of time
2701 //if follow_playhead is on, keep the playhead on the screen
2702 if (_follow_playhead)
2703 if (location < _leftmost_sample)
2704 location = _leftmost_sample;
2706 _session->request_locate (location);
2710 Editor::play_with_preroll ()
2712 samplepos_t start, end;
2713 if (UIConfiguration::instance().get_follow_edits() && get_selection_extents (start, end)) {
2714 const samplepos_t preroll = _session->preroll_samples (start);
2716 samplepos_t ret = start;
2718 if (start > preroll) {
2719 start = start - preroll;
2722 end = end + preroll; //"post-roll"
2724 AudioRange ar (start, end, 0);
2725 list<AudioRange> lar;
2728 _session->request_play_range (&lar, true);
2729 _session->set_requested_return_sample (ret); //force auto-return to return to range start, without the preroll
2731 samplepos_t ph = playhead_cursor->current_sample ();
2732 const samplepos_t preroll = _session->preroll_samples (ph);
2735 start = ph - preroll;
2739 _session->request_locate (start, true);
2740 _session->set_requested_return_sample (ph); //force auto-return to return to playhead location, without the preroll
2745 Editor::rec_with_preroll ()
2747 samplepos_t ph = playhead_cursor->current_sample ();
2748 samplepos_t preroll = _session->preroll_samples (ph);
2749 _session->request_preroll_record_trim (ph, preroll);
2753 Editor::rec_with_count_in ()
2755 _session->request_count_in_record ();
2759 Editor::play_location (Location& location)
2761 if (location.start() <= location.end()) {
2765 _session->request_bounded_roll (location.start(), location.end());
2769 Editor::loop_location (Location& location)
2771 if (location.start() <= location.end()) {
2777 if ((tll = transport_loop_location()) != 0) {
2778 tll->set (location.start(), location.end());
2780 // enable looping, reposition and start rolling
2781 _session->request_locate (tll->start(), true);
2782 _session->request_play_loop (true);
2787 Editor::do_layer_operation (LayerOperation op)
2789 if (selection->regions.empty ()) {
2793 bool const multiple = selection->regions.size() > 1;
2797 begin_reversible_command (_("raise regions"));
2799 begin_reversible_command (_("raise region"));
2805 begin_reversible_command (_("raise regions to top"));
2807 begin_reversible_command (_("raise region to top"));
2813 begin_reversible_command (_("lower regions"));
2815 begin_reversible_command (_("lower region"));
2821 begin_reversible_command (_("lower regions to bottom"));
2823 begin_reversible_command (_("lower region"));
2828 set<boost::shared_ptr<Playlist> > playlists = selection->regions.playlists ();
2829 for (set<boost::shared_ptr<Playlist> >::iterator i = playlists.begin(); i != playlists.end(); ++i) {
2830 (*i)->clear_owned_changes ();
2833 for (RegionSelection::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
2834 boost::shared_ptr<Region> r = (*i)->region ();
2846 r->lower_to_bottom ();
2850 for (set<boost::shared_ptr<Playlist> >::iterator i = playlists.begin(); i != playlists.end(); ++i) {
2851 vector<Command*> cmds;
2853 _session->add_commands (cmds);
2856 commit_reversible_command ();
2860 Editor::raise_region ()
2862 do_layer_operation (Raise);
2866 Editor::raise_region_to_top ()
2868 do_layer_operation (RaiseToTop);
2872 Editor::lower_region ()
2874 do_layer_operation (Lower);
2878 Editor::lower_region_to_bottom ()
2880 do_layer_operation (LowerToBottom);
2883 /** Show the region editor for the selected regions */
2885 Editor::show_region_properties ()
2887 selection->foreach_regionview (&RegionView::show_region_editor);
2890 /** Show the midi list editor for the selected MIDI regions */
2892 Editor::show_midi_list_editor ()
2894 selection->foreach_midi_regionview (&MidiRegionView::show_list_editor);
2898 Editor::rename_region ()
2900 RegionSelection rs = get_regions_from_selection_and_entered ();
2906 ArdourDialog d (_("Rename Region"), true, false);
2908 Label label (_("New name:"));
2911 hbox.set_spacing (6);
2912 hbox.pack_start (label, false, false);
2913 hbox.pack_start (entry, true, true);
2915 d.get_vbox()->set_border_width (12);
2916 d.get_vbox()->pack_start (hbox, false, false);
2918 d.add_button(Gtk::Stock::CANCEL, Gtk::RESPONSE_CANCEL);
2919 d.add_button(Gtk::Stock::OK, Gtk::RESPONSE_OK);
2921 d.set_size_request (300, -1);
2923 entry.set_text (rs.front()->region()->name());
2924 entry.select_region (0, -1);
2926 entry.signal_activate().connect (sigc::bind (sigc::mem_fun (d, &Dialog::response), RESPONSE_OK));
2932 int const ret = d.run();
2936 if (ret != RESPONSE_OK) {
2940 std::string str = entry.get_text();
2941 strip_whitespace_edges (str);
2943 rs.front()->region()->set_name (str);
2944 _regions->redisplay ();
2948 /** Start an audition of the first selected region */
2950 Editor::play_edit_range ()
2952 samplepos_t start, end;
2954 if (get_edit_op_range (start, end)) {
2955 _session->request_bounded_roll (start, end);
2960 Editor::play_selected_region ()
2962 samplepos_t start = max_samplepos;
2963 samplepos_t end = 0;
2965 RegionSelection rs = get_regions_from_selection_and_entered ();
2971 for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
2972 if ((*i)->region()->position() < start) {
2973 start = (*i)->region()->position();
2975 if ((*i)->region()->last_sample() + 1 > end) {
2976 end = (*i)->region()->last_sample() + 1;
2980 _session->request_bounded_roll (start, end);
2984 Editor::audition_playlist_region_standalone (boost::shared_ptr<Region> region)
2986 _session->audition_region (region);
2990 Editor::region_from_selection ()
2992 if (clicked_axisview == 0) {
2996 if (selection->time.empty()) {
3000 samplepos_t start = selection->time[clicked_selection].start;
3001 samplepos_t end = selection->time[clicked_selection].end;
3003 TrackViewList tracks = get_tracks_for_range_action ();
3005 samplepos_t selection_cnt = end - start + 1;
3007 for (TrackSelection::iterator i = tracks.begin(); i != tracks.end(); ++i) {
3008 boost::shared_ptr<Region> current;
3009 boost::shared_ptr<Playlist> pl;
3010 samplepos_t internal_start;
3013 if ((pl = (*i)->playlist()) == 0) {
3017 if ((current = pl->top_region_at (start)) == 0) {
3021 internal_start = start - current->position();
3022 RegionFactory::region_name (new_name, current->name(), true);
3026 plist.add (ARDOUR::Properties::start, current->start() + internal_start);
3027 plist.add (ARDOUR::Properties::length, selection_cnt);
3028 plist.add (ARDOUR::Properties::name, new_name);
3029 plist.add (ARDOUR::Properties::layer, 0);
3031 boost::shared_ptr<Region> region (RegionFactory::create (current, plist));
3036 Editor::create_region_from_selection (vector<boost::shared_ptr<Region> >& new_regions)
3038 if (selection->time.empty() || selection->tracks.empty()) {
3042 samplepos_t start, end;
3043 if (clicked_selection) {
3044 start = selection->time[clicked_selection].start;
3045 end = selection->time[clicked_selection].end;
3047 start = selection->time.start();
3048 end = selection->time.end_sample();
3051 TrackViewList ts = selection->tracks.filter_to_unique_playlists ();
3052 sort_track_selection (ts);
3054 for (TrackSelection::iterator i = ts.begin(); i != ts.end(); ++i) {
3055 boost::shared_ptr<Region> current;
3056 boost::shared_ptr<Playlist> playlist;
3057 samplepos_t internal_start;
3060 if ((playlist = (*i)->playlist()) == 0) {
3064 if ((current = playlist->top_region_at(start)) == 0) {
3068 internal_start = start - current->position();
3069 RegionFactory::region_name (new_name, current->name(), true);
3073 plist.add (ARDOUR::Properties::start, current->start() + internal_start);
3074 plist.add (ARDOUR::Properties::length, end - start + 1);
3075 plist.add (ARDOUR::Properties::name, new_name);
3077 new_regions.push_back (RegionFactory::create (current, plist));
3082 Editor::split_multichannel_region ()
3084 RegionSelection rs = get_regions_from_selection_and_entered ();
3090 vector< boost::shared_ptr<Region> > v;
3092 for (list<RegionView*>::iterator x = rs.begin(); x != rs.end(); ++x) {
3093 (*x)->region()->separate_by_channel (v);
3098 Editor::new_region_from_selection ()
3100 region_from_selection ();
3101 cancel_selection ();
3105 add_if_covered (RegionView* rv, const AudioRange* ar, RegionSelection* rs)
3107 switch (rv->region()->coverage (ar->start, ar->end - 1)) {
3108 // n.b. -1 because AudioRange::end is one past the end, but coverage expects inclusive ranges
3109 case Evoral::OverlapNone:
3117 * - selected tracks, or if there are none...
3118 * - tracks containing selected regions, or if there are none...
3123 Editor::get_tracks_for_range_action () const
3127 if (selection->tracks.empty()) {
3129 /* use tracks with selected regions */
3131 RegionSelection rs = selection->regions;
3133 for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
3134 TimeAxisView* tv = &(*i)->get_time_axis_view();
3136 if (!t.contains (tv)) {
3142 /* no regions and no tracks: use all tracks */
3148 t = selection->tracks;
3151 return t.filter_to_unique_playlists();
3155 Editor::separate_regions_between (const TimeSelection& ts)
3157 bool in_command = false;
3158 boost::shared_ptr<Playlist> playlist;
3159 RegionSelection new_selection;
3161 TrackViewList tmptracks = get_tracks_for_range_action ();
3162 sort_track_selection (tmptracks);
3164 for (TrackSelection::iterator i = tmptracks.begin(); i != tmptracks.end(); ++i) {
3166 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> ((*i));
3172 if (!rtv->is_track()) {
3176 /* no edits to destructive tracks */
3178 if (rtv->track()->destructive()) {
3182 if ((playlist = rtv->playlist()) != 0) {
3184 playlist->clear_changes ();
3186 /* XXX need to consider musical time selections here at some point */
3188 for (list<AudioRange>::const_iterator t = ts.begin(); t != ts.end(); ++t) {
3190 sigc::connection c = rtv->view()->RegionViewAdded.connect (
3191 sigc::mem_fun(*this, &Editor::collect_new_region_view));
3193 latest_regionviews.clear ();
3195 playlist->partition ((*t).start, (*t).end, false);
3199 if (!latest_regionviews.empty()) {
3201 rtv->view()->foreach_regionview (sigc::bind (
3202 sigc::ptr_fun (add_if_covered),
3203 &(*t), &new_selection));
3206 begin_reversible_command (_("separate"));
3210 /* pick up changes to existing regions */
3212 vector<Command*> cmds;
3213 playlist->rdiff (cmds);
3214 _session->add_commands (cmds);
3216 /* pick up changes to the playlist itself (adds/removes)
3219 _session->add_command(new StatefulDiffCommand (playlist));
3227 RangeSelectionAfterSplit rsas = Config->get_range_selection_after_split();
3229 //if our config preference says to clear the selection, clear the Range selection
3230 if (rsas == ClearSel) {
3231 selection->clear_time();
3232 //but leave track selection intact
3233 } else if (rsas == ForceSel) {
3234 //note: forcing the regions to be selected *might* force a tool-change to Object here
3235 selection->set(new_selection);
3238 commit_reversible_command ();
3242 struct PlaylistState {
3243 boost::shared_ptr<Playlist> playlist;
3247 /** Take tracks from get_tracks_for_range_action and cut any regions
3248 * on those tracks so that the tracks are empty over the time
3252 Editor::separate_region_from_selection ()
3254 /* preferentially use *all* ranges in the time selection if we're in range mode
3255 to allow discontiguous operation, since get_edit_op_range() currently
3256 returns a single range.
3259 if (!selection->time.empty()) {
3261 separate_regions_between (selection->time);
3268 if (get_edit_op_range (start, end)) {
3270 AudioRange ar (start, end, 1);
3274 separate_regions_between (ts);
3280 Editor::separate_region_from_punch ()
3282 Location* loc = _session->locations()->auto_punch_location();
3284 separate_regions_using_location (*loc);
3289 Editor::separate_region_from_loop ()
3291 Location* loc = _session->locations()->auto_loop_location();
3293 separate_regions_using_location (*loc);
3298 Editor::separate_regions_using_location (Location& loc)
3300 if (loc.is_mark()) {
3304 AudioRange ar (loc.start(), loc.end(), 1);
3309 separate_regions_between (ts);
3312 /** Separate regions under the selected region */
3314 Editor::separate_under_selected_regions ()
3316 vector<PlaylistState> playlists;
3320 rs = get_regions_from_selection_and_entered();
3322 if (!_session || rs.empty()) {
3326 begin_reversible_command (_("separate region under"));
3328 list<boost::shared_ptr<Region> > regions_to_remove;
3330 for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
3331 // we can't just remove the region(s) in this loop because
3332 // this removes them from the RegionSelection, and they thus
3333 // disappear from underneath the iterator, and the ++i above
3334 // SEGVs in a puzzling fashion.
3336 // so, first iterate over the regions to be removed from rs and
3337 // add them to the regions_to_remove list, and then
3338 // iterate over the list to actually remove them.
3340 regions_to_remove.push_back ((*i)->region());
3343 for (list<boost::shared_ptr<Region> >::iterator rl = regions_to_remove.begin(); rl != regions_to_remove.end(); ++rl) {
3345 boost::shared_ptr<Playlist> playlist = (*rl)->playlist();
3348 // is this check necessary?
3352 vector<PlaylistState>::iterator i;
3354 //only take state if this is a new playlist.
3355 for (i = playlists.begin(); i != playlists.end(); ++i) {
3356 if ((*i).playlist == playlist) {
3361 if (i == playlists.end()) {
3363 PlaylistState before;
3364 before.playlist = playlist;
3365 before.before = &playlist->get_state();
3366 playlist->clear_changes ();
3367 playlist->freeze ();
3368 playlists.push_back(before);
3371 //Partition on the region bounds
3372 playlist->partition ((*rl)->first_sample() - 1, (*rl)->last_sample() + 1, true);
3374 //Re-add region that was just removed due to the partition operation
3375 playlist->add_region ((*rl), (*rl)->first_sample());
3378 vector<PlaylistState>::iterator pl;
3380 for (pl = playlists.begin(); pl != playlists.end(); ++pl) {
3381 (*pl).playlist->thaw ();
3382 _session->add_command(new MementoCommand<Playlist>(*(*pl).playlist, (*pl).before, &(*pl).playlist->get_state()));
3385 commit_reversible_command ();
3389 Editor::crop_region_to_selection ()
3391 if (!selection->time.empty()) {
3393 begin_reversible_command (_("Crop Regions to Time Selection"));
3394 for (std::list<AudioRange>::iterator i = selection->time.begin(); i != selection->time.end(); ++i) {
3395 crop_region_to ((*i).start, (*i).end);
3397 commit_reversible_command();
3403 if (get_edit_op_range (start, end)) {
3404 begin_reversible_command (_("Crop Regions to Edit Range"));
3406 crop_region_to (start, end);
3408 commit_reversible_command();
3415 Editor::crop_region_to (samplepos_t start, samplepos_t end)
3417 vector<boost::shared_ptr<Playlist> > playlists;
3418 boost::shared_ptr<Playlist> playlist;
3421 if (selection->tracks.empty()) {
3422 ts = track_views.filter_to_unique_playlists();
3424 ts = selection->tracks.filter_to_unique_playlists ();
3427 sort_track_selection (ts);
3429 for (TrackSelection::iterator i = ts.begin(); i != ts.end(); ++i) {
3431 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> ((*i));
3437 boost::shared_ptr<Track> t = rtv->track();
3439 if (t != 0 && ! t->destructive()) {
3441 if ((playlist = rtv->playlist()) != 0) {
3442 playlists.push_back (playlist);
3447 if (playlists.empty()) {
3452 samplepos_t new_start;
3453 samplepos_t new_end;
3454 samplecnt_t new_length;
3456 for (vector<boost::shared_ptr<Playlist> >::iterator i = playlists.begin(); i != playlists.end(); ++i) {
3458 /* Only the top regions at start and end have to be cropped */
3459 boost::shared_ptr<Region> region_at_start = (*i)->top_region_at(start);
3460 boost::shared_ptr<Region> region_at_end = (*i)->top_region_at(end);
3462 vector<boost::shared_ptr<Region> > regions;
3464 if (region_at_start != 0) {
3465 regions.push_back (region_at_start);
3467 if (region_at_end != 0) {
3468 regions.push_back (region_at_end);
3471 /* now adjust lengths */
3472 for (vector<boost::shared_ptr<Region> >::iterator i = regions.begin(); i != regions.end(); ++i) {
3474 pos = (*i)->position();
3475 new_start = max (start, pos);
3476 if (max_samplepos - pos > (*i)->length()) {
3477 new_end = pos + (*i)->length() - 1;
3479 new_end = max_samplepos;
3481 new_end = min (end, new_end);
3482 new_length = new_end - new_start + 1;
3484 (*i)->clear_changes ();
3485 (*i)->trim_to (new_start, new_length);
3486 _session->add_command (new StatefulDiffCommand (*i));
3492 Editor::region_fill_track ()
3494 boost::shared_ptr<Playlist> playlist;
3495 RegionSelection regions = get_regions_from_selection_and_entered ();
3496 RegionSelection foo;
3498 samplepos_t const end = _session->current_end_sample ();
3500 if (regions.empty () || regions.end_sample () + 1 >= end) {
3504 samplepos_t const start_sample = regions.start ();
3505 samplepos_t const end_sample = regions.end_sample ();
3506 samplecnt_t const gap = end_sample - start_sample + 1;
3508 begin_reversible_command (Operations::region_fill);
3510 selection->clear_regions ();
3512 for (RegionSelection::iterator i = regions.begin(); i != regions.end(); ++i) {
3514 boost::shared_ptr<Region> r ((*i)->region());
3516 TimeAxisView& tv = (*i)->get_time_axis_view();
3517 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (&tv);
3518 latest_regionviews.clear ();
3519 sigc::connection c = rtv->view()->RegionViewAdded.connect (sigc::mem_fun(*this, &Editor::collect_new_region_view));
3521 samplepos_t const position = end_sample + (r->first_sample() - start_sample + 1);
3522 playlist = (*i)->region()->playlist();
3523 playlist->clear_changes ();
3524 playlist->duplicate_until (r, position, gap, end);
3525 _session->add_command(new StatefulDiffCommand (playlist));
3529 foo.insert (foo.end(), latest_regionviews.begin(), latest_regionviews.end());
3533 selection->set (foo);
3536 commit_reversible_command ();
3540 Editor::set_region_sync_position ()
3542 set_sync_point (get_preferred_edit_position (), get_regions_from_selection_and_edit_point ());
3546 Editor::set_sync_point (samplepos_t where, const RegionSelection& rs)
3548 bool in_command = false;
3550 for (RegionSelection::const_iterator r = rs.begin(); r != rs.end(); ++r) {
3552 if (!(*r)->region()->covers (where)) {
3556 boost::shared_ptr<Region> region ((*r)->region());
3559 begin_reversible_command (_("set sync point"));
3563 region->clear_changes ();
3564 region->set_sync_position (where);
3565 _session->add_command(new StatefulDiffCommand (region));
3569 commit_reversible_command ();
3573 /** Remove the sync positions of the selection */
3575 Editor::remove_region_sync ()
3577 RegionSelection rs = get_regions_from_selection_and_entered ();
3583 begin_reversible_command (_("remove region sync"));
3585 for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
3587 (*i)->region()->clear_changes ();
3588 (*i)->region()->clear_sync_position ();
3589 _session->add_command(new StatefulDiffCommand ((*i)->region()));
3592 commit_reversible_command ();
3596 Editor::naturalize_region ()
3598 RegionSelection rs = get_regions_from_selection_and_entered ();
3604 if (rs.size() > 1) {
3605 begin_reversible_command (_("move regions to original position"));
3607 begin_reversible_command (_("move region to original position"));
3610 for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
3611 (*i)->region()->clear_changes ();
3612 (*i)->region()->move_to_natural_position ();
3613 _session->add_command (new StatefulDiffCommand ((*i)->region()));
3616 commit_reversible_command ();
3620 Editor::align_regions (RegionPoint what)
3622 RegionSelection const rs = get_regions_from_selection_and_edit_point ();
3628 begin_reversible_command (_("align selection"));
3630 samplepos_t const position = get_preferred_edit_position ();
3632 for (RegionSelection::const_iterator i = rs.begin(); i != rs.end(); ++i) {
3633 align_region_internal ((*i)->region(), what, position);
3636 commit_reversible_command ();
3639 struct RegionSortByTime {
3640 bool operator() (const RegionView* a, const RegionView* b) {
3641 return a->region()->position() < b->region()->position();
3646 Editor::align_regions_relative (RegionPoint point)
3648 RegionSelection const rs = get_regions_from_selection_and_edit_point ();
3654 samplepos_t const position = get_preferred_edit_position ();
3656 samplepos_t distance = 0;
3657 samplepos_t pos = 0;
3660 list<RegionView*> sorted;
3661 rs.by_position (sorted);
3663 boost::shared_ptr<Region> r ((*sorted.begin())->region());
3668 if (position > r->position()) {
3669 distance = position - r->position();
3671 distance = r->position() - position;
3677 if (position > r->last_sample()) {
3678 distance = position - r->last_sample();
3679 pos = r->position() + distance;
3681 distance = r->last_sample() - position;
3682 pos = r->position() - distance;
3688 pos = r->adjust_to_sync (position);
3689 if (pos > r->position()) {
3690 distance = pos - r->position();
3692 distance = r->position() - pos;
3698 if (pos == r->position()) {
3702 begin_reversible_command (_("align selection (relative)"));
3704 /* move first one specially */
3706 r->clear_changes ();
3707 r->set_position (pos);
3708 _session->add_command(new StatefulDiffCommand (r));
3710 /* move rest by the same amount */
3714 for (list<RegionView*>::iterator i = sorted.begin(); i != sorted.end(); ++i) {
3716 boost::shared_ptr<Region> region ((*i)->region());
3718 region->clear_changes ();
3721 region->set_position (region->position() + distance);
3723 region->set_position (region->position() - distance);
3726 _session->add_command(new StatefulDiffCommand (region));
3730 commit_reversible_command ();
3734 Editor::align_region (boost::shared_ptr<Region> region, RegionPoint point, samplepos_t position)
3736 begin_reversible_command (_("align region"));
3737 align_region_internal (region, point, position);
3738 commit_reversible_command ();
3742 Editor::align_region_internal (boost::shared_ptr<Region> region, RegionPoint point, samplepos_t position)
3744 region->clear_changes ();
3748 region->set_position (region->adjust_to_sync (position));
3752 if (position > region->length()) {
3753 region->set_position (position - region->length());
3758 region->set_position (position);
3762 _session->add_command(new StatefulDiffCommand (region));
3766 Editor::trim_region_front ()
3772 Editor::trim_region_back ()
3774 trim_region (false);
3778 Editor::trim_region (bool front)
3780 samplepos_t where = get_preferred_edit_position();
3781 RegionSelection rs = get_regions_from_selection_and_edit_point ();
3787 begin_reversible_command (front ? _("trim front") : _("trim back"));
3789 for (list<RegionView*>::const_iterator i = rs.by_layer().begin(); i != rs.by_layer().end(); ++i) {
3790 if (!(*i)->region()->locked()) {
3792 (*i)->region()->clear_changes ();
3795 (*i)->region()->trim_front (where);
3797 (*i)->region()->trim_end (where);
3800 _session->add_command (new StatefulDiffCommand ((*i)->region()));
3804 commit_reversible_command ();
3807 /** Trim the end of the selected regions to the position of the edit cursor */
3809 Editor::trim_region_to_loop ()
3811 Location* loc = _session->locations()->auto_loop_location();
3815 trim_region_to_location (*loc, _("trim to loop"));
3819 Editor::trim_region_to_punch ()
3821 Location* loc = _session->locations()->auto_punch_location();
3825 trim_region_to_location (*loc, _("trim to punch"));
3829 Editor::trim_region_to_location (const Location& loc, const char* str)
3831 RegionSelection rs = get_regions_from_selection_and_entered ();
3832 bool in_command = false;
3834 for (RegionSelection::iterator x = rs.begin(); x != rs.end(); ++x) {
3835 RegionView* rv = (*x);
3837 /* require region to span proposed trim */
3838 switch (rv->region()->coverage (loc.start(), loc.end())) {
3839 case Evoral::OverlapInternal:
3845 RouteTimeAxisView* tav = dynamic_cast<RouteTimeAxisView*> (&rv->get_time_axis_view());
3853 start = loc.start();
3856 rv->region()->clear_changes ();
3857 rv->region()->trim_to (start, (end - start));
3860 begin_reversible_command (str);
3863 _session->add_command(new StatefulDiffCommand (rv->region()));
3867 commit_reversible_command ();
3872 Editor::trim_region_to_previous_region_end ()
3874 return trim_to_region(false);
3878 Editor::trim_region_to_next_region_start ()
3880 return trim_to_region(true);
3884 Editor::trim_to_region(bool forward)
3886 RegionSelection rs = get_regions_from_selection_and_entered ();
3887 bool in_command = false;
3889 boost::shared_ptr<Region> next_region;
3891 for (RegionSelection::iterator x = rs.begin(); x != rs.end(); ++x) {
3893 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (*x);
3899 AudioTimeAxisView* atav = dynamic_cast<AudioTimeAxisView*> (&arv->get_time_axis_view());
3905 boost::shared_ptr<Region> region = arv->region();
3906 boost::shared_ptr<Playlist> playlist (region->playlist());
3908 region->clear_changes ();
3912 next_region = playlist->find_next_region (region->first_sample(), Start, 1);
3918 region->trim_end (next_region->first_sample() - 1);
3919 arv->region_changed (PropertyChange (ARDOUR::Properties::length));
3923 next_region = playlist->find_next_region (region->first_sample(), Start, 0);
3929 region->trim_front (next_region->last_sample() + 1);
3930 arv->region_changed (ARDOUR::bounds_change);
3934 begin_reversible_command (_("trim to region"));
3937 _session->add_command(new StatefulDiffCommand (region));
3941 commit_reversible_command ();
3946 Editor::unfreeze_route ()
3948 if (clicked_routeview == 0 || !clicked_routeview->is_track()) {
3952 clicked_routeview->track()->unfreeze ();
3956 Editor::_freeze_thread (void* arg)
3958 return static_cast<Editor*>(arg)->freeze_thread ();
3962 Editor::freeze_thread ()
3964 /* create event pool because we may need to talk to the session */
3965 SessionEvent::create_per_thread_pool ("freeze events", 64);
3966 /* create per-thread buffers for process() tree to use */
3967 clicked_routeview->audio_track()->freeze_me (*current_interthread_info);
3968 current_interthread_info->done = true;
3973 Editor::freeze_route ()
3979 /* stop transport before we start. this is important */
3981 _session->request_transport_speed (0.0);
3983 /* wait for just a little while, because the above call is asynchronous */
3985 Glib::usleep (250000);
3987 if (clicked_routeview == 0 || !clicked_routeview->is_audio_track()) {
3991 if (!clicked_routeview->track()->bounceable (clicked_routeview->track()->main_outs(), true)) {
3993 _("This track/bus cannot be frozen because the signal adds or loses channels before reaching the outputs.\n"
3994 "This is typically caused by plugins that generate stereo output from mono input or vice versa.")
3996 d.set_title (_("Cannot freeze"));
4001 if (clicked_routeview->track()->has_external_redirects()) {
4002 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"
4003 "Freezing will only process the signal as far as the first send/insert/return."),
4004 clicked_routeview->track()->name()), true, MESSAGE_INFO, BUTTONS_NONE, true);
4006 d.add_button (_("Freeze anyway"), Gtk::RESPONSE_OK);
4007 d.add_button (_("Don't freeze"), Gtk::RESPONSE_CANCEL);
4008 d.set_title (_("Freeze Limits"));
4010 int response = d.run ();
4013 case Gtk::RESPONSE_CANCEL:
4020 InterThreadInfo itt;
4021 current_interthread_info = &itt;
4023 InterthreadProgressWindow ipw (current_interthread_info, _("Freeze"), _("Cancel Freeze"));
4025 pthread_create_and_store (X_("freezer"), &itt.thread, _freeze_thread, this);
4027 CursorContext::Handle cursor_ctx = CursorContext::create(*this, _cursors->wait);
4029 while (!itt.done && !itt.cancel) {
4030 gtk_main_iteration ();
4033 pthread_join (itt.thread, 0);
4034 current_interthread_info = 0;
4038 Editor::bounce_range_selection (bool replace, bool enable_processing)
4040 if (selection->time.empty()) {
4044 TrackSelection views = selection->tracks;
4046 for (TrackViewList::iterator i = views.begin(); i != views.end(); ++i) {
4048 if (enable_processing) {
4050 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (*i);
4052 if (rtv && rtv->track() && replace && enable_processing && !rtv->track()->bounceable (rtv->track()->main_outs(), false)) {
4054 _("You can't perform this operation because the processing of the signal "
4055 "will cause one or more of the tracks to end up with a region with more channels than this track has inputs.\n\n"
4056 "You can do this without processing, which is a different operation.")
4058 d.set_title (_("Cannot bounce"));
4065 samplepos_t start = selection->time[clicked_selection].start;
4066 samplepos_t end = selection->time[clicked_selection].end;
4067 samplepos_t cnt = end - start + 1;
4068 bool in_command = false;
4070 for (TrackViewList::iterator i = views.begin(); i != views.end(); ++i) {
4072 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (*i);
4078 boost::shared_ptr<Playlist> playlist;
4080 if ((playlist = rtv->playlist()) == 0) {
4084 InterThreadInfo itt;
4086 playlist->clear_changes ();
4087 playlist->clear_owned_changes ();
4089 boost::shared_ptr<Region> r;
4091 if (enable_processing) {
4092 r = rtv->track()->bounce_range (start, start+cnt, itt, rtv->track()->main_outs(), false);
4094 r = rtv->track()->bounce_range (start, start+cnt, itt, boost::shared_ptr<Processor>(), false);
4102 list<AudioRange> ranges;
4103 ranges.push_back (AudioRange (start, start+cnt, 0));
4104 playlist->cut (ranges); // discard result
4105 playlist->add_region (r, start);
4109 begin_reversible_command (_("bounce range"));
4112 vector<Command*> cmds;
4113 playlist->rdiff (cmds);
4114 _session->add_commands (cmds);
4116 _session->add_command (new StatefulDiffCommand (playlist));
4120 commit_reversible_command ();
4124 /** Delete selected regions, automation points or a time range */
4128 //special case: if the user is pointing in the editor/mixer strip, they may be trying to delete a plugin.
4129 //we need this because the editor-mixer strip is in the editor window, so it doesn't get the bindings from the mix window
4130 bool deleted = false;
4131 if (current_mixer_strip && current_mixer_strip == MixerStrip::entered_mixer_strip())
4132 deleted = current_mixer_strip->delete_processors ();
4138 /** Cut selected regions, automation points or a time range */
4145 /** Copy selected regions, automation points or a time range */
4153 /** @return true if a Cut, Copy or Clear is possible */
4155 Editor::can_cut_copy () const
4157 if (!selection->time.empty() || !selection->regions.empty() || !selection->points.empty())
4164 /** Cut, copy or clear selected regions, automation points or a time range.
4165 * @param op Operation (Delete, Cut, Copy or Clear)
4168 Editor::cut_copy (CutCopyOp op)
4170 /* only cancel selection if cut/copy is successful.*/
4176 opname = _("delete");
4185 opname = _("clear");
4189 /* if we're deleting something, and the mouse is still pressed,
4190 the thing we started a drag for will be gone when we release
4191 the mouse button(s). avoid this. see part 2 at the end of
4195 if (op == Delete || op == Cut || op == Clear) {
4196 if (_drags->active ()) {
4201 if (op != Delete) { //"Delete" doesn't change copy/paste buf
4202 cut_buffer->clear ();
4205 if (entered_marker) {
4207 /* cut/delete op while pointing at a marker */
4210 Location* loc = find_location_from_marker (entered_marker, ignored);
4212 if (_session && loc) {
4213 entered_marker = NULL;
4214 Glib::signal_idle().connect (sigc::bind (sigc::mem_fun(*this, &Editor::really_remove_marker), loc));
4221 switch (mouse_mode) {
4224 begin_reversible_command (opname + ' ' + X_("MIDI"));
4226 commit_reversible_command ();
4232 bool did_edit = false;
4234 if (!selection->regions.empty() || !selection->points.empty()) {
4235 begin_reversible_command (opname + ' ' + _("objects"));
4238 if (!selection->regions.empty()) {
4239 cut_copy_regions (op, selection->regions);
4241 if (op == Cut || op == Delete) {
4242 selection->clear_regions ();
4246 if (!selection->points.empty()) {
4247 cut_copy_points (op);
4249 if (op == Cut || op == Delete) {
4250 selection->clear_points ();
4253 } else if (selection->time.empty()) {
4254 samplepos_t start, end;
4255 /* no time selection, see if we can get an edit range
4258 if (get_edit_op_range (start, end)) {
4259 selection->set (start, end);
4261 } else if (!selection->time.empty()) {
4262 begin_reversible_command (opname + ' ' + _("range"));
4265 cut_copy_ranges (op);
4267 if (op == Cut || op == Delete) {
4268 selection->clear_time ();
4273 /* reset repeated paste state */
4275 last_paste_pos = -1;
4276 commit_reversible_command ();
4279 if (op == Delete || op == Cut || op == Clear) {
4285 struct AutomationRecord {
4286 AutomationRecord () : state (0) , line(NULL) {}
4287 AutomationRecord (XMLNode* s, const AutomationLine* l) : state (s) , line (l) {}
4289 XMLNode* state; ///< state before any operation
4290 const AutomationLine* line; ///< line this came from
4291 boost::shared_ptr<Evoral::ControlList> copy; ///< copied events for the cut buffer
4294 struct PointsSelectionPositionSorter {
4295 bool operator() (ControlPoint* a, ControlPoint* b) {
4296 return (*(a->model()))->when < (*(b->model()))->when;
4300 /** Cut, copy or clear selected automation points.
4301 * @param op Operation (Cut, Copy or Clear)
4304 Editor::cut_copy_points (Editing::CutCopyOp op, Temporal::Beats earliest, bool midi)
4306 if (selection->points.empty ()) {
4310 /* XXX: not ideal, as there may be more than one track involved in the point selection */
4311 _last_cut_copy_source_track = &selection->points.front()->line().trackview;
4313 /* Keep a record of the AutomationLists that we end up using in this operation */
4314 typedef std::map<boost::shared_ptr<AutomationList>, AutomationRecord> Lists;
4317 /* user could select points in any order */
4318 selection->points.sort(PointsSelectionPositionSorter ());
4320 /* Go through all selected points, making an AutomationRecord for each distinct AutomationList */
4321 for (PointSelection::iterator sel_point = selection->points.begin(); sel_point != selection->points.end(); ++sel_point) {
4322 const AutomationLine& line = (*sel_point)->line();
4323 const boost::shared_ptr<AutomationList> al = line.the_list();
4324 if (lists.find (al) == lists.end ()) {
4325 /* We haven't seen this list yet, so make a record for it. This includes
4326 taking a copy of its current state, in case this is needed for undo later.
4328 lists[al] = AutomationRecord (&al->get_state (), &line);
4332 if (op == Cut || op == Copy) {
4333 /* This operation will involve putting things in the cut buffer, so create an empty
4334 ControlList for each of our source lists to put the cut buffer data in.
4336 for (Lists::iterator i = lists.begin(); i != lists.end(); ++i) {
4337 i->second.copy = i->first->create (i->first->parameter (), i->first->descriptor());
4340 /* Add all selected points to the relevant copy ControlLists */
4341 MusicSample start (std::numeric_limits<samplepos_t>::max(), 0);
4342 for (PointSelection::iterator sel_point = selection->points.begin(); sel_point != selection->points.end(); ++sel_point) {
4343 boost::shared_ptr<AutomationList> al = (*sel_point)->line().the_list();
4344 AutomationList::const_iterator ctrl_evt = (*sel_point)->model ();
4346 lists[al].copy->fast_simple_add ((*ctrl_evt)->when, (*ctrl_evt)->value);
4348 /* Update earliest MIDI start time in beats */
4349 earliest = std::min(earliest, Temporal::Beats((*ctrl_evt)->when));
4351 /* Update earliest session start time in samples */
4352 start.sample = std::min(start.sample, (*sel_point)->line().session_position(ctrl_evt));
4356 /* Snap start time backwards, so copy/paste is snap aligned. */
4358 if (earliest == std::numeric_limits<Temporal::Beats>::max()) {
4359 earliest = Temporal::Beats(); // Weird... don't offset
4361 earliest.round_down_to_beat();
4363 if (start.sample == std::numeric_limits<double>::max()) {
4364 start.sample = 0; // Weird... don't offset
4366 snap_to(start, RoundDownMaybe);
4369 const double line_offset = midi ? earliest.to_double() : start.sample;
4370 for (Lists::iterator i = lists.begin(); i != lists.end(); ++i) {
4371 /* Correct this copy list so that it is relative to the earliest
4372 start time, so relative ordering between points is preserved
4373 when copying from several lists and the paste starts at the
4374 earliest copied piece of data. */
4375 boost::shared_ptr<Evoral::ControlList> &al_cpy = i->second.copy;
4376 for (AutomationList::iterator ctrl_evt = al_cpy->begin(); ctrl_evt != al_cpy->end(); ++ctrl_evt) {
4377 (*ctrl_evt)->when -= line_offset;
4380 /* And add it to the cut buffer */
4381 cut_buffer->add (al_cpy);
4385 if (op == Delete || op == Cut) {
4386 /* This operation needs to remove things from the main AutomationList, so do that now */
4388 for (Lists::iterator i = lists.begin(); i != lists.end(); ++i) {
4389 i->first->freeze ();
4392 /* Remove each selected point from its AutomationList */
4393 for (PointSelection::iterator sel_point = selection->points.begin(); sel_point != selection->points.end(); ++sel_point) {
4394 AutomationLine& line = (*sel_point)->line ();
4395 boost::shared_ptr<AutomationList> al = line.the_list();
4399 if (dynamic_cast<AudioRegionGainLine*> (&line)) {
4400 /* removing of first and last gain point in region gain lines is prohibited*/
4401 if (line.is_last_point (*(*sel_point)) || line.is_first_point (*(*sel_point))) {
4407 al->erase ((*sel_point)->model ());
4411 /* Thaw the lists and add undo records for them */
4412 for (Lists::iterator i = lists.begin(); i != lists.end(); ++i) {
4413 boost::shared_ptr<AutomationList> al = i->first;
4415 _session->add_command (new MementoCommand<AutomationList> (*al.get(), i->second.state, &(al->get_state ())));
4420 /** Cut, copy or clear selected automation points.
4421 * @param op Operation (Cut, Copy or Clear)
4424 Editor::cut_copy_midi (CutCopyOp op)
4426 Temporal::Beats earliest = std::numeric_limits<Temporal::Beats>::max();
4427 for (MidiRegionSelection::iterator i = selection->midi_regions.begin(); i != selection->midi_regions.end(); ++i) {
4428 MidiRegionView* mrv = dynamic_cast<MidiRegionView*>(*i);
4430 if (!mrv->selection().empty()) {
4431 earliest = std::min(earliest, (*mrv->selection().begin())->note()->time());
4433 mrv->cut_copy_clear (op);
4435 /* XXX: not ideal, as there may be more than one track involved in the selection */
4436 _last_cut_copy_source_track = &mrv->get_time_axis_view();
4440 if (!selection->points.empty()) {
4441 cut_copy_points (op, earliest, true);
4442 if (op == Cut || op == Delete) {
4443 selection->clear_points ();
4448 struct lt_playlist {
4449 bool operator () (const PlaylistState& a, const PlaylistState& b) {
4450 return a.playlist < b.playlist;
4454 struct PlaylistMapping {
4456 boost::shared_ptr<Playlist> pl;
4458 PlaylistMapping (TimeAxisView* tvp) : tv (tvp) {}
4461 /** Remove `clicked_regionview' */
4463 Editor::remove_clicked_region ()
4465 if (clicked_routeview == 0 || clicked_regionview == 0) {
4469 begin_reversible_command (_("remove region"));
4471 boost::shared_ptr<Playlist> playlist = clicked_routeview->playlist();
4472 boost::shared_ptr<Region> region = clicked_regionview->region();
4474 playlist->clear_changes ();
4475 playlist->clear_owned_changes ();
4476 playlist->remove_region (region);
4478 if (Config->get_edit_mode() == Ripple) {
4479 playlist->ripple (region->position(), - region->length(), boost::shared_ptr<Region>());
4482 /* We might have removed regions, which alters other regions' layering_index,
4483 so we need to do a recursive diff here.
4485 vector<Command*> cmds;
4486 playlist->rdiff (cmds);
4487 _session->add_commands (cmds);
4489 _session->add_command(new StatefulDiffCommand (playlist));
4490 commit_reversible_command ();
4495 Editor::recover_regions (ARDOUR::RegionList regions)
4497 #ifdef RECOVER_REGIONS_IS_WORKING
4498 begin_reversible_command (_("recover regions"));
4500 for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
4501 boost::shared_ptr<ARDOUR::Source> source = (*i)->source();
4503 RouteList routes = _session->get_routelist();
4504 for (RouteList::iterator it = routes.begin(); it != routes.end(); ++it) {
4505 boost::shared_ptr<ARDOUR::Track> track = boost::dynamic_pointer_cast<Track>(*it);
4508 if (source->captured_for() == track->) {
4509 //_session->add_command(new StatefulDiffCommand (playlist));
4515 commit_reversible_command ();
4520 /** Remove the selected regions */
4522 Editor::remove_selected_regions ()
4524 RegionSelection rs = get_regions_from_selection_and_entered ();
4526 if (!_session || rs.empty()) {
4530 list<boost::shared_ptr<Region> > regions_to_remove;
4532 for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
4533 // we can't just remove the region(s) in this loop because
4534 // this removes them from the RegionSelection, and they thus
4535 // disappear from underneath the iterator, and the ++i above
4536 // SEGVs in a puzzling fashion.
4538 // so, first iterate over the regions to be removed from rs and
4539 // add them to the regions_to_remove list, and then
4540 // iterate over the list to actually remove them.
4542 regions_to_remove.push_back ((*i)->region());
4545 vector<boost::shared_ptr<Playlist> > playlists;
4547 for (list<boost::shared_ptr<Region> >::iterator rl = regions_to_remove.begin(); rl != regions_to_remove.end(); ++rl) {
4549 boost::shared_ptr<Playlist> playlist = (*rl)->playlist();
4552 // is this check necessary?
4556 /* get_regions_from_selection_and_entered() guarantees that
4557 the playlists involved are unique, so there is no need
4561 playlists.push_back (playlist);
4563 playlist->clear_changes ();
4564 playlist->clear_owned_changes ();
4565 playlist->freeze ();
4566 playlist->remove_region (*rl);
4567 if (Config->get_edit_mode() == Ripple)
4568 playlist->ripple ((*rl)->position(), -(*rl)->length(), boost::shared_ptr<Region>());
4572 vector<boost::shared_ptr<Playlist> >::iterator pl;
4573 bool in_command = false;
4575 for (pl = playlists.begin(); pl != playlists.end(); ++pl) {
4578 /* We might have removed regions, which alters other regions' layering_index,
4579 so we need to do a recursive diff here.
4583 begin_reversible_command (_("remove region"));
4586 vector<Command*> cmds;
4587 (*pl)->rdiff (cmds);
4588 _session->add_commands (cmds);
4590 _session->add_command(new StatefulDiffCommand (*pl));
4594 commit_reversible_command ();
4598 /** Cut, copy or clear selected regions.
4599 * @param op Operation (Cut, Copy or Clear)
4602 Editor::cut_copy_regions (CutCopyOp op, RegionSelection& rs)
4604 /* we can't use a std::map here because the ordering is important, and we can't trivially sort
4605 a map when we want ordered access to both elements. i think.
4608 vector<PlaylistMapping> pmap;
4610 samplepos_t first_position = max_samplepos;
4612 typedef set<boost::shared_ptr<Playlist> > FreezeList;
4613 FreezeList freezelist;
4615 /* get ordering correct before we cut/copy */
4617 rs.sort_by_position_and_track ();
4619 for (RegionSelection::iterator x = rs.begin(); x != rs.end(); ++x) {
4621 first_position = min ((samplepos_t) (*x)->region()->position(), first_position);
4623 if (op == Cut || op == Clear || op == Delete) {
4624 boost::shared_ptr<Playlist> pl = (*x)->region()->playlist();
4627 FreezeList::iterator fl;
4629 // only take state if this is a new playlist.
4630 for (fl = freezelist.begin(); fl != freezelist.end(); ++fl) {
4636 if (fl == freezelist.end()) {
4637 pl->clear_changes();
4638 pl->clear_owned_changes ();
4640 freezelist.insert (pl);
4645 TimeAxisView* tv = &(*x)->get_time_axis_view();
4646 vector<PlaylistMapping>::iterator z;
4648 for (z = pmap.begin(); z != pmap.end(); ++z) {
4649 if ((*z).tv == tv) {
4654 if (z == pmap.end()) {
4655 pmap.push_back (PlaylistMapping (tv));
4659 for (RegionSelection::iterator x = rs.begin(); x != rs.end(); ) {
4661 boost::shared_ptr<Playlist> pl = (*x)->region()->playlist();
4664 /* region not yet associated with a playlist (e.g. unfinished
4671 TimeAxisView& tv = (*x)->get_time_axis_view();
4672 boost::shared_ptr<Playlist> npl;
4673 RegionSelection::iterator tmp;
4680 vector<PlaylistMapping>::iterator z;
4682 for (z = pmap.begin(); z != pmap.end(); ++z) {
4683 if ((*z).tv == &tv) {
4688 assert (z != pmap.end());
4691 npl = PlaylistFactory::create (pl->data_type(), *_session, "cutlist", true);
4699 boost::shared_ptr<Region> r = (*x)->region();
4700 boost::shared_ptr<Region> _xx;
4706 pl->remove_region (r);
4707 if (Config->get_edit_mode() == Ripple)
4708 pl->ripple (r->position(), -r->length(), boost::shared_ptr<Region>());
4712 _xx = RegionFactory::create (r);
4713 npl->add_region (_xx, r->position() - first_position);
4714 pl->remove_region (r);
4715 if (Config->get_edit_mode() == Ripple)
4716 pl->ripple (r->position(), -r->length(), boost::shared_ptr<Region>());
4720 /* copy region before adding, so we're not putting same object into two different playlists */
4721 npl->add_region (RegionFactory::create (r), r->position() - first_position);
4725 pl->remove_region (r);
4726 if (Config->get_edit_mode() == Ripple)
4727 pl->ripple (r->position(), -r->length(), boost::shared_ptr<Region>());
4736 list<boost::shared_ptr<Playlist> > foo;
4738 /* the pmap is in the same order as the tracks in which selected regions occurred */
4740 for (vector<PlaylistMapping>::iterator i = pmap.begin(); i != pmap.end(); ++i) {
4743 foo.push_back ((*i).pl);
4748 cut_buffer->set (foo);
4752 _last_cut_copy_source_track = 0;
4754 _last_cut_copy_source_track = pmap.front().tv;
4758 for (FreezeList::iterator pl = freezelist.begin(); pl != freezelist.end(); ++pl) {
4761 /* We might have removed regions, which alters other regions' layering_index,
4762 so we need to do a recursive diff here.
4764 vector<Command*> cmds;
4765 (*pl)->rdiff (cmds);
4766 _session->add_commands (cmds);
4768 _session->add_command (new StatefulDiffCommand (*pl));
4773 Editor::cut_copy_ranges (CutCopyOp op)
4775 TrackViewList ts = selection->tracks.filter_to_unique_playlists ();
4777 /* Sort the track selection now, so that it if is used, the playlists
4778 selected by the calls below to cut_copy_clear are in the order that
4779 their tracks appear in the editor. This makes things like paste
4780 of ranges work properly.
4783 sort_track_selection (ts);
4786 if (!entered_track) {
4789 ts.push_back (entered_track);
4792 for (TrackViewList::iterator i = ts.begin(); i != ts.end(); ++i) {
4793 (*i)->cut_copy_clear (*selection, op);
4798 Editor::paste (float times, bool from_context)
4800 DEBUG_TRACE (DEBUG::CutNPaste, "paste to preferred edit pos\n");
4801 MusicSample where (get_preferred_edit_position (EDIT_IGNORE_NONE, from_context), 0);
4802 paste_internal (where.sample, times, 0);
4806 Editor::mouse_paste ()
4808 MusicSample where (0, 0);
4810 if (!mouse_sample (where.sample, ignored)) {
4815 paste_internal (where.sample, 1, where.division);
4819 Editor::paste_internal (samplepos_t position, float times, const int32_t sub_num)
4821 DEBUG_TRACE (DEBUG::CutNPaste, string_compose ("apparent paste position is %1\n", position));
4823 if (cut_buffer->empty(internal_editing())) {
4827 if (position == max_samplepos) {
4828 position = get_preferred_edit_position();
4829 DEBUG_TRACE (DEBUG::CutNPaste, string_compose ("preferred edit position is %1\n", position));
4832 if (position != last_paste_pos) {
4833 /* paste in new location, reset repeated paste state */
4835 last_paste_pos = position;
4838 /* get everything in the correct order */
4841 if (!selection->tracks.empty()) {
4842 /* If there is a track selection, paste into exactly those tracks and
4843 * only those tracks. This allows the user to be explicit and override
4844 * the below "do the reasonable thing" logic. */
4845 ts = selection->tracks.filter_to_unique_playlists ();
4846 sort_track_selection (ts);
4848 /* Figure out which track to base the paste at. */
4849 TimeAxisView* base_track = NULL;
4850 if (_edit_point == Editing::EditAtMouse && entered_track) {
4851 /* With the mouse edit point, paste onto the track under the mouse. */
4852 base_track = entered_track;
4853 } else if (_edit_point == Editing::EditAtMouse && entered_regionview) {
4854 /* With the mouse edit point, paste onto the track of the region under the mouse. */
4855 base_track = &entered_regionview->get_time_axis_view();
4856 } else if (_last_cut_copy_source_track) {
4857 /* Paste to the track that the cut/copy came from (see mantis #333). */
4858 base_track = _last_cut_copy_source_track;
4860 /* This is "impossible" since we've copied... well, do nothing. */
4864 /* Walk up to parent if necessary, so base track is a route. */
4865 while (base_track->get_parent()) {
4866 base_track = base_track->get_parent();
4869 /* Add base track and all tracks below it. The paste logic will select
4870 the appropriate object types from the cut buffer in relative order. */
4871 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
4872 if ((*i)->order() >= base_track->order()) {
4877 /* Sort tracks so the nth track of type T will pick the nth object of type T. */
4878 sort_track_selection (ts);
4880 /* Add automation children of each track in order, for pasting several lines. */
4881 for (TrackViewList::iterator i = ts.begin(); i != ts.end();) {
4882 /* Add any automation children for pasting several lines */
4883 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*>(*i++);
4888 typedef RouteTimeAxisView::AutomationTracks ATracks;
4889 const ATracks& atracks = rtv->automation_tracks();
4890 for (ATracks::const_iterator a = atracks.begin(); a != atracks.end(); ++a) {
4891 i = ts.insert(i, a->second.get());
4896 /* We now have a list of trackviews starting at base_track, including
4897 automation children, in the order shown in the editor, e.g. R1,
4898 R1.A1, R1.A2, R2, R2.A1, ... */
4901 begin_reversible_command (Operations::paste);
4903 if (ts.size() == 1 && cut_buffer->lines.size() == 1 &&
4904 dynamic_cast<AutomationTimeAxisView*>(ts.front())) {
4905 /* Only one line copied, and one automation track selected. Do a
4906 "greedy" paste from one automation type to another. */
4908 PasteContext ctx(paste_count, times, ItemCounts(), true);
4909 ts.front()->paste (position, *cut_buffer, ctx, sub_num);
4913 /* Paste into tracks */
4915 PasteContext ctx(paste_count, times, ItemCounts(), false);
4916 for (TrackViewList::iterator i = ts.begin(); i != ts.end(); ++i) {
4917 (*i)->paste (position, *cut_buffer, ctx, sub_num);
4923 commit_reversible_command ();
4927 Editor::duplicate_regions (float times)
4929 RegionSelection rs (get_regions_from_selection_and_entered());
4930 duplicate_some_regions (rs, times);
4934 Editor::duplicate_some_regions (RegionSelection& regions, float times)
4936 if (regions.empty ()) {
4940 boost::shared_ptr<Playlist> playlist;
4941 std::set<boost::shared_ptr<Playlist> > playlists; // list of unique playlists affected by duplication
4942 RegionSelection sel = regions; // clear (below) may clear the argument list if its the current region selection
4943 RegionSelection foo;
4945 samplepos_t const start_sample = regions.start ();
4946 samplepos_t const end_sample = regions.end_sample ();
4947 samplecnt_t const span = end_sample - start_sample + 1;
4949 begin_reversible_command (Operations::duplicate_region);
4951 selection->clear_regions ();
4953 /* ripple first so that we don't move the duplicates that will be added */
4955 if (Config->get_edit_mode() == Ripple) {
4957 /* convert RegionSelection into RegionList so that we can pass it to ripple and exclude the regions we will duplicate */
4961 for (RegionSelection::iterator i = sel.begin(); i != sel.end(); ++i) {
4962 exclude.push_back ((*i)->region());
4963 playlist = (*i)->region()->playlist();
4964 if (playlists.insert (playlist).second) {
4965 /* successfully inserted into set, so it's the first time we've seen this playlist */
4966 playlist->clear_changes ();
4970 for (set<boost::shared_ptr<Playlist> >::iterator p = playlists.begin(); p != playlists.end(); ++p) {
4971 (*p)->ripple (start_sample, span * times, &exclude);
4975 for (RegionSelection::iterator i = sel.begin(); i != sel.end(); ++i) {
4977 boost::shared_ptr<Region> r ((*i)->region());
4979 TimeAxisView& tv = (*i)->get_time_axis_view();
4980 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (&tv);
4981 latest_regionviews.clear ();
4982 sigc::connection c = rtv->view()->RegionViewAdded.connect (sigc::mem_fun(*this, &Editor::collect_new_region_view));
4984 samplepos_t const position = end_sample + (r->first_sample() - start_sample + 1);
4985 playlist = (*i)->region()->playlist();
4987 if (Config->get_edit_mode() != Ripple) {
4988 if (playlists.insert (playlist).second) {
4989 playlist->clear_changes ();
4993 playlist->duplicate (r, position, span, times);
4997 foo.insert (foo.end(), latest_regionviews.begin(), latest_regionviews.end());
5000 for (set<boost::shared_ptr<Playlist> >::iterator p = playlists.begin(); p != playlists.end(); ++p) {
5001 _session->add_command (new StatefulDiffCommand (*p));
5002 vector<Command*> cmds;
5004 _session->add_commands (cmds);
5008 selection->set (foo);
5011 commit_reversible_command ();
5015 Editor::duplicate_selection (float times)
5017 if (selection->time.empty() || selection->tracks.empty()) {
5021 boost::shared_ptr<Playlist> playlist;
5023 TrackViewList ts = selection->tracks.filter_to_unique_playlists ();
5025 bool in_command = false;
5027 for (TrackViewList::iterator i = ts.begin(); i != ts.end(); ++i) {
5028 if ((playlist = (*i)->playlist()) == 0) {
5031 playlist->clear_changes ();
5033 if (clicked_selection) {
5034 playlist->duplicate_range (selection->time[clicked_selection], times);
5036 playlist->duplicate_ranges (selection->time, times);
5040 begin_reversible_command (_("duplicate range selection"));
5043 _session->add_command (new StatefulDiffCommand (playlist));
5048 if (times == 1.0f) {
5049 // now "move" range selection to after the current range selection
5050 samplecnt_t distance = 0;
5052 if (clicked_selection) {
5054 selection->time[clicked_selection].end - selection->time[clicked_selection].start;
5056 distance = selection->time.end_sample () - selection->time.start ();
5059 selection->move_time (distance);
5061 commit_reversible_command ();
5065 /** Reset all selected points to the relevant default value */
5067 Editor::reset_point_selection ()
5069 for (PointSelection::iterator i = selection->points.begin(); i != selection->points.end(); ++i) {
5070 ARDOUR::AutomationList::iterator j = (*i)->model ();
5071 (*j)->value = (*i)->line().the_list()->descriptor ().normal;
5076 Editor::center_playhead ()
5078 float const page = _visible_canvas_width * samples_per_pixel;
5079 center_screen_internal (playhead_cursor->current_sample (), page);
5083 Editor::center_edit_point ()
5085 float const page = _visible_canvas_width * samples_per_pixel;
5086 center_screen_internal (get_preferred_edit_position(), page);
5089 /** Caller must begin and commit a reversible command */
5091 Editor::clear_playlist (boost::shared_ptr<Playlist> playlist)
5093 playlist->clear_changes ();
5095 _session->add_command (new StatefulDiffCommand (playlist));
5099 Editor::nudge_track (bool use_edit, bool forwards)
5101 boost::shared_ptr<Playlist> playlist;
5102 samplepos_t distance;
5103 samplepos_t next_distance;
5107 start = get_preferred_edit_position();
5112 if ((distance = get_nudge_distance (start, next_distance)) == 0) {
5116 if (selection->tracks.empty()) {
5120 TrackViewList ts = selection->tracks.filter_to_unique_playlists ();
5121 bool in_command = false;
5123 for (TrackViewList::iterator i = ts.begin(); i != ts.end(); ++i) {
5125 if ((playlist = (*i)->playlist()) == 0) {
5129 playlist->clear_changes ();
5130 playlist->clear_owned_changes ();
5132 playlist->nudge_after (start, distance, forwards);
5135 begin_reversible_command (_("nudge track"));
5138 vector<Command*> cmds;
5140 playlist->rdiff (cmds);
5141 _session->add_commands (cmds);
5143 _session->add_command (new StatefulDiffCommand (playlist));
5147 commit_reversible_command ();
5152 Editor::remove_last_capture ()
5154 vector<string> choices;
5161 if (Config->get_verify_remove_last_capture()) {
5162 prompt = _("Do you really want to destroy the last capture?"
5163 "\n(This is destructive and cannot be undone)");
5165 choices.push_back (_("No, do nothing."));
5166 choices.push_back (_("Yes, destroy it."));
5168 Choice prompter (_("Destroy last capture"), prompt, choices);
5170 if (prompter.run () == 1) {
5171 _session->remove_last_capture ();
5172 _regions->redisplay ();
5176 _session->remove_last_capture();
5177 _regions->redisplay ();
5182 Editor::tag_regions (RegionList regions)
5184 ArdourDialog d (_("Tag Last Capture"), true, false);
5186 Label label (_("Tag:"));
5189 hbox.set_spacing (6);
5190 hbox.pack_start (label, false, false);
5191 hbox.pack_start (entry, true, true);
5193 d.get_vbox()->set_border_width (12);
5194 d.get_vbox()->pack_start (hbox, false, false);
5196 d.add_button(Gtk::Stock::CANCEL, Gtk::RESPONSE_CANCEL);
5197 d.add_button(Gtk::Stock::OK, Gtk::RESPONSE_OK);
5199 d.set_size_request (300, -1);
5201 entry.set_text (_("Good"));
5202 entry.select_region (0, -1);
5204 entry.signal_activate().connect (sigc::bind (sigc::mem_fun (d, &Dialog::response), RESPONSE_OK));
5210 int const ret = d.run();
5214 if (ret != RESPONSE_OK) {
5218 std::string tagstr = entry.get_text();
5219 strip_whitespace_edges (tagstr);
5221 if (!tagstr.empty()) {
5222 for (RegionList::iterator r = regions.begin(); r != regions.end(); r++) {
5223 (*r)->set_tags(tagstr);
5226 _regions->redisplay ();
5231 Editor::tag_selected_region ()
5233 std::list<boost::shared_ptr<Region> > rlist;
5235 RegionSelection rs = get_regions_from_selection_and_entered ();
5236 for (RegionSelection::iterator r = rs.begin(); r != rs.end(); r++) {
5237 rlist.push_back((*r)->region());
5244 Editor::tag_last_capture ()
5250 std::list<boost::shared_ptr<Region> > rlist;
5252 std::list<boost::shared_ptr<Source> > srcs;
5253 _session->get_last_capture_sources (srcs);
5254 for (std::list<boost::shared_ptr<Source> >::iterator i = srcs.begin(); i != srcs.end(); ++i) {
5255 boost::shared_ptr<ARDOUR::Source> source = (*i);
5258 set<boost::shared_ptr<Region> > regions;
5259 RegionFactory::get_regions_using_source (source, regions);
5260 for (set<boost::shared_ptr<Region> >::iterator r = regions.begin(); r != regions.end(); r++) {
5261 rlist.push_back(*r);
5271 Editor::normalize_region ()
5277 RegionSelection rs = get_regions_from_selection_and_entered ();
5283 NormalizeDialog dialog (rs.size() > 1);
5285 if (dialog.run () != RESPONSE_ACCEPT) {
5289 CursorContext::Handle cursor_ctx = CursorContext::create(*this, _cursors->wait);
5292 /* XXX: should really only count audio regions here */
5293 int const regions = rs.size ();
5295 /* Make a list of the selected audio regions' maximum amplitudes, and also
5296 obtain the maximum amplitude of them all.
5298 list<double> max_amps;
5299 list<double> rms_vals;
5302 bool use_rms = dialog.constrain_rms ();
5304 for (RegionSelection::const_iterator i = rs.begin(); i != rs.end(); ++i) {
5305 AudioRegionView const * arv = dynamic_cast<AudioRegionView const *> (*i);
5309 dialog.descend (1.0 / regions);
5310 double const a = arv->audio_region()->maximum_amplitude (&dialog);
5312 double r = arv->audio_region()->rms (&dialog);
5313 max_rms = max (max_rms, r);
5314 rms_vals.push_back (r);
5318 /* the user cancelled the operation */
5322 max_amps.push_back (a);
5323 max_amp = max (max_amp, a);
5327 list<double>::const_iterator a = max_amps.begin ();
5328 list<double>::const_iterator l = rms_vals.begin ();
5329 bool in_command = false;
5331 for (RegionSelection::iterator r = rs.begin(); r != rs.end(); ++r) {
5332 AudioRegionView* const arv = dynamic_cast<AudioRegionView*> (*r);
5337 arv->region()->clear_changes ();
5339 double amp = dialog.normalize_individually() ? *a : max_amp;
5340 double target = dialog.target_peak (); // dB
5343 double const amp_rms = dialog.normalize_individually() ? *l : max_rms;
5344 const double t_rms = dialog.target_rms ();
5345 const gain_t c_peak = dB_to_coefficient (target);
5346 const gain_t c_rms = dB_to_coefficient (t_rms);
5347 if ((amp_rms / c_rms) > (amp / c_peak)) {
5353 arv->audio_region()->normalize (amp, target);
5356 begin_reversible_command (_("normalize"));
5359 _session->add_command (new StatefulDiffCommand (arv->region()));
5366 commit_reversible_command ();
5372 Editor::reset_region_scale_amplitude ()
5378 RegionSelection rs = get_regions_from_selection_and_entered ();
5384 bool in_command = false;
5386 for (RegionSelection::iterator r = rs.begin(); r != rs.end(); ++r) {
5387 AudioRegionView* const arv = dynamic_cast<AudioRegionView*>(*r);
5390 arv->region()->clear_changes ();
5391 arv->audio_region()->set_scale_amplitude (1.0f);
5394 begin_reversible_command ("reset gain");
5397 _session->add_command (new StatefulDiffCommand (arv->region()));
5401 commit_reversible_command ();
5406 Editor::adjust_region_gain (bool up)
5408 RegionSelection rs = get_regions_from_selection_and_entered ();
5410 if (!_session || rs.empty()) {
5414 bool in_command = false;
5416 for (RegionSelection::iterator r = rs.begin(); r != rs.end(); ++r) {
5417 AudioRegionView* const arv = dynamic_cast<AudioRegionView*>(*r);
5422 arv->region()->clear_changes ();
5424 double dB = accurate_coefficient_to_dB (arv->audio_region()->scale_amplitude ());
5432 arv->audio_region()->set_scale_amplitude (dB_to_coefficient (dB));
5435 begin_reversible_command ("adjust region gain");
5438 _session->add_command (new StatefulDiffCommand (arv->region()));
5442 commit_reversible_command ();
5447 Editor::reset_region_gain ()
5449 RegionSelection rs = get_regions_from_selection_and_entered ();
5451 if (!_session || rs.empty()) {
5455 bool in_command = false;
5457 for (RegionSelection::iterator r = rs.begin(); r != rs.end(); ++r) {
5458 AudioRegionView* const arv = dynamic_cast<AudioRegionView*>(*r);
5463 arv->region()->clear_changes ();
5465 arv->audio_region()->set_scale_amplitude (1.0f);
5468 begin_reversible_command ("reset region gain");
5471 _session->add_command (new StatefulDiffCommand (arv->region()));
5475 commit_reversible_command ();
5480 Editor::reverse_region ()
5486 Reverse rev (*_session);
5487 apply_filter (rev, _("reverse regions"));
5491 Editor::strip_region_silence ()
5497 RegionSelection rs = get_regions_from_selection_and_entered ();
5503 std::list<RegionView*> audio_only;
5505 for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
5506 AudioRegionView* const arv = dynamic_cast<AudioRegionView*> (*i);
5508 audio_only.push_back (arv);
5512 assert (!audio_only.empty());
5514 StripSilenceDialog d (_session, audio_only);
5515 int const r = d.run ();
5519 if (r == Gtk::RESPONSE_OK) {
5520 ARDOUR::AudioIntervalMap silences;
5521 d.silences (silences);
5522 StripSilence s (*_session, silences, d.fade_length());
5524 apply_filter (s, _("strip silence"), &d);
5525 _session->add_extra_xml(d.get_state());
5530 Editor::apply_midi_note_edit_op_to_region (MidiOperator& op, MidiRegionView& mrv)
5532 Evoral::Sequence<Temporal::Beats>::Notes selected;
5533 mrv.selection_as_notelist (selected, true);
5535 vector<Evoral::Sequence<Temporal::Beats>::Notes> v;
5536 v.push_back (selected);
5538 Temporal::Beats pos_beats = Temporal::Beats (mrv.midi_region()->beat()) - mrv.midi_region()->start_beats();
5540 return op (mrv.midi_region()->model(), pos_beats, v);
5544 Editor::apply_midi_note_edit_op (MidiOperator& op, const RegionSelection& rs)
5550 bool in_command = false;
5552 for (RegionSelection::const_iterator r = rs.begin(); r != rs.end(); ) {
5553 RegionSelection::const_iterator tmp = r;
5556 MidiRegionView* const mrv = dynamic_cast<MidiRegionView*> (*r);
5559 Command* cmd = apply_midi_note_edit_op_to_region (op, *mrv);
5562 begin_reversible_command (op.name ());
5566 _session->add_command (cmd);
5574 commit_reversible_command ();
5575 _session->set_dirty ();
5580 Editor::fork_region ()
5582 RegionSelection rs = get_regions_from_selection_and_entered ();
5588 CursorContext::Handle cursor_ctx = CursorContext::create(*this, _cursors->wait);
5589 bool in_command = false;
5593 for (RegionSelection::iterator r = rs.begin(); r != rs.end(); ) {
5594 RegionSelection::iterator tmp = r;
5597 MidiRegionView* const mrv = dynamic_cast<MidiRegionView*>(*r);
5601 boost::shared_ptr<Playlist> playlist = mrv->region()->playlist();
5602 boost::shared_ptr<MidiSource> new_source = _session->create_midi_source_by_stealing_name (mrv->midi_view()->track());
5603 boost::shared_ptr<MidiRegion> newregion = mrv->midi_region()->clone (new_source);
5606 begin_reversible_command (_("Fork Region(s)"));
5609 playlist->clear_changes ();
5610 playlist->replace_region (mrv->region(), newregion, mrv->region()->position());
5611 _session->add_command(new StatefulDiffCommand (playlist));
5613 error << string_compose (_("Could not unlink %1"), mrv->region()->name()) << endmsg;
5621 commit_reversible_command ();
5626 Editor::quantize_region ()
5629 quantize_regions(get_regions_from_selection_and_entered ());
5634 Editor::quantize_regions (const RegionSelection& rs)
5636 if (rs.n_midi_regions() == 0) {
5640 if (!quantize_dialog) {
5641 quantize_dialog = new QuantizeDialog (*this);
5644 if (quantize_dialog->is_mapped()) {
5645 /* in progress already */
5649 quantize_dialog->present ();
5650 const int r = quantize_dialog->run ();
5651 quantize_dialog->hide ();
5653 if (r == Gtk::RESPONSE_OK) {
5654 Quantize quant (quantize_dialog->snap_start(),
5655 quantize_dialog->snap_end(),
5656 quantize_dialog->start_grid_size(),
5657 quantize_dialog->end_grid_size(),
5658 quantize_dialog->strength(),
5659 quantize_dialog->swing(),
5660 quantize_dialog->threshold());
5662 apply_midi_note_edit_op (quant, rs);
5667 Editor::legatize_region (bool shrink_only)
5670 legatize_regions(get_regions_from_selection_and_entered (), shrink_only);
5675 Editor::legatize_regions (const RegionSelection& rs, bool shrink_only)
5677 if (rs.n_midi_regions() == 0) {
5681 Legatize legatize(shrink_only);
5682 apply_midi_note_edit_op (legatize, rs);
5686 Editor::transform_region ()
5689 transform_regions(get_regions_from_selection_and_entered ());
5694 Editor::transform_regions (const RegionSelection& rs)
5696 if (rs.n_midi_regions() == 0) {
5703 const int r = td.run();
5706 if (r == Gtk::RESPONSE_OK) {
5707 Transform transform(td.get());
5708 apply_midi_note_edit_op(transform, rs);
5713 Editor::transpose_region ()
5716 transpose_regions(get_regions_from_selection_and_entered ());
5721 Editor::transpose_regions (const RegionSelection& rs)
5723 if (rs.n_midi_regions() == 0) {
5728 int const r = d.run ();
5730 if (r == RESPONSE_ACCEPT) {
5731 Transpose transpose(d.semitones ());
5732 apply_midi_note_edit_op (transpose, rs);
5737 Editor::insert_patch_change (bool from_context)
5739 RegionSelection rs = get_regions_from_selection_and_entered ();
5745 const samplepos_t p = get_preferred_edit_position (EDIT_IGNORE_NONE, from_context);
5747 /* XXX: bit of a hack; use the MIDNAM from the first selected region;
5748 there may be more than one, but the PatchChangeDialog can only offer
5749 one set of patch menus.
5751 MidiRegionView* first = dynamic_cast<MidiRegionView*> (rs.front ());
5753 Evoral::PatchChange<Temporal::Beats> empty (Temporal::Beats(), 0, 0, 0);
5754 PatchChangeDialog d (0, _session, empty, first->instrument_info(), Gtk::Stock::ADD);
5756 if (d.run() == RESPONSE_CANCEL) {
5760 for (RegionSelection::iterator i = rs.begin (); i != rs.end(); ++i) {
5761 MidiRegionView* const mrv = dynamic_cast<MidiRegionView*> (*i);
5763 if (p >= mrv->region()->first_sample() && p <= mrv->region()->last_sample()) {
5764 mrv->add_patch_change (p - mrv->region()->position(), d.patch ());
5771 Editor::apply_filter (Filter& filter, string command, ProgressReporter* progress)
5773 RegionSelection rs = get_regions_from_selection_and_entered ();
5779 CursorContext::Handle cursor_ctx = CursorContext::create(*this, _cursors->wait);
5780 bool in_command = false;
5785 int const N = rs.size ();
5787 for (RegionSelection::iterator r = rs.begin(); r != rs.end(); ) {
5788 RegionSelection::iterator tmp = r;
5791 AudioRegionView* const arv = dynamic_cast<AudioRegionView*>(*r);
5793 boost::shared_ptr<Playlist> playlist = arv->region()->playlist();
5796 progress->descend (1.0 / N);
5799 if (arv->audio_region()->apply (filter, progress) == 0) {
5801 playlist->clear_changes ();
5802 playlist->clear_owned_changes ();
5805 begin_reversible_command (command);
5809 if (filter.results.empty ()) {
5811 /* no regions returned; remove the old one */
5812 playlist->remove_region (arv->region ());
5816 std::vector<boost::shared_ptr<Region> >::iterator res = filter.results.begin ();
5818 /* first region replaces the old one */
5819 playlist->replace_region (arv->region(), *res, (*res)->position());
5823 while (res != filter.results.end()) {
5824 playlist->add_region (*res, (*res)->position());
5830 /* We might have removed regions, which alters other regions' layering_index,
5831 so we need to do a recursive diff here.
5833 vector<Command*> cmds;
5834 playlist->rdiff (cmds);
5835 _session->add_commands (cmds);
5837 _session->add_command(new StatefulDiffCommand (playlist));
5841 progress->ascend ();
5850 commit_reversible_command ();
5855 Editor::external_edit_region ()
5861 Editor::reset_region_gain_envelopes ()
5863 RegionSelection rs = get_regions_from_selection_and_entered ();
5865 if (!_session || rs.empty()) {
5869 bool in_command = false;
5871 for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
5872 AudioRegionView* const arv = dynamic_cast<AudioRegionView*>(*i);
5874 boost::shared_ptr<AutomationList> alist (arv->audio_region()->envelope());
5875 XMLNode& before (alist->get_state());
5877 arv->audio_region()->set_default_envelope ();
5880 begin_reversible_command (_("reset region gain"));
5883 _session->add_command (new MementoCommand<AutomationList>(*arv->audio_region()->envelope().get(), &before, &alist->get_state()));
5888 commit_reversible_command ();
5893 Editor::set_region_gain_visibility (RegionView* rv)
5895 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (rv);
5897 arv->update_envelope_visibility();
5902 Editor::set_gain_envelope_visibility ()
5908 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
5909 AudioTimeAxisView* v = dynamic_cast<AudioTimeAxisView*>(*i);
5911 v->audio_view()->foreach_regionview (sigc::mem_fun (this, &Editor::set_region_gain_visibility));
5917 Editor::toggle_gain_envelope_active ()
5919 if (_ignore_region_action) {
5923 RegionSelection rs = get_regions_from_selection_and_entered ();
5925 if (!_session || rs.empty()) {
5929 bool in_command = false;
5931 for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
5932 AudioRegionView* const arv = dynamic_cast<AudioRegionView*>(*i);
5934 arv->region()->clear_changes ();
5935 arv->audio_region()->set_envelope_active (!arv->audio_region()->envelope_active());
5938 begin_reversible_command (_("region gain envelope active"));
5941 _session->add_command (new StatefulDiffCommand (arv->region()));
5946 commit_reversible_command ();
5951 Editor::toggle_region_lock ()
5953 if (_ignore_region_action) {
5957 RegionSelection rs = get_regions_from_selection_and_entered ();
5959 if (!_session || rs.empty()) {
5963 begin_reversible_command (_("toggle region lock"));
5965 for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
5966 (*i)->region()->clear_changes ();
5967 (*i)->region()->set_locked (!(*i)->region()->locked());
5968 _session->add_command (new StatefulDiffCommand ((*i)->region()));
5971 commit_reversible_command ();
5975 Editor::toggle_region_video_lock ()
5977 if (_ignore_region_action) {
5981 RegionSelection rs = get_regions_from_selection_and_entered ();
5983 if (!_session || rs.empty()) {
5987 begin_reversible_command (_("Toggle Video Lock"));
5989 for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
5990 (*i)->region()->clear_changes ();
5991 (*i)->region()->set_video_locked (!(*i)->region()->video_locked());
5992 _session->add_command (new StatefulDiffCommand ((*i)->region()));
5995 commit_reversible_command ();
5999 Editor::toggle_region_lock_style ()
6001 if (_ignore_region_action) {
6005 RegionSelection rs = get_regions_from_selection_and_entered ();
6007 if (!_session || rs.empty()) {
6011 Glib::RefPtr<ToggleAction> a = Glib::RefPtr<ToggleAction>::cast_dynamic (_region_actions->get_action("toggle-region-lock-style"));
6012 vector<Widget*> proxies = a->get_proxies();
6013 Gtk::CheckMenuItem* cmi = dynamic_cast<Gtk::CheckMenuItem*> (proxies.front());
6017 begin_reversible_command (_("toggle region lock style"));
6019 for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
6020 (*i)->region()->clear_changes ();
6021 PositionLockStyle const ns = ((*i)->region()->position_lock_style() == AudioTime && !cmi->get_inconsistent()) ? MusicTime : AudioTime;
6022 (*i)->region()->set_position_lock_style (ns);
6023 _session->add_command (new StatefulDiffCommand ((*i)->region()));
6026 commit_reversible_command ();
6030 Editor::toggle_opaque_region ()
6032 if (_ignore_region_action) {
6036 RegionSelection rs = get_regions_from_selection_and_entered ();
6038 if (!_session || rs.empty()) {
6042 begin_reversible_command (_("change region opacity"));
6044 for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
6045 (*i)->region()->clear_changes ();
6046 (*i)->region()->set_opaque (!(*i)->region()->opaque());
6047 _session->add_command (new StatefulDiffCommand ((*i)->region()));
6050 commit_reversible_command ();
6054 Editor::toggle_record_enable ()
6056 bool new_state = false;
6058 for (TrackSelection::iterator i = selection->tracks.begin(); i != selection->tracks.end(); ++i) {
6059 RouteTimeAxisView *rtav = dynamic_cast<RouteTimeAxisView *>(*i);
6062 if (!rtav->is_track())
6066 new_state = !rtav->track()->rec_enable_control()->get_value();
6070 rtav->track()->rec_enable_control()->set_value (new_state, Controllable::UseGroup);
6075 tracklist_to_stripables (TrackViewList list)
6079 for (TrackSelection::iterator i = list.begin(); i != list.end(); ++i) {
6080 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> ((*i));
6082 if (rtv && rtv->is_track()) {
6083 ret.push_back (rtv->track());
6091 Editor::play_solo_selection (bool restart)
6093 //note: session::solo_selection takes care of invalidating the region playlist
6095 if ((!selection->tracks.empty()) && selection->time.length() > 0) { //a range is selected; solo the tracks and roll
6097 StripableList sl = tracklist_to_stripables (selection->tracks);
6098 _session->solo_selection (sl, true);
6101 samplepos_t start = selection->time.start();
6102 samplepos_t end = selection->time.end_sample();
6103 _session->request_bounded_roll (start, end);
6105 } else if (! selection->tracks.empty()) { //no range is selected, but tracks are selected; solo the tracks and roll
6106 StripableList sl = tracklist_to_stripables (selection->tracks);
6107 _session->solo_selection (sl, true);
6108 _session->request_cancel_play_range();
6109 transition_to_rolling (true);
6111 } else if (! selection->regions.empty()) { //solo any tracks with selected regions, and roll
6112 StripableList sl = tracklist_to_stripables (get_tracks_for_range_action());
6113 _session->solo_selection (sl, true);
6114 _session->request_cancel_play_range();
6115 transition_to_rolling (true);
6117 _session->request_cancel_play_range();
6118 transition_to_rolling (true); //no selection. just roll.
6123 Editor::toggle_solo ()
6125 bool new_state = false;
6127 boost::shared_ptr<ControlList> cl (new ControlList);
6129 for (TrackSelection::iterator i = selection->tracks.begin(); i != selection->tracks.end(); ++i) {
6130 StripableTimeAxisView *stav = dynamic_cast<StripableTimeAxisView *>(*i);
6132 if (!stav || !stav->stripable()->solo_control()) {
6137 new_state = !stav->stripable()->solo_control()->soloed ();
6141 cl->push_back (stav->stripable()->solo_control());
6144 _session->set_controls (cl, new_state ? 1.0 : 0.0, Controllable::UseGroup);
6148 Editor::toggle_mute ()
6150 bool new_state = false;
6152 boost::shared_ptr<ControlList> cl (new ControlList);
6154 for (TrackSelection::iterator i = selection->tracks.begin(); i != selection->tracks.end(); ++i) {
6155 StripableTimeAxisView *stav = dynamic_cast<StripableTimeAxisView *>(*i);
6157 if (!stav || !stav->stripable()->mute_control()) {
6162 new_state = !stav->stripable()->mute_control()->muted();
6166 cl->push_back (stav->stripable()->mute_control());
6169 _session->set_controls (cl, new_state, Controllable::UseGroup);
6173 Editor::toggle_solo_isolate ()
6179 Editor::fade_range ()
6181 TrackViewList ts = selection->tracks.filter_to_unique_playlists ();
6183 begin_reversible_command (_("fade range"));
6185 for (TrackViewList::iterator i = ts.begin(); i != ts.end(); ++i) {
6186 (*i)->fade_range (selection->time);
6189 commit_reversible_command ();
6194 Editor::set_fade_length (bool in)
6196 RegionSelection rs = get_regions_from_selection_and_entered ();
6202 /* we need a region to measure the offset from the start */
6204 RegionView* rv = rs.front ();
6206 samplepos_t pos = get_preferred_edit_position();
6210 if (pos > rv->region()->last_sample() || pos < rv->region()->first_sample()) {
6211 /* edit point is outside the relevant region */
6216 if (pos <= rv->region()->position()) {
6220 len = pos - rv->region()->position();
6221 cmd = _("set fade in length");
6223 if (pos >= rv->region()->last_sample()) {
6227 len = rv->region()->last_sample() - pos;
6228 cmd = _("set fade out length");
6231 bool in_command = false;
6233 for (RegionSelection::iterator x = rs.begin(); x != rs.end(); ++x) {
6234 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (*x);
6240 boost::shared_ptr<AutomationList> alist;
6242 alist = tmp->audio_region()->fade_in();
6244 alist = tmp->audio_region()->fade_out();
6247 XMLNode &before = alist->get_state();
6250 tmp->audio_region()->set_fade_in_length (len);
6251 tmp->audio_region()->set_fade_in_active (true);
6253 tmp->audio_region()->set_fade_out_length (len);
6254 tmp->audio_region()->set_fade_out_active (true);
6258 begin_reversible_command (cmd);
6261 XMLNode &after = alist->get_state();
6262 _session->add_command(new MementoCommand<AutomationList>(*alist, &before, &after));
6266 commit_reversible_command ();
6271 Editor::set_fade_in_shape (FadeShape shape)
6273 RegionSelection rs = get_regions_from_selection_and_entered ();
6278 bool in_command = false;
6280 for (RegionSelection::iterator x = rs.begin(); x != rs.end(); ++x) {
6281 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (*x);
6287 boost::shared_ptr<AutomationList> alist = tmp->audio_region()->fade_in();
6288 XMLNode &before = alist->get_state();
6290 tmp->audio_region()->set_fade_in_shape (shape);
6293 begin_reversible_command (_("set fade in shape"));
6296 XMLNode &after = alist->get_state();
6297 _session->add_command(new MementoCommand<AutomationList>(*alist.get(), &before, &after));
6301 commit_reversible_command ();
6306 Editor::set_fade_out_shape (FadeShape shape)
6308 RegionSelection rs = get_regions_from_selection_and_entered ();
6313 bool in_command = false;
6315 for (RegionSelection::iterator x = rs.begin(); x != rs.end(); ++x) {
6316 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (*x);
6322 boost::shared_ptr<AutomationList> alist = tmp->audio_region()->fade_out();
6323 XMLNode &before = alist->get_state();
6325 tmp->audio_region()->set_fade_out_shape (shape);
6328 begin_reversible_command (_("set fade out shape"));
6331 XMLNode &after = alist->get_state();
6332 _session->add_command(new MementoCommand<AutomationList>(*alist.get(), &before, &after));
6336 commit_reversible_command ();
6341 Editor::set_fade_in_active (bool yn)
6343 RegionSelection rs = get_regions_from_selection_and_entered ();
6348 bool in_command = false;
6350 for (RegionSelection::iterator x = rs.begin(); x != rs.end(); ++x) {
6351 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (*x);
6358 boost::shared_ptr<AudioRegion> ar (tmp->audio_region());
6360 ar->clear_changes ();
6361 ar->set_fade_in_active (yn);
6364 begin_reversible_command (_("set fade in active"));
6367 _session->add_command (new StatefulDiffCommand (ar));
6371 commit_reversible_command ();
6376 Editor::set_fade_out_active (bool yn)
6378 RegionSelection rs = get_regions_from_selection_and_entered ();
6383 bool in_command = false;
6385 for (RegionSelection::iterator x = rs.begin(); x != rs.end(); ++x) {
6386 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (*x);
6392 boost::shared_ptr<AudioRegion> ar (tmp->audio_region());
6394 ar->clear_changes ();
6395 ar->set_fade_out_active (yn);
6398 begin_reversible_command (_("set fade out active"));
6401 _session->add_command(new StatefulDiffCommand (ar));
6405 commit_reversible_command ();
6410 Editor::toggle_region_fades (int dir)
6412 if (_ignore_region_action) {
6416 boost::shared_ptr<AudioRegion> ar;
6419 RegionSelection rs = get_regions_from_selection_and_entered ();
6425 RegionSelection::iterator i;
6426 for (i = rs.begin(); i != rs.end(); ++i) {
6427 if ((ar = boost::dynamic_pointer_cast<AudioRegion>((*i)->region())) != 0) {
6429 yn = ar->fade_out_active ();
6431 yn = ar->fade_in_active ();
6437 if (i == rs.end()) {
6441 /* XXX should this undo-able? */
6442 bool in_command = false;
6444 for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
6445 if ((ar = boost::dynamic_pointer_cast<AudioRegion>((*i)->region())) == 0) {
6448 ar->clear_changes ();
6450 if (dir == 1 || dir == 0) {
6451 ar->set_fade_in_active (!yn);
6454 if (dir == -1 || dir == 0) {
6455 ar->set_fade_out_active (!yn);
6458 begin_reversible_command (_("toggle fade active"));
6461 _session->add_command(new StatefulDiffCommand (ar));
6465 commit_reversible_command ();
6470 /** Update region fade visibility after its configuration has been changed */
6472 Editor::update_region_fade_visibility ()
6474 bool _fade_visibility = _session->config.get_show_region_fades ();
6476 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
6477 AudioTimeAxisView* v = dynamic_cast<AudioTimeAxisView*>(*i);
6479 if (_fade_visibility) {
6480 v->audio_view()->show_all_fades ();
6482 v->audio_view()->hide_all_fades ();
6489 Editor::set_edit_point ()
6492 MusicSample where (0, 0);
6494 if (!mouse_sample (where.sample, ignored)) {
6500 if (selection->markers.empty()) {
6502 mouse_add_new_marker (where.sample);
6507 Location* loc = find_location_from_marker (selection->markers.front(), ignored);
6510 loc->move_to (where.sample, where.division);
6516 Editor::set_playhead_cursor ()
6518 if (entered_marker) {
6519 _session->request_locate (entered_marker->position(), _session->transport_rolling());
6521 MusicSample where (0, 0);
6524 if (!mouse_sample (where.sample, ignored)) {
6531 _session->request_locate (where.sample, _session->transport_rolling());
6535 //not sure what this was for; remove it for now.
6536 // if (UIConfiguration::instance().get_follow_edits() && (!_session || !_session->config.get_external_sync())) {
6537 // cancel_time_selection();
6543 Editor::split_region ()
6545 if (_dragging_playhead) {
6547 } else if (_drags->active ()) {
6548 /*any other kind of drag, bail out so we avoid Undo snafu*/
6552 //if a range is selected, separate it
6553 if (!selection->time.empty()) {
6554 separate_regions_between (selection->time);
6558 //if no range was selected, try to find some regions to split
6559 if (current_mouse_mode() == MouseObject || current_mouse_mode() == MouseRange ) { //don't try this for Internal Edit, Stretch, Draw, etc.
6563 //new behavior: the Split action will prioritize the entered_regionview rather than selected regions.
6564 //this fixes the unexpected case where you point at a region, but
6565 // * nothing happens OR
6566 // * some other region (maybe off-screen) is split.
6567 //NOTE: if the entered_regionview is /part of the selection/ then we should operate on the selection as usual
6568 if (_edit_point == EditAtMouse && entered_regionview && !entered_regionview->selected()) {
6569 rs.add (entered_regionview);
6571 rs = selection->regions; //might be empty
6575 TrackViewList tracks = selection->tracks;
6577 if (!tracks.empty()) {
6578 /* no region selected or entered, but some selected tracks:
6579 * act on all regions on the selected tracks at the edit point
6581 samplepos_t const where = get_preferred_edit_position (Editing::EDIT_IGNORE_NONE, false, false);
6582 get_regions_at(rs, where, tracks);
6586 const samplepos_t pos = get_preferred_edit_position();
6587 const int32_t division = get_grid_music_divisions (0);
6588 MusicSample where (pos, division);
6594 split_regions_at (where, rs);
6599 Editor::select_next_stripable (bool routes_only)
6601 _session->selection().select_next_stripable (false, routes_only);
6605 Editor::select_prev_stripable (bool routes_only)
6607 _session->selection().select_prev_stripable (false, routes_only);
6611 Editor::set_loop_from_selection (bool play)
6613 if (_session == 0) {
6617 samplepos_t start, end;
6618 if (!get_selection_extents (start, end))
6621 set_loop_range (start, end, _("set loop range from selection"));
6624 _session->request_play_loop (true, true);
6629 Editor::set_loop_from_region (bool play)
6631 samplepos_t start, end;
6632 if (!get_selection_extents (start, end))
6635 set_loop_range (start, end, _("set loop range from region"));
6638 _session->request_locate (start, true);
6639 _session->request_play_loop (true);
6644 Editor::set_punch_from_selection ()
6646 if (_session == 0) {
6650 samplepos_t start, end;
6651 if (!get_selection_extents (start, end))
6654 set_punch_range (start, end, _("set punch range from selection"));
6658 Editor::set_auto_punch_range ()
6660 // auto punch in/out button from a single button
6661 // If Punch In is unset, set punch range from playhead to end, enable punch in
6662 // If Punch In is set, the next punch sets Punch Out, unless the playhead has been
6663 // rewound beyond the Punch In marker, in which case that marker will be moved back
6664 // to the current playhead position.
6665 // If punch out is set, it clears the punch range and Punch In/Out buttons
6667 if (_session == 0) {
6671 Location* tpl = transport_punch_location();
6672 samplepos_t now = playhead_cursor->current_sample();
6673 samplepos_t begin = now;
6674 samplepos_t end = _session->current_end_sample();
6676 if (!_session->config.get_punch_in()) {
6677 // First Press - set punch in and create range from here to eternity
6678 set_punch_range (begin, end, _("Auto Punch In"));
6679 _session->config.set_punch_in(true);
6680 } else if (tpl && !_session->config.get_punch_out()) {
6681 // Second press - update end range marker and set punch_out
6682 if (now < tpl->start()) {
6683 // playhead has been rewound - move start back and pretend nothing happened
6685 set_punch_range (begin, end, _("Auto Punch In/Out"));
6687 // normal case for 2nd press - set the punch out
6688 end = playhead_cursor->current_sample ();
6689 set_punch_range (tpl->start(), now, _("Auto Punch In/Out"));
6690 _session->config.set_punch_out(true);
6693 if (_session->config.get_punch_out()) {
6694 _session->config.set_punch_out(false);
6697 if (_session->config.get_punch_in()) {
6698 _session->config.set_punch_in(false);
6703 // third press - unset punch in/out and remove range
6704 _session->locations()->remove(tpl);
6711 Editor::set_session_extents_from_selection ()
6713 if (_session == 0) {
6717 samplepos_t start, end;
6718 if (!get_selection_extents (start, end))
6722 if ((loc = _session->locations()->session_range_location()) == 0) {
6723 _session->set_session_extents (start, end); // this will create a new session range; no need for UNDO
6725 XMLNode &before = loc->get_state();
6727 _session->set_session_extents (start, end);
6729 XMLNode &after = loc->get_state();
6731 begin_reversible_command (_("set session start/end from selection"));
6733 _session->add_command (new MementoCommand<Location>(*loc, &before, &after));
6735 commit_reversible_command ();
6738 _session->set_session_range_is_free (false);
6742 Editor::set_punch_start_from_edit_point ()
6746 MusicSample start (0, 0);
6747 samplepos_t end = max_samplepos;
6749 //use the existing punch end, if any
6750 Location* tpl = transport_punch_location();
6755 if ((_edit_point == EditAtPlayhead) && _session->transport_rolling()) {
6756 start.sample = _session->audible_sample();
6758 start.sample = get_preferred_edit_position();
6761 //if there's not already a sensible selection endpoint, go "forever"
6762 if (start.sample > end) {
6763 end = max_samplepos;
6766 set_punch_range (start.sample, end, _("set punch start from EP"));
6772 Editor::set_punch_end_from_edit_point ()
6776 samplepos_t start = 0;
6777 MusicSample end (max_samplepos, 0);
6779 //use the existing punch start, if any
6780 Location* tpl = transport_punch_location();
6782 start = tpl->start();
6785 if ((_edit_point == EditAtPlayhead) && _session->transport_rolling()) {
6786 end.sample = _session->audible_sample();
6788 end.sample = get_preferred_edit_position();
6791 set_punch_range (start, end.sample, _("set punch end from EP"));
6797 Editor::set_loop_start_from_edit_point ()
6801 MusicSample start (0, 0);
6802 samplepos_t end = max_samplepos;
6804 //use the existing loop end, if any
6805 Location* tpl = transport_loop_location();
6810 if ((_edit_point == EditAtPlayhead) && _session->transport_rolling()) {
6811 start.sample = _session->audible_sample();
6813 start.sample = get_preferred_edit_position();
6816 //if there's not already a sensible selection endpoint, go "forever"
6817 if (start.sample > end) {
6818 end = max_samplepos;
6821 set_loop_range (start.sample, end, _("set loop start from EP"));
6827 Editor::set_loop_end_from_edit_point ()
6831 samplepos_t start = 0;
6832 MusicSample end (max_samplepos, 0);
6834 //use the existing loop start, if any
6835 Location* tpl = transport_loop_location();
6837 start = tpl->start();
6840 if ((_edit_point == EditAtPlayhead) && _session->transport_rolling()) {
6841 end.sample = _session->audible_sample();
6843 end.sample = get_preferred_edit_position();
6846 set_loop_range (start, end.sample, _("set loop end from EP"));
6851 Editor::set_punch_from_region ()
6853 samplepos_t start, end;
6854 if (!get_selection_extents (start, end))
6857 set_punch_range (start, end, _("set punch range from region"));
6861 Editor::pitch_shift_region ()
6863 RegionSelection rs = get_regions_from_selection_and_entered ();
6865 RegionSelection audio_rs;
6866 for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
6867 if (dynamic_cast<AudioRegionView*> (*i)) {
6868 audio_rs.push_back (*i);
6872 if (audio_rs.empty()) {
6876 pitch_shift (audio_rs, 1.2);
6880 Editor::set_tempo_from_region ()
6882 RegionSelection rs = get_regions_from_selection_and_entered ();
6884 if (!_session || rs.empty()) {
6888 RegionView* rv = rs.front();
6890 define_one_bar (rv->region()->position(), rv->region()->last_sample() + 1);
6894 Editor::use_range_as_bar ()
6896 samplepos_t start, end;
6897 if (get_edit_op_range (start, end)) {
6898 define_one_bar (start, end);
6903 Editor::define_one_bar (samplepos_t start, samplepos_t end)
6905 samplepos_t length = end - start;
6907 const Meter& m (_session->tempo_map().meter_at_sample (start));
6909 /* length = 1 bar */
6911 /* We're going to deliver a constant tempo here,
6912 so we can use samples per beat to determine length.
6913 now we want samples per beat.
6914 we have samples per bar, and beats per bar, so ...
6917 /* XXXX METER MATH */
6919 double samples_per_beat = length / m.divisions_per_bar();
6921 /* beats per minute = */
6923 double beats_per_minute = (_session->sample_rate() * 60.0) / samples_per_beat;
6925 /* now decide whether to:
6927 (a) set global tempo
6928 (b) add a new tempo marker
6932 const TempoSection& t (_session->tempo_map().tempo_section_at_sample (start));
6934 bool do_global = false;
6936 if ((_session->tempo_map().n_tempos() == 1) && (_session->tempo_map().n_meters() == 1)) {
6938 /* only 1 tempo & 1 meter: ask if the user wants to set the tempo
6939 at the start, or create a new marker
6942 vector<string> options;
6943 options.push_back (_("Cancel"));
6944 options.push_back (_("Add new marker"));
6945 options.push_back (_("Set global tempo"));
6948 _("Define one bar"),
6949 _("Do you want to set the global tempo or add a new tempo marker?"),
6953 c.set_default_response (2);
6969 /* more than 1 tempo and/or meter section already, go ahead do the "usual":
6970 if the marker is at the region starter, change it, otherwise add
6975 begin_reversible_command (_("set tempo from region"));
6976 XMLNode& before (_session->tempo_map().get_state());
6979 _session->tempo_map().change_initial_tempo (beats_per_minute, t.note_type(), t.end_note_types_per_minute());
6980 } else if (t.sample() == start) {
6981 _session->tempo_map().change_existing_tempo_at (start, beats_per_minute, t.note_type(), t.end_note_types_per_minute());
6983 /* constant tempo */
6984 const Tempo tempo (beats_per_minute, t.note_type());
6985 _session->tempo_map().add_tempo (tempo, 0.0, start, AudioTime);
6988 XMLNode& after (_session->tempo_map().get_state());
6990 _session->add_command (new MementoCommand<TempoMap>(_session->tempo_map(), &before, &after));
6991 commit_reversible_command ();
6995 Editor::split_region_at_transients ()
6997 AnalysisFeatureList positions;
6999 RegionSelection rs = get_regions_from_selection_and_entered ();
7001 if (!_session || rs.empty()) {
7005 begin_reversible_command (_("split regions"));
7007 for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ) {
7009 RegionSelection::iterator tmp;
7014 boost::shared_ptr<AudioRegion> ar = boost::dynamic_pointer_cast<AudioRegion> ((*i)->region());
7017 ar->transients (positions);
7018 split_region_at_points ((*i)->region(), positions, true);
7025 commit_reversible_command ();
7030 Editor::split_region_at_points (boost::shared_ptr<Region> r, AnalysisFeatureList& positions, bool can_ferret, bool select_new)
7032 bool use_rhythmic_rodent = false;
7034 boost::shared_ptr<Playlist> pl = r->playlist();
7036 list<boost::shared_ptr<Region> > new_regions;
7042 if (positions.empty()) {
7046 if (positions.size() > 20 && can_ferret) {
7047 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);
7048 MessageDialog msg (msgstr,
7051 Gtk::BUTTONS_OK_CANCEL);
7054 msg.add_button (_("Call for the Ferret!"), RESPONSE_APPLY);
7055 msg.set_secondary_text (_("Press OK to continue with this split operation\nor ask the Ferret dialog to tune the analysis"));
7057 msg.set_secondary_text (_("Press OK to continue with this split operation"));
7060 msg.set_title (_("Excessive split?"));
7063 int response = msg.run();
7069 case RESPONSE_APPLY:
7070 use_rhythmic_rodent = true;
7077 if (use_rhythmic_rodent) {
7078 show_rhythm_ferret ();
7082 AnalysisFeatureList::const_iterator x;
7084 pl->clear_changes ();
7085 pl->clear_owned_changes ();
7087 x = positions.begin();
7089 if (x == positions.end()) {
7094 pl->remove_region (r);
7096 samplepos_t pos = 0;
7098 samplepos_t rstart = r->first_sample ();
7099 samplepos_t rend = r->last_sample ();
7101 while (x != positions.end()) {
7103 /* deal with positons that are out of scope of present region bounds */
7104 if (*x <= rstart || *x > rend) {
7109 /* file start = original start + how far we from the initial position ? */
7111 samplepos_t file_start = r->start() + pos;
7113 /* length = next position - current position */
7115 samplepos_t len = (*x) - pos - rstart;
7117 /* XXX we do we really want to allow even single-sample regions?
7118 * shouldn't we have some kind of lower limit on region size?
7127 if (RegionFactory::region_name (new_name, r->name())) {
7131 /* do NOT announce new regions 1 by one, just wait till they are all done */
7135 plist.add (ARDOUR::Properties::start, file_start);
7136 plist.add (ARDOUR::Properties::length, len);
7137 plist.add (ARDOUR::Properties::name, new_name);
7138 plist.add (ARDOUR::Properties::layer, 0);
7139 // TODO set transients_offset
7141 boost::shared_ptr<Region> nr = RegionFactory::create (r->sources(), plist, false);
7142 /* because we set annouce to false, manually add the new region to the
7145 RegionFactory::map_add (nr);
7147 pl->add_region (nr, rstart + pos);
7150 new_regions.push_front(nr);
7159 RegionFactory::region_name (new_name, r->name());
7161 /* Add the final region */
7164 plist.add (ARDOUR::Properties::start, r->start() + pos);
7165 plist.add (ARDOUR::Properties::length, r->last_sample() - (r->position() + pos) + 1);
7166 plist.add (ARDOUR::Properties::name, new_name);
7167 plist.add (ARDOUR::Properties::layer, 0);
7169 boost::shared_ptr<Region> nr = RegionFactory::create (r->sources(), plist, false);
7170 /* because we set annouce to false, manually add the new region to the
7173 RegionFactory::map_add (nr);
7174 pl->add_region (nr, r->position() + pos);
7177 new_regions.push_front(nr);
7182 /* We might have removed regions, which alters other regions' layering_index,
7183 so we need to do a recursive diff here.
7185 vector<Command*> cmds;
7187 _session->add_commands (cmds);
7189 _session->add_command (new StatefulDiffCommand (pl));
7193 for (list<boost::shared_ptr<Region> >::iterator i = new_regions.begin(); i != new_regions.end(); ++i){
7194 set_selected_regionview_from_region_list ((*i), Selection::Add);
7200 Editor::place_transient()
7206 RegionSelection rs = get_regions_from_selection_and_edit_point ();
7212 samplepos_t where = get_preferred_edit_position();
7214 begin_reversible_command (_("place transient"));
7216 for (RegionSelection::iterator r = rs.begin(); r != rs.end(); ++r) {
7217 (*r)->region()->add_transient(where);
7220 commit_reversible_command ();
7224 Editor::remove_transient(ArdourCanvas::Item* item)
7230 ArdourCanvas::Line* _line = reinterpret_cast<ArdourCanvas::Line*> (item);
7233 AudioRegionView* _arv = reinterpret_cast<AudioRegionView*> (item->get_data ("regionview"));
7234 _arv->remove_transient (*(float*) _line->get_data ("position"));
7238 Editor::snap_regions_to_grid ()
7240 list <boost::shared_ptr<Playlist > > used_playlists;
7242 RegionSelection rs = get_regions_from_selection_and_entered ();
7244 if (!_session || rs.empty()) {
7248 begin_reversible_command (_("snap regions to grid"));
7250 for (RegionSelection::iterator r = rs.begin(); r != rs.end(); ++r) {
7252 boost::shared_ptr<Playlist> pl = (*r)->region()->playlist();
7254 if (!pl->frozen()) {
7255 /* we haven't seen this playlist before */
7257 /* remember used playlists so we can thaw them later */
7258 used_playlists.push_back(pl);
7261 (*r)->region()->clear_changes ();
7263 MusicSample start ((*r)->region()->first_sample (), 0);
7264 snap_to (start, RoundNearest, SnapToGrid_Unscaled, true);
7265 (*r)->region()->set_position (start.sample, start.division);
7266 _session->add_command(new StatefulDiffCommand ((*r)->region()));
7269 while (used_playlists.size() > 0) {
7270 list <boost::shared_ptr<Playlist > >::iterator i = used_playlists.begin();
7272 used_playlists.pop_front();
7275 commit_reversible_command ();
7279 Editor::close_region_gaps ()
7281 list <boost::shared_ptr<Playlist > > used_playlists;
7283 RegionSelection rs = get_regions_from_selection_and_entered ();
7285 if (!_session || rs.empty()) {
7289 Dialog dialog (_("Close Region Gaps"));
7292 table.set_spacings (12);
7293 table.set_border_width (12);
7294 Label* l = manage (left_aligned_label (_("Crossfade length")));
7295 table.attach (*l, 0, 1, 0, 1);
7297 SpinButton spin_crossfade (1, 0);
7298 spin_crossfade.set_range (0, 15);
7299 spin_crossfade.set_increments (1, 1);
7300 spin_crossfade.set_value (5);
7301 table.attach (spin_crossfade, 1, 2, 0, 1);
7303 table.attach (*manage (new Label (_("ms"))), 2, 3, 0, 1);
7305 l = manage (left_aligned_label (_("Pull-back length")));
7306 table.attach (*l, 0, 1, 1, 2);
7308 SpinButton spin_pullback (1, 0);
7309 spin_pullback.set_range (0, 100);
7310 spin_pullback.set_increments (1, 1);
7311 spin_pullback.set_value(30);
7312 table.attach (spin_pullback, 1, 2, 1, 2);
7314 table.attach (*manage (new Label (_("ms"))), 2, 3, 1, 2);
7316 dialog.get_vbox()->pack_start (table);
7317 dialog.add_button (Stock::CANCEL, RESPONSE_CANCEL);
7318 dialog.add_button (_("Ok"), RESPONSE_ACCEPT);
7321 if (dialog.run () == RESPONSE_CANCEL) {
7325 samplepos_t crossfade_len = spin_crossfade.get_value();
7326 samplepos_t pull_back_samples = spin_pullback.get_value();
7328 crossfade_len = lrintf (crossfade_len * _session->sample_rate()/1000);
7329 pull_back_samples = lrintf (pull_back_samples * _session->sample_rate()/1000);
7331 /* Iterate over the region list and make adjacent regions overlap by crossfade_len_ms */
7333 begin_reversible_command (_("close region gaps"));
7336 boost::shared_ptr<Region> last_region;
7338 rs.sort_by_position_and_track();
7340 for (RegionSelection::iterator r = rs.begin(); r != rs.end(); ++r) {
7342 boost::shared_ptr<Playlist> pl = (*r)->region()->playlist();
7344 if (!pl->frozen()) {
7345 /* we haven't seen this playlist before */
7347 /* remember used playlists so we can thaw them later */
7348 used_playlists.push_back(pl);
7352 samplepos_t position = (*r)->region()->position();
7354 if (idx == 0 || position < last_region->position()){
7355 last_region = (*r)->region();
7360 (*r)->region()->clear_changes ();
7361 (*r)->region()->trim_front((position - pull_back_samples));
7363 last_region->clear_changes ();
7364 last_region->trim_end ((position - pull_back_samples + crossfade_len));
7366 _session->add_command (new StatefulDiffCommand ((*r)->region()));
7367 _session->add_command (new StatefulDiffCommand (last_region));
7369 last_region = (*r)->region();
7373 while (used_playlists.size() > 0) {
7374 list <boost::shared_ptr<Playlist > >::iterator i = used_playlists.begin();
7376 used_playlists.pop_front();
7379 commit_reversible_command ();
7383 Editor::tab_to_transient (bool forward)
7385 AnalysisFeatureList positions;
7387 RegionSelection rs = get_regions_from_selection_and_entered ();
7393 samplepos_t pos = _session->audible_sample ();
7395 if (!selection->tracks.empty()) {
7397 /* don't waste time searching for transients in duplicate playlists.
7400 TrackViewList ts = selection->tracks.filter_to_unique_playlists ();
7402 for (TrackViewList::iterator t = ts.begin(); t != ts.end(); ++t) {
7404 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (*t);
7407 boost::shared_ptr<Track> tr = rtv->track();
7409 boost::shared_ptr<Playlist> pl = tr->playlist ();
7411 samplepos_t result = pl->find_next_transient (pos, forward ? 1 : -1);
7414 positions.push_back (result);
7427 for (RegionSelection::iterator r = rs.begin(); r != rs.end(); ++r) {
7428 (*r)->region()->get_transients (positions);
7432 TransientDetector::cleanup_transients (positions, _session->sample_rate(), 3.0);
7435 AnalysisFeatureList::iterator x;
7437 for (x = positions.begin(); x != positions.end(); ++x) {
7443 if (x != positions.end ()) {
7444 _session->request_locate (*x);
7448 AnalysisFeatureList::reverse_iterator x;
7450 for (x = positions.rbegin(); x != positions.rend(); ++x) {
7456 if (x != positions.rend ()) {
7457 _session->request_locate (*x);
7463 Editor::playhead_forward_to_grid ()
7469 MusicSample pos (playhead_cursor->current_sample (), 0);
7471 if ( _grid_type == GridTypeNone) {
7472 if (pos.sample < max_samplepos - current_page_samples()*0.1) {
7473 pos.sample += current_page_samples()*0.1;
7474 _session->request_locate (pos.sample);
7476 _session->request_locate (0);
7480 if (pos.sample < max_samplepos - 1) {
7482 pos = snap_to_grid (pos, RoundUpAlways, SnapToGrid_Scaled);
7483 _session->request_locate (pos.sample);
7488 /* keep PH visible in window */
7489 if (pos.sample > (_leftmost_sample + current_page_samples() *0.9)) {
7490 reset_x_origin (pos.sample - (current_page_samples()*0.9));
7496 Editor::playhead_backward_to_grid ()
7502 MusicSample pos (playhead_cursor->current_sample (), 0);
7504 if ( _grid_type == GridTypeNone) {
7505 if ( pos.sample > current_page_samples()*0.1 ) {
7506 pos.sample -= current_page_samples()*0.1;
7507 _session->request_locate (pos.sample);
7509 _session->request_locate (0);
7513 if (pos.sample > 2) {
7515 pos = snap_to_grid (pos, RoundDownAlways, SnapToGrid_Scaled);
7518 //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...
7519 //also see: jump_backward_to_mark
7520 if (_session->transport_rolling()) {
7521 if ((playhead_cursor->current_sample() - pos.sample) < _session->sample_rate()/2) {
7522 pos = snap_to_grid (pos, RoundDownAlways, SnapToGrid_Scaled);
7526 _session->request_locate (pos.sample, _session->transport_rolling());
7529 /* keep PH visible in window */
7530 if (pos.sample < (_leftmost_sample + current_page_samples() *0.1)) {
7531 reset_x_origin (pos.sample - (current_page_samples()*0.1));
7536 Editor::set_track_height (Height h)
7538 TrackSelection& ts (selection->tracks);
7540 for (TrackSelection::iterator x = ts.begin(); x != ts.end(); ++x) {
7541 (*x)->set_height_enum (h);
7546 Editor::toggle_tracks_active ()
7548 TrackSelection& ts (selection->tracks);
7550 bool target = false;
7556 for (TrackSelection::iterator x = ts.begin(); x != ts.end(); ++x) {
7557 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*>(*x);
7561 target = !rtv->_route->active();
7564 rtv->_route->set_active (target, this);
7570 Editor::remove_tracks ()
7572 /* this will delete GUI objects that may be the subject of an event
7573 handler in which this method is called. Defer actual deletion to the
7574 next idle callback, when all event handling is finished.
7576 Glib::signal_idle().connect (sigc::mem_fun (*this, &Editor::idle_remove_tracks));
7580 Editor::idle_remove_tracks ()
7582 Session::StateProtector sp (_session);
7584 return false; /* do not call again */
7588 Editor::_remove_tracks ()
7590 TrackSelection& ts (selection->tracks);
7596 if (!ARDOUR_UI_UTILS::engine_is_running ()) {
7600 vector<string> choices;
7605 const char* trackstr;
7608 vector<boost::shared_ptr<Route> > routes;
7609 vector<boost::shared_ptr<VCA> > vcas;
7610 bool special_bus = false;
7612 for (TrackSelection::iterator x = ts.begin(); x != ts.end(); ++x) {
7613 VCATimeAxisView* vtv = dynamic_cast<VCATimeAxisView*> (*x);
7615 vcas.push_back (vtv->vca());
7619 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (*x);
7623 if (rtv->is_track()) {
7628 routes.push_back (rtv->_route);
7630 if (rtv->route()->is_master() || rtv->route()->is_monitor()) {
7635 if (special_bus && !Config->get_allow_special_bus_removal()) {
7636 MessageDialog msg (_("That would be bad news ...."),
7640 msg.set_secondary_text (string_compose (_(
7641 "Removing the master or monitor bus is such a bad idea\n\
7642 that %1 is not going to allow it.\n\
7644 If you really want to do this sort of thing\n\
7645 edit your ardour.rc file to set the\n\
7646 \"allow-special-bus-removal\" option to be \"yes\""), PROGRAM_NAME));
7653 if (ntracks + nbusses + nvcas == 0) {
7659 trackstr = P_("track", "tracks", ntracks);
7660 busstr = P_("bus", "busses", nbusses);
7661 vcastr = P_("VCA", "VCAs", nvcas);
7663 if (ntracks > 0 && nbusses > 0 && nvcas > 0) {
7664 title = _("Remove various strips");
7665 prompt = string_compose (_("Do you really want to remove %1 %2, %3 %4 and %5 %6?"),
7666 ntracks, trackstr, nbusses, busstr, nvcas, vcastr);
7668 else if (ntracks > 0 && nbusses > 0) {
7669 title = string_compose (_("Remove %1 and %2"), trackstr, busstr);
7670 prompt = string_compose (_("Do you really want to remove %1 %2 and %3 %4?"),
7671 ntracks, trackstr, nbusses, busstr);
7673 else if (ntracks > 0 && nvcas > 0) {
7674 title = string_compose (_("Remove %1 and %2"), trackstr, vcastr);
7675 prompt = string_compose (_("Do you really want to remove %1 %2 and %3 %4?"),
7676 ntracks, trackstr, nvcas, vcastr);
7678 else if (nbusses > 0 && nvcas > 0) {
7679 title = string_compose (_("Remove %1 and %2"), busstr, vcastr);
7680 prompt = string_compose (_("Do you really want to remove %1 %2 and %3 %4?"),
7681 nbusses, busstr, nvcas, vcastr);
7683 else if (ntracks > 0) {
7684 title = string_compose (_("Remove %1"), trackstr);
7685 prompt = string_compose (_("Do you really want to remove %1 %2?"),
7688 else if (nbusses > 0) {
7689 title = string_compose (_("Remove %1"), busstr);
7690 prompt = string_compose (_("Do you really want to remove %1 %2?"),
7693 else if (nvcas > 0) {
7694 title = string_compose (_("Remove %1"), vcastr);
7695 prompt = string_compose (_("Do you really want to remove %1 %2?"),
7703 prompt += "\n" + string_compose ("(You may also lose the playlists associated with the %1)", trackstr) + "\n";
7706 prompt += "\n" + string(_("This action cannot be undone, and the session file will be overwritten!"));
7708 choices.push_back (_("No, do nothing."));
7709 if (ntracks + nbusses + nvcas > 1) {
7710 choices.push_back (_("Yes, remove them."));
7712 choices.push_back (_("Yes, remove it."));
7715 Choice prompter (title, prompt, choices);
7717 if (prompter.run () != 1) {
7721 if (current_mixer_strip && routes.size () > 1 && std::find (routes.begin(), routes.end(), current_mixer_strip->route()) != routes.end ()) {
7722 /* Route deletion calls Editor::timeaxisview_deleted() iteratively (for each deleted
7723 * route). If the deleted route is currently displayed in the Editor-Mixer (highly
7724 * likely because deletion requires selection) this will call
7725 * Editor::set_selected_mixer_strip () which is expensive (MixerStrip::set_route()).
7726 * It's likewise likely that the route that has just been displayed in the
7727 * Editor-Mixer will be next in line for deletion.
7729 * So simply switch to the master-bus (if present)
7731 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
7732 if ((*i)->stripable ()->is_master ()) {
7733 set_selected_mixer_strip (*(*i));
7740 PresentationInfo::ChangeSuspender cs;
7741 DisplaySuspender ds;
7743 boost::shared_ptr<RouteList> rl (new RouteList);
7744 for (vector<boost::shared_ptr<Route> >::iterator x = routes.begin(); x != routes.end(); ++x) {
7747 _session->remove_routes (rl);
7749 for (vector<boost::shared_ptr<VCA> >::iterator x = vcas.begin(); x != vcas.end(); ++x) {
7750 _session->vca_manager().remove_vca (*x);
7754 /* TrackSelection and RouteList leave scope,
7755 * destructors are called,
7756 * diskstream drops references, save_state is called (again for every track)
7761 Editor::do_insert_time ()
7763 if (selection->tracks.empty()) {
7764 MessageDialog msg (_("You must first select some tracks to Insert Time."),
7765 true, MESSAGE_INFO, BUTTONS_OK, true);
7766 msg.set_position (WIN_POS_MOUSE);
7771 if (Config->get_edit_mode() == Lock) {
7772 MessageDialog msg (_("You cannot insert time in Lock Edit mode."),
7773 true, MESSAGE_INFO, BUTTONS_OK, true);
7774 msg.set_position (WIN_POS_MOUSE);
7779 InsertRemoveTimeDialog d (*this);
7780 int response = d.run ();
7782 if (response != RESPONSE_OK) {
7786 if (d.distance() == 0) {
7793 d.intersected_region_action (),
7797 d.move_glued_markers(),
7798 d.move_locked_markers(),
7804 Editor::insert_time (
7805 samplepos_t pos, samplecnt_t samples, InsertTimeOption opt,
7806 bool all_playlists, bool ignore_music_glue, bool markers_too, bool glued_markers_too, bool locked_markers_too, bool tempo_too
7810 if (Config->get_edit_mode() == Lock) {
7813 bool in_command = false;
7815 TrackViewList ts = selection->tracks.filter_to_unique_playlists ();
7817 for (TrackViewList::iterator x = ts.begin(); x != ts.end(); ++x) {
7821 /* don't operate on any playlist more than once, which could
7822 * happen if "all playlists" is enabled, but there is more
7823 * than 1 track using playlists "from" a given track.
7826 set<boost::shared_ptr<Playlist> > pl;
7828 if (all_playlists) {
7829 RouteTimeAxisView* rtav = dynamic_cast<RouteTimeAxisView*> (*x);
7830 if (rtav && rtav->track ()) {
7831 vector<boost::shared_ptr<Playlist> > all = _session->playlists()->playlists_for_track (rtav->track ());
7832 for (vector<boost::shared_ptr<Playlist> >::iterator p = all.begin(); p != all.end(); ++p) {
7837 if ((*x)->playlist ()) {
7838 pl.insert ((*x)->playlist ());
7842 for (set<boost::shared_ptr<Playlist> >::iterator i = pl.begin(); i != pl.end(); ++i) {
7844 (*i)->clear_changes ();
7845 (*i)->clear_owned_changes ();
7848 begin_reversible_command (_("insert time"));
7852 if (opt == SplitIntersected) {
7853 /* non musical split */
7854 (*i)->split (MusicSample (pos, 0));
7857 (*i)->shift (pos, samples, (opt == MoveIntersected), ignore_music_glue);
7859 vector<Command*> cmds;
7861 _session->add_commands (cmds);
7863 _session->add_command (new StatefulDiffCommand (*i));
7867 RouteTimeAxisView* rtav = dynamic_cast<RouteTimeAxisView*> (*x);
7870 begin_reversible_command (_("insert time"));
7873 rtav->route ()->shift (pos, samples);
7880 const int32_t divisions = get_grid_music_divisions (0);
7881 XMLNode& before (_session->locations()->get_state());
7882 Locations::LocationList copy (_session->locations()->list());
7884 for (Locations::LocationList::iterator i = copy.begin(); i != copy.end(); ++i) {
7886 Locations::LocationList::const_iterator tmp;
7888 if ((*i)->position_lock_style() == AudioTime || glued_markers_too) {
7889 bool const was_locked = (*i)->locked ();
7890 if (locked_markers_too) {
7894 if ((*i)->start() >= pos) {
7895 // move end first, in case we're moving by more than the length of the range
7896 if (!(*i)->is_mark()) {
7897 (*i)->set_end ((*i)->end() + samples, false, true, divisions);
7899 (*i)->set_start ((*i)->start() + samples, false, true, divisions);
7911 begin_reversible_command (_("insert time"));
7914 XMLNode& after (_session->locations()->get_state());
7915 _session->add_command (new MementoCommand<Locations>(*_session->locations(), &before, &after));
7921 begin_reversible_command (_("insert time"));
7924 XMLNode& before (_session->tempo_map().get_state());
7925 _session->tempo_map().insert_time (pos, samples);
7926 XMLNode& after (_session->tempo_map().get_state());
7927 _session->add_command (new MementoCommand<TempoMap>(_session->tempo_map(), &before, &after));
7931 commit_reversible_command ();
7936 Editor::do_remove_time ()
7938 if (selection->tracks.empty()) {
7939 MessageDialog msg (_("You must first select some tracks to Remove Time."),
7940 true, MESSAGE_INFO, BUTTONS_OK, true);
7941 msg.set_position (WIN_POS_MOUSE);
7946 if (Config->get_edit_mode() == Lock) {
7947 MessageDialog msg (_("You cannot remove time in Lock Edit mode."),
7948 true, MESSAGE_INFO, BUTTONS_OK, true);
7949 msg.set_position (WIN_POS_MOUSE);
7954 InsertRemoveTimeDialog d (*this, true);
7956 int response = d.run ();
7958 if (response != RESPONSE_OK) {
7962 samplecnt_t distance = d.distance();
7964 if (distance == 0) {
7974 d.move_glued_markers(),
7975 d.move_locked_markers(),
7981 Editor::remove_time (samplepos_t pos, samplecnt_t samples, InsertTimeOption opt,
7982 bool ignore_music_glue, bool markers_too, bool glued_markers_too, bool locked_markers_too, bool tempo_too)
7984 if (Config->get_edit_mode() == Lock) {
7985 error << (_("Cannot insert or delete time when in Lock edit.")) << endmsg;
7988 bool in_command = false;
7990 for (TrackSelection::iterator x = selection->tracks.begin(); x != selection->tracks.end(); ++x) {
7992 boost::shared_ptr<Playlist> pl = (*x)->playlist();
7996 XMLNode &before = pl->get_state();
7999 begin_reversible_command (_("remove time"));
8003 std::list<AudioRange> rl;
8004 AudioRange ar(pos, pos+samples, 0);
8007 pl->shift (pos, -samples, true, ignore_music_glue);
8009 XMLNode &after = pl->get_state();
8011 _session->add_command (new MementoCommand<Playlist> (*pl, &before, &after));
8015 RouteTimeAxisView* rtav = dynamic_cast<RouteTimeAxisView*> (*x);
8018 begin_reversible_command (_("remove time"));
8021 rtav->route ()->shift (pos, -samples);
8025 const int32_t divisions = get_grid_music_divisions (0);
8026 std::list<Location*> loc_kill_list;
8031 XMLNode& before (_session->locations()->get_state());
8032 Locations::LocationList copy (_session->locations()->list());
8034 for (Locations::LocationList::iterator i = copy.begin(); i != copy.end(); ++i) {
8035 if ((*i)->position_lock_style() == AudioTime || glued_markers_too) {
8037 bool const was_locked = (*i)->locked ();
8038 if (locked_markers_too) {
8042 if (!(*i)->is_mark()) { // it's a range; have to handle both start and end
8043 if ((*i)->end() >= pos
8044 && (*i)->end() < pos+samples
8045 && (*i)->start() >= pos
8046 && (*i)->end() < pos+samples) { // range is completely enclosed; kill it
8048 loc_kill_list.push_back(*i);
8049 } else { // only start or end is included, try to do the right thing
8050 // move start before moving end, to avoid trying to move the end to before the start
8051 // if we're removing more time than the length of the range
8052 if ((*i)->start() >= pos && (*i)->start() < pos+samples) {
8053 // start is within cut
8054 (*i)->set_start (pos, false, true,divisions); // bring the start marker to the beginning of the cut
8056 } else if ((*i)->start() >= pos+samples) {
8057 // start (and thus entire range) lies beyond end of cut
8058 (*i)->set_start ((*i)->start() - samples, false, true, divisions); // slip the start marker back
8061 if ((*i)->end() >= pos && (*i)->end() < pos+samples) {
8062 // end is inside cut
8063 (*i)->set_end (pos, false, true, divisions); // bring the end to the cut
8065 } else if ((*i)->end() >= pos+samples) {
8066 // end is beyond end of cut
8067 (*i)->set_end ((*i)->end() - samples, false, true, divisions); // slip the end marker back
8072 } else if ((*i)->start() >= pos && (*i)->start() < pos+samples) {
8073 loc_kill_list.push_back(*i);
8075 } else if ((*i)->start() >= pos) {
8076 (*i)->set_start ((*i)->start() -samples, false, true, divisions);
8086 for (list<Location*>::iterator i = loc_kill_list.begin(); i != loc_kill_list.end(); ++i) {
8087 _session->locations()->remove (*i);
8092 begin_reversible_command (_("remove time"));
8095 XMLNode& after (_session->locations()->get_state());
8096 _session->add_command (new MementoCommand<Locations>(*_session->locations(), &before, &after));
8101 XMLNode& before (_session->tempo_map().get_state());
8103 if (_session->tempo_map().remove_time (pos, samples)) {
8105 begin_reversible_command (_("remove time"));
8108 XMLNode& after (_session->tempo_map().get_state());
8109 _session->add_command (new MementoCommand<TempoMap>(_session->tempo_map(), &before, &after));
8114 commit_reversible_command ();
8119 Editor::fit_selection ()
8121 if (!selection->tracks.empty()) {
8122 fit_tracks (selection->tracks);
8126 /* no selected tracks - use tracks with selected regions */
8128 if (!selection->regions.empty()) {
8129 for (RegionSelection::iterator r = selection->regions.begin(); r != selection->regions.end(); ++r) {
8130 tvl.push_back (&(*r)->get_time_axis_view ());
8136 } else if (internal_editing()) {
8137 /* no selected tracks, or regions, but in internal edit mode, so follow the mouse and use
8140 if (entered_track) {
8141 tvl.push_back (entered_track);
8149 Editor::fit_tracks (TrackViewList & tracks)
8151 if (tracks.empty()) {
8155 uint32_t child_heights = 0;
8156 int visible_tracks = 0;
8158 for (TrackSelection::iterator t = tracks.begin(); t != tracks.end(); ++t) {
8160 if (!(*t)->marked_for_display()) {
8164 child_heights += (*t)->effective_height() - (*t)->current_height();
8168 /* compute the per-track height from:
8170 * total canvas visible height
8171 * - height that will be taken by visible children of selected tracks
8172 * - height of the ruler/hscroll area
8174 uint32_t h = (uint32_t) floor ((trackviews_height() - child_heights) / visible_tracks);
8175 double first_y_pos = DBL_MAX;
8177 if (h < TimeAxisView::preset_height (HeightSmall)) {
8178 MessageDialog msg (_("There are too many tracks to fit in the current window"));
8179 /* too small to be displayed */
8183 undo_visual_stack.push_back (current_visual_state (true));
8184 PBD::Unwinder<bool> nsv (no_save_visual, true);
8186 /* build a list of all tracks, including children */
8189 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
8191 TimeAxisView::Children c = (*i)->get_child_list ();
8192 for (TimeAxisView::Children::iterator j = c.begin(); j != c.end(); ++j) {
8193 all.push_back (j->get());
8198 // find selection range.
8199 // if someone knows how to user TrackViewList::iterator for this
8201 int selected_top = -1;
8202 int selected_bottom = -1;
8204 for (TrackViewList::iterator t = all.begin(); t != all.end(); ++t, ++i) {
8205 if ((*t)->marked_for_display ()) {
8206 if (tracks.contains(*t)) {
8207 if (selected_top == -1) {
8210 selected_bottom = i;
8216 for (TrackViewList::iterator t = all.begin(); t != all.end(); ++t, ++i) {
8217 if ((*t)->marked_for_display ()) {
8218 if (tracks.contains(*t)) {
8219 (*t)->set_height (h);
8220 first_y_pos = std::min ((*t)->y_position (), first_y_pos);
8222 if (i > selected_top && i < selected_bottom) {
8223 hide_track_in_display (*t);
8230 set the controls_layout height now, because waiting for its size
8231 request signal handler will cause the vertical adjustment setting to fail
8234 controls_layout.property_height () = _full_canvas_height;
8235 vertical_adjustment.set_value (first_y_pos);
8237 redo_visual_stack.push_back (current_visual_state (true));
8239 visible_tracks_selector.set_text (_("Sel"));
8243 Editor::save_visual_state (uint32_t n)
8245 while (visual_states.size() <= n) {
8246 visual_states.push_back (0);
8249 if (visual_states[n] != 0) {
8250 delete visual_states[n];
8253 visual_states[n] = current_visual_state (true);
8258 Editor::goto_visual_state (uint32_t n)
8260 if (visual_states.size() <= n) {
8264 if (visual_states[n] == 0) {
8268 use_visual_state (*visual_states[n]);
8272 Editor::start_visual_state_op (uint32_t n)
8274 save_visual_state (n);
8276 PopUp* pup = new PopUp (WIN_POS_MOUSE, 1000, true);
8278 snprintf (buf, sizeof (buf), _("Saved view %u"), n+1);
8279 pup->set_text (buf);
8284 Editor::cancel_visual_state_op (uint32_t n)
8286 goto_visual_state (n);
8290 Editor::toggle_region_mute ()
8292 if (_ignore_region_action) {
8296 RegionSelection rs = get_regions_from_selection_and_entered ();
8302 if (rs.size() > 1) {
8303 begin_reversible_command (_("mute regions"));
8305 begin_reversible_command (_("mute region"));
8308 for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
8310 (*i)->region()->playlist()->clear_changes ();
8311 (*i)->region()->set_muted (!(*i)->region()->muted ());
8312 _session->add_command (new StatefulDiffCommand ((*i)->region()));
8316 commit_reversible_command ();
8320 Editor::combine_regions ()
8322 /* foreach track with selected regions, take all selected regions
8323 and join them into a new region containing the subregions (as a
8327 typedef set<RouteTimeAxisView*> RTVS;
8330 if (selection->regions.empty()) {
8334 for (RegionSelection::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
8335 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*>(&(*i)->get_time_axis_view());
8338 tracks.insert (rtv);
8342 begin_reversible_command (_("combine regions"));
8344 vector<RegionView*> new_selection;
8346 for (RTVS::iterator i = tracks.begin(); i != tracks.end(); ++i) {
8349 if ((rv = (*i)->combine_regions ()) != 0) {
8350 new_selection.push_back (rv);
8354 selection->clear_regions ();
8355 for (vector<RegionView*>::iterator i = new_selection.begin(); i != new_selection.end(); ++i) {
8356 selection->add (*i);
8359 commit_reversible_command ();
8363 Editor::uncombine_regions ()
8365 typedef set<RouteTimeAxisView*> RTVS;
8368 if (selection->regions.empty()) {
8372 for (RegionSelection::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
8373 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*>(&(*i)->get_time_axis_view());
8376 tracks.insert (rtv);
8380 begin_reversible_command (_("uncombine regions"));
8382 for (RTVS::iterator i = tracks.begin(); i != tracks.end(); ++i) {
8383 (*i)->uncombine_regions ();
8386 commit_reversible_command ();
8390 Editor::toggle_midi_input_active (bool flip_others)
8393 boost::shared_ptr<RouteList> rl (new RouteList);
8395 for (TrackSelection::iterator i = selection->tracks.begin(); i != selection->tracks.end(); ++i) {
8396 RouteTimeAxisView *rtav = dynamic_cast<RouteTimeAxisView *>(*i);
8402 boost::shared_ptr<MidiTrack> mt = rtav->midi_track();
8405 rl->push_back (rtav->route());
8406 onoff = !mt->input_active();
8410 _session->set_exclusive_input_active (rl, onoff, flip_others);
8413 static bool ok_fine (GdkEventAny*) { return true; }
8419 lock_dialog = new Gtk::Dialog (string_compose (_("%1: Locked"), PROGRAM_NAME), true);
8421 Gtk::Image* padlock = manage (new Gtk::Image (ARDOUR_UI_UTILS::get_icon ("padlock_closed")));
8422 lock_dialog->get_vbox()->pack_start (*padlock);
8423 lock_dialog->signal_delete_event ().connect (sigc::ptr_fun (ok_fine));
8425 ArdourButton* b = manage (new ArdourButton);
8426 b->set_name ("lock button");
8427 b->set_text (_("Click to unlock"));
8428 b->signal_clicked.connect (sigc::mem_fun (*this, &Editor::unlock));
8429 lock_dialog->get_vbox()->pack_start (*b);
8431 lock_dialog->get_vbox()->show_all ();
8432 lock_dialog->set_size_request (200, 200);
8435 delete _main_menu_disabler;
8436 _main_menu_disabler = new MainMenuDisabler;
8438 lock_dialog->present ();
8440 lock_dialog->get_window()->set_decorations (Gdk::WMDecoration (0));
8446 lock_dialog->hide ();
8448 delete _main_menu_disabler;
8449 _main_menu_disabler = 0;
8451 if (UIConfiguration::instance().get_lock_gui_after_seconds()) {
8452 start_lock_event_timing ();
8457 Editor::bring_in_callback (Gtk::Label* label, uint32_t n, uint32_t total, string name)
8459 Gtkmm2ext::UI::instance()->call_slot (invalidator (*this), boost::bind (&Editor::update_bring_in_message, this, label, n, total, name));
8463 Editor::update_bring_in_message (Gtk::Label* label, uint32_t n, uint32_t total, string name)
8465 Timers::TimerSuspender t;
8466 label->set_text (string_compose ("Copying %1, %2 of %3", name, n, total));
8467 Gtkmm2ext::UI::instance()->flush_pending (1);
8471 Editor::bring_all_sources_into_session ()
8478 ArdourDialog w (_("Moving embedded files into session folder"));
8479 w.get_vbox()->pack_start (msg);
8482 /* flush all pending GUI events because we're about to start copying
8486 Timers::TimerSuspender t;
8487 Gtkmm2ext::UI::instance()->flush_pending (3);
8491 _session->bring_all_sources_into_session (boost::bind (&Editor::bring_in_callback, this, &msg, _1, _2, _3));