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);
5529 Editor::apply_midi_note_edit_op_to_region (MidiOperator& op, MidiRegionView& mrv)
5531 Evoral::Sequence<Temporal::Beats>::Notes selected;
5532 mrv.selection_as_notelist (selected, true);
5534 vector<Evoral::Sequence<Temporal::Beats>::Notes> v;
5535 v.push_back (selected);
5537 Temporal::Beats pos_beats = Temporal::Beats (mrv.midi_region()->beat()) - mrv.midi_region()->start_beats();
5539 return op (mrv.midi_region()->model(), pos_beats, v);
5543 Editor::apply_midi_note_edit_op (MidiOperator& op, const RegionSelection& rs)
5549 bool in_command = false;
5551 for (RegionSelection::const_iterator r = rs.begin(); r != rs.end(); ) {
5552 RegionSelection::const_iterator tmp = r;
5555 MidiRegionView* const mrv = dynamic_cast<MidiRegionView*> (*r);
5558 Command* cmd = apply_midi_note_edit_op_to_region (op, *mrv);
5561 begin_reversible_command (op.name ());
5565 _session->add_command (cmd);
5573 commit_reversible_command ();
5574 _session->set_dirty ();
5579 Editor::fork_region ()
5581 RegionSelection rs = get_regions_from_selection_and_entered ();
5587 CursorContext::Handle cursor_ctx = CursorContext::create(*this, _cursors->wait);
5588 bool in_command = false;
5592 for (RegionSelection::iterator r = rs.begin(); r != rs.end(); ) {
5593 RegionSelection::iterator tmp = r;
5596 MidiRegionView* const mrv = dynamic_cast<MidiRegionView*>(*r);
5600 boost::shared_ptr<Playlist> playlist = mrv->region()->playlist();
5601 boost::shared_ptr<MidiSource> new_source = _session->create_midi_source_by_stealing_name (mrv->midi_view()->track());
5602 boost::shared_ptr<MidiRegion> newregion = mrv->midi_region()->clone (new_source);
5605 begin_reversible_command (_("Fork Region(s)"));
5608 playlist->clear_changes ();
5609 playlist->replace_region (mrv->region(), newregion, mrv->region()->position());
5610 _session->add_command(new StatefulDiffCommand (playlist));
5612 error << string_compose (_("Could not unlink %1"), mrv->region()->name()) << endmsg;
5620 commit_reversible_command ();
5625 Editor::quantize_region ()
5628 quantize_regions(get_regions_from_selection_and_entered ());
5633 Editor::quantize_regions (const RegionSelection& rs)
5635 if (rs.n_midi_regions() == 0) {
5639 if (!quantize_dialog) {
5640 quantize_dialog = new QuantizeDialog (*this);
5643 if (quantize_dialog->is_mapped()) {
5644 /* in progress already */
5648 quantize_dialog->present ();
5649 const int r = quantize_dialog->run ();
5650 quantize_dialog->hide ();
5652 if (r == Gtk::RESPONSE_OK) {
5653 Quantize quant (quantize_dialog->snap_start(),
5654 quantize_dialog->snap_end(),
5655 quantize_dialog->start_grid_size(),
5656 quantize_dialog->end_grid_size(),
5657 quantize_dialog->strength(),
5658 quantize_dialog->swing(),
5659 quantize_dialog->threshold());
5661 apply_midi_note_edit_op (quant, rs);
5666 Editor::legatize_region (bool shrink_only)
5669 legatize_regions(get_regions_from_selection_and_entered (), shrink_only);
5674 Editor::legatize_regions (const RegionSelection& rs, bool shrink_only)
5676 if (rs.n_midi_regions() == 0) {
5680 Legatize legatize(shrink_only);
5681 apply_midi_note_edit_op (legatize, rs);
5685 Editor::transform_region ()
5688 transform_regions(get_regions_from_selection_and_entered ());
5693 Editor::transform_regions (const RegionSelection& rs)
5695 if (rs.n_midi_regions() == 0) {
5702 const int r = td.run();
5705 if (r == Gtk::RESPONSE_OK) {
5706 Transform transform(td.get());
5707 apply_midi_note_edit_op(transform, rs);
5712 Editor::transpose_region ()
5715 transpose_regions(get_regions_from_selection_and_entered ());
5720 Editor::transpose_regions (const RegionSelection& rs)
5722 if (rs.n_midi_regions() == 0) {
5727 int const r = d.run ();
5729 if (r == RESPONSE_ACCEPT) {
5730 Transpose transpose(d.semitones ());
5731 apply_midi_note_edit_op (transpose, rs);
5736 Editor::insert_patch_change (bool from_context)
5738 RegionSelection rs = get_regions_from_selection_and_entered ();
5744 const samplepos_t p = get_preferred_edit_position (EDIT_IGNORE_NONE, from_context);
5746 /* XXX: bit of a hack; use the MIDNAM from the first selected region;
5747 there may be more than one, but the PatchChangeDialog can only offer
5748 one set of patch menus.
5750 MidiRegionView* first = dynamic_cast<MidiRegionView*> (rs.front ());
5752 Evoral::PatchChange<Temporal::Beats> empty (Temporal::Beats(), 0, 0, 0);
5753 PatchChangeDialog d (0, _session, empty, first->instrument_info(), Gtk::Stock::ADD);
5755 if (d.run() == RESPONSE_CANCEL) {
5759 for (RegionSelection::iterator i = rs.begin (); i != rs.end(); ++i) {
5760 MidiRegionView* const mrv = dynamic_cast<MidiRegionView*> (*i);
5762 if (p >= mrv->region()->first_sample() && p <= mrv->region()->last_sample()) {
5763 mrv->add_patch_change (p - mrv->region()->position(), d.patch ());
5770 Editor::apply_filter (Filter& filter, string command, ProgressReporter* progress)
5772 RegionSelection rs = get_regions_from_selection_and_entered ();
5778 CursorContext::Handle cursor_ctx = CursorContext::create(*this, _cursors->wait);
5779 bool in_command = false;
5784 int const N = rs.size ();
5786 for (RegionSelection::iterator r = rs.begin(); r != rs.end(); ) {
5787 RegionSelection::iterator tmp = r;
5790 AudioRegionView* const arv = dynamic_cast<AudioRegionView*>(*r);
5792 boost::shared_ptr<Playlist> playlist = arv->region()->playlist();
5795 progress->descend (1.0 / N);
5798 if (arv->audio_region()->apply (filter, progress) == 0) {
5800 playlist->clear_changes ();
5801 playlist->clear_owned_changes ();
5804 begin_reversible_command (command);
5808 if (filter.results.empty ()) {
5810 /* no regions returned; remove the old one */
5811 playlist->remove_region (arv->region ());
5815 std::vector<boost::shared_ptr<Region> >::iterator res = filter.results.begin ();
5817 /* first region replaces the old one */
5818 playlist->replace_region (arv->region(), *res, (*res)->position());
5822 while (res != filter.results.end()) {
5823 playlist->add_region (*res, (*res)->position());
5829 /* We might have removed regions, which alters other regions' layering_index,
5830 so we need to do a recursive diff here.
5832 vector<Command*> cmds;
5833 playlist->rdiff (cmds);
5834 _session->add_commands (cmds);
5836 _session->add_command(new StatefulDiffCommand (playlist));
5840 progress->ascend ();
5849 commit_reversible_command ();
5854 Editor::external_edit_region ()
5860 Editor::reset_region_gain_envelopes ()
5862 RegionSelection rs = get_regions_from_selection_and_entered ();
5864 if (!_session || rs.empty()) {
5868 bool in_command = false;
5870 for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
5871 AudioRegionView* const arv = dynamic_cast<AudioRegionView*>(*i);
5873 boost::shared_ptr<AutomationList> alist (arv->audio_region()->envelope());
5874 XMLNode& before (alist->get_state());
5876 arv->audio_region()->set_default_envelope ();
5879 begin_reversible_command (_("reset region gain"));
5882 _session->add_command (new MementoCommand<AutomationList>(*arv->audio_region()->envelope().get(), &before, &alist->get_state()));
5887 commit_reversible_command ();
5892 Editor::set_region_gain_visibility (RegionView* rv)
5894 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (rv);
5896 arv->update_envelope_visibility();
5901 Editor::set_gain_envelope_visibility ()
5907 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
5908 AudioTimeAxisView* v = dynamic_cast<AudioTimeAxisView*>(*i);
5910 v->audio_view()->foreach_regionview (sigc::mem_fun (this, &Editor::set_region_gain_visibility));
5916 Editor::toggle_gain_envelope_active ()
5918 if (_ignore_region_action) {
5922 RegionSelection rs = get_regions_from_selection_and_entered ();
5924 if (!_session || rs.empty()) {
5928 bool in_command = false;
5930 for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
5931 AudioRegionView* const arv = dynamic_cast<AudioRegionView*>(*i);
5933 arv->region()->clear_changes ();
5934 arv->audio_region()->set_envelope_active (!arv->audio_region()->envelope_active());
5937 begin_reversible_command (_("region gain envelope active"));
5940 _session->add_command (new StatefulDiffCommand (arv->region()));
5945 commit_reversible_command ();
5950 Editor::toggle_region_lock ()
5952 if (_ignore_region_action) {
5956 RegionSelection rs = get_regions_from_selection_and_entered ();
5958 if (!_session || rs.empty()) {
5962 begin_reversible_command (_("toggle region lock"));
5964 for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
5965 (*i)->region()->clear_changes ();
5966 (*i)->region()->set_locked (!(*i)->region()->locked());
5967 _session->add_command (new StatefulDiffCommand ((*i)->region()));
5970 commit_reversible_command ();
5974 Editor::toggle_region_video_lock ()
5976 if (_ignore_region_action) {
5980 RegionSelection rs = get_regions_from_selection_and_entered ();
5982 if (!_session || rs.empty()) {
5986 begin_reversible_command (_("Toggle Video Lock"));
5988 for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
5989 (*i)->region()->clear_changes ();
5990 (*i)->region()->set_video_locked (!(*i)->region()->video_locked());
5991 _session->add_command (new StatefulDiffCommand ((*i)->region()));
5994 commit_reversible_command ();
5998 Editor::toggle_region_lock_style ()
6000 if (_ignore_region_action) {
6004 RegionSelection rs = get_regions_from_selection_and_entered ();
6006 if (!_session || rs.empty()) {
6010 Glib::RefPtr<ToggleAction> a = Glib::RefPtr<ToggleAction>::cast_dynamic (_region_actions->get_action("toggle-region-lock-style"));
6011 vector<Widget*> proxies = a->get_proxies();
6012 Gtk::CheckMenuItem* cmi = dynamic_cast<Gtk::CheckMenuItem*> (proxies.front());
6016 begin_reversible_command (_("toggle region lock style"));
6018 for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
6019 (*i)->region()->clear_changes ();
6020 PositionLockStyle const ns = ((*i)->region()->position_lock_style() == AudioTime && !cmi->get_inconsistent()) ? MusicTime : AudioTime;
6021 (*i)->region()->set_position_lock_style (ns);
6022 _session->add_command (new StatefulDiffCommand ((*i)->region()));
6025 commit_reversible_command ();
6029 Editor::toggle_opaque_region ()
6031 if (_ignore_region_action) {
6035 RegionSelection rs = get_regions_from_selection_and_entered ();
6037 if (!_session || rs.empty()) {
6041 begin_reversible_command (_("change region opacity"));
6043 for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
6044 (*i)->region()->clear_changes ();
6045 (*i)->region()->set_opaque (!(*i)->region()->opaque());
6046 _session->add_command (new StatefulDiffCommand ((*i)->region()));
6049 commit_reversible_command ();
6053 Editor::toggle_record_enable ()
6055 bool new_state = false;
6057 for (TrackSelection::iterator i = selection->tracks.begin(); i != selection->tracks.end(); ++i) {
6058 RouteTimeAxisView *rtav = dynamic_cast<RouteTimeAxisView *>(*i);
6061 if (!rtav->is_track())
6065 new_state = !rtav->track()->rec_enable_control()->get_value();
6069 rtav->track()->rec_enable_control()->set_value (new_state, Controllable::UseGroup);
6074 tracklist_to_stripables (TrackViewList list)
6078 for (TrackSelection::iterator i = list.begin(); i != list.end(); ++i) {
6079 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> ((*i));
6081 if (rtv && rtv->is_track()) {
6082 ret.push_back (rtv->track());
6090 Editor::play_solo_selection (bool restart)
6092 //note: session::solo_selection takes care of invalidating the region playlist
6094 if ((!selection->tracks.empty()) && selection->time.length() > 0) { //a range is selected; solo the tracks and roll
6096 StripableList sl = tracklist_to_stripables (selection->tracks);
6097 _session->solo_selection (sl, true);
6100 samplepos_t start = selection->time.start();
6101 samplepos_t end = selection->time.end_sample();
6102 _session->request_bounded_roll (start, end);
6104 } else if (! selection->tracks.empty()) { //no range is selected, but tracks are selected; solo the tracks and roll
6105 StripableList sl = tracklist_to_stripables (selection->tracks);
6106 _session->solo_selection (sl, true);
6107 _session->request_cancel_play_range();
6108 transition_to_rolling (true);
6110 } else if (! selection->regions.empty()) { //solo any tracks with selected regions, and roll
6111 StripableList sl = tracklist_to_stripables (get_tracks_for_range_action());
6112 _session->solo_selection (sl, true);
6113 _session->request_cancel_play_range();
6114 transition_to_rolling (true);
6116 _session->request_cancel_play_range();
6117 transition_to_rolling (true); //no selection. just roll.
6122 Editor::toggle_solo ()
6124 bool new_state = false;
6126 boost::shared_ptr<ControlList> cl (new ControlList);
6128 for (TrackSelection::iterator i = selection->tracks.begin(); i != selection->tracks.end(); ++i) {
6129 StripableTimeAxisView *stav = dynamic_cast<StripableTimeAxisView *>(*i);
6131 if (!stav || !stav->stripable()->solo_control()) {
6136 new_state = !stav->stripable()->solo_control()->soloed ();
6140 cl->push_back (stav->stripable()->solo_control());
6143 _session->set_controls (cl, new_state ? 1.0 : 0.0, Controllable::UseGroup);
6147 Editor::toggle_mute ()
6149 bool new_state = false;
6151 boost::shared_ptr<ControlList> cl (new ControlList);
6153 for (TrackSelection::iterator i = selection->tracks.begin(); i != selection->tracks.end(); ++i) {
6154 StripableTimeAxisView *stav = dynamic_cast<StripableTimeAxisView *>(*i);
6156 if (!stav || !stav->stripable()->mute_control()) {
6161 new_state = !stav->stripable()->mute_control()->muted();
6165 cl->push_back (stav->stripable()->mute_control());
6168 _session->set_controls (cl, new_state, Controllable::UseGroup);
6172 Editor::toggle_solo_isolate ()
6178 Editor::fade_range ()
6180 TrackViewList ts = selection->tracks.filter_to_unique_playlists ();
6182 begin_reversible_command (_("fade range"));
6184 for (TrackViewList::iterator i = ts.begin(); i != ts.end(); ++i) {
6185 (*i)->fade_range (selection->time);
6188 commit_reversible_command ();
6193 Editor::set_fade_length (bool in)
6195 RegionSelection rs = get_regions_from_selection_and_entered ();
6201 /* we need a region to measure the offset from the start */
6203 RegionView* rv = rs.front ();
6205 samplepos_t pos = get_preferred_edit_position();
6209 if (pos > rv->region()->last_sample() || pos < rv->region()->first_sample()) {
6210 /* edit point is outside the relevant region */
6215 if (pos <= rv->region()->position()) {
6219 len = pos - rv->region()->position();
6220 cmd = _("set fade in length");
6222 if (pos >= rv->region()->last_sample()) {
6226 len = rv->region()->last_sample() - pos;
6227 cmd = _("set fade out length");
6230 bool in_command = false;
6232 for (RegionSelection::iterator x = rs.begin(); x != rs.end(); ++x) {
6233 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (*x);
6239 boost::shared_ptr<AutomationList> alist;
6241 alist = tmp->audio_region()->fade_in();
6243 alist = tmp->audio_region()->fade_out();
6246 XMLNode &before = alist->get_state();
6249 tmp->audio_region()->set_fade_in_length (len);
6250 tmp->audio_region()->set_fade_in_active (true);
6252 tmp->audio_region()->set_fade_out_length (len);
6253 tmp->audio_region()->set_fade_out_active (true);
6257 begin_reversible_command (cmd);
6260 XMLNode &after = alist->get_state();
6261 _session->add_command(new MementoCommand<AutomationList>(*alist, &before, &after));
6265 commit_reversible_command ();
6270 Editor::set_fade_in_shape (FadeShape shape)
6272 RegionSelection rs = get_regions_from_selection_and_entered ();
6277 bool in_command = false;
6279 for (RegionSelection::iterator x = rs.begin(); x != rs.end(); ++x) {
6280 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (*x);
6286 boost::shared_ptr<AutomationList> alist = tmp->audio_region()->fade_in();
6287 XMLNode &before = alist->get_state();
6289 tmp->audio_region()->set_fade_in_shape (shape);
6292 begin_reversible_command (_("set fade in shape"));
6295 XMLNode &after = alist->get_state();
6296 _session->add_command(new MementoCommand<AutomationList>(*alist.get(), &before, &after));
6300 commit_reversible_command ();
6305 Editor::set_fade_out_shape (FadeShape shape)
6307 RegionSelection rs = get_regions_from_selection_and_entered ();
6312 bool in_command = false;
6314 for (RegionSelection::iterator x = rs.begin(); x != rs.end(); ++x) {
6315 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (*x);
6321 boost::shared_ptr<AutomationList> alist = tmp->audio_region()->fade_out();
6322 XMLNode &before = alist->get_state();
6324 tmp->audio_region()->set_fade_out_shape (shape);
6327 begin_reversible_command (_("set fade out shape"));
6330 XMLNode &after = alist->get_state();
6331 _session->add_command(new MementoCommand<AutomationList>(*alist.get(), &before, &after));
6335 commit_reversible_command ();
6340 Editor::set_fade_in_active (bool yn)
6342 RegionSelection rs = get_regions_from_selection_and_entered ();
6347 bool in_command = false;
6349 for (RegionSelection::iterator x = rs.begin(); x != rs.end(); ++x) {
6350 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (*x);
6357 boost::shared_ptr<AudioRegion> ar (tmp->audio_region());
6359 ar->clear_changes ();
6360 ar->set_fade_in_active (yn);
6363 begin_reversible_command (_("set fade in active"));
6366 _session->add_command (new StatefulDiffCommand (ar));
6370 commit_reversible_command ();
6375 Editor::set_fade_out_active (bool yn)
6377 RegionSelection rs = get_regions_from_selection_and_entered ();
6382 bool in_command = false;
6384 for (RegionSelection::iterator x = rs.begin(); x != rs.end(); ++x) {
6385 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (*x);
6391 boost::shared_ptr<AudioRegion> ar (tmp->audio_region());
6393 ar->clear_changes ();
6394 ar->set_fade_out_active (yn);
6397 begin_reversible_command (_("set fade out active"));
6400 _session->add_command(new StatefulDiffCommand (ar));
6404 commit_reversible_command ();
6409 Editor::toggle_region_fades (int dir)
6411 if (_ignore_region_action) {
6415 boost::shared_ptr<AudioRegion> ar;
6418 RegionSelection rs = get_regions_from_selection_and_entered ();
6424 RegionSelection::iterator i;
6425 for (i = rs.begin(); i != rs.end(); ++i) {
6426 if ((ar = boost::dynamic_pointer_cast<AudioRegion>((*i)->region())) != 0) {
6428 yn = ar->fade_out_active ();
6430 yn = ar->fade_in_active ();
6436 if (i == rs.end()) {
6440 /* XXX should this undo-able? */
6441 bool in_command = false;
6443 for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
6444 if ((ar = boost::dynamic_pointer_cast<AudioRegion>((*i)->region())) == 0) {
6447 ar->clear_changes ();
6449 if (dir == 1 || dir == 0) {
6450 ar->set_fade_in_active (!yn);
6453 if (dir == -1 || dir == 0) {
6454 ar->set_fade_out_active (!yn);
6457 begin_reversible_command (_("toggle fade active"));
6460 _session->add_command(new StatefulDiffCommand (ar));
6464 commit_reversible_command ();
6469 /** Update region fade visibility after its configuration has been changed */
6471 Editor::update_region_fade_visibility ()
6473 bool _fade_visibility = _session->config.get_show_region_fades ();
6475 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
6476 AudioTimeAxisView* v = dynamic_cast<AudioTimeAxisView*>(*i);
6478 if (_fade_visibility) {
6479 v->audio_view()->show_all_fades ();
6481 v->audio_view()->hide_all_fades ();
6488 Editor::set_edit_point ()
6491 MusicSample where (0, 0);
6493 if (!mouse_sample (where.sample, ignored)) {
6499 if (selection->markers.empty()) {
6501 mouse_add_new_marker (where.sample);
6506 Location* loc = find_location_from_marker (selection->markers.front(), ignored);
6509 loc->move_to (where.sample, where.division);
6515 Editor::set_playhead_cursor ()
6517 if (entered_marker) {
6518 _session->request_locate (entered_marker->position(), _session->transport_rolling());
6520 MusicSample where (0, 0);
6523 if (!mouse_sample (where.sample, ignored)) {
6530 _session->request_locate (where.sample, _session->transport_rolling());
6534 //not sure what this was for; remove it for now.
6535 // if (UIConfiguration::instance().get_follow_edits() && (!_session || !_session->config.get_external_sync())) {
6536 // cancel_time_selection();
6542 Editor::split_region ()
6544 if (_dragging_playhead) {
6546 } else if (_drags->active ()) {
6547 /*any other kind of drag, bail out so we avoid Undo snafu*/
6551 //if a range is selected, separate it
6552 if (!selection->time.empty()) {
6553 separate_regions_between (selection->time);
6557 //if no range was selected, try to find some regions to split
6558 if (current_mouse_mode() == MouseObject || current_mouse_mode() == MouseRange ) { //don't try this for Internal Edit, Stretch, Draw, etc.
6562 //new behavior: the Split action will prioritize the entered_regionview rather than selected regions.
6563 //this fixes the unexpected case where you point at a region, but
6564 // * nothing happens OR
6565 // * some other region (maybe off-screen) is split.
6566 //NOTE: if the entered_regionview is /part of the selection/ then we should operate on the selection as usual
6567 if (_edit_point == EditAtMouse && entered_regionview && !entered_regionview->selected()) {
6568 rs.add (entered_regionview);
6570 rs = selection->regions; //might be empty
6574 TrackViewList tracks = selection->tracks;
6576 if (!tracks.empty()) {
6577 /* no region selected or entered, but some selected tracks:
6578 * act on all regions on the selected tracks at the edit point
6580 samplepos_t const where = get_preferred_edit_position (Editing::EDIT_IGNORE_NONE, false, false);
6581 get_regions_at(rs, where, tracks);
6585 const samplepos_t pos = get_preferred_edit_position();
6586 const int32_t division = get_grid_music_divisions (0);
6587 MusicSample where (pos, division);
6593 split_regions_at (where, rs);
6598 Editor::select_next_stripable (bool routes_only)
6600 _session->selection().select_next_stripable (false, routes_only);
6604 Editor::select_prev_stripable (bool routes_only)
6606 _session->selection().select_prev_stripable (false, routes_only);
6610 Editor::set_loop_from_selection (bool play)
6612 if (_session == 0) {
6616 samplepos_t start, end;
6617 if (!get_selection_extents (start, end))
6620 set_loop_range (start, end, _("set loop range from selection"));
6623 _session->request_play_loop (true, true);
6628 Editor::set_loop_from_region (bool play)
6630 samplepos_t start, end;
6631 if (!get_selection_extents (start, end))
6634 set_loop_range (start, end, _("set loop range from region"));
6637 _session->request_locate (start, true);
6638 _session->request_play_loop (true);
6643 Editor::set_punch_from_selection ()
6645 if (_session == 0) {
6649 samplepos_t start, end;
6650 if (!get_selection_extents (start, end))
6653 set_punch_range (start, end, _("set punch range from selection"));
6657 Editor::set_auto_punch_range ()
6659 // auto punch in/out button from a single button
6660 // If Punch In is unset, set punch range from playhead to end, enable punch in
6661 // If Punch In is set, the next punch sets Punch Out, unless the playhead has been
6662 // rewound beyond the Punch In marker, in which case that marker will be moved back
6663 // to the current playhead position.
6664 // If punch out is set, it clears the punch range and Punch In/Out buttons
6666 if (_session == 0) {
6670 Location* tpl = transport_punch_location();
6671 samplepos_t now = playhead_cursor->current_sample();
6672 samplepos_t begin = now;
6673 samplepos_t end = _session->current_end_sample();
6675 if (!_session->config.get_punch_in()) {
6676 // First Press - set punch in and create range from here to eternity
6677 set_punch_range (begin, end, _("Auto Punch In"));
6678 _session->config.set_punch_in(true);
6679 } else if (tpl && !_session->config.get_punch_out()) {
6680 // Second press - update end range marker and set punch_out
6681 if (now < tpl->start()) {
6682 // playhead has been rewound - move start back and pretend nothing happened
6684 set_punch_range (begin, end, _("Auto Punch In/Out"));
6686 // normal case for 2nd press - set the punch out
6687 end = playhead_cursor->current_sample ();
6688 set_punch_range (tpl->start(), now, _("Auto Punch In/Out"));
6689 _session->config.set_punch_out(true);
6692 if (_session->config.get_punch_out()) {
6693 _session->config.set_punch_out(false);
6696 if (_session->config.get_punch_in()) {
6697 _session->config.set_punch_in(false);
6702 // third press - unset punch in/out and remove range
6703 _session->locations()->remove(tpl);
6710 Editor::set_session_extents_from_selection ()
6712 if (_session == 0) {
6716 samplepos_t start, end;
6717 if (!get_selection_extents (start, end))
6721 if ((loc = _session->locations()->session_range_location()) == 0) {
6722 _session->set_session_extents (start, end); // this will create a new session range; no need for UNDO
6724 XMLNode &before = loc->get_state();
6726 _session->set_session_extents (start, end);
6728 XMLNode &after = loc->get_state();
6730 begin_reversible_command (_("set session start/end from selection"));
6732 _session->add_command (new MementoCommand<Location>(*loc, &before, &after));
6734 commit_reversible_command ();
6737 _session->set_session_range_is_free (false);
6741 Editor::set_punch_start_from_edit_point ()
6745 MusicSample start (0, 0);
6746 samplepos_t end = max_samplepos;
6748 //use the existing punch end, if any
6749 Location* tpl = transport_punch_location();
6754 if ((_edit_point == EditAtPlayhead) && _session->transport_rolling()) {
6755 start.sample = _session->audible_sample();
6757 start.sample = get_preferred_edit_position();
6760 //if there's not already a sensible selection endpoint, go "forever"
6761 if (start.sample > end) {
6762 end = max_samplepos;
6765 set_punch_range (start.sample, end, _("set punch start from EP"));
6771 Editor::set_punch_end_from_edit_point ()
6775 samplepos_t start = 0;
6776 MusicSample end (max_samplepos, 0);
6778 //use the existing punch start, if any
6779 Location* tpl = transport_punch_location();
6781 start = tpl->start();
6784 if ((_edit_point == EditAtPlayhead) && _session->transport_rolling()) {
6785 end.sample = _session->audible_sample();
6787 end.sample = get_preferred_edit_position();
6790 set_punch_range (start, end.sample, _("set punch end from EP"));
6796 Editor::set_loop_start_from_edit_point ()
6800 MusicSample start (0, 0);
6801 samplepos_t end = max_samplepos;
6803 //use the existing loop end, if any
6804 Location* tpl = transport_loop_location();
6809 if ((_edit_point == EditAtPlayhead) && _session->transport_rolling()) {
6810 start.sample = _session->audible_sample();
6812 start.sample = get_preferred_edit_position();
6815 //if there's not already a sensible selection endpoint, go "forever"
6816 if (start.sample > end) {
6817 end = max_samplepos;
6820 set_loop_range (start.sample, end, _("set loop start from EP"));
6826 Editor::set_loop_end_from_edit_point ()
6830 samplepos_t start = 0;
6831 MusicSample end (max_samplepos, 0);
6833 //use the existing loop start, if any
6834 Location* tpl = transport_loop_location();
6836 start = tpl->start();
6839 if ((_edit_point == EditAtPlayhead) && _session->transport_rolling()) {
6840 end.sample = _session->audible_sample();
6842 end.sample = get_preferred_edit_position();
6845 set_loop_range (start, end.sample, _("set loop end from EP"));
6850 Editor::set_punch_from_region ()
6852 samplepos_t start, end;
6853 if (!get_selection_extents (start, end))
6856 set_punch_range (start, end, _("set punch range from region"));
6860 Editor::pitch_shift_region ()
6862 RegionSelection rs = get_regions_from_selection_and_entered ();
6864 RegionSelection audio_rs;
6865 for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
6866 if (dynamic_cast<AudioRegionView*> (*i)) {
6867 audio_rs.push_back (*i);
6871 if (audio_rs.empty()) {
6875 pitch_shift (audio_rs, 1.2);
6879 Editor::set_tempo_from_region ()
6881 RegionSelection rs = get_regions_from_selection_and_entered ();
6883 if (!_session || rs.empty()) {
6887 RegionView* rv = rs.front();
6889 define_one_bar (rv->region()->position(), rv->region()->last_sample() + 1);
6893 Editor::use_range_as_bar ()
6895 samplepos_t start, end;
6896 if (get_edit_op_range (start, end)) {
6897 define_one_bar (start, end);
6902 Editor::define_one_bar (samplepos_t start, samplepos_t end)
6904 samplepos_t length = end - start;
6906 const Meter& m (_session->tempo_map().meter_at_sample (start));
6908 /* length = 1 bar */
6910 /* We're going to deliver a constant tempo here,
6911 so we can use samples per beat to determine length.
6912 now we want samples per beat.
6913 we have samples per bar, and beats per bar, so ...
6916 /* XXXX METER MATH */
6918 double samples_per_beat = length / m.divisions_per_bar();
6920 /* beats per minute = */
6922 double beats_per_minute = (_session->sample_rate() * 60.0) / samples_per_beat;
6924 /* now decide whether to:
6926 (a) set global tempo
6927 (b) add a new tempo marker
6931 const TempoSection& t (_session->tempo_map().tempo_section_at_sample (start));
6933 bool do_global = false;
6935 if ((_session->tempo_map().n_tempos() == 1) && (_session->tempo_map().n_meters() == 1)) {
6937 /* only 1 tempo & 1 meter: ask if the user wants to set the tempo
6938 at the start, or create a new marker
6941 vector<string> options;
6942 options.push_back (_("Cancel"));
6943 options.push_back (_("Add new marker"));
6944 options.push_back (_("Set global tempo"));
6947 _("Define one bar"),
6948 _("Do you want to set the global tempo or add a new tempo marker?"),
6952 c.set_default_response (2);
6968 /* more than 1 tempo and/or meter section already, go ahead do the "usual":
6969 if the marker is at the region starter, change it, otherwise add
6974 begin_reversible_command (_("set tempo from region"));
6975 XMLNode& before (_session->tempo_map().get_state());
6978 _session->tempo_map().change_initial_tempo (beats_per_minute, t.note_type(), t.end_note_types_per_minute());
6979 } else if (t.sample() == start) {
6980 _session->tempo_map().change_existing_tempo_at (start, beats_per_minute, t.note_type(), t.end_note_types_per_minute());
6982 /* constant tempo */
6983 const Tempo tempo (beats_per_minute, t.note_type());
6984 _session->tempo_map().add_tempo (tempo, 0.0, start, AudioTime);
6987 XMLNode& after (_session->tempo_map().get_state());
6989 _session->add_command (new MementoCommand<TempoMap>(_session->tempo_map(), &before, &after));
6990 commit_reversible_command ();
6994 Editor::split_region_at_transients ()
6996 AnalysisFeatureList positions;
6998 RegionSelection rs = get_regions_from_selection_and_entered ();
7000 if (!_session || rs.empty()) {
7004 begin_reversible_command (_("split regions"));
7006 for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ) {
7008 RegionSelection::iterator tmp;
7013 boost::shared_ptr<AudioRegion> ar = boost::dynamic_pointer_cast<AudioRegion> ((*i)->region());
7016 ar->transients (positions);
7017 split_region_at_points ((*i)->region(), positions, true);
7024 commit_reversible_command ();
7029 Editor::split_region_at_points (boost::shared_ptr<Region> r, AnalysisFeatureList& positions, bool can_ferret, bool select_new)
7031 bool use_rhythmic_rodent = false;
7033 boost::shared_ptr<Playlist> pl = r->playlist();
7035 list<boost::shared_ptr<Region> > new_regions;
7041 if (positions.empty()) {
7045 if (positions.size() > 20 && can_ferret) {
7046 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);
7047 MessageDialog msg (msgstr,
7050 Gtk::BUTTONS_OK_CANCEL);
7053 msg.add_button (_("Call for the Ferret!"), RESPONSE_APPLY);
7054 msg.set_secondary_text (_("Press OK to continue with this split operation\nor ask the Ferret dialog to tune the analysis"));
7056 msg.set_secondary_text (_("Press OK to continue with this split operation"));
7059 msg.set_title (_("Excessive split?"));
7062 int response = msg.run();
7068 case RESPONSE_APPLY:
7069 use_rhythmic_rodent = true;
7076 if (use_rhythmic_rodent) {
7077 show_rhythm_ferret ();
7081 AnalysisFeatureList::const_iterator x;
7083 pl->clear_changes ();
7084 pl->clear_owned_changes ();
7086 x = positions.begin();
7088 if (x == positions.end()) {
7093 pl->remove_region (r);
7095 samplepos_t pos = 0;
7097 samplepos_t rstart = r->first_sample ();
7098 samplepos_t rend = r->last_sample ();
7100 while (x != positions.end()) {
7102 /* deal with positons that are out of scope of present region bounds */
7103 if (*x <= rstart || *x > rend) {
7108 /* file start = original start + how far we from the initial position ? */
7110 samplepos_t file_start = r->start() + pos;
7112 /* length = next position - current position */
7114 samplepos_t len = (*x) - pos - rstart;
7116 /* XXX we do we really want to allow even single-sample regions?
7117 * shouldn't we have some kind of lower limit on region size?
7126 if (RegionFactory::region_name (new_name, r->name())) {
7130 /* do NOT announce new regions 1 by one, just wait till they are all done */
7134 plist.add (ARDOUR::Properties::start, file_start);
7135 plist.add (ARDOUR::Properties::length, len);
7136 plist.add (ARDOUR::Properties::name, new_name);
7137 plist.add (ARDOUR::Properties::layer, 0);
7138 // TODO set transients_offset
7140 boost::shared_ptr<Region> nr = RegionFactory::create (r->sources(), plist, false);
7141 /* because we set annouce to false, manually add the new region to the
7144 RegionFactory::map_add (nr);
7146 pl->add_region (nr, rstart + pos);
7149 new_regions.push_front(nr);
7158 RegionFactory::region_name (new_name, r->name());
7160 /* Add the final region */
7163 plist.add (ARDOUR::Properties::start, r->start() + pos);
7164 plist.add (ARDOUR::Properties::length, r->last_sample() - (r->position() + pos) + 1);
7165 plist.add (ARDOUR::Properties::name, new_name);
7166 plist.add (ARDOUR::Properties::layer, 0);
7168 boost::shared_ptr<Region> nr = RegionFactory::create (r->sources(), plist, false);
7169 /* because we set annouce to false, manually add the new region to the
7172 RegionFactory::map_add (nr);
7173 pl->add_region (nr, r->position() + pos);
7176 new_regions.push_front(nr);
7181 /* We might have removed regions, which alters other regions' layering_index,
7182 so we need to do a recursive diff here.
7184 vector<Command*> cmds;
7186 _session->add_commands (cmds);
7188 _session->add_command (new StatefulDiffCommand (pl));
7192 for (list<boost::shared_ptr<Region> >::iterator i = new_regions.begin(); i != new_regions.end(); ++i){
7193 set_selected_regionview_from_region_list ((*i), Selection::Add);
7199 Editor::place_transient()
7205 RegionSelection rs = get_regions_from_selection_and_edit_point ();
7211 samplepos_t where = get_preferred_edit_position();
7213 begin_reversible_command (_("place transient"));
7215 for (RegionSelection::iterator r = rs.begin(); r != rs.end(); ++r) {
7216 (*r)->region()->add_transient(where);
7219 commit_reversible_command ();
7223 Editor::remove_transient(ArdourCanvas::Item* item)
7229 ArdourCanvas::Line* _line = reinterpret_cast<ArdourCanvas::Line*> (item);
7232 AudioRegionView* _arv = reinterpret_cast<AudioRegionView*> (item->get_data ("regionview"));
7233 _arv->remove_transient (*(float*) _line->get_data ("position"));
7237 Editor::snap_regions_to_grid ()
7239 list <boost::shared_ptr<Playlist > > used_playlists;
7241 RegionSelection rs = get_regions_from_selection_and_entered ();
7243 if (!_session || rs.empty()) {
7247 begin_reversible_command (_("snap regions to grid"));
7249 for (RegionSelection::iterator r = rs.begin(); r != rs.end(); ++r) {
7251 boost::shared_ptr<Playlist> pl = (*r)->region()->playlist();
7253 if (!pl->frozen()) {
7254 /* we haven't seen this playlist before */
7256 /* remember used playlists so we can thaw them later */
7257 used_playlists.push_back(pl);
7260 (*r)->region()->clear_changes ();
7262 MusicSample start ((*r)->region()->first_sample (), 0);
7263 snap_to (start, RoundNearest, SnapToGrid_Unscaled, true);
7264 (*r)->region()->set_position (start.sample, start.division);
7265 _session->add_command(new StatefulDiffCommand ((*r)->region()));
7268 while (used_playlists.size() > 0) {
7269 list <boost::shared_ptr<Playlist > >::iterator i = used_playlists.begin();
7271 used_playlists.pop_front();
7274 commit_reversible_command ();
7278 Editor::close_region_gaps ()
7280 list <boost::shared_ptr<Playlist > > used_playlists;
7282 RegionSelection rs = get_regions_from_selection_and_entered ();
7284 if (!_session || rs.empty()) {
7288 Dialog dialog (_("Close Region Gaps"));
7291 table.set_spacings (12);
7292 table.set_border_width (12);
7293 Label* l = manage (left_aligned_label (_("Crossfade length")));
7294 table.attach (*l, 0, 1, 0, 1);
7296 SpinButton spin_crossfade (1, 0);
7297 spin_crossfade.set_range (0, 15);
7298 spin_crossfade.set_increments (1, 1);
7299 spin_crossfade.set_value (5);
7300 table.attach (spin_crossfade, 1, 2, 0, 1);
7302 table.attach (*manage (new Label (_("ms"))), 2, 3, 0, 1);
7304 l = manage (left_aligned_label (_("Pull-back length")));
7305 table.attach (*l, 0, 1, 1, 2);
7307 SpinButton spin_pullback (1, 0);
7308 spin_pullback.set_range (0, 100);
7309 spin_pullback.set_increments (1, 1);
7310 spin_pullback.set_value(30);
7311 table.attach (spin_pullback, 1, 2, 1, 2);
7313 table.attach (*manage (new Label (_("ms"))), 2, 3, 1, 2);
7315 dialog.get_vbox()->pack_start (table);
7316 dialog.add_button (Stock::CANCEL, RESPONSE_CANCEL);
7317 dialog.add_button (_("Ok"), RESPONSE_ACCEPT);
7320 if (dialog.run () == RESPONSE_CANCEL) {
7324 samplepos_t crossfade_len = spin_crossfade.get_value();
7325 samplepos_t pull_back_samples = spin_pullback.get_value();
7327 crossfade_len = lrintf (crossfade_len * _session->sample_rate()/1000);
7328 pull_back_samples = lrintf (pull_back_samples * _session->sample_rate()/1000);
7330 /* Iterate over the region list and make adjacent regions overlap by crossfade_len_ms */
7332 begin_reversible_command (_("close region gaps"));
7335 boost::shared_ptr<Region> last_region;
7337 rs.sort_by_position_and_track();
7339 for (RegionSelection::iterator r = rs.begin(); r != rs.end(); ++r) {
7341 boost::shared_ptr<Playlist> pl = (*r)->region()->playlist();
7343 if (!pl->frozen()) {
7344 /* we haven't seen this playlist before */
7346 /* remember used playlists so we can thaw them later */
7347 used_playlists.push_back(pl);
7351 samplepos_t position = (*r)->region()->position();
7353 if (idx == 0 || position < last_region->position()){
7354 last_region = (*r)->region();
7359 (*r)->region()->clear_changes ();
7360 (*r)->region()->trim_front((position - pull_back_samples));
7362 last_region->clear_changes ();
7363 last_region->trim_end ((position - pull_back_samples + crossfade_len));
7365 _session->add_command (new StatefulDiffCommand ((*r)->region()));
7366 _session->add_command (new StatefulDiffCommand (last_region));
7368 last_region = (*r)->region();
7372 while (used_playlists.size() > 0) {
7373 list <boost::shared_ptr<Playlist > >::iterator i = used_playlists.begin();
7375 used_playlists.pop_front();
7378 commit_reversible_command ();
7382 Editor::tab_to_transient (bool forward)
7384 AnalysisFeatureList positions;
7386 RegionSelection rs = get_regions_from_selection_and_entered ();
7392 samplepos_t pos = _session->audible_sample ();
7394 if (!selection->tracks.empty()) {
7396 /* don't waste time searching for transients in duplicate playlists.
7399 TrackViewList ts = selection->tracks.filter_to_unique_playlists ();
7401 for (TrackViewList::iterator t = ts.begin(); t != ts.end(); ++t) {
7403 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (*t);
7406 boost::shared_ptr<Track> tr = rtv->track();
7408 boost::shared_ptr<Playlist> pl = tr->playlist ();
7410 samplepos_t result = pl->find_next_transient (pos, forward ? 1 : -1);
7413 positions.push_back (result);
7426 for (RegionSelection::iterator r = rs.begin(); r != rs.end(); ++r) {
7427 (*r)->region()->get_transients (positions);
7431 TransientDetector::cleanup_transients (positions, _session->sample_rate(), 3.0);
7434 AnalysisFeatureList::iterator x;
7436 for (x = positions.begin(); x != positions.end(); ++x) {
7442 if (x != positions.end ()) {
7443 _session->request_locate (*x);
7447 AnalysisFeatureList::reverse_iterator x;
7449 for (x = positions.rbegin(); x != positions.rend(); ++x) {
7455 if (x != positions.rend ()) {
7456 _session->request_locate (*x);
7462 Editor::playhead_forward_to_grid ()
7468 MusicSample pos (playhead_cursor->current_sample (), 0);
7470 if ( _grid_type == GridTypeNone) {
7471 if (pos.sample < max_samplepos - current_page_samples()*0.1) {
7472 pos.sample += current_page_samples()*0.1;
7473 _session->request_locate (pos.sample);
7475 _session->request_locate (0);
7479 if (pos.sample < max_samplepos - 1) {
7481 pos = snap_to_grid (pos, RoundUpAlways, SnapToGrid_Scaled);
7482 _session->request_locate (pos.sample);
7487 /* keep PH visible in window */
7488 if (pos.sample > (_leftmost_sample + current_page_samples() *0.9)) {
7489 reset_x_origin (pos.sample - (current_page_samples()*0.9));
7495 Editor::playhead_backward_to_grid ()
7501 MusicSample pos (playhead_cursor->current_sample (), 0);
7503 if ( _grid_type == GridTypeNone) {
7504 if ( pos.sample > current_page_samples()*0.1 ) {
7505 pos.sample -= current_page_samples()*0.1;
7506 _session->request_locate (pos.sample);
7508 _session->request_locate (0);
7512 if (pos.sample > 2) {
7514 pos = snap_to_grid (pos, RoundDownAlways, SnapToGrid_Scaled);
7517 //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...
7518 //also see: jump_backward_to_mark
7519 if (_session->transport_rolling()) {
7520 if ((playhead_cursor->current_sample() - pos.sample) < _session->sample_rate()/2) {
7521 pos = snap_to_grid (pos, RoundDownAlways, SnapToGrid_Scaled);
7525 _session->request_locate (pos.sample, _session->transport_rolling());
7528 /* keep PH visible in window */
7529 if (pos.sample < (_leftmost_sample + current_page_samples() *0.1)) {
7530 reset_x_origin (pos.sample - (current_page_samples()*0.1));
7535 Editor::set_track_height (Height h)
7537 TrackSelection& ts (selection->tracks);
7539 for (TrackSelection::iterator x = ts.begin(); x != ts.end(); ++x) {
7540 (*x)->set_height_enum (h);
7545 Editor::toggle_tracks_active ()
7547 TrackSelection& ts (selection->tracks);
7549 bool target = false;
7555 for (TrackSelection::iterator x = ts.begin(); x != ts.end(); ++x) {
7556 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*>(*x);
7560 target = !rtv->_route->active();
7563 rtv->_route->set_active (target, this);
7569 Editor::remove_tracks ()
7571 /* this will delete GUI objects that may be the subject of an event
7572 handler in which this method is called. Defer actual deletion to the
7573 next idle callback, when all event handling is finished.
7575 Glib::signal_idle().connect (sigc::mem_fun (*this, &Editor::idle_remove_tracks));
7579 Editor::idle_remove_tracks ()
7581 Session::StateProtector sp (_session);
7583 return false; /* do not call again */
7587 Editor::_remove_tracks ()
7589 TrackSelection& ts (selection->tracks);
7595 if (!ARDOUR_UI_UTILS::engine_is_running ()) {
7599 vector<string> choices;
7604 const char* trackstr;
7607 vector<boost::shared_ptr<Route> > routes;
7608 vector<boost::shared_ptr<VCA> > vcas;
7609 bool special_bus = false;
7611 for (TrackSelection::iterator x = ts.begin(); x != ts.end(); ++x) {
7612 VCATimeAxisView* vtv = dynamic_cast<VCATimeAxisView*> (*x);
7614 vcas.push_back (vtv->vca());
7618 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (*x);
7622 if (rtv->is_track()) {
7627 routes.push_back (rtv->_route);
7629 if (rtv->route()->is_master() || rtv->route()->is_monitor()) {
7634 if (special_bus && !Config->get_allow_special_bus_removal()) {
7635 MessageDialog msg (_("That would be bad news ...."),
7639 msg.set_secondary_text (string_compose (_(
7640 "Removing the master or monitor bus is such a bad idea\n\
7641 that %1 is not going to allow it.\n\
7643 If you really want to do this sort of thing\n\
7644 edit your ardour.rc file to set the\n\
7645 \"allow-special-bus-removal\" option to be \"yes\""), PROGRAM_NAME));
7652 if (ntracks + nbusses + nvcas == 0) {
7658 trackstr = P_("track", "tracks", ntracks);
7659 busstr = P_("bus", "busses", nbusses);
7660 vcastr = P_("VCA", "VCAs", nvcas);
7662 if (ntracks > 0 && nbusses > 0 && nvcas > 0) {
7663 title = _("Remove various strips");
7664 prompt = string_compose (_("Do you really want to remove %1 %2, %3 %4 and %5 %6?"),
7665 ntracks, trackstr, nbusses, busstr, nvcas, vcastr);
7667 else if (ntracks > 0 && nbusses > 0) {
7668 title = string_compose (_("Remove %1 and %2"), trackstr, busstr);
7669 prompt = string_compose (_("Do you really want to remove %1 %2 and %3 %4?"),
7670 ntracks, trackstr, nbusses, busstr);
7672 else if (ntracks > 0 && nvcas > 0) {
7673 title = string_compose (_("Remove %1 and %2"), trackstr, vcastr);
7674 prompt = string_compose (_("Do you really want to remove %1 %2 and %3 %4?"),
7675 ntracks, trackstr, nvcas, vcastr);
7677 else if (nbusses > 0 && nvcas > 0) {
7678 title = string_compose (_("Remove %1 and %2"), busstr, vcastr);
7679 prompt = string_compose (_("Do you really want to remove %1 %2 and %3 %4?"),
7680 nbusses, busstr, nvcas, vcastr);
7682 else if (ntracks > 0) {
7683 title = string_compose (_("Remove %1"), trackstr);
7684 prompt = string_compose (_("Do you really want to remove %1 %2?"),
7687 else if (nbusses > 0) {
7688 title = string_compose (_("Remove %1"), busstr);
7689 prompt = string_compose (_("Do you really want to remove %1 %2?"),
7692 else if (nvcas > 0) {
7693 title = string_compose (_("Remove %1"), vcastr);
7694 prompt = string_compose (_("Do you really want to remove %1 %2?"),
7702 prompt += "\n" + string_compose ("(You may also lose the playlists associated with the %1)", trackstr) + "\n";
7705 prompt += "\n" + string(_("This action cannot be undone, and the session file will be overwritten!"));
7707 choices.push_back (_("No, do nothing."));
7708 if (ntracks + nbusses + nvcas > 1) {
7709 choices.push_back (_("Yes, remove them."));
7711 choices.push_back (_("Yes, remove it."));
7714 Choice prompter (title, prompt, choices);
7716 if (prompter.run () != 1) {
7720 if (current_mixer_strip && routes.size () > 1 && std::find (routes.begin(), routes.end(), current_mixer_strip->route()) != routes.end ()) {
7721 /* Route deletion calls Editor::timeaxisview_deleted() iteratively (for each deleted
7722 * route). If the deleted route is currently displayed in the Editor-Mixer (highly
7723 * likely because deletion requires selection) this will call
7724 * Editor::set_selected_mixer_strip () which is expensive (MixerStrip::set_route()).
7725 * It's likewise likely that the route that has just been displayed in the
7726 * Editor-Mixer will be next in line for deletion.
7728 * So simply switch to the master-bus (if present)
7730 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
7731 if ((*i)->stripable ()->is_master ()) {
7732 set_selected_mixer_strip (*(*i));
7739 PresentationInfo::ChangeSuspender cs;
7740 DisplaySuspender ds;
7742 boost::shared_ptr<RouteList> rl (new RouteList);
7743 for (vector<boost::shared_ptr<Route> >::iterator x = routes.begin(); x != routes.end(); ++x) {
7746 _session->remove_routes (rl);
7748 for (vector<boost::shared_ptr<VCA> >::iterator x = vcas.begin(); x != vcas.end(); ++x) {
7749 _session->vca_manager().remove_vca (*x);
7753 /* TrackSelection and RouteList leave scope,
7754 * destructors are called,
7755 * diskstream drops references, save_state is called (again for every track)
7760 Editor::do_insert_time ()
7762 if (selection->tracks.empty()) {
7763 MessageDialog msg (_("You must first select some tracks to Insert Time."),
7764 true, MESSAGE_INFO, BUTTONS_OK, true);
7765 msg.set_position (WIN_POS_MOUSE);
7770 if (Config->get_edit_mode() == Lock) {
7771 MessageDialog msg (_("You cannot insert time in Lock Edit mode."),
7772 true, MESSAGE_INFO, BUTTONS_OK, true);
7773 msg.set_position (WIN_POS_MOUSE);
7778 InsertRemoveTimeDialog d (*this);
7779 int response = d.run ();
7781 if (response != RESPONSE_OK) {
7785 if (d.distance() == 0) {
7792 d.intersected_region_action (),
7796 d.move_glued_markers(),
7797 d.move_locked_markers(),
7803 Editor::insert_time (
7804 samplepos_t pos, samplecnt_t samples, InsertTimeOption opt,
7805 bool all_playlists, bool ignore_music_glue, bool markers_too, bool glued_markers_too, bool locked_markers_too, bool tempo_too
7809 if (Config->get_edit_mode() == Lock) {
7812 bool in_command = false;
7814 TrackViewList ts = selection->tracks.filter_to_unique_playlists ();
7816 for (TrackViewList::iterator x = ts.begin(); x != ts.end(); ++x) {
7820 /* don't operate on any playlist more than once, which could
7821 * happen if "all playlists" is enabled, but there is more
7822 * than 1 track using playlists "from" a given track.
7825 set<boost::shared_ptr<Playlist> > pl;
7827 if (all_playlists) {
7828 RouteTimeAxisView* rtav = dynamic_cast<RouteTimeAxisView*> (*x);
7829 if (rtav && rtav->track ()) {
7830 vector<boost::shared_ptr<Playlist> > all = _session->playlists()->playlists_for_track (rtav->track ());
7831 for (vector<boost::shared_ptr<Playlist> >::iterator p = all.begin(); p != all.end(); ++p) {
7836 if ((*x)->playlist ()) {
7837 pl.insert ((*x)->playlist ());
7841 for (set<boost::shared_ptr<Playlist> >::iterator i = pl.begin(); i != pl.end(); ++i) {
7843 (*i)->clear_changes ();
7844 (*i)->clear_owned_changes ();
7847 begin_reversible_command (_("insert time"));
7851 if (opt == SplitIntersected) {
7852 /* non musical split */
7853 (*i)->split (MusicSample (pos, 0));
7856 (*i)->shift (pos, samples, (opt == MoveIntersected), ignore_music_glue);
7858 vector<Command*> cmds;
7860 _session->add_commands (cmds);
7862 _session->add_command (new StatefulDiffCommand (*i));
7866 RouteTimeAxisView* rtav = dynamic_cast<RouteTimeAxisView*> (*x);
7869 begin_reversible_command (_("insert time"));
7872 rtav->route ()->shift (pos, samples);
7879 const int32_t divisions = get_grid_music_divisions (0);
7880 XMLNode& before (_session->locations()->get_state());
7881 Locations::LocationList copy (_session->locations()->list());
7883 for (Locations::LocationList::iterator i = copy.begin(); i != copy.end(); ++i) {
7885 Locations::LocationList::const_iterator tmp;
7887 if ((*i)->position_lock_style() == AudioTime || glued_markers_too) {
7888 bool const was_locked = (*i)->locked ();
7889 if (locked_markers_too) {
7893 if ((*i)->start() >= pos) {
7894 // move end first, in case we're moving by more than the length of the range
7895 if (!(*i)->is_mark()) {
7896 (*i)->set_end ((*i)->end() + samples, false, true, divisions);
7898 (*i)->set_start ((*i)->start() + samples, false, true, divisions);
7910 begin_reversible_command (_("insert time"));
7913 XMLNode& after (_session->locations()->get_state());
7914 _session->add_command (new MementoCommand<Locations>(*_session->locations(), &before, &after));
7920 begin_reversible_command (_("insert time"));
7923 XMLNode& before (_session->tempo_map().get_state());
7924 _session->tempo_map().insert_time (pos, samples);
7925 XMLNode& after (_session->tempo_map().get_state());
7926 _session->add_command (new MementoCommand<TempoMap>(_session->tempo_map(), &before, &after));
7930 commit_reversible_command ();
7935 Editor::do_remove_time ()
7937 if (selection->tracks.empty()) {
7938 MessageDialog msg (_("You must first select some tracks to Remove Time."),
7939 true, MESSAGE_INFO, BUTTONS_OK, true);
7940 msg.set_position (WIN_POS_MOUSE);
7945 if (Config->get_edit_mode() == Lock) {
7946 MessageDialog msg (_("You cannot remove time in Lock Edit mode."),
7947 true, MESSAGE_INFO, BUTTONS_OK, true);
7948 msg.set_position (WIN_POS_MOUSE);
7953 InsertRemoveTimeDialog d (*this, true);
7955 int response = d.run ();
7957 if (response != RESPONSE_OK) {
7961 samplecnt_t distance = d.distance();
7963 if (distance == 0) {
7973 d.move_glued_markers(),
7974 d.move_locked_markers(),
7980 Editor::remove_time (samplepos_t pos, samplecnt_t samples, InsertTimeOption opt,
7981 bool ignore_music_glue, bool markers_too, bool glued_markers_too, bool locked_markers_too, bool tempo_too)
7983 if (Config->get_edit_mode() == Lock) {
7984 error << (_("Cannot insert or delete time when in Lock edit.")) << endmsg;
7987 bool in_command = false;
7989 for (TrackSelection::iterator x = selection->tracks.begin(); x != selection->tracks.end(); ++x) {
7991 boost::shared_ptr<Playlist> pl = (*x)->playlist();
7995 XMLNode &before = pl->get_state();
7998 begin_reversible_command (_("remove time"));
8002 std::list<AudioRange> rl;
8003 AudioRange ar(pos, pos+samples, 0);
8006 pl->shift (pos, -samples, true, ignore_music_glue);
8008 XMLNode &after = pl->get_state();
8010 _session->add_command (new MementoCommand<Playlist> (*pl, &before, &after));
8014 RouteTimeAxisView* rtav = dynamic_cast<RouteTimeAxisView*> (*x);
8017 begin_reversible_command (_("remove time"));
8020 rtav->route ()->shift (pos, -samples);
8024 const int32_t divisions = get_grid_music_divisions (0);
8025 std::list<Location*> loc_kill_list;
8030 XMLNode& before (_session->locations()->get_state());
8031 Locations::LocationList copy (_session->locations()->list());
8033 for (Locations::LocationList::iterator i = copy.begin(); i != copy.end(); ++i) {
8034 if ((*i)->position_lock_style() == AudioTime || glued_markers_too) {
8036 bool const was_locked = (*i)->locked ();
8037 if (locked_markers_too) {
8041 if (!(*i)->is_mark()) { // it's a range; have to handle both start and end
8042 if ((*i)->end() >= pos
8043 && (*i)->end() < pos+samples
8044 && (*i)->start() >= pos
8045 && (*i)->end() < pos+samples) { // range is completely enclosed; kill it
8047 loc_kill_list.push_back(*i);
8048 } else { // only start or end is included, try to do the right thing
8049 // move start before moving end, to avoid trying to move the end to before the start
8050 // if we're removing more time than the length of the range
8051 if ((*i)->start() >= pos && (*i)->start() < pos+samples) {
8052 // start is within cut
8053 (*i)->set_start (pos, false, true,divisions); // bring the start marker to the beginning of the cut
8055 } else if ((*i)->start() >= pos+samples) {
8056 // start (and thus entire range) lies beyond end of cut
8057 (*i)->set_start ((*i)->start() - samples, false, true, divisions); // slip the start marker back
8060 if ((*i)->end() >= pos && (*i)->end() < pos+samples) {
8061 // end is inside cut
8062 (*i)->set_end (pos, false, true, divisions); // bring the end to the cut
8064 } else if ((*i)->end() >= pos+samples) {
8065 // end is beyond end of cut
8066 (*i)->set_end ((*i)->end() - samples, false, true, divisions); // slip the end marker back
8071 } else if ((*i)->start() >= pos && (*i)->start() < pos+samples) {
8072 loc_kill_list.push_back(*i);
8074 } else if ((*i)->start() >= pos) {
8075 (*i)->set_start ((*i)->start() -samples, false, true, divisions);
8085 for (list<Location*>::iterator i = loc_kill_list.begin(); i != loc_kill_list.end(); ++i) {
8086 _session->locations()->remove (*i);
8091 begin_reversible_command (_("remove time"));
8094 XMLNode& after (_session->locations()->get_state());
8095 _session->add_command (new MementoCommand<Locations>(*_session->locations(), &before, &after));
8100 XMLNode& before (_session->tempo_map().get_state());
8102 if (_session->tempo_map().remove_time (pos, samples)) {
8104 begin_reversible_command (_("remove time"));
8107 XMLNode& after (_session->tempo_map().get_state());
8108 _session->add_command (new MementoCommand<TempoMap>(_session->tempo_map(), &before, &after));
8113 commit_reversible_command ();
8118 Editor::fit_selection ()
8120 if (!selection->tracks.empty()) {
8121 fit_tracks (selection->tracks);
8125 /* no selected tracks - use tracks with selected regions */
8127 if (!selection->regions.empty()) {
8128 for (RegionSelection::iterator r = selection->regions.begin(); r != selection->regions.end(); ++r) {
8129 tvl.push_back (&(*r)->get_time_axis_view ());
8135 } else if (internal_editing()) {
8136 /* no selected tracks, or regions, but in internal edit mode, so follow the mouse and use
8139 if (entered_track) {
8140 tvl.push_back (entered_track);
8148 Editor::fit_tracks (TrackViewList & tracks)
8150 if (tracks.empty()) {
8154 uint32_t child_heights = 0;
8155 int visible_tracks = 0;
8157 for (TrackSelection::iterator t = tracks.begin(); t != tracks.end(); ++t) {
8159 if (!(*t)->marked_for_display()) {
8163 child_heights += (*t)->effective_height() - (*t)->current_height();
8167 /* compute the per-track height from:
8169 * total canvas visible height
8170 * - height that will be taken by visible children of selected tracks
8171 * - height of the ruler/hscroll area
8173 uint32_t h = (uint32_t) floor ((trackviews_height() - child_heights) / visible_tracks);
8174 double first_y_pos = DBL_MAX;
8176 if (h < TimeAxisView::preset_height (HeightSmall)) {
8177 MessageDialog msg (_("There are too many tracks to fit in the current window"));
8178 /* too small to be displayed */
8182 undo_visual_stack.push_back (current_visual_state (true));
8183 PBD::Unwinder<bool> nsv (no_save_visual, true);
8185 /* build a list of all tracks, including children */
8188 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
8190 TimeAxisView::Children c = (*i)->get_child_list ();
8191 for (TimeAxisView::Children::iterator j = c.begin(); j != c.end(); ++j) {
8192 all.push_back (j->get());
8197 // find selection range.
8198 // if someone knows how to user TrackViewList::iterator for this
8200 int selected_top = -1;
8201 int selected_bottom = -1;
8203 for (TrackViewList::iterator t = all.begin(); t != all.end(); ++t, ++i) {
8204 if ((*t)->marked_for_display ()) {
8205 if (tracks.contains(*t)) {
8206 if (selected_top == -1) {
8209 selected_bottom = i;
8215 for (TrackViewList::iterator t = all.begin(); t != all.end(); ++t, ++i) {
8216 if ((*t)->marked_for_display ()) {
8217 if (tracks.contains(*t)) {
8218 (*t)->set_height (h);
8219 first_y_pos = std::min ((*t)->y_position (), first_y_pos);
8221 if (i > selected_top && i < selected_bottom) {
8222 hide_track_in_display (*t);
8229 set the controls_layout height now, because waiting for its size
8230 request signal handler will cause the vertical adjustment setting to fail
8233 controls_layout.property_height () = _full_canvas_height;
8234 vertical_adjustment.set_value (first_y_pos);
8236 redo_visual_stack.push_back (current_visual_state (true));
8238 visible_tracks_selector.set_text (_("Sel"));
8242 Editor::save_visual_state (uint32_t n)
8244 while (visual_states.size() <= n) {
8245 visual_states.push_back (0);
8248 if (visual_states[n] != 0) {
8249 delete visual_states[n];
8252 visual_states[n] = current_visual_state (true);
8257 Editor::goto_visual_state (uint32_t n)
8259 if (visual_states.size() <= n) {
8263 if (visual_states[n] == 0) {
8267 use_visual_state (*visual_states[n]);
8271 Editor::start_visual_state_op (uint32_t n)
8273 save_visual_state (n);
8275 PopUp* pup = new PopUp (WIN_POS_MOUSE, 1000, true);
8277 snprintf (buf, sizeof (buf), _("Saved view %u"), n+1);
8278 pup->set_text (buf);
8283 Editor::cancel_visual_state_op (uint32_t n)
8285 goto_visual_state (n);
8289 Editor::toggle_region_mute ()
8291 if (_ignore_region_action) {
8295 RegionSelection rs = get_regions_from_selection_and_entered ();
8301 if (rs.size() > 1) {
8302 begin_reversible_command (_("mute regions"));
8304 begin_reversible_command (_("mute region"));
8307 for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
8309 (*i)->region()->playlist()->clear_changes ();
8310 (*i)->region()->set_muted (!(*i)->region()->muted ());
8311 _session->add_command (new StatefulDiffCommand ((*i)->region()));
8315 commit_reversible_command ();
8319 Editor::combine_regions ()
8321 /* foreach track with selected regions, take all selected regions
8322 and join them into a new region containing the subregions (as a
8326 typedef set<RouteTimeAxisView*> RTVS;
8329 if (selection->regions.empty()) {
8333 for (RegionSelection::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
8334 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*>(&(*i)->get_time_axis_view());
8337 tracks.insert (rtv);
8341 begin_reversible_command (_("combine regions"));
8343 vector<RegionView*> new_selection;
8345 for (RTVS::iterator i = tracks.begin(); i != tracks.end(); ++i) {
8348 if ((rv = (*i)->combine_regions ()) != 0) {
8349 new_selection.push_back (rv);
8353 selection->clear_regions ();
8354 for (vector<RegionView*>::iterator i = new_selection.begin(); i != new_selection.end(); ++i) {
8355 selection->add (*i);
8358 commit_reversible_command ();
8362 Editor::uncombine_regions ()
8364 typedef set<RouteTimeAxisView*> RTVS;
8367 if (selection->regions.empty()) {
8371 for (RegionSelection::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
8372 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*>(&(*i)->get_time_axis_view());
8375 tracks.insert (rtv);
8379 begin_reversible_command (_("uncombine regions"));
8381 for (RTVS::iterator i = tracks.begin(); i != tracks.end(); ++i) {
8382 (*i)->uncombine_regions ();
8385 commit_reversible_command ();
8389 Editor::toggle_midi_input_active (bool flip_others)
8392 boost::shared_ptr<RouteList> rl (new RouteList);
8394 for (TrackSelection::iterator i = selection->tracks.begin(); i != selection->tracks.end(); ++i) {
8395 RouteTimeAxisView *rtav = dynamic_cast<RouteTimeAxisView *>(*i);
8401 boost::shared_ptr<MidiTrack> mt = rtav->midi_track();
8404 rl->push_back (rtav->route());
8405 onoff = !mt->input_active();
8409 _session->set_exclusive_input_active (rl, onoff, flip_others);
8412 static bool ok_fine (GdkEventAny*) { return true; }
8418 lock_dialog = new Gtk::Dialog (string_compose (_("%1: Locked"), PROGRAM_NAME), true);
8420 Gtk::Image* padlock = manage (new Gtk::Image (ARDOUR_UI_UTILS::get_icon ("padlock_closed")));
8421 lock_dialog->get_vbox()->pack_start (*padlock);
8422 lock_dialog->signal_delete_event ().connect (sigc::ptr_fun (ok_fine));
8424 ArdourButton* b = manage (new ArdourButton);
8425 b->set_name ("lock button");
8426 b->set_text (_("Click to unlock"));
8427 b->signal_clicked.connect (sigc::mem_fun (*this, &Editor::unlock));
8428 lock_dialog->get_vbox()->pack_start (*b);
8430 lock_dialog->get_vbox()->show_all ();
8431 lock_dialog->set_size_request (200, 200);
8434 delete _main_menu_disabler;
8435 _main_menu_disabler = new MainMenuDisabler;
8437 lock_dialog->present ();
8439 lock_dialog->get_window()->set_decorations (Gdk::WMDecoration (0));
8445 lock_dialog->hide ();
8447 delete _main_menu_disabler;
8448 _main_menu_disabler = 0;
8450 if (UIConfiguration::instance().get_lock_gui_after_seconds()) {
8451 start_lock_event_timing ();
8456 Editor::bring_in_callback (Gtk::Label* label, uint32_t n, uint32_t total, string name)
8458 Gtkmm2ext::UI::instance()->call_slot (invalidator (*this), boost::bind (&Editor::update_bring_in_message, this, label, n, total, name));
8462 Editor::update_bring_in_message (Gtk::Label* label, uint32_t n, uint32_t total, string name)
8464 Timers::TimerSuspender t;
8465 label->set_text (string_compose ("Copying %1, %2 of %3", name, n, total));
8466 Gtkmm2ext::UI::instance()->flush_pending (1);
8470 Editor::bring_all_sources_into_session ()
8477 ArdourDialog w (_("Moving embedded files into session folder"));
8478 w.get_vbox()->pack_start (msg);
8481 /* flush all pending GUI events because we're about to start copying
8485 Timers::TimerSuspender t;
8486 Gtkmm2ext::UI::instance()->flush_pending (3);
8490 _session->bring_all_sources_into_session (boost::bind (&Editor::bring_in_callback, this, &msg, _1, _2, _3));