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_message.h"
86 #include "ardour_ui.h"
87 #include "audio_region_view.h"
88 #include "audio_streamview.h"
89 #include "audio_time_axis.h"
90 #include "automation_region_view.h"
91 #include "automation_time_axis.h"
92 #include "control_point.h"
96 #include "editor_cursors.h"
97 #include "editor_drag.h"
98 #include "editor_regions.h"
99 #include "editor_sources.h"
100 #include "editor_routes.h"
101 #include "gui_thread.h"
102 #include "insert_remove_time_dialog.h"
103 #include "interthread_progress_window.h"
104 #include "item_counts.h"
105 #include "keyboard.h"
106 #include "midi_region_view.h"
107 #include "mixer_ui.h"
108 #include "mixer_strip.h"
109 #include "mouse_cursors.h"
110 #include "normalize_dialog.h"
112 #include "paste_context.h"
113 #include "patch_change_dialog.h"
114 #include "quantize_dialog.h"
115 #include "region_gain_line.h"
116 #include "rgb_macros.h"
117 #include "route_time_axis.h"
118 #include "selection.h"
119 #include "selection_templates.h"
120 #include "streamview.h"
121 #include "strip_silence_dialog.h"
122 #include "time_axis_view.h"
124 #include "transpose_dialog.h"
125 #include "transform_dialog.h"
126 #include "ui_config.h"
128 #include "vca_time_axis.h"
130 #include "pbd/i18n.h"
133 using namespace ARDOUR;
136 using namespace Gtkmm2ext;
137 using namespace ArdourWidgets;
138 using namespace Editing;
139 using Gtkmm2ext::Keyboard;
141 /***********************************************************************
143 ***********************************************************************/
146 Editor::undo (uint32_t n)
148 if (_session && _session->actively_recording()) {
149 /* no undo allowed while recording. Session will check also,
150 but we don't even want to get to that.
155 if (_drags->active ()) {
162 if (_session->undo_depth() == 0) {
163 undo_action->set_sensitive(false);
165 redo_action->set_sensitive(true);
166 begin_selection_op_history ();
171 Editor::redo (uint32_t n)
173 if (_session && _session->actively_recording()) {
174 /* no redo allowed while recording. Session will check also,
175 but we don't even want to get to that.
180 if (_drags->active ()) {
187 if (_session->redo_depth() == 0) {
188 redo_action->set_sensitive(false);
190 undo_action->set_sensitive(true);
191 begin_selection_op_history ();
196 Editor::split_regions_at (MusicSample where, RegionSelection& regions)
200 list<boost::shared_ptr<Playlist> > used_playlists;
201 list<RouteTimeAxisView*> used_trackviews;
203 if (regions.empty()) {
207 begin_reversible_command (_("split"));
210 if (regions.size() == 1) {
211 /* TODO: if splitting a single region, and snap-to is using
212 region boundaries, mabye we shouldn't pay attention to them? */
215 EditorFreeze(); /* Emit Signal */
218 for (RegionSelection::iterator a = regions.begin(); a != regions.end(); ) {
220 RegionSelection::iterator tmp;
222 /* XXX this test needs to be more complicated, to make sure we really
223 have something to split.
226 if (!(*a)->region()->covers (where.sample)) {
234 boost::shared_ptr<Playlist> pl = (*a)->region()->playlist();
242 /* we haven't seen this playlist before */
244 /* remember used playlists so we can thaw them later */
245 used_playlists.push_back(pl);
247 TimeAxisView& tv = (*a)->get_time_axis_view();
248 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (&tv);
250 used_trackviews.push_back (rtv);
257 pl->clear_changes ();
258 pl->split_region ((*a)->region(), where);
259 _session->add_command (new StatefulDiffCommand (pl));
265 latest_regionviews.clear ();
267 vector<sigc::connection> region_added_connections;
269 for (list<RouteTimeAxisView*>::iterator i = used_trackviews.begin(); i != used_trackviews.end(); ++i) {
270 region_added_connections.push_back ((*i)->view()->RegionViewAdded.connect (sigc::mem_fun(*this, &Editor::collect_new_region_view)));
273 while (used_playlists.size() > 0) {
274 list <boost::shared_ptr<Playlist > >::iterator i = used_playlists.begin();
276 used_playlists.pop_front();
279 for (vector<sigc::connection>::iterator c = region_added_connections.begin(); c != region_added_connections.end(); ++c) {
284 EditorThaw(); /* Emit Signal */
287 RegionSelectionAfterSplit rsas = Config->get_region_selection_after_split();
289 //if the user has "Clear Selection" as their post-split behavior, then clear the selection
290 if (!latest_regionviews.empty() && (rsas == None)) {
291 selection->clear_objects();
292 selection->clear_time();
293 //but leave track selection intact
296 //if the user doesn't want to preserve the "Existing" selection, then clear the selection
297 if (!(rsas & Existing)) {
298 selection->clear_objects();
299 selection->clear_time();
302 //if the user wants newly-created regions to be selected, then select them:
303 if (mouse_mode == MouseObject) {
304 for (RegionSelection::iterator ri = latest_regionviews.begin(); ri != latest_regionviews.end(); ri++) {
305 if ((*ri)->region()->position() < where.sample) {
306 // new regions created before the split
307 if (rsas & NewlyCreatedLeft) {
308 selection->add (*ri);
311 // new regions created after the split
312 if (rsas & NewlyCreatedRight) {
313 selection->add (*ri);
319 commit_reversible_command ();
322 /** Move one extreme of the current range selection. If more than one range is selected,
323 * the start of the earliest range or the end of the latest range is moved.
325 * @param move_end true to move the end of the current range selection, false to move
327 * @param next true to move the extreme to the next region boundary, false to move to
331 Editor::move_range_selection_start_or_end_to_region_boundary (bool move_end, bool next)
333 if (selection->time.start() == selection->time.end_sample()) {
337 samplepos_t start = selection->time.start ();
338 samplepos_t end = selection->time.end_sample ();
340 /* the position of the thing we may move */
341 samplepos_t pos = move_end ? end : start;
342 int dir = next ? 1 : -1;
344 /* so we don't find the current region again */
345 if (dir > 0 || pos > 0) {
349 samplepos_t const target = get_region_boundary (pos, dir, true, false);
364 begin_reversible_selection_op (_("alter selection"));
365 selection->set_preserving_all_ranges (start, end);
366 commit_reversible_selection_op ();
370 Editor::nudge_forward_release (GdkEventButton* ev)
372 if (ev->state & Keyboard::PrimaryModifier) {
373 nudge_forward (false, true);
375 nudge_forward (false, false);
381 Editor::nudge_backward_release (GdkEventButton* ev)
383 if (ev->state & Keyboard::PrimaryModifier) {
384 nudge_backward (false, true);
386 nudge_backward (false, false);
393 Editor::nudge_forward (bool next, bool force_playhead)
395 samplepos_t distance;
396 samplepos_t next_distance;
402 RegionSelection rs = get_regions_from_selection_and_entered ();
404 if (!force_playhead && !rs.empty()) {
406 begin_reversible_command (_("nudge regions forward"));
408 for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
409 boost::shared_ptr<Region> r ((*i)->region());
411 distance = get_nudge_distance (r->position(), next_distance);
414 distance = next_distance;
418 r->set_position (r->position() + distance);
419 _session->add_command (new StatefulDiffCommand (r));
422 commit_reversible_command ();
425 } else if (!force_playhead && !selection->markers.empty()) {
428 bool in_command = false;
429 const int32_t divisions = get_grid_music_divisions (0);
431 for (MarkerSelection::iterator i = selection->markers.begin(); i != selection->markers.end(); ++i) {
433 Location* loc = find_location_from_marker ((*i), is_start);
437 XMLNode& before (loc->get_state());
440 distance = get_nudge_distance (loc->start(), next_distance);
442 distance = next_distance;
444 if (max_samplepos - distance > loc->start() + loc->length()) {
445 loc->set_start (loc->start() + distance, false, true, divisions);
447 loc->set_start (max_samplepos - loc->length(), false, true, divisions);
450 distance = get_nudge_distance (loc->end(), next_distance);
452 distance = next_distance;
454 if (max_samplepos - distance > loc->end()) {
455 loc->set_end (loc->end() + distance, false, true, divisions);
457 loc->set_end (max_samplepos, false, true, divisions);
459 if (loc->is_session_range()) {
460 _session->set_session_range_is_free (false);
464 begin_reversible_command (_("nudge location forward"));
467 XMLNode& after (loc->get_state());
468 _session->add_command (new MementoCommand<Location>(*loc, &before, &after));
473 commit_reversible_command ();
476 distance = get_nudge_distance (playhead_cursor->current_sample (), next_distance);
477 _session->request_locate (playhead_cursor->current_sample () + distance);
482 Editor::nudge_backward (bool next, bool force_playhead)
484 samplepos_t distance;
485 samplepos_t next_distance;
491 RegionSelection rs = get_regions_from_selection_and_entered ();
493 if (!force_playhead && !rs.empty()) {
495 begin_reversible_command (_("nudge regions backward"));
497 for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
498 boost::shared_ptr<Region> r ((*i)->region());
500 distance = get_nudge_distance (r->position(), next_distance);
503 distance = next_distance;
508 if (r->position() > distance) {
509 r->set_position (r->position() - distance);
513 _session->add_command (new StatefulDiffCommand (r));
516 commit_reversible_command ();
518 } else if (!force_playhead && !selection->markers.empty()) {
521 bool in_command = false;
523 for (MarkerSelection::iterator i = selection->markers.begin(); i != selection->markers.end(); ++i) {
525 Location* loc = find_location_from_marker ((*i), is_start);
529 XMLNode& before (loc->get_state());
532 distance = get_nudge_distance (loc->start(), next_distance);
534 distance = next_distance;
536 if (distance < loc->start()) {
537 loc->set_start (loc->start() - distance, false, true, get_grid_music_divisions(0));
539 loc->set_start (0, false, true, get_grid_music_divisions(0));
542 distance = get_nudge_distance (loc->end(), next_distance);
545 distance = next_distance;
548 if (distance < loc->end() - loc->length()) {
549 loc->set_end (loc->end() - distance, false, true, get_grid_music_divisions(0));
551 loc->set_end (loc->length(), false, true, get_grid_music_divisions(0));
553 if (loc->is_session_range()) {
554 _session->set_session_range_is_free (false);
558 begin_reversible_command (_("nudge location forward"));
561 XMLNode& after (loc->get_state());
562 _session->add_command (new MementoCommand<Location>(*loc, &before, &after));
566 commit_reversible_command ();
571 distance = get_nudge_distance (playhead_cursor->current_sample (), next_distance);
573 if (playhead_cursor->current_sample () > distance) {
574 _session->request_locate (playhead_cursor->current_sample () - distance);
576 _session->goto_start();
582 Editor::nudge_forward_capture_offset ()
584 RegionSelection rs = get_regions_from_selection_and_entered ();
586 if (!_session || rs.empty()) {
590 begin_reversible_command (_("nudge forward"));
592 samplepos_t const distance = _session->worst_output_latency();
594 for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
595 boost::shared_ptr<Region> r ((*i)->region());
598 r->set_position (r->position() + distance);
599 _session->add_command(new StatefulDiffCommand (r));
602 commit_reversible_command ();
606 Editor::nudge_backward_capture_offset ()
608 RegionSelection rs = get_regions_from_selection_and_entered ();
610 if (!_session || rs.empty()) {
614 begin_reversible_command (_("nudge backward"));
616 samplepos_t const distance = _session->worst_output_latency();
618 for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
619 boost::shared_ptr<Region> r ((*i)->region());
623 if (r->position() > distance) {
624 r->set_position (r->position() - distance);
628 _session->add_command(new StatefulDiffCommand (r));
631 commit_reversible_command ();
634 struct RegionSelectionPositionSorter {
635 bool operator() (RegionView* a, RegionView* b) {
636 return a->region()->position() < b->region()->position();
641 Editor::sequence_regions ()
644 samplepos_t r_end_prev;
652 RegionSelection rs = get_regions_from_selection_and_entered ();
653 rs.sort(RegionSelectionPositionSorter());
657 bool in_command = false;
659 for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
660 boost::shared_ptr<Region> r ((*i)->region());
668 if(r->position_locked())
675 r->set_position(r_end_prev);
679 begin_reversible_command (_("sequence regions"));
682 _session->add_command (new StatefulDiffCommand (r));
684 r_end=r->position() + r->length();
690 commit_reversible_command ();
699 Editor::move_to_start ()
701 _session->goto_start ();
705 Editor::move_to_end ()
708 _session->request_locate (_session->current_end_sample());
712 Editor::build_region_boundary_cache ()
715 /* TODO: maybe set a timer so we don't recalutate when lots of changes are coming in */
716 /* TODO: maybe somehow defer this until session is fully loaded. */
718 if (!_region_boundary_cache_dirty)
722 vector<RegionPoint> interesting_points;
723 boost::shared_ptr<Region> r;
724 TrackViewList tracks;
727 region_boundary_cache.clear ();
733 bool maybe_first_sample = false;
735 if (UIConfiguration::instance().get_snap_to_region_start()) {
736 interesting_points.push_back (Start);
737 maybe_first_sample = true;
740 if (UIConfiguration::instance().get_snap_to_region_end()) {
741 interesting_points.push_back (End);
744 if (UIConfiguration::instance().get_snap_to_region_sync()) {
745 interesting_points.push_back (SyncPoint);
748 /* if no snap selections are set, boundary cache should be left empty */
749 if ( interesting_points.empty() ) {
750 _region_boundary_cache_dirty = false;
754 TimeAxisView *ontrack = 0;
757 tlist = track_views.filter_to_unique_playlists ();
759 if (maybe_first_sample) {
760 TrackViewList::const_iterator i;
761 for (i = tlist.begin(); i != tlist.end(); ++i) {
762 boost::shared_ptr<Playlist> pl = (*i)->playlist();
763 if (pl && pl->count_regions_at (0)) {
764 region_boundary_cache.push_back (0);
770 //allow regions to snap to the video start (if any) as if it were a "region"
771 if (ARDOUR_UI::instance()->video_timeline) {
772 region_boundary_cache.push_back (ARDOUR_UI::instance()->video_timeline->get_video_start_offset());
775 std::pair<samplepos_t, samplepos_t> ext = session_gui_extents (false);
776 samplepos_t session_end = ext.second;
778 while (pos < session_end && !at_end) {
781 samplepos_t lpos = session_end;
783 for (vector<RegionPoint>::iterator p = interesting_points.begin(); p != interesting_points.end(); ++p) {
785 if ((r = find_next_region (pos, *p, 1, tlist, &ontrack)) == 0) {
786 if (*p == interesting_points.back()) {
789 /* move to next point type */
795 rpos = r->first_sample();
799 rpos = r->last_sample();
803 rpos = r->sync_position ();
814 /* prevent duplicates, but we don't use set<> because we want to be able
818 vector<samplepos_t>::iterator ri;
820 for (ri = region_boundary_cache.begin(); ri != region_boundary_cache.end(); ++ri) {
826 if (ri == region_boundary_cache.end()) {
827 region_boundary_cache.push_back (rpos);
834 /* finally sort to be sure that the order is correct */
836 sort (region_boundary_cache.begin(), region_boundary_cache.end());
838 _region_boundary_cache_dirty = false;
841 boost::shared_ptr<Region>
842 Editor::find_next_region (samplepos_t sample, RegionPoint point, int32_t dir, TrackViewList& tracks, TimeAxisView **ontrack)
844 TrackViewList::iterator i;
845 samplepos_t closest = max_samplepos;
846 boost::shared_ptr<Region> ret;
847 samplepos_t rpos = 0;
849 samplepos_t track_sample;
851 for (i = tracks.begin(); i != tracks.end(); ++i) {
853 samplecnt_t distance;
854 boost::shared_ptr<Region> r;
856 track_sample = sample;
858 if ((r = (*i)->find_next_region (track_sample, point, dir)) == 0) {
864 rpos = r->first_sample ();
868 rpos = r->last_sample ();
872 rpos = r->sync_position ();
877 distance = rpos - sample;
879 distance = sample - rpos;
882 if (distance < closest) {
894 Editor::find_next_region_boundary (samplepos_t pos, int32_t dir, const TrackViewList& tracks)
896 samplecnt_t distance = max_samplepos;
897 samplepos_t current_nearest = -1;
899 for (TrackViewList::const_iterator i = tracks.begin(); i != tracks.end(); ++i) {
900 samplepos_t contender;
903 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (*i);
909 if ((contender = rtv->find_next_region_boundary (pos, dir)) < 0) {
913 d = ::llabs (pos - contender);
916 current_nearest = contender;
921 return current_nearest;
925 Editor::get_region_boundary (samplepos_t pos, int32_t dir, bool with_selection, bool only_onscreen)
930 if (with_selection && Config->get_region_boundaries_from_selected_tracks()) {
932 if (!selection->tracks.empty()) {
934 target = find_next_region_boundary (pos, dir, selection->tracks);
938 if (only_onscreen || Config->get_region_boundaries_from_onscreen_tracks()) {
939 get_onscreen_tracks (tvl);
940 target = find_next_region_boundary (pos, dir, tvl);
942 target = find_next_region_boundary (pos, dir, track_views);
948 if (only_onscreen || Config->get_region_boundaries_from_onscreen_tracks()) {
949 get_onscreen_tracks (tvl);
950 target = find_next_region_boundary (pos, dir, tvl);
952 target = find_next_region_boundary (pos, dir, track_views);
960 Editor::cursor_to_region_boundary (bool with_selection, int32_t dir)
962 samplepos_t pos = playhead_cursor->current_sample ();
969 // so we don't find the current region again..
970 if (dir > 0 || pos > 0) {
974 if ((target = get_region_boundary (pos, dir, with_selection, false)) < 0) {
978 _session->request_locate (target);
982 Editor::cursor_to_next_region_boundary (bool with_selection)
984 cursor_to_region_boundary (with_selection, 1);
988 Editor::cursor_to_previous_region_boundary (bool with_selection)
990 cursor_to_region_boundary (with_selection, -1);
994 Editor::cursor_to_region_point (EditorCursor* cursor, RegionPoint point, int32_t dir)
996 boost::shared_ptr<Region> r;
997 samplepos_t pos = cursor->current_sample ();
1003 TimeAxisView *ontrack = 0;
1005 // so we don't find the current region again..
1009 if (!selection->tracks.empty()) {
1011 r = find_next_region (pos, point, dir, selection->tracks, &ontrack);
1013 } else if (clicked_axisview) {
1016 t.push_back (clicked_axisview);
1018 r = find_next_region (pos, point, dir, t, &ontrack);
1022 r = find_next_region (pos, point, dir, track_views, &ontrack);
1031 pos = r->first_sample ();
1035 pos = r->last_sample ();
1039 pos = r->sync_position ();
1043 if (cursor == playhead_cursor) {
1044 _session->request_locate (pos);
1046 cursor->set_position (pos);
1051 Editor::cursor_to_next_region_point (EditorCursor* cursor, RegionPoint point)
1053 cursor_to_region_point (cursor, point, 1);
1057 Editor::cursor_to_previous_region_point (EditorCursor* cursor, RegionPoint point)
1059 cursor_to_region_point (cursor, point, -1);
1063 Editor::cursor_to_selection_start (EditorCursor *cursor)
1065 samplepos_t pos = 0;
1067 switch (mouse_mode) {
1069 if (!selection->regions.empty()) {
1070 pos = selection->regions.start();
1075 if (!selection->time.empty()) {
1076 pos = selection->time.start ();
1084 if (cursor == playhead_cursor) {
1085 _session->request_locate (pos);
1087 cursor->set_position (pos);
1092 Editor::cursor_to_selection_end (EditorCursor *cursor)
1094 samplepos_t pos = 0;
1096 switch (mouse_mode) {
1098 if (!selection->regions.empty()) {
1099 pos = selection->regions.end_sample();
1104 if (!selection->time.empty()) {
1105 pos = selection->time.end_sample ();
1113 if (cursor == playhead_cursor) {
1114 _session->request_locate (pos);
1116 cursor->set_position (pos);
1121 Editor::selected_marker_to_region_boundary (bool with_selection, int32_t dir)
1131 if (selection->markers.empty()) {
1135 if (!mouse_sample (mouse, ignored)) {
1139 add_location_mark (mouse);
1142 if ((loc = find_location_from_marker (selection->markers.front(), ignored)) == 0) {
1146 samplepos_t pos = loc->start();
1148 // so we don't find the current region again..
1149 if (dir > 0 || pos > 0) {
1153 if ((target = get_region_boundary (pos, dir, with_selection, false)) < 0) {
1157 loc->move_to (target, 0);
1161 Editor::selected_marker_to_next_region_boundary (bool with_selection)
1163 selected_marker_to_region_boundary (with_selection, 1);
1167 Editor::selected_marker_to_previous_region_boundary (bool with_selection)
1169 selected_marker_to_region_boundary (with_selection, -1);
1173 Editor::selected_marker_to_region_point (RegionPoint point, int32_t dir)
1175 boost::shared_ptr<Region> r;
1180 if (!_session || selection->markers.empty()) {
1184 if ((loc = find_location_from_marker (selection->markers.front(), ignored)) == 0) {
1188 TimeAxisView *ontrack = 0;
1192 // so we don't find the current region again..
1196 if (!selection->tracks.empty()) {
1198 r = find_next_region (pos, point, dir, selection->tracks, &ontrack);
1202 r = find_next_region (pos, point, dir, track_views, &ontrack);
1211 pos = r->first_sample ();
1215 pos = r->last_sample ();
1219 pos = r->adjust_to_sync (r->first_sample());
1223 loc->move_to (pos, 0);
1227 Editor::selected_marker_to_next_region_point (RegionPoint point)
1229 selected_marker_to_region_point (point, 1);
1233 Editor::selected_marker_to_previous_region_point (RegionPoint point)
1235 selected_marker_to_region_point (point, -1);
1239 Editor::selected_marker_to_selection_start ()
1241 samplepos_t pos = 0;
1245 if (!_session || selection->markers.empty()) {
1249 if ((loc = find_location_from_marker (selection->markers.front(), ignored)) == 0) {
1253 switch (mouse_mode) {
1255 if (!selection->regions.empty()) {
1256 pos = selection->regions.start();
1261 if (!selection->time.empty()) {
1262 pos = selection->time.start ();
1270 loc->move_to (pos, 0);
1274 Editor::selected_marker_to_selection_end ()
1276 samplepos_t pos = 0;
1280 if (!_session || selection->markers.empty()) {
1284 if ((loc = find_location_from_marker (selection->markers.front(), ignored)) == 0) {
1288 switch (mouse_mode) {
1290 if (!selection->regions.empty()) {
1291 pos = selection->regions.end_sample();
1296 if (!selection->time.empty()) {
1297 pos = selection->time.end_sample ();
1305 loc->move_to (pos, 0);
1309 Editor::scroll_playhead (bool forward)
1311 samplepos_t pos = playhead_cursor->current_sample ();
1312 samplecnt_t delta = (samplecnt_t) floor (current_page_samples() / 0.8);
1315 if (pos == max_samplepos) {
1319 if (pos < max_samplepos - delta) {
1322 pos = max_samplepos;
1338 _session->request_locate (pos);
1342 Editor::cursor_align (bool playhead_to_edit)
1348 if (playhead_to_edit) {
1350 if (selection->markers.empty()) {
1354 _session->request_locate (selection->markers.front()->position(), _session->transport_rolling());
1357 const int32_t divisions = get_grid_music_divisions (0);
1358 /* move selected markers to playhead */
1360 for (MarkerSelection::iterator i = selection->markers.begin(); i != selection->markers.end(); ++i) {
1363 Location* loc = find_location_from_marker (*i, ignored);
1365 if (loc->is_mark()) {
1366 loc->set_start (playhead_cursor->current_sample (), false, true, divisions);
1368 loc->set (playhead_cursor->current_sample (),
1369 playhead_cursor->current_sample () + loc->length(), true, divisions);
1376 Editor::scroll_backward (float pages)
1378 samplepos_t const one_page = (samplepos_t) rint (_visible_canvas_width * samples_per_pixel);
1379 samplepos_t const cnt = (samplepos_t) floor (pages * one_page);
1382 if (_leftmost_sample < cnt) {
1385 sample = _leftmost_sample - cnt;
1388 reset_x_origin (sample);
1392 Editor::scroll_forward (float pages)
1394 samplepos_t const one_page = (samplepos_t) rint (_visible_canvas_width * samples_per_pixel);
1395 samplepos_t const cnt = (samplepos_t) floor (pages * one_page);
1398 if (max_samplepos - cnt < _leftmost_sample) {
1399 sample = max_samplepos - cnt;
1401 sample = _leftmost_sample + cnt;
1404 reset_x_origin (sample);
1408 Editor::scroll_tracks_down ()
1410 double vert_value = vertical_adjustment.get_value() + vertical_adjustment.get_page_size();
1411 if (vert_value > vertical_adjustment.get_upper() - _visible_canvas_height) {
1412 vert_value = vertical_adjustment.get_upper() - _visible_canvas_height;
1415 vertical_adjustment.set_value (vert_value);
1419 Editor::scroll_tracks_up ()
1421 vertical_adjustment.set_value (vertical_adjustment.get_value() - vertical_adjustment.get_page_size());
1425 Editor::scroll_tracks_down_line ()
1427 double vert_value = vertical_adjustment.get_value() + 60;
1429 if (vert_value > vertical_adjustment.get_upper() - _visible_canvas_height) {
1430 vert_value = vertical_adjustment.get_upper() - _visible_canvas_height;
1433 vertical_adjustment.set_value (vert_value);
1437 Editor::scroll_tracks_up_line ()
1439 reset_y_origin (vertical_adjustment.get_value() - 60);
1443 Editor::select_topmost_track ()
1445 const double top_of_trackviews = vertical_adjustment.get_value();
1446 for (TrackViewList::iterator t = track_views.begin(); t != track_views.end(); ++t) {
1447 if ((*t)->hidden()) {
1450 std::pair<TimeAxisView*,double> res = (*t)->covers_y_position (top_of_trackviews);
1452 selection->set (*t);
1459 Editor::scroll_down_one_track (bool skip_child_views)
1461 TrackViewList::reverse_iterator next = track_views.rend();
1462 const double top_of_trackviews = vertical_adjustment.get_value();
1464 for (TrackViewList::reverse_iterator t = track_views.rbegin(); t != track_views.rend(); ++t) {
1465 if ((*t)->hidden()) {
1469 /* If this is the upper-most visible trackview, we want to display
1470 * the one above it (next)
1472 * Note that covers_y_position() is recursive and includes child views
1474 std::pair<TimeAxisView*,double> res = (*t)->covers_y_position (top_of_trackviews);
1477 if (skip_child_views) {
1480 /* automation lane (one level, non-recursive)
1482 * - if no automation lane exists -> move to next tack
1483 * - if the first (here: bottom-most) matches -> move to next tack
1484 * - if no y-axis match is found -> the current track is at the top
1485 * -> move to last (here: top-most) automation lane
1487 TimeAxisView::Children kids = (*t)->get_child_list();
1488 TimeAxisView::Children::reverse_iterator nkid = kids.rend();
1490 for (TimeAxisView::Children::reverse_iterator ci = kids.rbegin(); ci != kids.rend(); ++ci) {
1491 if ((*ci)->hidden()) {
1495 std::pair<TimeAxisView*,double> dev;
1496 dev = (*ci)->covers_y_position (top_of_trackviews);
1498 /* some automation lane is currently at the top */
1499 if (ci == kids.rbegin()) {
1500 /* first (bottom-most) autmation lane is at the top.
1501 * -> move to next track
1510 if (nkid != kids.rend()) {
1511 ensure_time_axis_view_is_visible (**nkid, true);
1519 /* move to the track below the first one that covers the */
1521 if (next != track_views.rend()) {
1522 ensure_time_axis_view_is_visible (**next, true);
1530 Editor::scroll_up_one_track (bool skip_child_views)
1532 TrackViewList::iterator prev = track_views.end();
1533 double top_of_trackviews = vertical_adjustment.get_value ();
1535 for (TrackViewList::iterator t = track_views.begin(); t != track_views.end(); ++t) {
1537 if ((*t)->hidden()) {
1541 /* find the trackview at the top of the trackview group
1543 * Note that covers_y_position() is recursive and includes child views
1545 std::pair<TimeAxisView*,double> res = (*t)->covers_y_position (top_of_trackviews);
1548 if (skip_child_views) {
1551 /* automation lane (one level, non-recursive)
1553 * - if no automation lane exists -> move to prev tack
1554 * - if no y-axis match is found -> the current track is at the top -> move to prev track
1555 * (actually last automation lane of previous track, see below)
1556 * - if first (top-most) lane is at the top -> move to this track
1557 * - else move up one lane
1559 TimeAxisView::Children kids = (*t)->get_child_list();
1560 TimeAxisView::Children::iterator pkid = kids.end();
1562 for (TimeAxisView::Children::iterator ci = kids.begin(); ci != kids.end(); ++ci) {
1563 if ((*ci)->hidden()) {
1567 std::pair<TimeAxisView*,double> dev;
1568 dev = (*ci)->covers_y_position (top_of_trackviews);
1570 /* some automation lane is currently at the top */
1571 if (ci == kids.begin()) {
1572 /* first (top-most) autmation lane is at the top.
1573 * jump directly to this track's top
1575 ensure_time_axis_view_is_visible (**t, true);
1578 else if (pkid != kids.end()) {
1579 /* some other automation lane is at the top.
1580 * move up to prev automation lane.
1582 ensure_time_axis_view_is_visible (**pkid, true);
1585 assert(0); // not reached
1596 if (prev != track_views.end()) {
1597 // move to bottom-most automation-lane of the previous track
1598 TimeAxisView::Children kids = (*prev)->get_child_list();
1599 TimeAxisView::Children::reverse_iterator pkid = kids.rend();
1600 if (!skip_child_views) {
1601 // find the last visible lane
1602 for (TimeAxisView::Children::reverse_iterator ci = kids.rbegin(); ci != kids.rend(); ++ci) {
1603 if (!(*ci)->hidden()) {
1609 if (pkid != kids.rend()) {
1610 ensure_time_axis_view_is_visible (**pkid, true);
1612 ensure_time_axis_view_is_visible (**prev, true);
1621 Editor::scroll_left_step ()
1623 samplepos_t xdelta = (current_page_samples() / 8);
1625 if (_leftmost_sample > xdelta) {
1626 reset_x_origin (_leftmost_sample - xdelta);
1634 Editor::scroll_right_step ()
1636 samplepos_t xdelta = (current_page_samples() / 8);
1638 if (max_samplepos - xdelta > _leftmost_sample) {
1639 reset_x_origin (_leftmost_sample + xdelta);
1641 reset_x_origin (max_samplepos - current_page_samples());
1646 Editor::scroll_left_half_page ()
1648 samplepos_t xdelta = (current_page_samples() / 2);
1649 if (_leftmost_sample > xdelta) {
1650 reset_x_origin (_leftmost_sample - xdelta);
1657 Editor::scroll_right_half_page ()
1659 samplepos_t xdelta = (current_page_samples() / 2);
1660 if (max_samplepos - xdelta > _leftmost_sample) {
1661 reset_x_origin (_leftmost_sample + xdelta);
1663 reset_x_origin (max_samplepos - current_page_samples());
1670 Editor::tav_zoom_step (bool coarser)
1672 DisplaySuspender ds;
1676 if (selection->tracks.empty()) {
1679 ts = &selection->tracks;
1682 for (TrackViewList::iterator i = ts->begin(); i != ts->end(); ++i) {
1683 TimeAxisView *tv = (static_cast<TimeAxisView*>(*i));
1684 tv->step_height (coarser);
1689 Editor::tav_zoom_smooth (bool coarser, bool force_all)
1691 DisplaySuspender ds;
1695 if (selection->tracks.empty() || force_all) {
1698 ts = &selection->tracks;
1701 for (TrackViewList::iterator i = ts->begin(); i != ts->end(); ++i) {
1702 TimeAxisView *tv = (static_cast<TimeAxisView*>(*i));
1703 uint32_t h = tv->current_height ();
1708 if (h >= TimeAxisView::preset_height (HeightSmall)) {
1713 tv->set_height (h + 5);
1719 Editor::temporal_zoom_step_mouse_focus_scale (bool zoom_out, double scale)
1721 Editing::ZoomFocus temp_focus = zoom_focus;
1722 zoom_focus = Editing::ZoomFocusMouse;
1723 temporal_zoom_step_scale (zoom_out, scale);
1724 zoom_focus = temp_focus;
1728 Editor::temporal_zoom_step_mouse_focus (bool zoom_out)
1730 temporal_zoom_step_mouse_focus_scale (zoom_out, 2.0);
1734 Editor::temporal_zoom_step (bool zoom_out)
1736 temporal_zoom_step_scale (zoom_out, 2.0);
1740 Editor::temporal_zoom_step_scale (bool zoom_out, double scale)
1742 ENSURE_GUI_THREAD (*this, &Editor::temporal_zoom_step, zoom_out, scale)
1744 samplecnt_t nspp = samples_per_pixel;
1748 if (nspp == samples_per_pixel) {
1753 if (nspp == samples_per_pixel) {
1758 //zoom-behavior-tweaks
1759 //limit our maximum zoom to the session gui extents value
1760 std::pair<samplepos_t, samplepos_t> ext = session_gui_extents();
1761 samplecnt_t session_extents_pp = (ext.second - ext.first) / _visible_canvas_width;
1762 if (nspp > session_extents_pp)
1763 nspp = session_extents_pp;
1765 temporal_zoom (nspp);
1769 Editor::temporal_zoom (samplecnt_t fpp)
1775 samplepos_t current_page = current_page_samples();
1776 samplepos_t current_leftmost = _leftmost_sample;
1777 samplepos_t current_rightmost;
1778 samplepos_t current_center;
1779 samplepos_t new_page_size;
1780 samplepos_t half_page_size;
1781 samplepos_t leftmost_after_zoom = 0;
1783 bool in_track_canvas;
1784 bool use_mouse_sample = true;
1788 if (fpp == samples_per_pixel) {
1792 // Imposing an arbitrary limit to zoom out as too much zoom out produces
1793 // segfaults for lack of memory. If somebody decides this is not high enough I
1794 // believe it can be raisen to higher values but some limit must be in place.
1796 // This constant represents 1 day @ 48kHz on a 1600 pixel wide display
1797 // all of which is used for the editor track displays. The whole day
1798 // would be 4147200000 samples, so 2592000 samples per pixel.
1800 nfpp = min (fpp, (samplecnt_t) 2592000);
1801 nfpp = max ((samplecnt_t) 1, nfpp);
1803 new_page_size = (samplepos_t) floor (_visible_canvas_width * nfpp);
1804 half_page_size = new_page_size / 2;
1806 Editing::ZoomFocus zf = zoom_focus;
1808 if (zf == ZoomFocusEdit && _edit_point == EditAtMouse) {
1809 zf = ZoomFocusMouse;
1814 leftmost_after_zoom = current_leftmost;
1817 case ZoomFocusRight:
1818 current_rightmost = _leftmost_sample + current_page;
1819 if (current_rightmost < new_page_size) {
1820 leftmost_after_zoom = 0;
1822 leftmost_after_zoom = current_rightmost - new_page_size;
1826 case ZoomFocusCenter:
1827 current_center = current_leftmost + (current_page/2);
1828 if (current_center < half_page_size) {
1829 leftmost_after_zoom = 0;
1831 leftmost_after_zoom = current_center - half_page_size;
1835 case ZoomFocusPlayhead:
1836 /* centre playhead */
1837 l = playhead_cursor->current_sample () - (new_page_size * 0.5);
1840 leftmost_after_zoom = 0;
1841 } else if (l > max_samplepos) {
1842 leftmost_after_zoom = max_samplepos - new_page_size;
1844 leftmost_after_zoom = (samplepos_t) l;
1848 case ZoomFocusMouse:
1849 /* try to keep the mouse over the same point in the display */
1851 if (_drags->active()) {
1852 where = _drags->current_pointer_sample ();
1853 } else if (!mouse_sample (where, in_track_canvas)) {
1854 use_mouse_sample = false;
1857 if (use_mouse_sample) {
1858 l = - ((new_page_size * ((where - current_leftmost)/(double)current_page)) - where);
1861 leftmost_after_zoom = 0;
1862 } else if (l > max_samplepos) {
1863 leftmost_after_zoom = max_samplepos - new_page_size;
1865 leftmost_after_zoom = (samplepos_t) l;
1868 /* use playhead instead */
1869 where = playhead_cursor->current_sample ();
1871 if (where < half_page_size) {
1872 leftmost_after_zoom = 0;
1874 leftmost_after_zoom = where - half_page_size;
1880 /* try to keep the edit point in the same place */
1881 where = get_preferred_edit_position ();
1883 double l = - ((new_page_size * ((where - current_leftmost)/(double)current_page)) - where);
1886 leftmost_after_zoom = 0;
1887 } else if (l > max_samplepos) {
1888 leftmost_after_zoom = max_samplepos - new_page_size;
1890 leftmost_after_zoom = (samplepos_t) l;
1897 // leftmost_after_zoom = min (leftmost_after_zoom, _session->current_end_sample());
1899 reposition_and_zoom (leftmost_after_zoom, nfpp);
1903 Editor::calc_extra_zoom_edges(samplepos_t &start, samplepos_t &end)
1905 /* this func helps make sure we leave a little space
1906 at each end of the editor so that the zoom doesn't fit the region
1907 precisely to the screen.
1910 GdkScreen* screen = gdk_screen_get_default ();
1911 const gint pixwidth = gdk_screen_get_width (screen);
1912 const gint mmwidth = gdk_screen_get_width_mm (screen);
1913 const double pix_per_mm = (double) pixwidth/ (double) mmwidth;
1914 const double one_centimeter_in_pixels = pix_per_mm * 10.0;
1916 const samplepos_t range = end - start;
1917 const samplecnt_t new_fpp = (samplecnt_t) ceil ((double) range / (double) _visible_canvas_width);
1918 const samplepos_t extra_samples = (samplepos_t) floor (one_centimeter_in_pixels * new_fpp);
1920 if (start > extra_samples) {
1921 start -= extra_samples;
1926 if (max_samplepos - extra_samples > end) {
1927 end += extra_samples;
1929 end = max_samplepos;
1934 Editor::get_selection_extents (samplepos_t &start, samplepos_t &end) const
1936 start = max_samplepos;
1940 //ToDo: if notes are selected, set extents to that selection
1942 //ToDo: if control points are selected, set extents to that selection
1944 if (!selection->regions.empty()) {
1945 RegionSelection rs = get_regions_from_selection_and_entered ();
1947 for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
1949 if ((*i)->region()->position() < start) {
1950 start = (*i)->region()->position();
1953 if ((*i)->region()->last_sample() + 1 > end) {
1954 end = (*i)->region()->last_sample() + 1;
1958 } else if (!selection->time.empty()) {
1959 start = selection->time.start();
1960 end = selection->time.end_sample();
1962 ret = false; //no selection found
1965 if ((start == 0 && end == 0) || end < start) {
1974 Editor::temporal_zoom_selection (Editing::ZoomAxis axes)
1976 if (!selection) return;
1978 if (selection->regions.empty() && selection->time.empty()) {
1979 if (axes == Horizontal || axes == Both) {
1980 temporal_zoom_step(true);
1982 if (axes == Vertical || axes == Both) {
1983 if (!track_views.empty()) {
1987 //implicit hack: by extending the top & bottom check outside the current view limits, we include the trackviews immediately above & below what is visible
1988 const double top = vertical_adjustment.get_value() - 10;
1989 const double btm = top + _visible_canvas_height + 10;
1991 for (TrackViewList::iterator iter = track_views.begin(); iter != track_views.end(); ++iter) {
1992 if ((*iter)->covered_by_y_range (top, btm)) {
1993 tvl.push_back(*iter);
2003 //ToDo: if notes are selected, zoom to that
2005 //ToDo: if control points are selected, zoom to that
2007 if (axes == Horizontal || axes == Both) {
2009 samplepos_t start, end;
2010 if (get_selection_extents (start, end)) {
2011 calc_extra_zoom_edges (start, end);
2012 temporal_zoom_by_sample (start, end);
2016 if (axes == Vertical || axes == Both) {
2020 //normally, we don't do anything "automatic" to the user's selection.
2021 //but in this case, we will clear the selection after a zoom-to-selection.
2026 Editor::temporal_zoom_session ()
2028 ENSURE_GUI_THREAD (*this, &Editor::temporal_zoom_session)
2031 samplecnt_t start = _session->current_start_sample();
2032 samplecnt_t end = _session->current_end_sample();
2034 if (_session->actively_recording ()) {
2035 samplepos_t cur = playhead_cursor->current_sample ();
2037 /* recording beyond the end marker; zoom out
2038 * by 5 seconds more so that if 'follow
2039 * playhead' is active we don't immediately
2042 end = cur + _session->sample_rate() * 5;
2046 if ((start == 0 && end == 0) || end < start) {
2050 calc_extra_zoom_edges(start, end);
2052 temporal_zoom_by_sample (start, end);
2057 Editor::temporal_zoom_extents ()
2059 ENSURE_GUI_THREAD (*this, &Editor::temporal_zoom_extents)
2062 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
2064 samplecnt_t start = ext.first;
2065 samplecnt_t end = ext.second;
2067 if (_session->actively_recording ()) {
2068 samplepos_t cur = playhead_cursor->current_sample ();
2070 /* recording beyond the end marker; zoom out
2071 * by 5 seconds more so that if 'follow
2072 * playhead' is active we don't immediately
2075 end = cur + _session->sample_rate() * 5;
2079 if ((start == 0 && end == 0) || end < start) {
2083 calc_extra_zoom_edges(start, end);
2085 temporal_zoom_by_sample (start, end);
2090 Editor::temporal_zoom_by_sample (samplepos_t start, samplepos_t end)
2092 if (!_session) return;
2094 if ((start == 0 && end == 0) || end < start) {
2098 samplepos_t range = end - start;
2100 const samplecnt_t new_fpp = (samplecnt_t) ceil ((double) range / (double) _visible_canvas_width);
2102 samplepos_t new_page = range;
2103 samplepos_t middle = (samplepos_t) floor ((double) start + ((double) range / 2.0f));
2104 samplepos_t new_leftmost = (samplepos_t) floor ((double) middle - ((double) new_page / 2.0f));
2106 if (new_leftmost > middle) {
2110 if (new_leftmost < 0) {
2114 reposition_and_zoom (new_leftmost, new_fpp);
2118 Editor::temporal_zoom_to_sample (bool coarser, samplepos_t sample)
2124 samplecnt_t range_before = sample - _leftmost_sample;
2125 samplecnt_t new_spp;
2128 if (samples_per_pixel <= 1) {
2131 new_spp = samples_per_pixel + (samples_per_pixel/2);
2133 range_before += range_before/2;
2135 if (samples_per_pixel >= 1) {
2136 new_spp = samples_per_pixel - (samples_per_pixel/2);
2138 /* could bail out here since we cannot zoom any finer,
2139 but leave that to the equality test below
2141 new_spp = samples_per_pixel;
2144 range_before -= range_before/2;
2147 if (new_spp == samples_per_pixel) {
2151 /* zoom focus is automatically taken as @param sample when this
2155 samplepos_t new_leftmost = sample - (samplepos_t)range_before;
2157 if (new_leftmost > sample) {
2161 if (new_leftmost < 0) {
2165 reposition_and_zoom (new_leftmost, new_spp);
2170 Editor::choose_new_marker_name(string &name, bool is_range) {
2172 if (!UIConfiguration::instance().get_name_new_markers()) {
2173 /* don't prompt user for a new name */
2177 Prompter dialog (true);
2179 dialog.set_prompt (_("New Name:"));
2182 dialog.set_title(_("New Range"));
2184 dialog.set_title (_("New Location Marker"));
2187 dialog.set_name ("MarkNameWindow");
2188 dialog.set_size_request (250, -1);
2189 dialog.set_position (Gtk::WIN_POS_MOUSE);
2191 dialog.add_button (Stock::OK, RESPONSE_ACCEPT);
2192 dialog.set_initial_text (name);
2196 switch (dialog.run ()) {
2197 case RESPONSE_ACCEPT:
2203 dialog.get_result(name);
2210 Editor::add_location_from_selection ()
2214 if (selection->time.empty()) {
2218 if (_session == 0 || clicked_axisview == 0) {
2222 samplepos_t start = selection->time[clicked_selection].start;
2223 samplepos_t end = selection->time[clicked_selection].end;
2225 _session->locations()->next_available_name(rangename,"selection");
2226 if (!choose_new_marker_name(rangename, true)) {
2229 Location *location = new Location (*_session, start, end, rangename, Location::IsRangeMarker, get_grid_music_divisions(0));
2231 begin_reversible_command (_("add marker"));
2233 XMLNode &before = _session->locations()->get_state();
2234 _session->locations()->add (location, true);
2235 XMLNode &after = _session->locations()->get_state();
2236 _session->add_command(new MementoCommand<Locations>(*(_session->locations()), &before, &after));
2238 commit_reversible_command ();
2242 Editor::add_location_mark (samplepos_t where)
2246 select_new_marker = true;
2248 _session->locations()->next_available_name(markername,"mark");
2249 if (!choose_new_marker_name(markername)) {
2252 Location *location = new Location (*_session, where, where, markername, Location::IsMark, get_grid_music_divisions (0));
2253 begin_reversible_command (_("add marker"));
2255 XMLNode &before = _session->locations()->get_state();
2256 _session->locations()->add (location, true);
2257 XMLNode &after = _session->locations()->get_state();
2258 _session->add_command(new MementoCommand<Locations>(*(_session->locations()), &before, &after));
2260 commit_reversible_command ();
2264 Editor::set_session_start_from_playhead ()
2270 if ((loc = _session->locations()->session_range_location()) == 0) { //should never happen
2271 _session->set_session_extents (_session->audible_sample(), _session->audible_sample());
2273 XMLNode &before = loc->get_state();
2275 _session->set_session_extents (_session->audible_sample(), loc->end());
2277 XMLNode &after = loc->get_state();
2279 begin_reversible_command (_("Set session start"));
2281 _session->add_command (new MementoCommand<Location>(*loc, &before, &after));
2283 commit_reversible_command ();
2286 _session->set_session_range_is_free (false);
2290 Editor::set_session_end_from_playhead ()
2296 if ((loc = _session->locations()->session_range_location()) == 0) { //should never happen
2297 _session->set_session_extents (_session->audible_sample(), _session->audible_sample());
2299 XMLNode &before = loc->get_state();
2301 _session->set_session_extents (loc->start(), _session->audible_sample());
2303 XMLNode &after = loc->get_state();
2305 begin_reversible_command (_("Set session start"));
2307 _session->add_command (new MementoCommand<Location>(*loc, &before, &after));
2309 commit_reversible_command ();
2312 _session->set_session_range_is_free (false);
2317 Editor::toggle_location_at_playhead_cursor ()
2319 if (!do_remove_location_at_playhead_cursor())
2321 add_location_from_playhead_cursor();
2326 Editor::add_location_from_playhead_cursor ()
2328 add_location_mark (_session->audible_sample());
2332 Editor::do_remove_location_at_playhead_cursor ()
2334 bool removed = false;
2337 XMLNode &before = _session->locations()->get_state();
2339 //find location(s) at this time
2340 Locations::LocationList locs;
2341 _session->locations()->find_all_between (_session->audible_sample(), _session->audible_sample()+1, locs, Location::Flags(0));
2342 for (Locations::LocationList::iterator i = locs.begin(); i != locs.end(); ++i) {
2343 if ((*i)->is_mark()) {
2344 _session->locations()->remove (*i);
2351 begin_reversible_command (_("remove marker"));
2352 XMLNode &after = _session->locations()->get_state();
2353 _session->add_command(new MementoCommand<Locations>(*(_session->locations()), &before, &after));
2354 commit_reversible_command ();
2361 Editor::remove_location_at_playhead_cursor ()
2363 do_remove_location_at_playhead_cursor ();
2366 /** Add a range marker around each selected region */
2368 Editor::add_locations_from_region ()
2370 RegionSelection rs = get_regions_from_selection_and_entered ();
2375 bool commit = false;
2377 XMLNode &before = _session->locations()->get_state();
2379 for (RegionSelection::iterator i = rs.begin (); i != rs.end (); ++i) {
2381 boost::shared_ptr<Region> region = (*i)->region ();
2383 Location *location = new Location (*_session, region->position(), region->last_sample(), region->name(), Location::IsRangeMarker, 0);
2385 _session->locations()->add (location, true);
2390 begin_reversible_command (selection->regions.size () > 1 ? _("add markers") : _("add marker"));
2391 XMLNode &after = _session->locations()->get_state();
2392 _session->add_command (new MementoCommand<Locations>(*(_session->locations()), &before, &after));
2393 commit_reversible_command ();
2397 /** Add a single range marker around all selected regions */
2399 Editor::add_location_from_region ()
2401 RegionSelection rs = get_regions_from_selection_and_entered ();
2407 XMLNode &before = _session->locations()->get_state();
2411 if (rs.size() > 1) {
2412 _session->locations()->next_available_name(markername, "regions");
2414 RegionView* rv = *(rs.begin());
2415 boost::shared_ptr<Region> region = rv->region();
2416 markername = region->name();
2419 if (!choose_new_marker_name(markername)) {
2423 // single range spanning all selected
2424 Location *location = new Location (*_session, selection->regions.start(), selection->regions.end_sample(), markername, Location::IsRangeMarker, 0);
2425 _session->locations()->add (location, true);
2427 begin_reversible_command (_("add marker"));
2428 XMLNode &after = _session->locations()->get_state();
2429 _session->add_command (new MementoCommand<Locations>(*(_session->locations()), &before, &after));
2430 commit_reversible_command ();
2436 Editor::jump_forward_to_mark ()
2442 samplepos_t pos = _session->locations()->first_mark_after (playhead_cursor->current_sample());
2448 _session->request_locate (pos, _session->transport_rolling());
2452 Editor::jump_backward_to_mark ()
2458 samplepos_t pos = _session->locations()->first_mark_before (playhead_cursor->current_sample());
2460 //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...
2461 if (_session->transport_rolling()) {
2462 if ((playhead_cursor->current_sample() - pos) < _session->sample_rate()/2) {
2463 samplepos_t prior = _session->locations()->first_mark_before (pos);
2472 _session->request_locate (pos, _session->transport_rolling());
2478 samplepos_t const pos = _session->audible_sample ();
2481 _session->locations()->next_available_name (markername, "mark");
2483 if (!choose_new_marker_name (markername)) {
2487 _session->locations()->add (new Location (*_session, pos, 0, markername, Location::IsMark, 0), true);
2491 Editor::clear_markers ()
2494 begin_reversible_command (_("clear markers"));
2496 XMLNode &before = _session->locations()->get_state();
2497 _session->locations()->clear_markers ();
2498 XMLNode &after = _session->locations()->get_state();
2499 _session->add_command(new MementoCommand<Locations>(*(_session->locations()), &before, &after));
2501 commit_reversible_command ();
2506 Editor::clear_ranges ()
2509 begin_reversible_command (_("clear ranges"));
2511 XMLNode &before = _session->locations()->get_state();
2513 _session->locations()->clear_ranges ();
2515 XMLNode &after = _session->locations()->get_state();
2516 _session->add_command(new MementoCommand<Locations>(*(_session->locations()), &before, &after));
2518 commit_reversible_command ();
2523 Editor::clear_locations ()
2525 begin_reversible_command (_("clear locations"));
2527 XMLNode &before = _session->locations()->get_state();
2528 _session->locations()->clear ();
2529 XMLNode &after = _session->locations()->get_state();
2530 _session->add_command(new MementoCommand<Locations>(*(_session->locations()), &before, &after));
2532 commit_reversible_command ();
2536 Editor::unhide_markers ()
2538 for (LocationMarkerMap::iterator i = location_markers.begin(); i != location_markers.end(); ++i) {
2539 Location *l = (*i).first;
2540 if (l->is_hidden() && l->is_mark()) {
2541 l->set_hidden(false, this);
2547 Editor::unhide_ranges ()
2549 for (LocationMarkerMap::iterator i = location_markers.begin(); i != location_markers.end(); ++i) {
2550 Location *l = (*i).first;
2551 if (l->is_hidden() && l->is_range_marker()) {
2552 l->set_hidden(false, this);
2557 /* INSERT/REPLACE */
2560 Editor::insert_source_list_selection (float times)
2562 RouteTimeAxisView *tv = 0;
2563 boost::shared_ptr<Playlist> playlist;
2565 if (clicked_routeview != 0) {
2566 tv = clicked_routeview;
2567 } else if (!selection->tracks.empty()) {
2568 if ((tv = dynamic_cast<RouteTimeAxisView*>(selection->tracks.front())) == 0) {
2571 } else if (entered_track != 0) {
2572 if ((tv = dynamic_cast<RouteTimeAxisView*>(entered_track)) == 0) {
2579 if ((playlist = tv->playlist()) == 0) {
2583 boost::shared_ptr<Region> region = _sources->get_single_selection ();
2588 begin_reversible_command (_("insert region"));
2589 playlist->clear_changes ();
2590 playlist->add_region ((RegionFactory::create (region, true)), get_preferred_edit_position(), times);
2591 if (Config->get_edit_mode() == Ripple)
2592 playlist->ripple (get_preferred_edit_position(), region->length() * times, boost::shared_ptr<Region>());
2594 _session->add_command(new StatefulDiffCommand (playlist));
2595 commit_reversible_command ();
2598 /* BUILT-IN EFFECTS */
2601 Editor::reverse_selection ()
2606 /* GAIN ENVELOPE EDITING */
2609 Editor::edit_envelope ()
2616 Editor::transition_to_rolling (bool fwd)
2622 if (_session->config.get_external_sync()) {
2623 switch (TransportMasterManager::instance().current()->type()) {
2627 /* transport controlled by the master */
2632 if (_session->is_auditioning()) {
2633 _session->cancel_audition ();
2637 _session->request_transport_speed (fwd ? 1.0f : -1.0f);
2641 Editor::play_from_start ()
2643 _session->request_locate (_session->current_start_sample(), true);
2647 Editor::play_from_edit_point ()
2649 _session->request_locate (get_preferred_edit_position(), true);
2653 Editor::play_from_edit_point_and_return ()
2655 samplepos_t start_sample;
2656 samplepos_t return_sample;
2658 start_sample = get_preferred_edit_position (EDIT_IGNORE_PHEAD);
2660 if (_session->transport_rolling()) {
2661 _session->request_locate (start_sample, false);
2665 /* don't reset the return sample if its already set */
2667 if ((return_sample = _session->requested_return_sample()) < 0) {
2668 return_sample = _session->audible_sample();
2671 if (start_sample >= 0) {
2672 _session->request_roll_at_and_return (start_sample, return_sample);
2677 Editor::play_selection ()
2679 samplepos_t start, end;
2680 if (!get_selection_extents (start, end))
2683 AudioRange ar (start, end, 0);
2684 list<AudioRange> lar;
2687 _session->request_play_range (&lar, true);
2692 Editor::maybe_locate_with_edit_preroll (samplepos_t location)
2694 if (_session->transport_rolling() || !UIConfiguration::instance().get_follow_edits() || _session->config.get_external_sync())
2697 location -= _session->preroll_samples (location);
2699 //don't try to locate before the beginning of time
2704 //if follow_playhead is on, keep the playhead on the screen
2705 if (_follow_playhead)
2706 if (location < _leftmost_sample)
2707 location = _leftmost_sample;
2709 _session->request_locate (location);
2713 Editor::play_with_preroll ()
2715 samplepos_t start, end;
2716 if (UIConfiguration::instance().get_follow_edits() && get_selection_extents (start, end)) {
2717 const samplepos_t preroll = _session->preroll_samples (start);
2719 samplepos_t ret = start;
2721 if (start > preroll) {
2722 start = start - preroll;
2725 end = end + preroll; //"post-roll"
2727 AudioRange ar (start, end, 0);
2728 list<AudioRange> lar;
2731 _session->request_play_range (&lar, true);
2732 _session->set_requested_return_sample (ret); //force auto-return to return to range start, without the preroll
2734 samplepos_t ph = playhead_cursor->current_sample ();
2735 const samplepos_t preroll = _session->preroll_samples (ph);
2738 start = ph - preroll;
2742 _session->request_locate (start, true);
2743 _session->set_requested_return_sample (ph); //force auto-return to return to playhead location, without the preroll
2748 Editor::rec_with_preroll ()
2750 samplepos_t ph = playhead_cursor->current_sample ();
2751 samplepos_t preroll = _session->preroll_samples (ph);
2752 _session->request_preroll_record_trim (ph, preroll);
2756 Editor::rec_with_count_in ()
2758 _session->request_count_in_record ();
2762 Editor::play_location (Location& location)
2764 if (location.start() <= location.end()) {
2768 _session->request_bounded_roll (location.start(), location.end());
2772 Editor::loop_location (Location& location)
2774 if (location.start() <= location.end()) {
2780 if ((tll = transport_loop_location()) != 0) {
2781 tll->set (location.start(), location.end());
2783 // enable looping, reposition and start rolling
2784 _session->request_locate (tll->start(), true);
2785 _session->request_play_loop (true);
2790 Editor::do_layer_operation (LayerOperation op)
2792 if (selection->regions.empty ()) {
2796 bool const multiple = selection->regions.size() > 1;
2800 begin_reversible_command (_("raise regions"));
2802 begin_reversible_command (_("raise region"));
2808 begin_reversible_command (_("raise regions to top"));
2810 begin_reversible_command (_("raise region to top"));
2816 begin_reversible_command (_("lower regions"));
2818 begin_reversible_command (_("lower region"));
2824 begin_reversible_command (_("lower regions to bottom"));
2826 begin_reversible_command (_("lower region"));
2831 set<boost::shared_ptr<Playlist> > playlists = selection->regions.playlists ();
2832 for (set<boost::shared_ptr<Playlist> >::iterator i = playlists.begin(); i != playlists.end(); ++i) {
2833 (*i)->clear_owned_changes ();
2836 for (RegionSelection::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
2837 boost::shared_ptr<Region> r = (*i)->region ();
2849 r->lower_to_bottom ();
2853 for (set<boost::shared_ptr<Playlist> >::iterator i = playlists.begin(); i != playlists.end(); ++i) {
2854 vector<Command*> cmds;
2856 _session->add_commands (cmds);
2859 commit_reversible_command ();
2863 Editor::raise_region ()
2865 do_layer_operation (Raise);
2869 Editor::raise_region_to_top ()
2871 do_layer_operation (RaiseToTop);
2875 Editor::lower_region ()
2877 do_layer_operation (Lower);
2881 Editor::lower_region_to_bottom ()
2883 do_layer_operation (LowerToBottom);
2886 /** Show the region editor for the selected regions */
2888 Editor::show_region_properties ()
2890 selection->foreach_regionview (&RegionView::show_region_editor);
2893 /** Show the midi list editor for the selected MIDI regions */
2895 Editor::show_midi_list_editor ()
2897 selection->foreach_midi_regionview (&MidiRegionView::show_list_editor);
2901 Editor::rename_region ()
2903 RegionSelection rs = get_regions_from_selection_and_entered ();
2909 ArdourDialog d (_("Rename Region"), true, false);
2911 Label label (_("New name:"));
2914 hbox.set_spacing (6);
2915 hbox.pack_start (label, false, false);
2916 hbox.pack_start (entry, true, true);
2918 d.get_vbox()->set_border_width (12);
2919 d.get_vbox()->pack_start (hbox, false, false);
2921 d.add_button(Gtk::Stock::CANCEL, Gtk::RESPONSE_CANCEL);
2922 d.add_button(Gtk::Stock::OK, Gtk::RESPONSE_OK);
2924 d.set_size_request (300, -1);
2926 entry.set_text (rs.front()->region()->name());
2927 entry.select_region (0, -1);
2929 entry.signal_activate().connect (sigc::bind (sigc::mem_fun (d, &Dialog::response), RESPONSE_OK));
2935 int const ret = d.run();
2939 if (ret != RESPONSE_OK) {
2943 std::string str = entry.get_text();
2944 strip_whitespace_edges (str);
2946 rs.front()->region()->set_name (str);
2947 _regions->redisplay ();
2951 /** Start an audition of the first selected region */
2953 Editor::play_edit_range ()
2955 samplepos_t start, end;
2957 if (get_edit_op_range (start, end)) {
2958 _session->request_bounded_roll (start, end);
2963 Editor::play_selected_region ()
2965 samplepos_t start = max_samplepos;
2966 samplepos_t end = 0;
2968 RegionSelection rs = get_regions_from_selection_and_entered ();
2974 for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
2975 if ((*i)->region()->position() < start) {
2976 start = (*i)->region()->position();
2978 if ((*i)->region()->last_sample() + 1 > end) {
2979 end = (*i)->region()->last_sample() + 1;
2983 _session->request_bounded_roll (start, end);
2987 Editor::audition_playlist_region_standalone (boost::shared_ptr<Region> region)
2989 _session->audition_region (region);
2993 Editor::region_from_selection ()
2995 if (clicked_axisview == 0) {
2999 if (selection->time.empty()) {
3003 samplepos_t start = selection->time[clicked_selection].start;
3004 samplepos_t end = selection->time[clicked_selection].end;
3006 TrackViewList tracks = get_tracks_for_range_action ();
3008 samplepos_t selection_cnt = end - start + 1;
3010 for (TrackSelection::iterator i = tracks.begin(); i != tracks.end(); ++i) {
3011 boost::shared_ptr<Region> current;
3012 boost::shared_ptr<Playlist> pl;
3013 samplepos_t internal_start;
3016 if ((pl = (*i)->playlist()) == 0) {
3020 if ((current = pl->top_region_at (start)) == 0) {
3024 internal_start = start - current->position();
3025 RegionFactory::region_name (new_name, current->name(), true);
3029 plist.add (ARDOUR::Properties::start, current->start() + internal_start);
3030 plist.add (ARDOUR::Properties::length, selection_cnt);
3031 plist.add (ARDOUR::Properties::name, new_name);
3032 plist.add (ARDOUR::Properties::layer, 0);
3034 boost::shared_ptr<Region> region (RegionFactory::create (current, plist));
3039 Editor::create_region_from_selection (vector<boost::shared_ptr<Region> >& new_regions)
3041 if (selection->time.empty() || selection->tracks.empty()) {
3045 samplepos_t start, end;
3046 if (clicked_selection) {
3047 start = selection->time[clicked_selection].start;
3048 end = selection->time[clicked_selection].end;
3050 start = selection->time.start();
3051 end = selection->time.end_sample();
3054 TrackViewList ts = selection->tracks.filter_to_unique_playlists ();
3055 sort_track_selection (ts);
3057 for (TrackSelection::iterator i = ts.begin(); i != ts.end(); ++i) {
3058 boost::shared_ptr<Region> current;
3059 boost::shared_ptr<Playlist> playlist;
3060 samplepos_t internal_start;
3063 if ((playlist = (*i)->playlist()) == 0) {
3067 if ((current = playlist->top_region_at(start)) == 0) {
3071 internal_start = start - current->position();
3072 RegionFactory::region_name (new_name, current->name(), true);
3076 plist.add (ARDOUR::Properties::start, current->start() + internal_start);
3077 plist.add (ARDOUR::Properties::length, end - start + 1);
3078 plist.add (ARDOUR::Properties::name, new_name);
3080 new_regions.push_back (RegionFactory::create (current, plist));
3085 Editor::split_multichannel_region ()
3087 RegionSelection rs = get_regions_from_selection_and_entered ();
3093 vector< boost::shared_ptr<Region> > v;
3095 for (list<RegionView*>::iterator x = rs.begin(); x != rs.end(); ++x) {
3096 (*x)->region()->separate_by_channel (v);
3101 Editor::new_region_from_selection ()
3103 region_from_selection ();
3104 cancel_selection ();
3108 add_if_covered (RegionView* rv, const AudioRange* ar, RegionSelection* rs)
3110 switch (rv->region()->coverage (ar->start, ar->end - 1)) {
3111 // n.b. -1 because AudioRange::end is one past the end, but coverage expects inclusive ranges
3112 case Evoral::OverlapNone:
3120 * - selected tracks, or if there are none...
3121 * - tracks containing selected regions, or if there are none...
3126 Editor::get_tracks_for_range_action () const
3130 if (selection->tracks.empty()) {
3132 /* use tracks with selected regions */
3134 RegionSelection rs = selection->regions;
3136 for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
3137 TimeAxisView* tv = &(*i)->get_time_axis_view();
3139 if (!t.contains (tv)) {
3145 /* no regions and no tracks: use all tracks */
3151 t = selection->tracks;
3154 return t.filter_to_unique_playlists();
3158 Editor::separate_regions_between (const TimeSelection& ts)
3160 bool in_command = false;
3161 boost::shared_ptr<Playlist> playlist;
3162 RegionSelection new_selection;
3164 TrackViewList tmptracks = get_tracks_for_range_action ();
3165 sort_track_selection (tmptracks);
3167 for (TrackSelection::iterator i = tmptracks.begin(); i != tmptracks.end(); ++i) {
3169 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> ((*i));
3175 if (!rtv->is_track()) {
3179 /* no edits to destructive tracks */
3181 if (rtv->track()->destructive()) {
3185 if ((playlist = rtv->playlist()) != 0) {
3187 playlist->clear_changes ();
3189 /* XXX need to consider musical time selections here at some point */
3191 for (list<AudioRange>::const_iterator t = ts.begin(); t != ts.end(); ++t) {
3193 sigc::connection c = rtv->view()->RegionViewAdded.connect (
3194 sigc::mem_fun(*this, &Editor::collect_new_region_view));
3196 latest_regionviews.clear ();
3198 playlist->partition ((*t).start, (*t).end, false);
3202 if (!latest_regionviews.empty()) {
3204 rtv->view()->foreach_regionview (sigc::bind (
3205 sigc::ptr_fun (add_if_covered),
3206 &(*t), &new_selection));
3209 begin_reversible_command (_("separate"));
3213 /* pick up changes to existing regions */
3215 vector<Command*> cmds;
3216 playlist->rdiff (cmds);
3217 _session->add_commands (cmds);
3219 /* pick up changes to the playlist itself (adds/removes)
3222 _session->add_command(new StatefulDiffCommand (playlist));
3230 RangeSelectionAfterSplit rsas = Config->get_range_selection_after_split();
3232 //if our config preference says to clear the selection, clear the Range selection
3233 if (rsas == ClearSel) {
3234 selection->clear_time();
3235 //but leave track selection intact
3236 } else if (rsas == ForceSel) {
3237 //note: forcing the regions to be selected *might* force a tool-change to Object here
3238 selection->set(new_selection);
3241 commit_reversible_command ();
3245 struct PlaylistState {
3246 boost::shared_ptr<Playlist> playlist;
3250 /** Take tracks from get_tracks_for_range_action and cut any regions
3251 * on those tracks so that the tracks are empty over the time
3255 Editor::separate_region_from_selection ()
3257 /* preferentially use *all* ranges in the time selection if we're in range mode
3258 to allow discontiguous operation, since get_edit_op_range() currently
3259 returns a single range.
3262 if (!selection->time.empty()) {
3264 separate_regions_between (selection->time);
3271 if (get_edit_op_range (start, end)) {
3273 AudioRange ar (start, end, 1);
3277 separate_regions_between (ts);
3283 Editor::separate_region_from_punch ()
3285 Location* loc = _session->locations()->auto_punch_location();
3287 separate_regions_using_location (*loc);
3292 Editor::separate_region_from_loop ()
3294 Location* loc = _session->locations()->auto_loop_location();
3296 separate_regions_using_location (*loc);
3301 Editor::separate_regions_using_location (Location& loc)
3303 if (loc.is_mark()) {
3307 AudioRange ar (loc.start(), loc.end(), 1);
3312 separate_regions_between (ts);
3315 /** Separate regions under the selected region */
3317 Editor::separate_under_selected_regions ()
3319 vector<PlaylistState> playlists;
3323 rs = get_regions_from_selection_and_entered();
3325 if (!_session || rs.empty()) {
3329 begin_reversible_command (_("separate region under"));
3331 list<boost::shared_ptr<Region> > regions_to_remove;
3333 for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
3334 // we can't just remove the region(s) in this loop because
3335 // this removes them from the RegionSelection, and they thus
3336 // disappear from underneath the iterator, and the ++i above
3337 // SEGVs in a puzzling fashion.
3339 // so, first iterate over the regions to be removed from rs and
3340 // add them to the regions_to_remove list, and then
3341 // iterate over the list to actually remove them.
3343 regions_to_remove.push_back ((*i)->region());
3346 for (list<boost::shared_ptr<Region> >::iterator rl = regions_to_remove.begin(); rl != regions_to_remove.end(); ++rl) {
3348 boost::shared_ptr<Playlist> playlist = (*rl)->playlist();
3351 // is this check necessary?
3355 vector<PlaylistState>::iterator i;
3357 //only take state if this is a new playlist.
3358 for (i = playlists.begin(); i != playlists.end(); ++i) {
3359 if ((*i).playlist == playlist) {
3364 if (i == playlists.end()) {
3366 PlaylistState before;
3367 before.playlist = playlist;
3368 before.before = &playlist->get_state();
3369 playlist->clear_changes ();
3370 playlist->freeze ();
3371 playlists.push_back(before);
3374 //Partition on the region bounds
3375 playlist->partition ((*rl)->first_sample() - 1, (*rl)->last_sample() + 1, true);
3377 //Re-add region that was just removed due to the partition operation
3378 playlist->add_region ((*rl), (*rl)->first_sample());
3381 vector<PlaylistState>::iterator pl;
3383 for (pl = playlists.begin(); pl != playlists.end(); ++pl) {
3384 (*pl).playlist->thaw ();
3385 _session->add_command(new MementoCommand<Playlist>(*(*pl).playlist, (*pl).before, &(*pl).playlist->get_state()));
3388 commit_reversible_command ();
3392 Editor::crop_region_to_selection ()
3394 if (!selection->time.empty()) {
3396 begin_reversible_command (_("Crop Regions to Time Selection"));
3397 for (std::list<AudioRange>::iterator i = selection->time.begin(); i != selection->time.end(); ++i) {
3398 crop_region_to ((*i).start, (*i).end);
3400 commit_reversible_command();
3406 if (get_edit_op_range (start, end)) {
3407 begin_reversible_command (_("Crop Regions to Edit Range"));
3409 crop_region_to (start, end);
3411 commit_reversible_command();
3418 Editor::crop_region_to (samplepos_t start, samplepos_t end)
3420 vector<boost::shared_ptr<Playlist> > playlists;
3421 boost::shared_ptr<Playlist> playlist;
3424 if (selection->tracks.empty()) {
3425 ts = track_views.filter_to_unique_playlists();
3427 ts = selection->tracks.filter_to_unique_playlists ();
3430 sort_track_selection (ts);
3432 for (TrackSelection::iterator i = ts.begin(); i != ts.end(); ++i) {
3434 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> ((*i));
3440 boost::shared_ptr<Track> t = rtv->track();
3442 if (t != 0 && ! t->destructive()) {
3444 if ((playlist = rtv->playlist()) != 0) {
3445 playlists.push_back (playlist);
3450 if (playlists.empty()) {
3455 samplepos_t new_start;
3456 samplepos_t new_end;
3457 samplecnt_t new_length;
3459 for (vector<boost::shared_ptr<Playlist> >::iterator i = playlists.begin(); i != playlists.end(); ++i) {
3461 /* Only the top regions at start and end have to be cropped */
3462 boost::shared_ptr<Region> region_at_start = (*i)->top_region_at(start);
3463 boost::shared_ptr<Region> region_at_end = (*i)->top_region_at(end);
3465 vector<boost::shared_ptr<Region> > regions;
3467 if (region_at_start != 0) {
3468 regions.push_back (region_at_start);
3470 if (region_at_end != 0) {
3471 regions.push_back (region_at_end);
3474 /* now adjust lengths */
3475 for (vector<boost::shared_ptr<Region> >::iterator i = regions.begin(); i != regions.end(); ++i) {
3477 pos = (*i)->position();
3478 new_start = max (start, pos);
3479 if (max_samplepos - pos > (*i)->length()) {
3480 new_end = pos + (*i)->length() - 1;
3482 new_end = max_samplepos;
3484 new_end = min (end, new_end);
3485 new_length = new_end - new_start + 1;
3487 (*i)->clear_changes ();
3488 (*i)->trim_to (new_start, new_length);
3489 _session->add_command (new StatefulDiffCommand (*i));
3495 Editor::region_fill_track ()
3497 boost::shared_ptr<Playlist> playlist;
3498 RegionSelection regions = get_regions_from_selection_and_entered ();
3499 RegionSelection foo;
3501 samplepos_t const end = _session->current_end_sample ();
3503 if (regions.empty () || regions.end_sample () + 1 >= end) {
3507 samplepos_t const start_sample = regions.start ();
3508 samplepos_t const end_sample = regions.end_sample ();
3509 samplecnt_t const gap = end_sample - start_sample + 1;
3511 begin_reversible_command (Operations::region_fill);
3513 selection->clear_regions ();
3515 for (RegionSelection::iterator i = regions.begin(); i != regions.end(); ++i) {
3517 boost::shared_ptr<Region> r ((*i)->region());
3519 TimeAxisView& tv = (*i)->get_time_axis_view();
3520 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (&tv);
3521 latest_regionviews.clear ();
3522 sigc::connection c = rtv->view()->RegionViewAdded.connect (sigc::mem_fun(*this, &Editor::collect_new_region_view));
3524 samplepos_t const position = end_sample + (r->first_sample() - start_sample + 1);
3525 playlist = (*i)->region()->playlist();
3526 playlist->clear_changes ();
3527 playlist->duplicate_until (r, position, gap, end);
3528 _session->add_command(new StatefulDiffCommand (playlist));
3532 foo.insert (foo.end(), latest_regionviews.begin(), latest_regionviews.end());
3536 selection->set (foo);
3539 commit_reversible_command ();
3543 Editor::set_region_sync_position ()
3545 set_sync_point (get_preferred_edit_position (), get_regions_from_selection_and_edit_point ());
3549 Editor::set_sync_point (samplepos_t where, const RegionSelection& rs)
3551 bool in_command = false;
3553 for (RegionSelection::const_iterator r = rs.begin(); r != rs.end(); ++r) {
3555 if (!(*r)->region()->covers (where)) {
3559 boost::shared_ptr<Region> region ((*r)->region());
3562 begin_reversible_command (_("set sync point"));
3566 region->clear_changes ();
3567 region->set_sync_position (where);
3568 _session->add_command(new StatefulDiffCommand (region));
3572 commit_reversible_command ();
3576 /** Remove the sync positions of the selection */
3578 Editor::remove_region_sync ()
3580 RegionSelection rs = get_regions_from_selection_and_entered ();
3586 begin_reversible_command (_("remove region sync"));
3588 for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
3590 (*i)->region()->clear_changes ();
3591 (*i)->region()->clear_sync_position ();
3592 _session->add_command(new StatefulDiffCommand ((*i)->region()));
3595 commit_reversible_command ();
3599 Editor::naturalize_region ()
3601 RegionSelection rs = get_regions_from_selection_and_entered ();
3607 if (rs.size() > 1) {
3608 begin_reversible_command (_("move regions to original position"));
3610 begin_reversible_command (_("move region to original position"));
3613 for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
3614 (*i)->region()->clear_changes ();
3615 (*i)->region()->move_to_natural_position ();
3616 _session->add_command (new StatefulDiffCommand ((*i)->region()));
3619 commit_reversible_command ();
3623 Editor::align_regions (RegionPoint what)
3625 RegionSelection const rs = get_regions_from_selection_and_edit_point ();
3631 begin_reversible_command (_("align selection"));
3633 samplepos_t const position = get_preferred_edit_position ();
3635 for (RegionSelection::const_iterator i = rs.begin(); i != rs.end(); ++i) {
3636 align_region_internal ((*i)->region(), what, position);
3639 commit_reversible_command ();
3642 struct RegionSortByTime {
3643 bool operator() (const RegionView* a, const RegionView* b) {
3644 return a->region()->position() < b->region()->position();
3649 Editor::align_regions_relative (RegionPoint point)
3651 RegionSelection const rs = get_regions_from_selection_and_edit_point ();
3657 samplepos_t const position = get_preferred_edit_position ();
3659 samplepos_t distance = 0;
3660 samplepos_t pos = 0;
3663 list<RegionView*> sorted;
3664 rs.by_position (sorted);
3666 boost::shared_ptr<Region> r ((*sorted.begin())->region());
3671 if (position > r->position()) {
3672 distance = position - r->position();
3674 distance = r->position() - position;
3680 if (position > r->last_sample()) {
3681 distance = position - r->last_sample();
3682 pos = r->position() + distance;
3684 distance = r->last_sample() - position;
3685 pos = r->position() - distance;
3691 pos = r->adjust_to_sync (position);
3692 if (pos > r->position()) {
3693 distance = pos - r->position();
3695 distance = r->position() - pos;
3701 if (pos == r->position()) {
3705 begin_reversible_command (_("align selection (relative)"));
3707 /* move first one specially */
3709 r->clear_changes ();
3710 r->set_position (pos);
3711 _session->add_command(new StatefulDiffCommand (r));
3713 /* move rest by the same amount */
3717 for (list<RegionView*>::iterator i = sorted.begin(); i != sorted.end(); ++i) {
3719 boost::shared_ptr<Region> region ((*i)->region());
3721 region->clear_changes ();
3724 region->set_position (region->position() + distance);
3726 region->set_position (region->position() - distance);
3729 _session->add_command(new StatefulDiffCommand (region));
3733 commit_reversible_command ();
3737 Editor::align_region (boost::shared_ptr<Region> region, RegionPoint point, samplepos_t position)
3739 begin_reversible_command (_("align region"));
3740 align_region_internal (region, point, position);
3741 commit_reversible_command ();
3745 Editor::align_region_internal (boost::shared_ptr<Region> region, RegionPoint point, samplepos_t position)
3747 region->clear_changes ();
3751 region->set_position (region->adjust_to_sync (position));
3755 if (position > region->length()) {
3756 region->set_position (position - region->length());
3761 region->set_position (position);
3765 _session->add_command(new StatefulDiffCommand (region));
3769 Editor::trim_region_front ()
3775 Editor::trim_region_back ()
3777 trim_region (false);
3781 Editor::trim_region (bool front)
3783 samplepos_t where = get_preferred_edit_position();
3784 RegionSelection rs = get_regions_from_selection_and_edit_point ();
3790 begin_reversible_command (front ? _("trim front") : _("trim back"));
3792 for (list<RegionView*>::const_iterator i = rs.by_layer().begin(); i != rs.by_layer().end(); ++i) {
3793 if (!(*i)->region()->locked()) {
3795 (*i)->region()->clear_changes ();
3798 (*i)->region()->trim_front (where);
3800 (*i)->region()->trim_end (where);
3803 _session->add_command (new StatefulDiffCommand ((*i)->region()));
3807 commit_reversible_command ();
3810 /** Trim the end of the selected regions to the position of the edit cursor */
3812 Editor::trim_region_to_loop ()
3814 Location* loc = _session->locations()->auto_loop_location();
3818 trim_region_to_location (*loc, _("trim to loop"));
3822 Editor::trim_region_to_punch ()
3824 Location* loc = _session->locations()->auto_punch_location();
3828 trim_region_to_location (*loc, _("trim to punch"));
3832 Editor::trim_region_to_location (const Location& loc, const char* str)
3834 RegionSelection rs = get_regions_from_selection_and_entered ();
3835 bool in_command = false;
3837 for (RegionSelection::iterator x = rs.begin(); x != rs.end(); ++x) {
3838 RegionView* rv = (*x);
3840 /* require region to span proposed trim */
3841 switch (rv->region()->coverage (loc.start(), loc.end())) {
3842 case Evoral::OverlapInternal:
3848 RouteTimeAxisView* tav = dynamic_cast<RouteTimeAxisView*> (&rv->get_time_axis_view());
3856 start = loc.start();
3859 rv->region()->clear_changes ();
3860 rv->region()->trim_to (start, (end - start));
3863 begin_reversible_command (str);
3866 _session->add_command(new StatefulDiffCommand (rv->region()));
3870 commit_reversible_command ();
3875 Editor::trim_region_to_previous_region_end ()
3877 return trim_to_region(false);
3881 Editor::trim_region_to_next_region_start ()
3883 return trim_to_region(true);
3887 Editor::trim_to_region(bool forward)
3889 RegionSelection rs = get_regions_from_selection_and_entered ();
3890 bool in_command = false;
3892 boost::shared_ptr<Region> next_region;
3894 for (RegionSelection::iterator x = rs.begin(); x != rs.end(); ++x) {
3896 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (*x);
3902 AudioTimeAxisView* atav = dynamic_cast<AudioTimeAxisView*> (&arv->get_time_axis_view());
3908 boost::shared_ptr<Region> region = arv->region();
3909 boost::shared_ptr<Playlist> playlist (region->playlist());
3911 region->clear_changes ();
3915 next_region = playlist->find_next_region (region->first_sample(), Start, 1);
3921 region->trim_end (next_region->first_sample() - 1);
3922 arv->region_changed (PropertyChange (ARDOUR::Properties::length));
3926 next_region = playlist->find_next_region (region->first_sample(), Start, 0);
3932 region->trim_front (next_region->last_sample() + 1);
3933 arv->region_changed (ARDOUR::bounds_change);
3937 begin_reversible_command (_("trim to region"));
3940 _session->add_command(new StatefulDiffCommand (region));
3944 commit_reversible_command ();
3949 Editor::unfreeze_route ()
3951 if (clicked_routeview == 0 || !clicked_routeview->is_track()) {
3955 clicked_routeview->track()->unfreeze ();
3959 Editor::_freeze_thread (void* arg)
3961 return static_cast<Editor*>(arg)->freeze_thread ();
3965 Editor::freeze_thread ()
3967 /* create event pool because we may need to talk to the session */
3968 SessionEvent::create_per_thread_pool ("freeze events", 64);
3969 /* create per-thread buffers for process() tree to use */
3970 clicked_routeview->audio_track()->freeze_me (*current_interthread_info);
3971 current_interthread_info->done = true;
3976 Editor::freeze_route ()
3982 /* stop transport before we start. this is important */
3984 _session->request_transport_speed (0.0);
3986 /* wait for just a little while, because the above call is asynchronous */
3988 Glib::usleep (250000);
3990 if (clicked_routeview == 0 || !clicked_routeview->is_audio_track()) {
3994 if (!clicked_routeview->track()->bounceable (clicked_routeview->track()->main_outs(), true)) {
3995 ArdourMessageDialog d (
3996 _("This track/bus cannot be frozen because the signal adds or loses channels before reaching the outputs.\n"
3997 "This is typically caused by plugins that generate stereo output from mono input or vice versa.")
3999 d.set_title (_("Cannot freeze"));
4004 if (clicked_routeview->track()->has_external_redirects()) {
4005 ArdourMessageDialog d (string_compose (_("<b>%1</b>\n\nThis track has at least one send/insert/return as part of its signal flow.\n\n"
4006 "Freezing will only process the signal as far as the first send/insert/return."),
4007 clicked_routeview->track()->name()), true, MESSAGE_INFO, BUTTONS_NONE, true);
4009 d.add_button (_("Freeze anyway"), Gtk::RESPONSE_OK);
4010 d.add_button (_("Don't freeze"), Gtk::RESPONSE_CANCEL);
4011 d.set_title (_("Freeze Limits"));
4013 int response = d.run ();
4016 case Gtk::RESPONSE_CANCEL:
4023 InterThreadInfo itt;
4024 current_interthread_info = &itt;
4026 InterthreadProgressWindow ipw (current_interthread_info, _("Freeze"), _("Cancel Freeze"));
4028 pthread_create_and_store (X_("freezer"), &itt.thread, _freeze_thread, this);
4030 CursorContext::Handle cursor_ctx = CursorContext::create(*this, _cursors->wait);
4032 while (!itt.done && !itt.cancel) {
4033 gtk_main_iteration ();
4036 pthread_join (itt.thread, 0);
4037 current_interthread_info = 0;
4041 Editor::bounce_range_selection (bool replace, bool enable_processing)
4043 if (selection->time.empty()) {
4047 TrackSelection views = selection->tracks;
4049 for (TrackViewList::iterator i = views.begin(); i != views.end(); ++i) {
4051 if (enable_processing) {
4053 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (*i);
4055 if (rtv && rtv->track() && replace && enable_processing && !rtv->track()->bounceable (rtv->track()->main_outs(), false)) {
4056 ArdourMessageDialog d (
4057 _("You can't perform this operation because the processing of the signal "
4058 "will cause one or more of the tracks to end up with a region with more channels than this track has inputs.\n\n"
4059 "You can do this without processing, which is a different operation.")
4061 d.set_title (_("Cannot bounce"));
4068 samplepos_t start = selection->time[clicked_selection].start;
4069 samplepos_t end = selection->time[clicked_selection].end;
4070 samplepos_t cnt = end - start + 1;
4071 bool in_command = false;
4073 for (TrackViewList::iterator i = views.begin(); i != views.end(); ++i) {
4075 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (*i);
4081 boost::shared_ptr<Playlist> playlist;
4083 if ((playlist = rtv->playlist()) == 0) {
4087 InterThreadInfo itt;
4089 playlist->clear_changes ();
4090 playlist->clear_owned_changes ();
4092 boost::shared_ptr<Region> r;
4094 if (enable_processing) {
4095 r = rtv->track()->bounce_range (start, start+cnt, itt, rtv->track()->main_outs(), false);
4097 r = rtv->track()->bounce_range (start, start+cnt, itt, boost::shared_ptr<Processor>(), false);
4105 list<AudioRange> ranges;
4106 ranges.push_back (AudioRange (start, start+cnt, 0));
4107 playlist->cut (ranges); // discard result
4108 playlist->add_region (r, start);
4112 begin_reversible_command (_("bounce range"));
4115 vector<Command*> cmds;
4116 playlist->rdiff (cmds);
4117 _session->add_commands (cmds);
4119 _session->add_command (new StatefulDiffCommand (playlist));
4123 commit_reversible_command ();
4127 /** Delete selected regions, automation points or a time range */
4131 //special case: if the user is pointing in the editor/mixer strip, they may be trying to delete a plugin.
4132 //we need this because the editor-mixer strip is in the editor window, so it doesn't get the bindings from the mix window
4133 bool deleted = false;
4134 if (current_mixer_strip && current_mixer_strip == MixerStrip::entered_mixer_strip())
4135 deleted = current_mixer_strip->delete_processors ();
4141 /** Cut selected regions, automation points or a time range */
4148 /** Copy selected regions, automation points or a time range */
4156 /** @return true if a Cut, Copy or Clear is possible */
4158 Editor::can_cut_copy () const
4160 if (!selection->time.empty() || !selection->regions.empty() || !selection->points.empty())
4167 /** Cut, copy or clear selected regions, automation points or a time range.
4168 * @param op Operation (Delete, Cut, Copy or Clear)
4171 Editor::cut_copy (CutCopyOp op)
4173 /* only cancel selection if cut/copy is successful.*/
4179 opname = _("delete");
4188 opname = _("clear");
4192 /* if we're deleting something, and the mouse is still pressed,
4193 the thing we started a drag for will be gone when we release
4194 the mouse button(s). avoid this. see part 2 at the end of
4198 if (op == Delete || op == Cut || op == Clear) {
4199 if (_drags->active ()) {
4204 if (op != Delete) { //"Delete" doesn't change copy/paste buf
4205 cut_buffer->clear ();
4208 if (entered_marker) {
4210 /* cut/delete op while pointing at a marker */
4213 Location* loc = find_location_from_marker (entered_marker, ignored);
4215 if (_session && loc) {
4216 entered_marker = NULL;
4217 Glib::signal_idle().connect (sigc::bind (sigc::mem_fun(*this, &Editor::really_remove_marker), loc));
4224 switch (mouse_mode) {
4227 begin_reversible_command (opname + ' ' + X_("MIDI"));
4229 commit_reversible_command ();
4235 bool did_edit = false;
4237 if (!selection->regions.empty() || !selection->points.empty()) {
4238 begin_reversible_command (opname + ' ' + _("objects"));
4241 if (!selection->regions.empty()) {
4242 cut_copy_regions (op, selection->regions);
4244 if (op == Cut || op == Delete) {
4245 selection->clear_regions ();
4249 if (!selection->points.empty()) {
4250 cut_copy_points (op);
4252 if (op == Cut || op == Delete) {
4253 selection->clear_points ();
4256 } else if (selection->time.empty()) {
4257 samplepos_t start, end;
4258 /* no time selection, see if we can get an edit range
4261 if (get_edit_op_range (start, end)) {
4262 selection->set (start, end);
4264 } else if (!selection->time.empty()) {
4265 begin_reversible_command (opname + ' ' + _("range"));
4268 cut_copy_ranges (op);
4270 if (op == Cut || op == Delete) {
4271 selection->clear_time ();
4276 /* reset repeated paste state */
4278 last_paste_pos = -1;
4279 commit_reversible_command ();
4282 if (op == Delete || op == Cut || op == Clear) {
4288 struct AutomationRecord {
4289 AutomationRecord () : state (0) , line(NULL) {}
4290 AutomationRecord (XMLNode* s, const AutomationLine* l) : state (s) , line (l) {}
4292 XMLNode* state; ///< state before any operation
4293 const AutomationLine* line; ///< line this came from
4294 boost::shared_ptr<Evoral::ControlList> copy; ///< copied events for the cut buffer
4297 struct PointsSelectionPositionSorter {
4298 bool operator() (ControlPoint* a, ControlPoint* b) {
4299 return (*(a->model()))->when < (*(b->model()))->when;
4303 /** Cut, copy or clear selected automation points.
4304 * @param op Operation (Cut, Copy or Clear)
4307 Editor::cut_copy_points (Editing::CutCopyOp op, Temporal::Beats earliest, bool midi)
4309 if (selection->points.empty ()) {
4313 /* XXX: not ideal, as there may be more than one track involved in the point selection */
4314 _last_cut_copy_source_track = &selection->points.front()->line().trackview;
4316 /* Keep a record of the AutomationLists that we end up using in this operation */
4317 typedef std::map<boost::shared_ptr<AutomationList>, AutomationRecord> Lists;
4320 /* user could select points in any order */
4321 selection->points.sort(PointsSelectionPositionSorter ());
4323 /* Go through all selected points, making an AutomationRecord for each distinct AutomationList */
4324 for (PointSelection::iterator sel_point = selection->points.begin(); sel_point != selection->points.end(); ++sel_point) {
4325 const AutomationLine& line = (*sel_point)->line();
4326 const boost::shared_ptr<AutomationList> al = line.the_list();
4327 if (lists.find (al) == lists.end ()) {
4328 /* We haven't seen this list yet, so make a record for it. This includes
4329 taking a copy of its current state, in case this is needed for undo later.
4331 lists[al] = AutomationRecord (&al->get_state (), &line);
4335 if (op == Cut || op == Copy) {
4336 /* This operation will involve putting things in the cut buffer, so create an empty
4337 ControlList for each of our source lists to put the cut buffer data in.
4339 for (Lists::iterator i = lists.begin(); i != lists.end(); ++i) {
4340 i->second.copy = i->first->create (i->first->parameter (), i->first->descriptor());
4343 /* Add all selected points to the relevant copy ControlLists */
4344 MusicSample start (std::numeric_limits<samplepos_t>::max(), 0);
4345 for (PointSelection::iterator sel_point = selection->points.begin(); sel_point != selection->points.end(); ++sel_point) {
4346 boost::shared_ptr<AutomationList> al = (*sel_point)->line().the_list();
4347 AutomationList::const_iterator ctrl_evt = (*sel_point)->model ();
4349 lists[al].copy->fast_simple_add ((*ctrl_evt)->when, (*ctrl_evt)->value);
4351 /* Update earliest MIDI start time in beats */
4352 earliest = std::min(earliest, Temporal::Beats((*ctrl_evt)->when));
4354 /* Update earliest session start time in samples */
4355 start.sample = std::min(start.sample, (*sel_point)->line().session_position(ctrl_evt));
4359 /* Snap start time backwards, so copy/paste is snap aligned. */
4361 if (earliest == std::numeric_limits<Temporal::Beats>::max()) {
4362 earliest = Temporal::Beats(); // Weird... don't offset
4364 earliest.round_down_to_beat();
4366 if (start.sample == std::numeric_limits<double>::max()) {
4367 start.sample = 0; // Weird... don't offset
4369 snap_to(start, RoundDownMaybe);
4372 const double line_offset = midi ? earliest.to_double() : start.sample;
4373 for (Lists::iterator i = lists.begin(); i != lists.end(); ++i) {
4374 /* Correct this copy list so that it is relative to the earliest
4375 start time, so relative ordering between points is preserved
4376 when copying from several lists and the paste starts at the
4377 earliest copied piece of data. */
4378 boost::shared_ptr<Evoral::ControlList> &al_cpy = i->second.copy;
4379 for (AutomationList::iterator ctrl_evt = al_cpy->begin(); ctrl_evt != al_cpy->end(); ++ctrl_evt) {
4380 (*ctrl_evt)->when -= line_offset;
4383 /* And add it to the cut buffer */
4384 cut_buffer->add (al_cpy);
4388 if (op == Delete || op == Cut) {
4389 /* This operation needs to remove things from the main AutomationList, so do that now */
4391 for (Lists::iterator i = lists.begin(); i != lists.end(); ++i) {
4392 i->first->freeze ();
4395 /* Remove each selected point from its AutomationList */
4396 for (PointSelection::iterator sel_point = selection->points.begin(); sel_point != selection->points.end(); ++sel_point) {
4397 AutomationLine& line = (*sel_point)->line ();
4398 boost::shared_ptr<AutomationList> al = line.the_list();
4402 if (dynamic_cast<AudioRegionGainLine*> (&line)) {
4403 /* removing of first and last gain point in region gain lines is prohibited*/
4404 if (line.is_last_point (*(*sel_point)) || line.is_first_point (*(*sel_point))) {
4410 al->erase ((*sel_point)->model ());
4414 /* Thaw the lists and add undo records for them */
4415 for (Lists::iterator i = lists.begin(); i != lists.end(); ++i) {
4416 boost::shared_ptr<AutomationList> al = i->first;
4418 _session->add_command (new MementoCommand<AutomationList> (*al.get(), i->second.state, &(al->get_state ())));
4423 /** Cut, copy or clear selected automation points.
4424 * @param op Operation (Cut, Copy or Clear)
4427 Editor::cut_copy_midi (CutCopyOp op)
4429 Temporal::Beats earliest = std::numeric_limits<Temporal::Beats>::max();
4430 for (MidiRegionSelection::iterator i = selection->midi_regions.begin(); i != selection->midi_regions.end(); ++i) {
4431 MidiRegionView* mrv = dynamic_cast<MidiRegionView*>(*i);
4433 if (!mrv->selection().empty()) {
4434 earliest = std::min(earliest, (*mrv->selection().begin())->note()->time());
4436 mrv->cut_copy_clear (op);
4438 /* XXX: not ideal, as there may be more than one track involved in the selection */
4439 _last_cut_copy_source_track = &mrv->get_time_axis_view();
4443 if (!selection->points.empty()) {
4444 cut_copy_points (op, earliest, true);
4445 if (op == Cut || op == Delete) {
4446 selection->clear_points ();
4451 struct lt_playlist {
4452 bool operator () (const PlaylistState& a, const PlaylistState& b) {
4453 return a.playlist < b.playlist;
4457 struct PlaylistMapping {
4459 boost::shared_ptr<Playlist> pl;
4461 PlaylistMapping (TimeAxisView* tvp) : tv (tvp) {}
4464 /** Remove `clicked_regionview' */
4466 Editor::remove_clicked_region ()
4468 if (clicked_routeview == 0 || clicked_regionview == 0) {
4472 begin_reversible_command (_("remove region"));
4474 boost::shared_ptr<Playlist> playlist = clicked_routeview->playlist();
4475 boost::shared_ptr<Region> region = clicked_regionview->region();
4477 playlist->clear_changes ();
4478 playlist->clear_owned_changes ();
4479 playlist->remove_region (region);
4481 if (Config->get_edit_mode() == Ripple) {
4482 playlist->ripple (region->position(), - region->length(), boost::shared_ptr<Region>());
4485 /* We might have removed regions, which alters other regions' layering_index,
4486 so we need to do a recursive diff here.
4488 vector<Command*> cmds;
4489 playlist->rdiff (cmds);
4490 _session->add_commands (cmds);
4492 _session->add_command(new StatefulDiffCommand (playlist));
4493 commit_reversible_command ();
4498 Editor::recover_regions (ARDOUR::RegionList regions)
4500 #ifdef RECOVER_REGIONS_IS_WORKING
4501 begin_reversible_command (_("recover regions"));
4503 for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
4504 boost::shared_ptr<ARDOUR::Source> source = (*i)->source();
4506 RouteList routes = _session->get_routelist();
4507 for (RouteList::iterator it = routes.begin(); it != routes.end(); ++it) {
4508 boost::shared_ptr<ARDOUR::Track> track = boost::dynamic_pointer_cast<Track>(*it);
4511 if (source->captured_for() == track->) {
4512 //_session->add_command(new StatefulDiffCommand (playlist));
4518 commit_reversible_command ();
4523 /** Remove the selected regions */
4525 Editor::remove_selected_regions ()
4527 RegionSelection rs = get_regions_from_selection_and_entered ();
4529 if (!_session || rs.empty()) {
4533 list<boost::shared_ptr<Region> > regions_to_remove;
4535 for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
4536 // we can't just remove the region(s) in this loop because
4537 // this removes them from the RegionSelection, and they thus
4538 // disappear from underneath the iterator, and the ++i above
4539 // SEGVs in a puzzling fashion.
4541 // so, first iterate over the regions to be removed from rs and
4542 // add them to the regions_to_remove list, and then
4543 // iterate over the list to actually remove them.
4545 regions_to_remove.push_back ((*i)->region());
4548 vector<boost::shared_ptr<Playlist> > playlists;
4550 for (list<boost::shared_ptr<Region> >::iterator rl = regions_to_remove.begin(); rl != regions_to_remove.end(); ++rl) {
4552 boost::shared_ptr<Playlist> playlist = (*rl)->playlist();
4555 // is this check necessary?
4559 /* get_regions_from_selection_and_entered() guarantees that
4560 the playlists involved are unique, so there is no need
4564 playlists.push_back (playlist);
4566 playlist->clear_changes ();
4567 playlist->clear_owned_changes ();
4568 playlist->freeze ();
4569 playlist->remove_region (*rl);
4570 if (Config->get_edit_mode() == Ripple)
4571 playlist->ripple ((*rl)->position(), -(*rl)->length(), boost::shared_ptr<Region>());
4575 vector<boost::shared_ptr<Playlist> >::iterator pl;
4576 bool in_command = false;
4578 for (pl = playlists.begin(); pl != playlists.end(); ++pl) {
4581 /* We might have removed regions, which alters other regions' layering_index,
4582 so we need to do a recursive diff here.
4586 begin_reversible_command (_("remove region"));
4589 vector<Command*> cmds;
4590 (*pl)->rdiff (cmds);
4591 _session->add_commands (cmds);
4593 _session->add_command(new StatefulDiffCommand (*pl));
4597 commit_reversible_command ();
4601 /** Cut, copy or clear selected regions.
4602 * @param op Operation (Cut, Copy or Clear)
4605 Editor::cut_copy_regions (CutCopyOp op, RegionSelection& rs)
4607 /* we can't use a std::map here because the ordering is important, and we can't trivially sort
4608 a map when we want ordered access to both elements. i think.
4611 vector<PlaylistMapping> pmap;
4613 samplepos_t first_position = max_samplepos;
4615 typedef set<boost::shared_ptr<Playlist> > FreezeList;
4616 FreezeList freezelist;
4618 /* get ordering correct before we cut/copy */
4620 rs.sort_by_position_and_track ();
4622 for (RegionSelection::iterator x = rs.begin(); x != rs.end(); ++x) {
4624 first_position = min ((samplepos_t) (*x)->region()->position(), first_position);
4626 if (op == Cut || op == Clear || op == Delete) {
4627 boost::shared_ptr<Playlist> pl = (*x)->region()->playlist();
4630 FreezeList::iterator fl;
4632 // only take state if this is a new playlist.
4633 for (fl = freezelist.begin(); fl != freezelist.end(); ++fl) {
4639 if (fl == freezelist.end()) {
4640 pl->clear_changes();
4641 pl->clear_owned_changes ();
4643 freezelist.insert (pl);
4648 TimeAxisView* tv = &(*x)->get_time_axis_view();
4649 vector<PlaylistMapping>::iterator z;
4651 for (z = pmap.begin(); z != pmap.end(); ++z) {
4652 if ((*z).tv == tv) {
4657 if (z == pmap.end()) {
4658 pmap.push_back (PlaylistMapping (tv));
4662 for (RegionSelection::iterator x = rs.begin(); x != rs.end(); ) {
4664 boost::shared_ptr<Playlist> pl = (*x)->region()->playlist();
4667 /* region not yet associated with a playlist (e.g. unfinished
4674 TimeAxisView& tv = (*x)->get_time_axis_view();
4675 boost::shared_ptr<Playlist> npl;
4676 RegionSelection::iterator tmp;
4683 vector<PlaylistMapping>::iterator z;
4685 for (z = pmap.begin(); z != pmap.end(); ++z) {
4686 if ((*z).tv == &tv) {
4691 assert (z != pmap.end());
4694 npl = PlaylistFactory::create (pl->data_type(), *_session, "cutlist", true);
4702 boost::shared_ptr<Region> r = (*x)->region();
4703 boost::shared_ptr<Region> _xx;
4709 pl->remove_region (r);
4710 if (Config->get_edit_mode() == Ripple)
4711 pl->ripple (r->position(), -r->length(), boost::shared_ptr<Region>());
4715 _xx = RegionFactory::create (r);
4716 npl->add_region (_xx, r->position() - first_position);
4717 pl->remove_region (r);
4718 if (Config->get_edit_mode() == Ripple)
4719 pl->ripple (r->position(), -r->length(), boost::shared_ptr<Region>());
4723 /* copy region before adding, so we're not putting same object into two different playlists */
4724 npl->add_region (RegionFactory::create (r), r->position() - first_position);
4728 pl->remove_region (r);
4729 if (Config->get_edit_mode() == Ripple)
4730 pl->ripple (r->position(), -r->length(), boost::shared_ptr<Region>());
4739 list<boost::shared_ptr<Playlist> > foo;
4741 /* the pmap is in the same order as the tracks in which selected regions occurred */
4743 for (vector<PlaylistMapping>::iterator i = pmap.begin(); i != pmap.end(); ++i) {
4746 foo.push_back ((*i).pl);
4751 cut_buffer->set (foo);
4755 _last_cut_copy_source_track = 0;
4757 _last_cut_copy_source_track = pmap.front().tv;
4761 for (FreezeList::iterator pl = freezelist.begin(); pl != freezelist.end(); ++pl) {
4764 /* We might have removed regions, which alters other regions' layering_index,
4765 so we need to do a recursive diff here.
4767 vector<Command*> cmds;
4768 (*pl)->rdiff (cmds);
4769 _session->add_commands (cmds);
4771 _session->add_command (new StatefulDiffCommand (*pl));
4776 Editor::cut_copy_ranges (CutCopyOp op)
4778 TrackViewList ts = selection->tracks.filter_to_unique_playlists ();
4780 /* Sort the track selection now, so that it if is used, the playlists
4781 selected by the calls below to cut_copy_clear are in the order that
4782 their tracks appear in the editor. This makes things like paste
4783 of ranges work properly.
4786 sort_track_selection (ts);
4789 if (!entered_track) {
4792 ts.push_back (entered_track);
4795 for (TrackViewList::iterator i = ts.begin(); i != ts.end(); ++i) {
4796 (*i)->cut_copy_clear (*selection, op);
4801 Editor::paste (float times, bool from_context)
4803 DEBUG_TRACE (DEBUG::CutNPaste, "paste to preferred edit pos\n");
4804 MusicSample where (get_preferred_edit_position (EDIT_IGNORE_NONE, from_context), 0);
4805 paste_internal (where.sample, times, 0);
4809 Editor::mouse_paste ()
4811 MusicSample where (0, 0);
4813 if (!mouse_sample (where.sample, ignored)) {
4818 paste_internal (where.sample, 1, where.division);
4822 Editor::paste_internal (samplepos_t position, float times, const int32_t sub_num)
4824 DEBUG_TRACE (DEBUG::CutNPaste, string_compose ("apparent paste position is %1\n", position));
4826 if (cut_buffer->empty(internal_editing())) {
4830 if (position == max_samplepos) {
4831 position = get_preferred_edit_position();
4832 DEBUG_TRACE (DEBUG::CutNPaste, string_compose ("preferred edit position is %1\n", position));
4835 if (position != last_paste_pos) {
4836 /* paste in new location, reset repeated paste state */
4838 last_paste_pos = position;
4841 /* get everything in the correct order */
4844 if (!selection->tracks.empty()) {
4845 /* If there is a track selection, paste into exactly those tracks and
4846 * only those tracks. This allows the user to be explicit and override
4847 * the below "do the reasonable thing" logic. */
4848 ts = selection->tracks.filter_to_unique_playlists ();
4849 sort_track_selection (ts);
4851 /* Figure out which track to base the paste at. */
4852 TimeAxisView* base_track = NULL;
4853 if (_edit_point == Editing::EditAtMouse && entered_track) {
4854 /* With the mouse edit point, paste onto the track under the mouse. */
4855 base_track = entered_track;
4856 } else if (_edit_point == Editing::EditAtMouse && entered_regionview) {
4857 /* With the mouse edit point, paste onto the track of the region under the mouse. */
4858 base_track = &entered_regionview->get_time_axis_view();
4859 } else if (_last_cut_copy_source_track) {
4860 /* Paste to the track that the cut/copy came from (see mantis #333). */
4861 base_track = _last_cut_copy_source_track;
4863 /* This is "impossible" since we've copied... well, do nothing. */
4867 /* Walk up to parent if necessary, so base track is a route. */
4868 while (base_track->get_parent()) {
4869 base_track = base_track->get_parent();
4872 /* Add base track and all tracks below it. The paste logic will select
4873 the appropriate object types from the cut buffer in relative order. */
4874 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
4875 if ((*i)->order() >= base_track->order()) {
4880 /* Sort tracks so the nth track of type T will pick the nth object of type T. */
4881 sort_track_selection (ts);
4883 /* Add automation children of each track in order, for pasting several lines. */
4884 for (TrackViewList::iterator i = ts.begin(); i != ts.end();) {
4885 /* Add any automation children for pasting several lines */
4886 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*>(*i++);
4891 typedef RouteTimeAxisView::AutomationTracks ATracks;
4892 const ATracks& atracks = rtv->automation_tracks();
4893 for (ATracks::const_iterator a = atracks.begin(); a != atracks.end(); ++a) {
4894 i = ts.insert(i, a->second.get());
4899 /* We now have a list of trackviews starting at base_track, including
4900 automation children, in the order shown in the editor, e.g. R1,
4901 R1.A1, R1.A2, R2, R2.A1, ... */
4904 begin_reversible_command (Operations::paste);
4906 if (ts.size() == 1 && cut_buffer->lines.size() == 1 &&
4907 dynamic_cast<AutomationTimeAxisView*>(ts.front())) {
4908 /* Only one line copied, and one automation track selected. Do a
4909 "greedy" paste from one automation type to another. */
4911 PasteContext ctx(paste_count, times, ItemCounts(), true);
4912 ts.front()->paste (position, *cut_buffer, ctx, sub_num);
4916 /* Paste into tracks */
4918 PasteContext ctx(paste_count, times, ItemCounts(), false);
4919 for (TrackViewList::iterator i = ts.begin(); i != ts.end(); ++i) {
4920 (*i)->paste (position, *cut_buffer, ctx, sub_num);
4926 commit_reversible_command ();
4930 Editor::duplicate_regions (float times)
4932 RegionSelection rs (get_regions_from_selection_and_entered());
4933 duplicate_some_regions (rs, times);
4937 Editor::duplicate_some_regions (RegionSelection& regions, float times)
4939 if (regions.empty ()) {
4943 boost::shared_ptr<Playlist> playlist;
4944 std::set<boost::shared_ptr<Playlist> > playlists; // list of unique playlists affected by duplication
4945 RegionSelection sel = regions; // clear (below) may clear the argument list if its the current region selection
4946 RegionSelection foo;
4948 samplepos_t const start_sample = regions.start ();
4949 samplepos_t const end_sample = regions.end_sample ();
4950 samplecnt_t const span = end_sample - start_sample + 1;
4952 begin_reversible_command (Operations::duplicate_region);
4954 selection->clear_regions ();
4956 /* ripple first so that we don't move the duplicates that will be added */
4958 if (Config->get_edit_mode() == Ripple) {
4960 /* convert RegionSelection into RegionList so that we can pass it to ripple and exclude the regions we will duplicate */
4964 for (RegionSelection::iterator i = sel.begin(); i != sel.end(); ++i) {
4965 exclude.push_back ((*i)->region());
4966 playlist = (*i)->region()->playlist();
4967 if (playlists.insert (playlist).second) {
4968 /* successfully inserted into set, so it's the first time we've seen this playlist */
4969 playlist->clear_changes ();
4973 for (set<boost::shared_ptr<Playlist> >::iterator p = playlists.begin(); p != playlists.end(); ++p) {
4974 (*p)->ripple (start_sample, span * times, &exclude);
4978 for (RegionSelection::iterator i = sel.begin(); i != sel.end(); ++i) {
4980 boost::shared_ptr<Region> r ((*i)->region());
4982 TimeAxisView& tv = (*i)->get_time_axis_view();
4983 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (&tv);
4984 latest_regionviews.clear ();
4985 sigc::connection c = rtv->view()->RegionViewAdded.connect (sigc::mem_fun(*this, &Editor::collect_new_region_view));
4987 samplepos_t const position = end_sample + (r->first_sample() - start_sample + 1);
4988 playlist = (*i)->region()->playlist();
4990 if (Config->get_edit_mode() != Ripple) {
4991 if (playlists.insert (playlist).second) {
4992 playlist->clear_changes ();
4996 playlist->duplicate (r, position, span, times);
5000 foo.insert (foo.end(), latest_regionviews.begin(), latest_regionviews.end());
5003 for (set<boost::shared_ptr<Playlist> >::iterator p = playlists.begin(); p != playlists.end(); ++p) {
5004 _session->add_command (new StatefulDiffCommand (*p));
5005 vector<Command*> cmds;
5007 _session->add_commands (cmds);
5011 selection->set (foo);
5014 commit_reversible_command ();
5018 Editor::duplicate_selection (float times)
5020 if (selection->time.empty() || selection->tracks.empty()) {
5024 boost::shared_ptr<Playlist> playlist;
5026 TrackViewList ts = selection->tracks.filter_to_unique_playlists ();
5028 bool in_command = false;
5030 for (TrackViewList::iterator i = ts.begin(); i != ts.end(); ++i) {
5031 if ((playlist = (*i)->playlist()) == 0) {
5034 playlist->clear_changes ();
5036 if (clicked_selection) {
5037 playlist->duplicate_range (selection->time[clicked_selection], times);
5039 playlist->duplicate_ranges (selection->time, times);
5043 begin_reversible_command (_("duplicate range selection"));
5046 _session->add_command (new StatefulDiffCommand (playlist));
5051 if (times == 1.0f) {
5052 // now "move" range selection to after the current range selection
5053 samplecnt_t distance = 0;
5055 if (clicked_selection) {
5057 selection->time[clicked_selection].end - selection->time[clicked_selection].start;
5059 distance = selection->time.end_sample () - selection->time.start ();
5062 selection->move_time (distance);
5064 commit_reversible_command ();
5068 /** Reset all selected points to the relevant default value */
5070 Editor::reset_point_selection ()
5072 for (PointSelection::iterator i = selection->points.begin(); i != selection->points.end(); ++i) {
5073 ARDOUR::AutomationList::iterator j = (*i)->model ();
5074 (*j)->value = (*i)->line().the_list()->descriptor ().normal;
5079 Editor::center_playhead ()
5081 float const page = _visible_canvas_width * samples_per_pixel;
5082 center_screen_internal (playhead_cursor->current_sample (), page);
5086 Editor::center_edit_point ()
5088 float const page = _visible_canvas_width * samples_per_pixel;
5089 center_screen_internal (get_preferred_edit_position(), page);
5092 /** Caller must begin and commit a reversible command */
5094 Editor::clear_playlist (boost::shared_ptr<Playlist> playlist)
5096 playlist->clear_changes ();
5098 _session->add_command (new StatefulDiffCommand (playlist));
5102 Editor::nudge_track (bool use_edit, bool forwards)
5104 boost::shared_ptr<Playlist> playlist;
5105 samplepos_t distance;
5106 samplepos_t next_distance;
5110 start = get_preferred_edit_position();
5115 if ((distance = get_nudge_distance (start, next_distance)) == 0) {
5119 if (selection->tracks.empty()) {
5123 TrackViewList ts = selection->tracks.filter_to_unique_playlists ();
5124 bool in_command = false;
5126 for (TrackViewList::iterator i = ts.begin(); i != ts.end(); ++i) {
5128 if ((playlist = (*i)->playlist()) == 0) {
5132 playlist->clear_changes ();
5133 playlist->clear_owned_changes ();
5135 playlist->nudge_after (start, distance, forwards);
5138 begin_reversible_command (_("nudge track"));
5141 vector<Command*> cmds;
5143 playlist->rdiff (cmds);
5144 _session->add_commands (cmds);
5146 _session->add_command (new StatefulDiffCommand (playlist));
5150 commit_reversible_command ();
5155 Editor::remove_last_capture ()
5157 vector<string> choices;
5164 if (Config->get_verify_remove_last_capture()) {
5165 prompt = _("Do you really want to destroy the last capture?"
5166 "\n(This is destructive and cannot be undone)");
5168 choices.push_back (_("No, do nothing."));
5169 choices.push_back (_("Yes, destroy it."));
5171 Choice prompter (_("Destroy last capture"), prompt, choices);
5173 if (prompter.run () == 1) {
5174 _session->remove_last_capture ();
5175 _regions->redisplay ();
5179 _session->remove_last_capture();
5180 _regions->redisplay ();
5185 Editor::tag_regions (RegionList regions)
5187 ArdourDialog d (_("Tag Last Capture"), true, false);
5189 Label label (_("Tag:"));
5192 hbox.set_spacing (6);
5193 hbox.pack_start (label, false, false);
5194 hbox.pack_start (entry, true, true);
5196 d.get_vbox()->set_border_width (12);
5197 d.get_vbox()->pack_start (hbox, false, false);
5199 d.add_button(Gtk::Stock::CANCEL, Gtk::RESPONSE_CANCEL);
5200 d.add_button(Gtk::Stock::OK, Gtk::RESPONSE_OK);
5202 d.set_size_request (300, -1);
5204 entry.set_text (_("Good"));
5205 entry.select_region (0, -1);
5207 entry.signal_activate().connect (sigc::bind (sigc::mem_fun (d, &Dialog::response), RESPONSE_OK));
5213 int const ret = d.run();
5217 if (ret != RESPONSE_OK) {
5221 std::string tagstr = entry.get_text();
5222 strip_whitespace_edges (tagstr);
5224 if (!tagstr.empty()) {
5225 for (RegionList::iterator r = regions.begin(); r != regions.end(); r++) {
5226 (*r)->set_tags(tagstr);
5229 _regions->redisplay ();
5234 Editor::tag_selected_region ()
5236 std::list<boost::shared_ptr<Region> > rlist;
5238 RegionSelection rs = get_regions_from_selection_and_entered ();
5239 for (RegionSelection::iterator r = rs.begin(); r != rs.end(); r++) {
5240 rlist.push_back((*r)->region());
5247 Editor::tag_last_capture ()
5253 std::list<boost::shared_ptr<Region> > rlist;
5255 std::list<boost::shared_ptr<Source> > srcs;
5256 _session->get_last_capture_sources (srcs);
5257 for (std::list<boost::shared_ptr<Source> >::iterator i = srcs.begin(); i != srcs.end(); ++i) {
5258 boost::shared_ptr<ARDOUR::Source> source = (*i);
5261 set<boost::shared_ptr<Region> > regions;
5262 RegionFactory::get_regions_using_source (source, regions);
5263 for (set<boost::shared_ptr<Region> >::iterator r = regions.begin(); r != regions.end(); r++) {
5264 rlist.push_back(*r);
5274 Editor::normalize_region ()
5280 RegionSelection rs = get_regions_from_selection_and_entered ();
5286 NormalizeDialog dialog (rs.size() > 1);
5288 if (dialog.run () != RESPONSE_ACCEPT) {
5292 CursorContext::Handle cursor_ctx = CursorContext::create(*this, _cursors->wait);
5295 /* XXX: should really only count audio regions here */
5296 int const regions = rs.size ();
5298 /* Make a list of the selected audio regions' maximum amplitudes, and also
5299 obtain the maximum amplitude of them all.
5301 list<double> max_amps;
5302 list<double> rms_vals;
5305 bool use_rms = dialog.constrain_rms ();
5307 for (RegionSelection::const_iterator i = rs.begin(); i != rs.end(); ++i) {
5308 AudioRegionView const * arv = dynamic_cast<AudioRegionView const *> (*i);
5312 dialog.descend (1.0 / regions);
5313 double const a = arv->audio_region()->maximum_amplitude (&dialog);
5315 double r = arv->audio_region()->rms (&dialog);
5316 max_rms = max (max_rms, r);
5317 rms_vals.push_back (r);
5321 /* the user cancelled the operation */
5325 max_amps.push_back (a);
5326 max_amp = max (max_amp, a);
5330 list<double>::const_iterator a = max_amps.begin ();
5331 list<double>::const_iterator l = rms_vals.begin ();
5332 bool in_command = false;
5334 for (RegionSelection::iterator r = rs.begin(); r != rs.end(); ++r) {
5335 AudioRegionView* const arv = dynamic_cast<AudioRegionView*> (*r);
5340 arv->region()->clear_changes ();
5342 double amp = dialog.normalize_individually() ? *a : max_amp;
5343 double target = dialog.target_peak (); // dB
5346 double const amp_rms = dialog.normalize_individually() ? *l : max_rms;
5347 const double t_rms = dialog.target_rms ();
5348 const gain_t c_peak = dB_to_coefficient (target);
5349 const gain_t c_rms = dB_to_coefficient (t_rms);
5350 if ((amp_rms / c_rms) > (amp / c_peak)) {
5356 arv->audio_region()->normalize (amp, target);
5359 begin_reversible_command (_("normalize"));
5362 _session->add_command (new StatefulDiffCommand (arv->region()));
5369 commit_reversible_command ();
5375 Editor::reset_region_scale_amplitude ()
5381 RegionSelection rs = get_regions_from_selection_and_entered ();
5387 bool in_command = false;
5389 for (RegionSelection::iterator r = rs.begin(); r != rs.end(); ++r) {
5390 AudioRegionView* const arv = dynamic_cast<AudioRegionView*>(*r);
5393 arv->region()->clear_changes ();
5394 arv->audio_region()->set_scale_amplitude (1.0f);
5397 begin_reversible_command ("reset gain");
5400 _session->add_command (new StatefulDiffCommand (arv->region()));
5404 commit_reversible_command ();
5409 Editor::adjust_region_gain (bool up)
5411 RegionSelection rs = get_regions_from_selection_and_entered ();
5413 if (!_session || rs.empty()) {
5417 bool in_command = false;
5419 for (RegionSelection::iterator r = rs.begin(); r != rs.end(); ++r) {
5420 AudioRegionView* const arv = dynamic_cast<AudioRegionView*>(*r);
5425 arv->region()->clear_changes ();
5427 double dB = accurate_coefficient_to_dB (arv->audio_region()->scale_amplitude ());
5435 arv->audio_region()->set_scale_amplitude (dB_to_coefficient (dB));
5438 begin_reversible_command ("adjust region gain");
5441 _session->add_command (new StatefulDiffCommand (arv->region()));
5445 commit_reversible_command ();
5450 Editor::reset_region_gain ()
5452 RegionSelection rs = get_regions_from_selection_and_entered ();
5454 if (!_session || rs.empty()) {
5458 bool in_command = false;
5460 for (RegionSelection::iterator r = rs.begin(); r != rs.end(); ++r) {
5461 AudioRegionView* const arv = dynamic_cast<AudioRegionView*>(*r);
5466 arv->region()->clear_changes ();
5468 arv->audio_region()->set_scale_amplitude (1.0f);
5471 begin_reversible_command ("reset region gain");
5474 _session->add_command (new StatefulDiffCommand (arv->region()));
5478 commit_reversible_command ();
5483 Editor::reverse_region ()
5489 Reverse rev (*_session);
5490 apply_filter (rev, _("reverse regions"));
5494 Editor::strip_region_silence ()
5500 RegionSelection rs = get_regions_from_selection_and_entered ();
5506 std::list<RegionView*> audio_only;
5508 for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
5509 AudioRegionView* const arv = dynamic_cast<AudioRegionView*> (*i);
5511 audio_only.push_back (arv);
5515 assert (!audio_only.empty());
5517 StripSilenceDialog d (_session, audio_only);
5518 int const r = d.run ();
5522 if (r == Gtk::RESPONSE_OK) {
5523 ARDOUR::AudioIntervalMap silences;
5524 d.silences (silences);
5525 StripSilence s (*_session, silences, d.fade_length());
5527 apply_filter (s, _("strip silence"), &d);
5532 Editor::apply_midi_note_edit_op_to_region (MidiOperator& op, MidiRegionView& mrv)
5534 Evoral::Sequence<Temporal::Beats>::Notes selected;
5535 mrv.selection_as_notelist (selected, true);
5537 vector<Evoral::Sequence<Temporal::Beats>::Notes> v;
5538 v.push_back (selected);
5540 Temporal::Beats pos_beats = Temporal::Beats (mrv.midi_region()->beat()) - mrv.midi_region()->start_beats();
5542 return op (mrv.midi_region()->model(), pos_beats, v);
5546 Editor::apply_midi_note_edit_op (MidiOperator& op, const RegionSelection& rs)
5552 bool in_command = false;
5554 for (RegionSelection::const_iterator r = rs.begin(); r != rs.end(); ) {
5555 RegionSelection::const_iterator tmp = r;
5558 MidiRegionView* const mrv = dynamic_cast<MidiRegionView*> (*r);
5561 Command* cmd = apply_midi_note_edit_op_to_region (op, *mrv);
5564 begin_reversible_command (op.name ());
5568 _session->add_command (cmd);
5576 commit_reversible_command ();
5577 _session->set_dirty ();
5582 Editor::fork_region ()
5584 RegionSelection rs = get_regions_from_selection_and_entered ();
5590 CursorContext::Handle cursor_ctx = CursorContext::create(*this, _cursors->wait);
5591 bool in_command = false;
5595 for (RegionSelection::iterator r = rs.begin(); r != rs.end(); ) {
5596 RegionSelection::iterator tmp = r;
5599 MidiRegionView* const mrv = dynamic_cast<MidiRegionView*>(*r);
5603 boost::shared_ptr<Playlist> playlist = mrv->region()->playlist();
5604 boost::shared_ptr<MidiSource> new_source = _session->create_midi_source_by_stealing_name (mrv->midi_view()->track());
5605 boost::shared_ptr<MidiRegion> newregion = mrv->midi_region()->clone (new_source);
5608 begin_reversible_command (_("Fork Region(s)"));
5611 playlist->clear_changes ();
5612 playlist->replace_region (mrv->region(), newregion, mrv->region()->position());
5613 _session->add_command(new StatefulDiffCommand (playlist));
5615 error << string_compose (_("Could not unlink %1"), mrv->region()->name()) << endmsg;
5623 commit_reversible_command ();
5628 Editor::quantize_region ()
5631 quantize_regions(get_regions_from_selection_and_entered ());
5636 Editor::quantize_regions (const RegionSelection& rs)
5638 if (rs.n_midi_regions() == 0) {
5642 if (!quantize_dialog) {
5643 quantize_dialog = new QuantizeDialog (*this);
5646 if (quantize_dialog->is_mapped()) {
5647 /* in progress already */
5651 quantize_dialog->present ();
5652 const int r = quantize_dialog->run ();
5653 quantize_dialog->hide ();
5655 if (r == Gtk::RESPONSE_OK) {
5656 Quantize quant (quantize_dialog->snap_start(),
5657 quantize_dialog->snap_end(),
5658 quantize_dialog->start_grid_size(),
5659 quantize_dialog->end_grid_size(),
5660 quantize_dialog->strength(),
5661 quantize_dialog->swing(),
5662 quantize_dialog->threshold());
5664 apply_midi_note_edit_op (quant, rs);
5669 Editor::legatize_region (bool shrink_only)
5672 legatize_regions(get_regions_from_selection_and_entered (), shrink_only);
5677 Editor::legatize_regions (const RegionSelection& rs, bool shrink_only)
5679 if (rs.n_midi_regions() == 0) {
5683 Legatize legatize(shrink_only);
5684 apply_midi_note_edit_op (legatize, rs);
5688 Editor::transform_region ()
5691 transform_regions(get_regions_from_selection_and_entered ());
5696 Editor::transform_regions (const RegionSelection& rs)
5698 if (rs.n_midi_regions() == 0) {
5705 const int r = td.run();
5708 if (r == Gtk::RESPONSE_OK) {
5709 Transform transform(td.get());
5710 apply_midi_note_edit_op(transform, rs);
5715 Editor::transpose_region ()
5718 transpose_regions(get_regions_from_selection_and_entered ());
5723 Editor::transpose_regions (const RegionSelection& rs)
5725 if (rs.n_midi_regions() == 0) {
5730 int const r = d.run ();
5732 if (r == RESPONSE_ACCEPT) {
5733 Transpose transpose(d.semitones ());
5734 apply_midi_note_edit_op (transpose, rs);
5739 Editor::insert_patch_change (bool from_context)
5741 RegionSelection rs = get_regions_from_selection_and_entered ();
5747 const samplepos_t p = get_preferred_edit_position (EDIT_IGNORE_NONE, from_context);
5749 /* XXX: bit of a hack; use the MIDNAM from the first selected region;
5750 there may be more than one, but the PatchChangeDialog can only offer
5751 one set of patch menus.
5753 MidiRegionView* first = dynamic_cast<MidiRegionView*> (rs.front ());
5755 Evoral::PatchChange<Temporal::Beats> empty (Temporal::Beats(), 0, 0, 0);
5756 PatchChangeDialog d (0, _session, empty, first->instrument_info(), Gtk::Stock::ADD);
5758 if (d.run() == RESPONSE_CANCEL) {
5762 for (RegionSelection::iterator i = rs.begin (); i != rs.end(); ++i) {
5763 MidiRegionView* const mrv = dynamic_cast<MidiRegionView*> (*i);
5765 if (p >= mrv->region()->first_sample() && p <= mrv->region()->last_sample()) {
5766 mrv->add_patch_change (p - mrv->region()->position(), d.patch ());
5773 Editor::apply_filter (Filter& filter, string command, ProgressReporter* progress)
5775 RegionSelection rs = get_regions_from_selection_and_entered ();
5781 CursorContext::Handle cursor_ctx = CursorContext::create(*this, _cursors->wait);
5782 bool in_command = false;
5787 int const N = rs.size ();
5789 for (RegionSelection::iterator r = rs.begin(); r != rs.end(); ) {
5790 RegionSelection::iterator tmp = r;
5793 AudioRegionView* const arv = dynamic_cast<AudioRegionView*>(*r);
5795 boost::shared_ptr<Playlist> playlist = arv->region()->playlist();
5798 progress->descend (1.0 / N);
5801 if (arv->audio_region()->apply (filter, progress) == 0) {
5803 playlist->clear_changes ();
5804 playlist->clear_owned_changes ();
5807 begin_reversible_command (command);
5811 if (filter.results.empty ()) {
5813 /* no regions returned; remove the old one */
5814 playlist->remove_region (arv->region ());
5818 std::vector<boost::shared_ptr<Region> >::iterator res = filter.results.begin ();
5820 /* first region replaces the old one */
5821 playlist->replace_region (arv->region(), *res, (*res)->position());
5825 while (res != filter.results.end()) {
5826 playlist->add_region (*res, (*res)->position());
5832 /* We might have removed regions, which alters other regions' layering_index,
5833 so we need to do a recursive diff here.
5835 vector<Command*> cmds;
5836 playlist->rdiff (cmds);
5837 _session->add_commands (cmds);
5839 _session->add_command(new StatefulDiffCommand (playlist));
5843 progress->ascend ();
5852 commit_reversible_command ();
5857 Editor::external_edit_region ()
5863 Editor::reset_region_gain_envelopes ()
5865 RegionSelection rs = get_regions_from_selection_and_entered ();
5867 if (!_session || rs.empty()) {
5871 bool in_command = false;
5873 for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
5874 AudioRegionView* const arv = dynamic_cast<AudioRegionView*>(*i);
5876 boost::shared_ptr<AutomationList> alist (arv->audio_region()->envelope());
5877 XMLNode& before (alist->get_state());
5879 arv->audio_region()->set_default_envelope ();
5882 begin_reversible_command (_("reset region gain"));
5885 _session->add_command (new MementoCommand<AutomationList>(*arv->audio_region()->envelope().get(), &before, &alist->get_state()));
5890 commit_reversible_command ();
5895 Editor::set_region_gain_visibility (RegionView* rv)
5897 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (rv);
5899 arv->update_envelope_visibility();
5904 Editor::set_gain_envelope_visibility ()
5910 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
5911 AudioTimeAxisView* v = dynamic_cast<AudioTimeAxisView*>(*i);
5913 v->audio_view()->foreach_regionview (sigc::mem_fun (this, &Editor::set_region_gain_visibility));
5919 Editor::toggle_gain_envelope_active ()
5921 if (_ignore_region_action) {
5925 RegionSelection rs = get_regions_from_selection_and_entered ();
5927 if (!_session || rs.empty()) {
5931 bool in_command = false;
5933 for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
5934 AudioRegionView* const arv = dynamic_cast<AudioRegionView*>(*i);
5936 arv->region()->clear_changes ();
5937 arv->audio_region()->set_envelope_active (!arv->audio_region()->envelope_active());
5940 begin_reversible_command (_("region gain envelope active"));
5943 _session->add_command (new StatefulDiffCommand (arv->region()));
5948 commit_reversible_command ();
5953 Editor::toggle_region_lock ()
5955 if (_ignore_region_action) {
5959 RegionSelection rs = get_regions_from_selection_and_entered ();
5961 if (!_session || rs.empty()) {
5965 begin_reversible_command (_("toggle region lock"));
5967 for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
5968 (*i)->region()->clear_changes ();
5969 (*i)->region()->set_locked (!(*i)->region()->locked());
5970 _session->add_command (new StatefulDiffCommand ((*i)->region()));
5973 commit_reversible_command ();
5977 Editor::toggle_region_video_lock ()
5979 if (_ignore_region_action) {
5983 RegionSelection rs = get_regions_from_selection_and_entered ();
5985 if (!_session || rs.empty()) {
5989 begin_reversible_command (_("Toggle Video Lock"));
5991 for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
5992 (*i)->region()->clear_changes ();
5993 (*i)->region()->set_video_locked (!(*i)->region()->video_locked());
5994 _session->add_command (new StatefulDiffCommand ((*i)->region()));
5997 commit_reversible_command ();
6001 Editor::toggle_region_lock_style ()
6003 if (_ignore_region_action) {
6007 RegionSelection rs = get_regions_from_selection_and_entered ();
6009 if (!_session || rs.empty()) {
6013 Glib::RefPtr<ToggleAction> a = Glib::RefPtr<ToggleAction>::cast_dynamic (_region_actions->get_action("toggle-region-lock-style"));
6014 vector<Widget*> proxies = a->get_proxies();
6015 Gtk::CheckMenuItem* cmi = dynamic_cast<Gtk::CheckMenuItem*> (proxies.front());
6019 begin_reversible_command (_("toggle region lock style"));
6021 for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
6022 (*i)->region()->clear_changes ();
6023 PositionLockStyle const ns = ((*i)->region()->position_lock_style() == AudioTime && !cmi->get_inconsistent()) ? MusicTime : AudioTime;
6024 (*i)->region()->set_position_lock_style (ns);
6025 _session->add_command (new StatefulDiffCommand ((*i)->region()));
6028 commit_reversible_command ();
6032 Editor::toggle_opaque_region ()
6034 if (_ignore_region_action) {
6038 RegionSelection rs = get_regions_from_selection_and_entered ();
6040 if (!_session || rs.empty()) {
6044 begin_reversible_command (_("change region opacity"));
6046 for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
6047 (*i)->region()->clear_changes ();
6048 (*i)->region()->set_opaque (!(*i)->region()->opaque());
6049 _session->add_command (new StatefulDiffCommand ((*i)->region()));
6052 commit_reversible_command ();
6056 Editor::toggle_record_enable ()
6058 bool new_state = false;
6060 for (TrackSelection::iterator i = selection->tracks.begin(); i != selection->tracks.end(); ++i) {
6061 RouteTimeAxisView *rtav = dynamic_cast<RouteTimeAxisView *>(*i);
6064 if (!rtav->is_track())
6068 new_state = !rtav->track()->rec_enable_control()->get_value();
6072 rtav->track()->rec_enable_control()->set_value (new_state, Controllable::UseGroup);
6077 tracklist_to_stripables (TrackViewList list)
6081 for (TrackSelection::iterator i = list.begin(); i != list.end(); ++i) {
6082 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> ((*i));
6084 if (rtv && rtv->is_track()) {
6085 ret.push_back (rtv->track());
6093 Editor::play_solo_selection (bool restart)
6095 //note: session::solo_selection takes care of invalidating the region playlist
6097 if ((!selection->tracks.empty()) && selection->time.length() > 0) { //a range is selected; solo the tracks and roll
6099 StripableList sl = tracklist_to_stripables (selection->tracks);
6100 _session->solo_selection (sl, true);
6103 samplepos_t start = selection->time.start();
6104 samplepos_t end = selection->time.end_sample();
6105 _session->request_bounded_roll (start, end);
6107 } else if (! selection->tracks.empty()) { //no range is selected, but tracks are selected; solo the tracks and roll
6108 StripableList sl = tracklist_to_stripables (selection->tracks);
6109 _session->solo_selection (sl, true);
6110 _session->request_cancel_play_range();
6111 transition_to_rolling (true);
6113 } else if (! selection->regions.empty()) { //solo any tracks with selected regions, and roll
6114 StripableList sl = tracklist_to_stripables (get_tracks_for_range_action());
6115 _session->solo_selection (sl, true);
6116 _session->request_cancel_play_range();
6117 transition_to_rolling (true);
6119 _session->request_cancel_play_range();
6120 transition_to_rolling (true); //no selection. just roll.
6125 Editor::toggle_solo ()
6127 bool new_state = false;
6129 boost::shared_ptr<ControlList> cl (new ControlList);
6131 for (TrackSelection::iterator i = selection->tracks.begin(); i != selection->tracks.end(); ++i) {
6132 StripableTimeAxisView *stav = dynamic_cast<StripableTimeAxisView *>(*i);
6134 if (!stav || !stav->stripable()->solo_control()) {
6139 new_state = !stav->stripable()->solo_control()->soloed ();
6143 cl->push_back (stav->stripable()->solo_control());
6146 _session->set_controls (cl, new_state ? 1.0 : 0.0, Controllable::UseGroup);
6150 Editor::toggle_mute ()
6152 bool new_state = false;
6154 boost::shared_ptr<ControlList> cl (new ControlList);
6156 for (TrackSelection::iterator i = selection->tracks.begin(); i != selection->tracks.end(); ++i) {
6157 StripableTimeAxisView *stav = dynamic_cast<StripableTimeAxisView *>(*i);
6159 if (!stav || !stav->stripable()->mute_control()) {
6164 new_state = !stav->stripable()->mute_control()->muted();
6168 boost::shared_ptr<MuteControl> mc = stav->stripable()->mute_control();
6170 mc->start_touch (_session->audible_sample ());
6173 _session->set_controls (cl, new_state, Controllable::UseGroup);
6177 Editor::toggle_solo_isolate ()
6183 Editor::fade_range ()
6185 TrackViewList ts = selection->tracks.filter_to_unique_playlists ();
6187 begin_reversible_command (_("fade range"));
6189 for (TrackViewList::iterator i = ts.begin(); i != ts.end(); ++i) {
6190 (*i)->fade_range (selection->time);
6193 commit_reversible_command ();
6198 Editor::set_fade_length (bool in)
6200 RegionSelection rs = get_regions_from_selection_and_entered ();
6206 /* we need a region to measure the offset from the start */
6208 RegionView* rv = rs.front ();
6210 samplepos_t pos = get_preferred_edit_position();
6214 if (pos > rv->region()->last_sample() || pos < rv->region()->first_sample()) {
6215 /* edit point is outside the relevant region */
6220 if (pos <= rv->region()->position()) {
6224 len = pos - rv->region()->position();
6225 cmd = _("set fade in length");
6227 if (pos >= rv->region()->last_sample()) {
6231 len = rv->region()->last_sample() - pos;
6232 cmd = _("set fade out length");
6235 bool in_command = false;
6237 for (RegionSelection::iterator x = rs.begin(); x != rs.end(); ++x) {
6238 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (*x);
6244 boost::shared_ptr<AutomationList> alist;
6246 alist = tmp->audio_region()->fade_in();
6248 alist = tmp->audio_region()->fade_out();
6251 XMLNode &before = alist->get_state();
6254 tmp->audio_region()->set_fade_in_length (len);
6255 tmp->audio_region()->set_fade_in_active (true);
6257 tmp->audio_region()->set_fade_out_length (len);
6258 tmp->audio_region()->set_fade_out_active (true);
6262 begin_reversible_command (cmd);
6265 XMLNode &after = alist->get_state();
6266 _session->add_command(new MementoCommand<AutomationList>(*alist, &before, &after));
6270 commit_reversible_command ();
6275 Editor::set_fade_in_shape (FadeShape shape)
6277 RegionSelection rs = get_regions_from_selection_and_entered ();
6282 bool in_command = false;
6284 for (RegionSelection::iterator x = rs.begin(); x != rs.end(); ++x) {
6285 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (*x);
6291 boost::shared_ptr<AutomationList> alist = tmp->audio_region()->fade_in();
6292 XMLNode &before = alist->get_state();
6294 tmp->audio_region()->set_fade_in_shape (shape);
6297 begin_reversible_command (_("set fade in shape"));
6300 XMLNode &after = alist->get_state();
6301 _session->add_command(new MementoCommand<AutomationList>(*alist.get(), &before, &after));
6305 commit_reversible_command ();
6310 Editor::set_fade_out_shape (FadeShape shape)
6312 RegionSelection rs = get_regions_from_selection_and_entered ();
6317 bool in_command = false;
6319 for (RegionSelection::iterator x = rs.begin(); x != rs.end(); ++x) {
6320 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (*x);
6326 boost::shared_ptr<AutomationList> alist = tmp->audio_region()->fade_out();
6327 XMLNode &before = alist->get_state();
6329 tmp->audio_region()->set_fade_out_shape (shape);
6332 begin_reversible_command (_("set fade out shape"));
6335 XMLNode &after = alist->get_state();
6336 _session->add_command(new MementoCommand<AutomationList>(*alist.get(), &before, &after));
6340 commit_reversible_command ();
6345 Editor::set_fade_in_active (bool yn)
6347 RegionSelection rs = get_regions_from_selection_and_entered ();
6352 bool in_command = false;
6354 for (RegionSelection::iterator x = rs.begin(); x != rs.end(); ++x) {
6355 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (*x);
6362 boost::shared_ptr<AudioRegion> ar (tmp->audio_region());
6364 ar->clear_changes ();
6365 ar->set_fade_in_active (yn);
6368 begin_reversible_command (_("set fade in active"));
6371 _session->add_command (new StatefulDiffCommand (ar));
6375 commit_reversible_command ();
6380 Editor::set_fade_out_active (bool yn)
6382 RegionSelection rs = get_regions_from_selection_and_entered ();
6387 bool in_command = false;
6389 for (RegionSelection::iterator x = rs.begin(); x != rs.end(); ++x) {
6390 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (*x);
6396 boost::shared_ptr<AudioRegion> ar (tmp->audio_region());
6398 ar->clear_changes ();
6399 ar->set_fade_out_active (yn);
6402 begin_reversible_command (_("set fade out active"));
6405 _session->add_command(new StatefulDiffCommand (ar));
6409 commit_reversible_command ();
6414 Editor::toggle_region_fades (int dir)
6416 if (_ignore_region_action) {
6420 boost::shared_ptr<AudioRegion> ar;
6423 RegionSelection rs = get_regions_from_selection_and_entered ();
6429 RegionSelection::iterator i;
6430 for (i = rs.begin(); i != rs.end(); ++i) {
6431 if ((ar = boost::dynamic_pointer_cast<AudioRegion>((*i)->region())) != 0) {
6433 yn = ar->fade_out_active ();
6435 yn = ar->fade_in_active ();
6441 if (i == rs.end()) {
6445 /* XXX should this undo-able? */
6446 bool in_command = false;
6448 for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
6449 if ((ar = boost::dynamic_pointer_cast<AudioRegion>((*i)->region())) == 0) {
6452 ar->clear_changes ();
6454 if (dir == 1 || dir == 0) {
6455 ar->set_fade_in_active (!yn);
6458 if (dir == -1 || dir == 0) {
6459 ar->set_fade_out_active (!yn);
6462 begin_reversible_command (_("toggle fade active"));
6465 _session->add_command(new StatefulDiffCommand (ar));
6469 commit_reversible_command ();
6474 /** Update region fade visibility after its configuration has been changed */
6476 Editor::update_region_fade_visibility ()
6478 bool _fade_visibility = _session->config.get_show_region_fades ();
6480 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
6481 AudioTimeAxisView* v = dynamic_cast<AudioTimeAxisView*>(*i);
6483 if (_fade_visibility) {
6484 v->audio_view()->show_all_fades ();
6486 v->audio_view()->hide_all_fades ();
6493 Editor::set_edit_point ()
6496 MusicSample where (0, 0);
6498 if (!mouse_sample (where.sample, ignored)) {
6504 if (selection->markers.empty()) {
6506 mouse_add_new_marker (where.sample);
6511 Location* loc = find_location_from_marker (selection->markers.front(), ignored);
6514 loc->move_to (where.sample, where.division);
6520 Editor::set_playhead_cursor ()
6522 if (entered_marker) {
6523 _session->request_locate (entered_marker->position(), _session->transport_rolling());
6525 MusicSample where (0, 0);
6528 if (!mouse_sample (where.sample, ignored)) {
6535 _session->request_locate (where.sample, _session->transport_rolling());
6539 //not sure what this was for; remove it for now.
6540 // if (UIConfiguration::instance().get_follow_edits() && (!_session || !_session->config.get_external_sync())) {
6541 // cancel_time_selection();
6547 Editor::split_region ()
6549 if (_dragging_playhead) {
6551 } else if (_drags->active ()) {
6552 /*any other kind of drag, bail out so we avoid Undo snafu*/
6556 //if a range is selected, separate it
6557 if (!selection->time.empty()) {
6558 separate_regions_between (selection->time);
6562 //if no range was selected, try to find some regions to split
6563 if (current_mouse_mode() == MouseObject || current_mouse_mode() == MouseRange ) { //don't try this for Internal Edit, Stretch, Draw, etc.
6567 //new behavior: the Split action will prioritize the entered_regionview rather than selected regions.
6568 //this fixes the unexpected case where you point at a region, but
6569 // * nothing happens OR
6570 // * some other region (maybe off-screen) is split.
6571 //NOTE: if the entered_regionview is /part of the selection/ then we should operate on the selection as usual
6572 if (_edit_point == EditAtMouse && entered_regionview && !entered_regionview->selected()) {
6573 rs.add (entered_regionview);
6575 rs = selection->regions; //might be empty
6579 TrackViewList tracks = selection->tracks;
6581 if (!tracks.empty()) {
6582 /* no region selected or entered, but some selected tracks:
6583 * act on all regions on the selected tracks at the edit point
6585 samplepos_t const where = get_preferred_edit_position (Editing::EDIT_IGNORE_NONE, false, false);
6586 get_regions_at(rs, where, tracks);
6590 const samplepos_t pos = get_preferred_edit_position();
6591 const int32_t division = get_grid_music_divisions (0);
6592 MusicSample where (pos, division);
6598 split_regions_at (where, rs);
6603 Editor::select_next_stripable (bool routes_only)
6605 _session->selection().select_next_stripable (false, routes_only);
6609 Editor::select_prev_stripable (bool routes_only)
6611 _session->selection().select_prev_stripable (false, routes_only);
6615 Editor::set_loop_from_selection (bool play)
6617 if (_session == 0) {
6621 samplepos_t start, end;
6622 if (!get_selection_extents (start, end))
6625 set_loop_range (start, end, _("set loop range from selection"));
6628 _session->request_play_loop (true, true);
6633 Editor::set_loop_from_region (bool play)
6635 samplepos_t start, end;
6636 if (!get_selection_extents (start, end))
6639 set_loop_range (start, end, _("set loop range from region"));
6642 _session->request_locate (start, true);
6643 _session->request_play_loop (true);
6648 Editor::set_punch_from_selection ()
6650 if (_session == 0) {
6654 samplepos_t start, end;
6655 if (!get_selection_extents (start, end))
6658 set_punch_range (start, end, _("set punch range from selection"));
6662 Editor::set_auto_punch_range ()
6664 // auto punch in/out button from a single button
6665 // If Punch In is unset, set punch range from playhead to end, enable punch in
6666 // If Punch In is set, the next punch sets Punch Out, unless the playhead has been
6667 // rewound beyond the Punch In marker, in which case that marker will be moved back
6668 // to the current playhead position.
6669 // If punch out is set, it clears the punch range and Punch In/Out buttons
6671 if (_session == 0) {
6675 Location* tpl = transport_punch_location();
6676 samplepos_t now = playhead_cursor->current_sample();
6677 samplepos_t begin = now;
6678 samplepos_t end = _session->current_end_sample();
6680 if (!_session->config.get_punch_in()) {
6681 // First Press - set punch in and create range from here to eternity
6682 set_punch_range (begin, end, _("Auto Punch In"));
6683 _session->config.set_punch_in(true);
6684 } else if (tpl && !_session->config.get_punch_out()) {
6685 // Second press - update end range marker and set punch_out
6686 if (now < tpl->start()) {
6687 // playhead has been rewound - move start back and pretend nothing happened
6689 set_punch_range (begin, end, _("Auto Punch In/Out"));
6691 // normal case for 2nd press - set the punch out
6692 end = playhead_cursor->current_sample ();
6693 set_punch_range (tpl->start(), now, _("Auto Punch In/Out"));
6694 _session->config.set_punch_out(true);
6697 if (_session->config.get_punch_out()) {
6698 _session->config.set_punch_out(false);
6701 if (_session->config.get_punch_in()) {
6702 _session->config.set_punch_in(false);
6707 // third press - unset punch in/out and remove range
6708 _session->locations()->remove(tpl);
6715 Editor::set_session_extents_from_selection ()
6717 if (_session == 0) {
6721 samplepos_t start, end;
6722 if (!get_selection_extents (start, end))
6726 if ((loc = _session->locations()->session_range_location()) == 0) {
6727 _session->set_session_extents (start, end); // this will create a new session range; no need for UNDO
6729 XMLNode &before = loc->get_state();
6731 _session->set_session_extents (start, end);
6733 XMLNode &after = loc->get_state();
6735 begin_reversible_command (_("set session start/end from selection"));
6737 _session->add_command (new MementoCommand<Location>(*loc, &before, &after));
6739 commit_reversible_command ();
6742 _session->set_session_range_is_free (false);
6746 Editor::set_punch_start_from_edit_point ()
6750 MusicSample start (0, 0);
6751 samplepos_t end = max_samplepos;
6753 //use the existing punch end, if any
6754 Location* tpl = transport_punch_location();
6759 if ((_edit_point == EditAtPlayhead) && _session->transport_rolling()) {
6760 start.sample = _session->audible_sample();
6762 start.sample = get_preferred_edit_position();
6765 //if there's not already a sensible selection endpoint, go "forever"
6766 if (start.sample > end) {
6767 end = max_samplepos;
6770 set_punch_range (start.sample, end, _("set punch start from EP"));
6776 Editor::set_punch_end_from_edit_point ()
6780 samplepos_t start = 0;
6781 MusicSample end (max_samplepos, 0);
6783 //use the existing punch start, if any
6784 Location* tpl = transport_punch_location();
6786 start = tpl->start();
6789 if ((_edit_point == EditAtPlayhead) && _session->transport_rolling()) {
6790 end.sample = _session->audible_sample();
6792 end.sample = get_preferred_edit_position();
6795 set_punch_range (start, end.sample, _("set punch end from EP"));
6801 Editor::set_loop_start_from_edit_point ()
6805 MusicSample start (0, 0);
6806 samplepos_t end = max_samplepos;
6808 //use the existing loop end, if any
6809 Location* tpl = transport_loop_location();
6814 if ((_edit_point == EditAtPlayhead) && _session->transport_rolling()) {
6815 start.sample = _session->audible_sample();
6817 start.sample = get_preferred_edit_position();
6820 //if there's not already a sensible selection endpoint, go "forever"
6821 if (start.sample > end) {
6822 end = max_samplepos;
6825 set_loop_range (start.sample, end, _("set loop start from EP"));
6831 Editor::set_loop_end_from_edit_point ()
6835 samplepos_t start = 0;
6836 MusicSample end (max_samplepos, 0);
6838 //use the existing loop start, if any
6839 Location* tpl = transport_loop_location();
6841 start = tpl->start();
6844 if ((_edit_point == EditAtPlayhead) && _session->transport_rolling()) {
6845 end.sample = _session->audible_sample();
6847 end.sample = get_preferred_edit_position();
6850 set_loop_range (start, end.sample, _("set loop end from EP"));
6855 Editor::set_punch_from_region ()
6857 samplepos_t start, end;
6858 if (!get_selection_extents (start, end))
6861 set_punch_range (start, end, _("set punch range from region"));
6865 Editor::pitch_shift_region ()
6867 RegionSelection rs = get_regions_from_selection_and_entered ();
6869 RegionSelection audio_rs;
6870 for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
6871 if (dynamic_cast<AudioRegionView*> (*i)) {
6872 audio_rs.push_back (*i);
6876 if (audio_rs.empty()) {
6880 pitch_shift (audio_rs, 1.2);
6884 Editor::set_tempo_from_region ()
6886 RegionSelection rs = get_regions_from_selection_and_entered ();
6888 if (!_session || rs.empty()) {
6892 RegionView* rv = rs.front();
6894 define_one_bar (rv->region()->position(), rv->region()->last_sample() + 1);
6898 Editor::use_range_as_bar ()
6900 samplepos_t start, end;
6901 if (get_edit_op_range (start, end)) {
6902 define_one_bar (start, end);
6907 Editor::define_one_bar (samplepos_t start, samplepos_t end)
6909 samplepos_t length = end - start;
6911 const Meter& m (_session->tempo_map().meter_at_sample (start));
6913 /* length = 1 bar */
6915 /* We're going to deliver a constant tempo here,
6916 so we can use samples per beat to determine length.
6917 now we want samples per beat.
6918 we have samples per bar, and beats per bar, so ...
6921 /* XXXX METER MATH */
6923 double samples_per_beat = length / m.divisions_per_bar();
6925 /* beats per minute = */
6927 double beats_per_minute = (_session->sample_rate() * 60.0) / samples_per_beat;
6929 /* now decide whether to:
6931 (a) set global tempo
6932 (b) add a new tempo marker
6936 const TempoSection& t (_session->tempo_map().tempo_section_at_sample (start));
6938 bool do_global = false;
6940 if ((_session->tempo_map().n_tempos() == 1) && (_session->tempo_map().n_meters() == 1)) {
6942 /* only 1 tempo & 1 meter: ask if the user wants to set the tempo
6943 at the start, or create a new marker
6946 vector<string> options;
6947 options.push_back (_("Cancel"));
6948 options.push_back (_("Add new marker"));
6949 options.push_back (_("Set global tempo"));
6952 _("Define one bar"),
6953 _("Do you want to set the global tempo or add a new tempo marker?"),
6957 c.set_default_response (2);
6973 /* more than 1 tempo and/or meter section already, go ahead do the "usual":
6974 if the marker is at the region starter, change it, otherwise add
6979 begin_reversible_command (_("set tempo from region"));
6980 XMLNode& before (_session->tempo_map().get_state());
6983 _session->tempo_map().change_initial_tempo (beats_per_minute, t.note_type(), t.end_note_types_per_minute());
6984 } else if (t.sample() == start) {
6985 _session->tempo_map().change_existing_tempo_at (start, beats_per_minute, t.note_type(), t.end_note_types_per_minute());
6987 /* constant tempo */
6988 const Tempo tempo (beats_per_minute, t.note_type());
6989 _session->tempo_map().add_tempo (tempo, 0.0, start, AudioTime);
6992 XMLNode& after (_session->tempo_map().get_state());
6994 _session->add_command (new MementoCommand<TempoMap>(_session->tempo_map(), &before, &after));
6995 commit_reversible_command ();
6999 Editor::split_region_at_transients ()
7001 AnalysisFeatureList positions;
7003 RegionSelection rs = get_regions_from_selection_and_entered ();
7005 if (!_session || rs.empty()) {
7009 begin_reversible_command (_("split regions"));
7011 for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ) {
7013 RegionSelection::iterator tmp;
7018 boost::shared_ptr<AudioRegion> ar = boost::dynamic_pointer_cast<AudioRegion> ((*i)->region());
7021 ar->transients (positions);
7022 split_region_at_points ((*i)->region(), positions, true);
7029 commit_reversible_command ();
7034 Editor::split_region_at_points (boost::shared_ptr<Region> r, AnalysisFeatureList& positions, bool can_ferret, bool select_new)
7036 bool use_rhythmic_rodent = false;
7038 boost::shared_ptr<Playlist> pl = r->playlist();
7040 list<boost::shared_ptr<Region> > new_regions;
7046 if (positions.empty()) {
7050 if (positions.size() > 20 && can_ferret) {
7051 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);
7052 ArdourMessageDialog msg (msgstr,
7055 Gtk::BUTTONS_OK_CANCEL);
7058 msg.add_button (_("Call for the Ferret!"), RESPONSE_APPLY);
7059 msg.set_secondary_text (_("Press OK to continue with this split operation\nor ask the Ferret dialog to tune the analysis"));
7061 msg.set_secondary_text (_("Press OK to continue with this split operation"));
7064 msg.set_title (_("Excessive split?"));
7065 int response = msg.run();
7071 case RESPONSE_APPLY:
7072 use_rhythmic_rodent = true;
7079 if (use_rhythmic_rodent) {
7080 show_rhythm_ferret ();
7084 AnalysisFeatureList::const_iterator x;
7086 pl->clear_changes ();
7087 pl->clear_owned_changes ();
7089 x = positions.begin();
7091 if (x == positions.end()) {
7096 pl->remove_region (r);
7098 samplepos_t pos = 0;
7100 samplepos_t rstart = r->first_sample ();
7101 samplepos_t rend = r->last_sample ();
7103 while (x != positions.end()) {
7105 /* deal with positons that are out of scope of present region bounds */
7106 if (*x <= rstart || *x > rend) {
7111 /* file start = original start + how far we from the initial position ? */
7113 samplepos_t file_start = r->start() + pos;
7115 /* length = next position - current position */
7117 samplepos_t len = (*x) - pos - rstart;
7119 /* XXX we do we really want to allow even single-sample regions?
7120 * shouldn't we have some kind of lower limit on region size?
7129 if (RegionFactory::region_name (new_name, r->name())) {
7133 /* do NOT announce new regions 1 by one, just wait till they are all done */
7137 plist.add (ARDOUR::Properties::start, file_start);
7138 plist.add (ARDOUR::Properties::length, len);
7139 plist.add (ARDOUR::Properties::name, new_name);
7140 plist.add (ARDOUR::Properties::layer, 0);
7141 // TODO set transients_offset
7143 boost::shared_ptr<Region> nr = RegionFactory::create (r->sources(), plist, false);
7144 /* because we set annouce to false, manually add the new region to the
7147 RegionFactory::map_add (nr);
7149 pl->add_region (nr, rstart + pos);
7152 new_regions.push_front(nr);
7161 RegionFactory::region_name (new_name, r->name());
7163 /* Add the final region */
7166 plist.add (ARDOUR::Properties::start, r->start() + pos);
7167 plist.add (ARDOUR::Properties::length, r->last_sample() - (r->position() + pos) + 1);
7168 plist.add (ARDOUR::Properties::name, new_name);
7169 plist.add (ARDOUR::Properties::layer, 0);
7171 boost::shared_ptr<Region> nr = RegionFactory::create (r->sources(), plist, false);
7172 /* because we set annouce to false, manually add the new region to the
7175 RegionFactory::map_add (nr);
7176 pl->add_region (nr, r->position() + pos);
7179 new_regions.push_front(nr);
7184 /* We might have removed regions, which alters other regions' layering_index,
7185 so we need to do a recursive diff here.
7187 vector<Command*> cmds;
7189 _session->add_commands (cmds);
7191 _session->add_command (new StatefulDiffCommand (pl));
7195 for (list<boost::shared_ptr<Region> >::iterator i = new_regions.begin(); i != new_regions.end(); ++i){
7196 set_selected_regionview_from_region_list ((*i), Selection::Add);
7202 Editor::place_transient()
7208 RegionSelection rs = get_regions_from_selection_and_edit_point ();
7214 samplepos_t where = get_preferred_edit_position();
7216 begin_reversible_command (_("place transient"));
7218 for (RegionSelection::iterator r = rs.begin(); r != rs.end(); ++r) {
7219 (*r)->region()->add_transient(where);
7222 commit_reversible_command ();
7226 Editor::remove_transient(ArdourCanvas::Item* item)
7232 ArdourCanvas::Line* _line = reinterpret_cast<ArdourCanvas::Line*> (item);
7235 AudioRegionView* _arv = reinterpret_cast<AudioRegionView*> (item->get_data ("regionview"));
7236 _arv->remove_transient (*(float*) _line->get_data ("position"));
7240 Editor::snap_regions_to_grid ()
7242 list <boost::shared_ptr<Playlist > > used_playlists;
7244 RegionSelection rs = get_regions_from_selection_and_entered ();
7246 if (!_session || rs.empty()) {
7250 begin_reversible_command (_("snap regions to grid"));
7252 for (RegionSelection::iterator r = rs.begin(); r != rs.end(); ++r) {
7254 boost::shared_ptr<Playlist> pl = (*r)->region()->playlist();
7256 if (!pl->frozen()) {
7257 /* we haven't seen this playlist before */
7259 /* remember used playlists so we can thaw them later */
7260 used_playlists.push_back(pl);
7263 (*r)->region()->clear_changes ();
7265 MusicSample start ((*r)->region()->first_sample (), 0);
7266 snap_to (start, RoundNearest, SnapToGrid_Unscaled, true);
7267 (*r)->region()->set_position (start.sample, start.division);
7268 _session->add_command(new StatefulDiffCommand ((*r)->region()));
7271 while (used_playlists.size() > 0) {
7272 list <boost::shared_ptr<Playlist > >::iterator i = used_playlists.begin();
7274 used_playlists.pop_front();
7277 commit_reversible_command ();
7281 Editor::close_region_gaps ()
7283 list <boost::shared_ptr<Playlist > > used_playlists;
7285 RegionSelection rs = get_regions_from_selection_and_entered ();
7287 if (!_session || rs.empty()) {
7291 Dialog dialog (_("Close Region Gaps"));
7294 table.set_spacings (12);
7295 table.set_border_width (12);
7296 Label* l = manage (left_aligned_label (_("Crossfade length")));
7297 table.attach (*l, 0, 1, 0, 1);
7299 SpinButton spin_crossfade (1, 0);
7300 spin_crossfade.set_range (0, 15);
7301 spin_crossfade.set_increments (1, 1);
7302 spin_crossfade.set_value (5);
7303 table.attach (spin_crossfade, 1, 2, 0, 1);
7305 table.attach (*manage (new Label (_("ms"))), 2, 3, 0, 1);
7307 l = manage (left_aligned_label (_("Pull-back length")));
7308 table.attach (*l, 0, 1, 1, 2);
7310 SpinButton spin_pullback (1, 0);
7311 spin_pullback.set_range (0, 100);
7312 spin_pullback.set_increments (1, 1);
7313 spin_pullback.set_value(30);
7314 table.attach (spin_pullback, 1, 2, 1, 2);
7316 table.attach (*manage (new Label (_("ms"))), 2, 3, 1, 2);
7318 dialog.get_vbox()->pack_start (table);
7319 dialog.add_button (Stock::CANCEL, RESPONSE_CANCEL);
7320 dialog.add_button (_("Ok"), RESPONSE_ACCEPT);
7323 if (dialog.run () == RESPONSE_CANCEL) {
7327 samplepos_t crossfade_len = spin_crossfade.get_value();
7328 samplepos_t pull_back_samples = spin_pullback.get_value();
7330 crossfade_len = lrintf (crossfade_len * _session->sample_rate()/1000);
7331 pull_back_samples = lrintf (pull_back_samples * _session->sample_rate()/1000);
7333 /* Iterate over the region list and make adjacent regions overlap by crossfade_len_ms */
7335 begin_reversible_command (_("close region gaps"));
7338 boost::shared_ptr<Region> last_region;
7340 rs.sort_by_position_and_track();
7342 for (RegionSelection::iterator r = rs.begin(); r != rs.end(); ++r) {
7344 boost::shared_ptr<Playlist> pl = (*r)->region()->playlist();
7346 if (!pl->frozen()) {
7347 /* we haven't seen this playlist before */
7349 /* remember used playlists so we can thaw them later */
7350 used_playlists.push_back(pl);
7354 samplepos_t position = (*r)->region()->position();
7356 if (idx == 0 || position < last_region->position()){
7357 last_region = (*r)->region();
7362 (*r)->region()->clear_changes ();
7363 (*r)->region()->trim_front((position - pull_back_samples));
7365 last_region->clear_changes ();
7366 last_region->trim_end ((position - pull_back_samples + crossfade_len));
7368 _session->add_command (new StatefulDiffCommand ((*r)->region()));
7369 _session->add_command (new StatefulDiffCommand (last_region));
7371 last_region = (*r)->region();
7375 while (used_playlists.size() > 0) {
7376 list <boost::shared_ptr<Playlist > >::iterator i = used_playlists.begin();
7378 used_playlists.pop_front();
7381 commit_reversible_command ();
7385 Editor::tab_to_transient (bool forward)
7387 AnalysisFeatureList positions;
7389 RegionSelection rs = get_regions_from_selection_and_entered ();
7395 samplepos_t pos = _session->audible_sample ();
7397 if (!selection->tracks.empty()) {
7399 /* don't waste time searching for transients in duplicate playlists.
7402 TrackViewList ts = selection->tracks.filter_to_unique_playlists ();
7404 for (TrackViewList::iterator t = ts.begin(); t != ts.end(); ++t) {
7406 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (*t);
7409 boost::shared_ptr<Track> tr = rtv->track();
7411 boost::shared_ptr<Playlist> pl = tr->playlist ();
7413 samplepos_t result = pl->find_next_transient (pos, forward ? 1 : -1);
7416 positions.push_back (result);
7429 for (RegionSelection::iterator r = rs.begin(); r != rs.end(); ++r) {
7430 (*r)->region()->get_transients (positions);
7434 TransientDetector::cleanup_transients (positions, _session->sample_rate(), 3.0);
7437 AnalysisFeatureList::iterator x;
7439 for (x = positions.begin(); x != positions.end(); ++x) {
7445 if (x != positions.end ()) {
7446 _session->request_locate (*x);
7450 AnalysisFeatureList::reverse_iterator x;
7452 for (x = positions.rbegin(); x != positions.rend(); ++x) {
7458 if (x != positions.rend ()) {
7459 _session->request_locate (*x);
7465 Editor::playhead_forward_to_grid ()
7471 MusicSample pos (playhead_cursor->current_sample (), 0);
7473 if ( _grid_type == GridTypeNone) {
7474 if (pos.sample < max_samplepos - current_page_samples()*0.1) {
7475 pos.sample += current_page_samples()*0.1;
7476 _session->request_locate (pos.sample);
7478 _session->request_locate (0);
7482 if (pos.sample < max_samplepos - 1) {
7484 pos = snap_to_grid (pos, RoundUpAlways, SnapToGrid_Scaled);
7485 _session->request_locate (pos.sample);
7490 /* keep PH visible in window */
7491 if (pos.sample > (_leftmost_sample + current_page_samples() *0.9)) {
7492 reset_x_origin (pos.sample - (current_page_samples()*0.9));
7498 Editor::playhead_backward_to_grid ()
7504 MusicSample pos (playhead_cursor->current_sample (), 0);
7506 if ( _grid_type == GridTypeNone) {
7507 if ( pos.sample > current_page_samples()*0.1 ) {
7508 pos.sample -= current_page_samples()*0.1;
7509 _session->request_locate (pos.sample);
7511 _session->request_locate (0);
7515 if (pos.sample > 2) {
7517 pos = snap_to_grid (pos, RoundDownAlways, SnapToGrid_Scaled);
7520 //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...
7521 //also see: jump_backward_to_mark
7522 if (_session->transport_rolling()) {
7523 if ((playhead_cursor->current_sample() - pos.sample) < _session->sample_rate()/2) {
7524 pos = snap_to_grid (pos, RoundDownAlways, SnapToGrid_Scaled);
7528 _session->request_locate (pos.sample, _session->transport_rolling());
7531 /* keep PH visible in window */
7532 if (pos.sample < (_leftmost_sample + current_page_samples() *0.1)) {
7533 reset_x_origin (pos.sample - (current_page_samples()*0.1));
7538 Editor::set_track_height (Height h)
7540 TrackSelection& ts (selection->tracks);
7542 for (TrackSelection::iterator x = ts.begin(); x != ts.end(); ++x) {
7543 (*x)->set_height_enum (h);
7548 Editor::toggle_tracks_active ()
7550 TrackSelection& ts (selection->tracks);
7552 bool target = false;
7558 for (TrackSelection::iterator x = ts.begin(); x != ts.end(); ++x) {
7559 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*>(*x);
7563 target = !rtv->_route->active();
7566 rtv->_route->set_active (target, this);
7572 Editor::remove_tracks ()
7574 /* this will delete GUI objects that may be the subject of an event
7575 handler in which this method is called. Defer actual deletion to the
7576 next idle callback, when all event handling is finished.
7578 Glib::signal_idle().connect (sigc::mem_fun (*this, &Editor::idle_remove_tracks));
7582 Editor::idle_remove_tracks ()
7584 Session::StateProtector sp (_session);
7586 return false; /* do not call again */
7590 Editor::_remove_tracks ()
7592 TrackSelection& ts (selection->tracks);
7598 if (!ARDOUR_UI_UTILS::engine_is_running ()) {
7602 vector<string> choices;
7607 const char* trackstr;
7610 vector<boost::shared_ptr<Route> > routes;
7611 vector<boost::shared_ptr<VCA> > vcas;
7612 bool special_bus = false;
7614 for (TrackSelection::iterator x = ts.begin(); x != ts.end(); ++x) {
7615 VCATimeAxisView* vtv = dynamic_cast<VCATimeAxisView*> (*x);
7617 vcas.push_back (vtv->vca());
7621 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (*x);
7625 if (rtv->is_track()) {
7630 routes.push_back (rtv->_route);
7632 if (rtv->route()->is_master() || rtv->route()->is_monitor()) {
7637 if (special_bus && !Config->get_allow_special_bus_removal()) {
7638 ArdourMessageDialog msg (_("That would be bad news ...."),
7642 msg.set_secondary_text (string_compose (_("Removing the master or monitor bus is such a bad idea\n\
7643 that %1 is not going to allow it.\n\
7645 If you really want to do this sort of thing\n\
7646 edit your ardour.rc file to set the\n\
7647 \"allow-special-bus-removal\" option to be \"yes\""), PROGRAM_NAME));
7653 if (ntracks + nbusses + nvcas == 0) {
7659 trackstr = P_("track", "tracks", ntracks);
7660 busstr = P_("bus", "busses", nbusses);
7661 vcastr = P_("VCA", "VCAs", nvcas);
7663 if (ntracks > 0 && nbusses > 0 && nvcas > 0) {
7664 title = _("Remove various strips");
7665 prompt = string_compose (_("Do you really want to remove %1 %2, %3 %4 and %5 %6?"),
7666 ntracks, trackstr, nbusses, busstr, nvcas, vcastr);
7668 else if (ntracks > 0 && nbusses > 0) {
7669 title = string_compose (_("Remove %1 and %2"), trackstr, busstr);
7670 prompt = string_compose (_("Do you really want to remove %1 %2 and %3 %4?"),
7671 ntracks, trackstr, nbusses, busstr);
7673 else if (ntracks > 0 && nvcas > 0) {
7674 title = string_compose (_("Remove %1 and %2"), trackstr, vcastr);
7675 prompt = string_compose (_("Do you really want to remove %1 %2 and %3 %4?"),
7676 ntracks, trackstr, nvcas, vcastr);
7678 else if (nbusses > 0 && nvcas > 0) {
7679 title = string_compose (_("Remove %1 and %2"), busstr, vcastr);
7680 prompt = string_compose (_("Do you really want to remove %1 %2 and %3 %4?"),
7681 nbusses, busstr, nvcas, vcastr);
7683 else if (ntracks > 0) {
7684 title = string_compose (_("Remove %1"), trackstr);
7685 prompt = string_compose (_("Do you really want to remove %1 %2?"),
7688 else if (nbusses > 0) {
7689 title = string_compose (_("Remove %1"), busstr);
7690 prompt = string_compose (_("Do you really want to remove %1 %2?"),
7693 else if (nvcas > 0) {
7694 title = string_compose (_("Remove %1"), vcastr);
7695 prompt = string_compose (_("Do you really want to remove %1 %2?"),
7703 prompt += "\n" + string_compose ("(You may also lose the playlists associated with the %1)", trackstr) + "\n";
7706 prompt += "\n" + string(_("This action cannot be undone, and the session file will be overwritten!"));
7708 choices.push_back (_("No, do nothing."));
7709 if (ntracks + nbusses + nvcas > 1) {
7710 choices.push_back (_("Yes, remove them."));
7712 choices.push_back (_("Yes, remove it."));
7715 Choice prompter (title, prompt, choices);
7717 if (prompter.run () != 1) {
7721 if (current_mixer_strip && routes.size () > 1 && std::find (routes.begin(), routes.end(), current_mixer_strip->route()) != routes.end ()) {
7722 /* Route deletion calls Editor::timeaxisview_deleted() iteratively (for each deleted
7723 * route). If the deleted route is currently displayed in the Editor-Mixer (highly
7724 * likely because deletion requires selection) this will call
7725 * Editor::set_selected_mixer_strip () which is expensive (MixerStrip::set_route()).
7726 * It's likewise likely that the route that has just been displayed in the
7727 * Editor-Mixer will be next in line for deletion.
7729 * So simply switch to the master-bus (if present)
7731 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
7732 if ((*i)->stripable ()->is_master ()) {
7733 set_selected_mixer_strip (*(*i));
7740 PresentationInfo::ChangeSuspender cs;
7741 DisplaySuspender ds;
7743 boost::shared_ptr<RouteList> rl (new RouteList);
7744 for (vector<boost::shared_ptr<Route> >::iterator x = routes.begin(); x != routes.end(); ++x) {
7747 _session->remove_routes (rl);
7749 for (vector<boost::shared_ptr<VCA> >::iterator x = vcas.begin(); x != vcas.end(); ++x) {
7750 _session->vca_manager().remove_vca (*x);
7754 /* TrackSelection and RouteList leave scope,
7755 * destructors are called,
7756 * diskstream drops references, save_state is called (again for every track)
7761 Editor::do_insert_time ()
7763 if (selection->tracks.empty()) {
7764 ArdourMessageDialog msg (_("You must first select some tracks to Insert Time."),
7765 true, MESSAGE_INFO, BUTTONS_OK, true);
7770 if (Config->get_edit_mode() == Lock) {
7771 ArdourMessageDialog msg (_("You cannot insert time in Lock Edit mode."),
7772 true, MESSAGE_INFO, BUTTONS_OK, true);
7777 InsertRemoveTimeDialog d (*this);
7778 int response = d.run ();
7780 if (response != RESPONSE_OK) {
7784 if (d.distance() == 0) {
7791 d.intersected_region_action (),
7795 d.move_glued_markers(),
7796 d.move_locked_markers(),
7802 Editor::insert_time (
7803 samplepos_t pos, samplecnt_t samples, InsertTimeOption opt,
7804 bool all_playlists, bool ignore_music_glue, bool markers_too, bool glued_markers_too, bool locked_markers_too, bool tempo_too
7808 if (Config->get_edit_mode() == Lock) {
7811 bool in_command = false;
7813 TrackViewList ts = selection->tracks.filter_to_unique_playlists ();
7815 for (TrackViewList::iterator x = ts.begin(); x != ts.end(); ++x) {
7819 /* don't operate on any playlist more than once, which could
7820 * happen if "all playlists" is enabled, but there is more
7821 * than 1 track using playlists "from" a given track.
7824 set<boost::shared_ptr<Playlist> > pl;
7826 if (all_playlists) {
7827 RouteTimeAxisView* rtav = dynamic_cast<RouteTimeAxisView*> (*x);
7828 if (rtav && rtav->track ()) {
7829 vector<boost::shared_ptr<Playlist> > all = _session->playlists()->playlists_for_track (rtav->track ());
7830 for (vector<boost::shared_ptr<Playlist> >::iterator p = all.begin(); p != all.end(); ++p) {
7835 if ((*x)->playlist ()) {
7836 pl.insert ((*x)->playlist ());
7840 for (set<boost::shared_ptr<Playlist> >::iterator i = pl.begin(); i != pl.end(); ++i) {
7842 (*i)->clear_changes ();
7843 (*i)->clear_owned_changes ();
7846 begin_reversible_command (_("insert time"));
7850 if (opt == SplitIntersected) {
7851 /* non musical split */
7852 (*i)->split (MusicSample (pos, 0));
7855 (*i)->shift (pos, samples, (opt == MoveIntersected), ignore_music_glue);
7857 vector<Command*> cmds;
7859 _session->add_commands (cmds);
7861 _session->add_command (new StatefulDiffCommand (*i));
7865 RouteTimeAxisView* rtav = dynamic_cast<RouteTimeAxisView*> (*x);
7868 begin_reversible_command (_("insert time"));
7871 rtav->route ()->shift (pos, samples);
7878 const int32_t divisions = get_grid_music_divisions (0);
7879 XMLNode& before (_session->locations()->get_state());
7880 Locations::LocationList copy (_session->locations()->list());
7882 for (Locations::LocationList::iterator i = copy.begin(); i != copy.end(); ++i) {
7884 Locations::LocationList::const_iterator tmp;
7886 if ((*i)->position_lock_style() == AudioTime || glued_markers_too) {
7887 bool const was_locked = (*i)->locked ();
7888 if (locked_markers_too) {
7892 if ((*i)->start() >= pos) {
7893 // move end first, in case we're moving by more than the length of the range
7894 if (!(*i)->is_mark()) {
7895 (*i)->set_end ((*i)->end() + samples, false, true, divisions);
7897 (*i)->set_start ((*i)->start() + samples, false, true, divisions);
7909 begin_reversible_command (_("insert time"));
7912 XMLNode& after (_session->locations()->get_state());
7913 _session->add_command (new MementoCommand<Locations>(*_session->locations(), &before, &after));
7919 begin_reversible_command (_("insert time"));
7922 XMLNode& before (_session->tempo_map().get_state());
7923 _session->tempo_map().insert_time (pos, samples);
7924 XMLNode& after (_session->tempo_map().get_state());
7925 _session->add_command (new MementoCommand<TempoMap>(_session->tempo_map(), &before, &after));
7929 commit_reversible_command ();
7934 Editor::do_remove_time ()
7936 if (selection->tracks.empty()) {
7937 ArdourMessageDialog msg (_("You must first select some tracks to Remove Time."),
7938 true, MESSAGE_INFO, BUTTONS_OK, true);
7943 if (Config->get_edit_mode() == Lock) {
7944 ArdourMessageDialog msg (_("You cannot remove time in Lock Edit mode."),
7945 true, MESSAGE_INFO, BUTTONS_OK, true);
7950 InsertRemoveTimeDialog d (*this, true);
7952 int response = d.run ();
7954 if (response != RESPONSE_OK) {
7958 samplecnt_t distance = d.distance();
7960 if (distance == 0) {
7970 d.move_glued_markers(),
7971 d.move_locked_markers(),
7977 Editor::remove_time (samplepos_t pos, samplecnt_t samples, InsertTimeOption opt,
7978 bool ignore_music_glue, bool markers_too, bool glued_markers_too, bool locked_markers_too, bool tempo_too)
7980 if (Config->get_edit_mode() == Lock) {
7981 error << (_("Cannot insert or delete time when in Lock edit.")) << endmsg;
7984 bool in_command = false;
7986 for (TrackSelection::iterator x = selection->tracks.begin(); x != selection->tracks.end(); ++x) {
7988 boost::shared_ptr<Playlist> pl = (*x)->playlist();
7992 XMLNode &before = pl->get_state();
7995 begin_reversible_command (_("remove time"));
7999 std::list<AudioRange> rl;
8000 AudioRange ar(pos, pos+samples, 0);
8003 pl->shift (pos, -samples, true, ignore_music_glue);
8005 XMLNode &after = pl->get_state();
8007 _session->add_command (new MementoCommand<Playlist> (*pl, &before, &after));
8011 RouteTimeAxisView* rtav = dynamic_cast<RouteTimeAxisView*> (*x);
8014 begin_reversible_command (_("remove time"));
8017 rtav->route ()->shift (pos, -samples);
8021 const int32_t divisions = get_grid_music_divisions (0);
8022 std::list<Location*> loc_kill_list;
8027 XMLNode& before (_session->locations()->get_state());
8028 Locations::LocationList copy (_session->locations()->list());
8030 for (Locations::LocationList::iterator i = copy.begin(); i != copy.end(); ++i) {
8031 if ((*i)->position_lock_style() == AudioTime || glued_markers_too) {
8033 bool const was_locked = (*i)->locked ();
8034 if (locked_markers_too) {
8038 if (!(*i)->is_mark()) { // it's a range; have to handle both start and end
8039 if ((*i)->end() >= pos
8040 && (*i)->end() < pos+samples
8041 && (*i)->start() >= pos
8042 && (*i)->end() < pos+samples) { // range is completely enclosed; kill it
8044 loc_kill_list.push_back(*i);
8045 } else { // only start or end is included, try to do the right thing
8046 // move start before moving end, to avoid trying to move the end to before the start
8047 // if we're removing more time than the length of the range
8048 if ((*i)->start() >= pos && (*i)->start() < pos+samples) {
8049 // start is within cut
8050 (*i)->set_start (pos, false, true,divisions); // bring the start marker to the beginning of the cut
8052 } else if ((*i)->start() >= pos+samples) {
8053 // start (and thus entire range) lies beyond end of cut
8054 (*i)->set_start ((*i)->start() - samples, false, true, divisions); // slip the start marker back
8057 if ((*i)->end() >= pos && (*i)->end() < pos+samples) {
8058 // end is inside cut
8059 (*i)->set_end (pos, false, true, divisions); // bring the end to the cut
8061 } else if ((*i)->end() >= pos+samples) {
8062 // end is beyond end of cut
8063 (*i)->set_end ((*i)->end() - samples, false, true, divisions); // slip the end marker back
8068 } else if ((*i)->start() >= pos && (*i)->start() < pos+samples) {
8069 loc_kill_list.push_back(*i);
8071 } else if ((*i)->start() >= pos) {
8072 (*i)->set_start ((*i)->start() -samples, false, true, divisions);
8082 for (list<Location*>::iterator i = loc_kill_list.begin(); i != loc_kill_list.end(); ++i) {
8083 _session->locations()->remove (*i);
8088 begin_reversible_command (_("remove time"));
8091 XMLNode& after (_session->locations()->get_state());
8092 _session->add_command (new MementoCommand<Locations>(*_session->locations(), &before, &after));
8097 XMLNode& before (_session->tempo_map().get_state());
8099 if (_session->tempo_map().remove_time (pos, samples)) {
8101 begin_reversible_command (_("remove time"));
8104 XMLNode& after (_session->tempo_map().get_state());
8105 _session->add_command (new MementoCommand<TempoMap>(_session->tempo_map(), &before, &after));
8110 commit_reversible_command ();
8115 Editor::fit_selection ()
8117 if (!selection->tracks.empty()) {
8118 fit_tracks (selection->tracks);
8122 /* no selected tracks - use tracks with selected regions */
8124 if (!selection->regions.empty()) {
8125 for (RegionSelection::iterator r = selection->regions.begin(); r != selection->regions.end(); ++r) {
8126 tvl.push_back (&(*r)->get_time_axis_view ());
8132 } else if (internal_editing()) {
8133 /* no selected tracks, or regions, but in internal edit mode, so follow the mouse and use
8136 if (entered_track) {
8137 tvl.push_back (entered_track);
8145 Editor::fit_tracks (TrackViewList & tracks)
8147 if (tracks.empty()) {
8151 uint32_t child_heights = 0;
8152 int visible_tracks = 0;
8154 for (TrackSelection::iterator t = tracks.begin(); t != tracks.end(); ++t) {
8156 if (!(*t)->marked_for_display()) {
8160 child_heights += (*t)->effective_height() - (*t)->current_height();
8164 /* compute the per-track height from:
8166 * total canvas visible height
8167 * - height that will be taken by visible children of selected tracks
8168 * - height of the ruler/hscroll area
8170 uint32_t h = (uint32_t) floor ((trackviews_height() - child_heights) / visible_tracks);
8171 double first_y_pos = DBL_MAX;
8173 if (h < TimeAxisView::preset_height (HeightSmall)) {
8174 ArdourMessageDialog msg (_("There are too many tracks to fit in the current window"));
8176 /* too small to be displayed */
8180 undo_visual_stack.push_back (current_visual_state (true));
8181 PBD::Unwinder<bool> nsv (no_save_visual, true);
8183 /* build a list of all tracks, including children */
8186 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
8188 TimeAxisView::Children c = (*i)->get_child_list ();
8189 for (TimeAxisView::Children::iterator j = c.begin(); j != c.end(); ++j) {
8190 all.push_back (j->get());
8195 // find selection range.
8196 // if someone knows how to user TrackViewList::iterator for this
8198 int selected_top = -1;
8199 int selected_bottom = -1;
8201 for (TrackViewList::iterator t = all.begin(); t != all.end(); ++t, ++i) {
8202 if ((*t)->marked_for_display ()) {
8203 if (tracks.contains(*t)) {
8204 if (selected_top == -1) {
8207 selected_bottom = i;
8213 for (TrackViewList::iterator t = all.begin(); t != all.end(); ++t, ++i) {
8214 if ((*t)->marked_for_display ()) {
8215 if (tracks.contains(*t)) {
8216 (*t)->set_height (h);
8217 first_y_pos = std::min ((*t)->y_position (), first_y_pos);
8219 if (i > selected_top && i < selected_bottom) {
8220 hide_track_in_display (*t);
8227 set the controls_layout height now, because waiting for its size
8228 request signal handler will cause the vertical adjustment setting to fail
8231 controls_layout.property_height () = _full_canvas_height;
8232 vertical_adjustment.set_value (first_y_pos);
8234 redo_visual_stack.push_back (current_visual_state (true));
8236 visible_tracks_selector.set_text (_("Sel"));
8240 Editor::save_visual_state (uint32_t n)
8242 while (visual_states.size() <= n) {
8243 visual_states.push_back (0);
8246 if (visual_states[n] != 0) {
8247 delete visual_states[n];
8250 visual_states[n] = current_visual_state (true);
8255 Editor::goto_visual_state (uint32_t n)
8257 if (visual_states.size() <= n) {
8261 if (visual_states[n] == 0) {
8265 use_visual_state (*visual_states[n]);
8269 Editor::start_visual_state_op (uint32_t n)
8271 save_visual_state (n);
8273 PopUp* pup = new PopUp (WIN_POS_MOUSE, 1000, true);
8275 snprintf (buf, sizeof (buf), _("Saved view %u"), n+1);
8276 pup->set_text (buf);
8281 Editor::cancel_visual_state_op (uint32_t n)
8283 goto_visual_state (n);
8287 Editor::toggle_region_mute ()
8289 if (_ignore_region_action) {
8293 RegionSelection rs = get_regions_from_selection_and_entered ();
8299 if (rs.size() > 1) {
8300 begin_reversible_command (_("mute regions"));
8302 begin_reversible_command (_("mute region"));
8305 for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
8307 (*i)->region()->playlist()->clear_changes ();
8308 (*i)->region()->set_muted (!(*i)->region()->muted ());
8309 _session->add_command (new StatefulDiffCommand ((*i)->region()));
8313 commit_reversible_command ();
8317 Editor::combine_regions ()
8319 /* foreach track with selected regions, take all selected regions
8320 and join them into a new region containing the subregions (as a
8324 typedef set<RouteTimeAxisView*> RTVS;
8327 if (selection->regions.empty()) {
8331 for (RegionSelection::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
8332 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*>(&(*i)->get_time_axis_view());
8335 tracks.insert (rtv);
8339 begin_reversible_command (_("combine regions"));
8341 vector<RegionView*> new_selection;
8343 for (RTVS::iterator i = tracks.begin(); i != tracks.end(); ++i) {
8346 if ((rv = (*i)->combine_regions ()) != 0) {
8347 new_selection.push_back (rv);
8351 selection->clear_regions ();
8352 for (vector<RegionView*>::iterator i = new_selection.begin(); i != new_selection.end(); ++i) {
8353 selection->add (*i);
8356 commit_reversible_command ();
8360 Editor::uncombine_regions ()
8362 typedef set<RouteTimeAxisView*> RTVS;
8365 if (selection->regions.empty()) {
8369 for (RegionSelection::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
8370 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*>(&(*i)->get_time_axis_view());
8373 tracks.insert (rtv);
8377 begin_reversible_command (_("uncombine regions"));
8379 for (RTVS::iterator i = tracks.begin(); i != tracks.end(); ++i) {
8380 (*i)->uncombine_regions ();
8383 commit_reversible_command ();
8387 Editor::toggle_midi_input_active (bool flip_others)
8390 boost::shared_ptr<RouteList> rl (new RouteList);
8392 for (TrackSelection::iterator i = selection->tracks.begin(); i != selection->tracks.end(); ++i) {
8393 RouteTimeAxisView *rtav = dynamic_cast<RouteTimeAxisView *>(*i);
8399 boost::shared_ptr<MidiTrack> mt = rtav->midi_track();
8402 rl->push_back (rtav->route());
8403 onoff = !mt->input_active();
8407 _session->set_exclusive_input_active (rl, onoff, flip_others);
8410 static bool ok_fine (GdkEventAny*) { return true; }
8416 lock_dialog = new Gtk::Dialog (string_compose (_("%1: Locked"), PROGRAM_NAME), true);
8418 Gtk::Image* padlock = manage (new Gtk::Image (ARDOUR_UI_UTILS::get_icon ("padlock_closed")));
8419 lock_dialog->get_vbox()->pack_start (*padlock);
8420 lock_dialog->signal_delete_event ().connect (sigc::ptr_fun (ok_fine));
8422 ArdourButton* b = manage (new ArdourButton);
8423 b->set_name ("lock button");
8424 b->set_text (_("Click to unlock"));
8425 b->signal_clicked.connect (sigc::mem_fun (*this, &Editor::unlock));
8426 lock_dialog->get_vbox()->pack_start (*b);
8428 lock_dialog->get_vbox()->show_all ();
8429 lock_dialog->set_size_request (200, 200);
8432 delete _main_menu_disabler;
8433 _main_menu_disabler = new MainMenuDisabler;
8435 lock_dialog->present ();
8437 lock_dialog->get_window()->set_decorations (Gdk::WMDecoration (0));
8443 lock_dialog->hide ();
8445 delete _main_menu_disabler;
8446 _main_menu_disabler = 0;
8448 if (UIConfiguration::instance().get_lock_gui_after_seconds()) {
8449 start_lock_event_timing ();
8454 Editor::bring_in_callback (Gtk::Label* label, uint32_t n, uint32_t total, string name)
8456 Gtkmm2ext::UI::instance()->call_slot (invalidator (*this), boost::bind (&Editor::update_bring_in_message, this, label, n, total, name));
8460 Editor::update_bring_in_message (Gtk::Label* label, uint32_t n, uint32_t total, string name)
8462 Timers::TimerSuspender t;
8463 label->set_text (string_compose ("Copying %1, %2 of %3", name, n, total));
8464 Gtkmm2ext::UI::instance()->flush_pending (1);
8468 Editor::bring_all_sources_into_session ()
8475 ArdourDialog w (_("Moving embedded files into session folder"));
8476 w.get_vbox()->pack_start (msg);
8479 /* flush all pending GUI events because we're about to start copying
8483 Timers::TimerSuspender t;
8484 Gtkmm2ext::UI::instance()->flush_pending (3);
8488 _session->bring_all_sources_into_session (boost::bind (&Editor::bring_in_callback, this, &msg, _1, _2, _3));