2 Copyright (C) 2000-2004 Paul Davis
4 This program is free software; you can redistribute it and/or modify
5 it under the terms of the GNU General Public License as published by
6 the Free Software Foundation; either version 2 of the License, or
7 (at your option) any later version.
9 This program is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 GNU General Public License for more details.
14 You should have received a copy of the GNU General Public License
15 along with this program; if not, write to the Free Software
16 Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
20 /* Note: public Editor methods are documented in public_editor.h */
31 #include <gtkmm/messagedialog.h>
33 #include "pbd/error.h"
34 #include "pbd/basename.h"
35 #include "pbd/pthread_utils.h"
36 #include "pbd/memento_command.h"
37 #include "pbd/unwind.h"
38 #include "pbd/whitespace.h"
39 #include "pbd/stateful_diff_command.h"
41 #include "gtkmm2ext/utils.h"
43 #include "widgets/choice.h"
44 #include "widgets/popup.h"
45 #include "widgets/prompter.h"
47 #include "ardour/audio_track.h"
48 #include "ardour/audioregion.h"
49 #include "ardour/boost_debug.h"
50 #include "ardour/dB.h"
51 #include "ardour/location.h"
52 #include "ardour/midi_region.h"
53 #include "ardour/midi_track.h"
54 #include "ardour/operations.h"
55 #include "ardour/playlist_factory.h"
56 #include "ardour/profile.h"
57 #include "ardour/quantize.h"
58 #include "ardour/legatize.h"
59 #include "ardour/region_factory.h"
60 #include "ardour/reverse.h"
61 #include "ardour/selection.h"
62 #include "ardour/session.h"
63 #include "ardour/session_playlists.h"
64 #include "ardour/strip_silence.h"
65 #include "ardour/transient_detector.h"
66 #include "ardour/transport_master_manager.h"
67 #include "ardour/transpose.h"
68 #include "ardour/vca_manager.h"
70 #include "canvas/canvas.h"
73 #include "ardour_ui.h"
74 #include "audio_region_view.h"
75 #include "audio_streamview.h"
76 #include "audio_time_axis.h"
77 #include "automation_region_view.h"
78 #include "automation_time_axis.h"
79 #include "control_point.h"
83 #include "editor_cursors.h"
84 #include "editor_drag.h"
85 #include "editor_regions.h"
86 #include "editor_routes.h"
87 #include "gui_thread.h"
88 #include "insert_remove_time_dialog.h"
89 #include "interthread_progress_window.h"
90 #include "item_counts.h"
92 #include "midi_region_view.h"
94 #include "mixer_strip.h"
95 #include "mouse_cursors.h"
96 #include "normalize_dialog.h"
98 #include "paste_context.h"
99 #include "patch_change_dialog.h"
100 #include "quantize_dialog.h"
101 #include "region_gain_line.h"
102 #include "rgb_macros.h"
103 #include "route_time_axis.h"
104 #include "selection.h"
105 #include "selection_templates.h"
106 #include "streamview.h"
107 #include "strip_silence_dialog.h"
108 #include "time_axis_view.h"
110 #include "transpose_dialog.h"
111 #include "transform_dialog.h"
112 #include "ui_config.h"
114 #include "vca_time_axis.h"
116 #include "pbd/i18n.h"
119 using namespace ARDOUR;
122 using namespace Gtkmm2ext;
123 using namespace ArdourWidgets;
124 using namespace Editing;
125 using Gtkmm2ext::Keyboard;
127 /***********************************************************************
129 ***********************************************************************/
132 Editor::undo (uint32_t n)
134 if (_session && _session->actively_recording()) {
135 /* no undo allowed while recording. Session will check also,
136 but we don't even want to get to that.
141 if (_drags->active ()) {
147 if (_session->undo_depth() == 0) {
148 undo_action->set_sensitive(false);
150 redo_action->set_sensitive(true);
151 begin_selection_op_history ();
156 Editor::redo (uint32_t n)
158 if (_session && _session->actively_recording()) {
159 /* no redo allowed while recording. Session will check also,
160 but we don't even want to get to that.
165 if (_drags->active ()) {
171 if (_session->redo_depth() == 0) {
172 redo_action->set_sensitive(false);
174 undo_action->set_sensitive(true);
175 begin_selection_op_history ();
180 Editor::split_regions_at (MusicSample where, RegionSelection& regions)
184 list<boost::shared_ptr<Playlist> > used_playlists;
185 list<RouteTimeAxisView*> used_trackviews;
187 if (regions.empty()) {
191 begin_reversible_command (_("split"));
194 if (regions.size() == 1) {
195 /* TODO: if splitting a single region, and snap-to is using
196 region boundaries, mabye we shouldn't pay attention to them? */
199 EditorFreeze(); /* Emit Signal */
202 for (RegionSelection::iterator a = regions.begin(); a != regions.end(); ) {
204 RegionSelection::iterator tmp;
206 /* XXX this test needs to be more complicated, to make sure we really
207 have something to split.
210 if (!(*a)->region()->covers (where.sample)) {
218 boost::shared_ptr<Playlist> pl = (*a)->region()->playlist();
226 /* we haven't seen this playlist before */
228 /* remember used playlists so we can thaw them later */
229 used_playlists.push_back(pl);
231 TimeAxisView& tv = (*a)->get_time_axis_view();
232 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (&tv);
234 used_trackviews.push_back (rtv);
241 pl->clear_changes ();
242 pl->split_region ((*a)->region(), where);
243 _session->add_command (new StatefulDiffCommand (pl));
249 latest_regionviews.clear ();
251 vector<sigc::connection> region_added_connections;
253 for (list<RouteTimeAxisView*>::iterator i = used_trackviews.begin(); i != used_trackviews.end(); ++i) {
254 region_added_connections.push_back ((*i)->view()->RegionViewAdded.connect (sigc::mem_fun(*this, &Editor::collect_new_region_view)));
257 while (used_playlists.size() > 0) {
258 list <boost::shared_ptr<Playlist > >::iterator i = used_playlists.begin();
260 used_playlists.pop_front();
263 for (vector<sigc::connection>::iterator c = region_added_connections.begin(); c != region_added_connections.end(); ++c) {
268 EditorThaw(); /* Emit Signal */
271 RegionSelectionAfterSplit rsas = Config->get_region_selection_after_split();
273 //if the user has "Clear Selection" as their post-split behavior, then clear the selection
274 if (!latest_regionviews.empty() && (rsas == None)) {
275 selection->clear_objects();
276 selection->clear_time();
277 //but leave track selection intact
280 //if the user doesn't want to preserve the "Existing" selection, then clear the selection
281 if (!(rsas & Existing)) {
282 selection->clear_objects();
283 selection->clear_time();
286 //if the user wants newly-created regions to be selected, then select them:
287 if (mouse_mode == MouseObject) {
288 for (RegionSelection::iterator ri = latest_regionviews.begin(); ri != latest_regionviews.end(); ri++) {
289 if ((*ri)->region()->position() < where.sample) {
290 // new regions created before the split
291 if (rsas & NewlyCreatedLeft) {
292 selection->add (*ri);
295 // new regions created after the split
296 if (rsas & NewlyCreatedRight) {
297 selection->add (*ri);
303 commit_reversible_command ();
306 /** Move one extreme of the current range selection. If more than one range is selected,
307 * the start of the earliest range or the end of the latest range is moved.
309 * @param move_end true to move the end of the current range selection, false to move
311 * @param next true to move the extreme to the next region boundary, false to move to
315 Editor::move_range_selection_start_or_end_to_region_boundary (bool move_end, bool next)
317 if (selection->time.start() == selection->time.end_sample()) {
321 samplepos_t start = selection->time.start ();
322 samplepos_t end = selection->time.end_sample ();
324 /* the position of the thing we may move */
325 samplepos_t pos = move_end ? end : start;
326 int dir = next ? 1 : -1;
328 /* so we don't find the current region again */
329 if (dir > 0 || pos > 0) {
333 samplepos_t const target = get_region_boundary (pos, dir, true, false);
348 begin_reversible_selection_op (_("alter selection"));
349 selection->set_preserving_all_ranges (start, end);
350 commit_reversible_selection_op ();
354 Editor::nudge_forward_release (GdkEventButton* ev)
356 if (ev->state & Keyboard::PrimaryModifier) {
357 nudge_forward (false, true);
359 nudge_forward (false, false);
365 Editor::nudge_backward_release (GdkEventButton* ev)
367 if (ev->state & Keyboard::PrimaryModifier) {
368 nudge_backward (false, true);
370 nudge_backward (false, false);
377 Editor::nudge_forward (bool next, bool force_playhead)
379 samplepos_t distance;
380 samplepos_t next_distance;
386 RegionSelection rs = get_regions_from_selection_and_entered ();
388 if (!force_playhead && !rs.empty()) {
390 begin_reversible_command (_("nudge regions forward"));
392 for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
393 boost::shared_ptr<Region> r ((*i)->region());
395 distance = get_nudge_distance (r->position(), next_distance);
398 distance = next_distance;
402 r->set_position (r->position() + distance);
403 _session->add_command (new StatefulDiffCommand (r));
406 commit_reversible_command ();
409 } else if (!force_playhead && !selection->markers.empty()) {
412 bool in_command = false;
413 const int32_t divisions = get_grid_music_divisions (0);
415 for (MarkerSelection::iterator i = selection->markers.begin(); i != selection->markers.end(); ++i) {
417 Location* loc = find_location_from_marker ((*i), is_start);
421 XMLNode& before (loc->get_state());
424 distance = get_nudge_distance (loc->start(), next_distance);
426 distance = next_distance;
428 if (max_samplepos - distance > loc->start() + loc->length()) {
429 loc->set_start (loc->start() + distance, false, true, divisions);
431 loc->set_start (max_samplepos - loc->length(), false, true, divisions);
434 distance = get_nudge_distance (loc->end(), next_distance);
436 distance = next_distance;
438 if (max_samplepos - distance > loc->end()) {
439 loc->set_end (loc->end() + distance, false, true, divisions);
441 loc->set_end (max_samplepos, false, true, divisions);
443 if (loc->is_session_range()) {
444 _session->set_session_range_is_free (false);
448 begin_reversible_command (_("nudge location forward"));
451 XMLNode& after (loc->get_state());
452 _session->add_command (new MementoCommand<Location>(*loc, &before, &after));
457 commit_reversible_command ();
460 distance = get_nudge_distance (playhead_cursor->current_sample (), next_distance);
461 _session->request_locate (playhead_cursor->current_sample () + distance);
466 Editor::nudge_backward (bool next, bool force_playhead)
468 samplepos_t distance;
469 samplepos_t next_distance;
475 RegionSelection rs = get_regions_from_selection_and_entered ();
477 if (!force_playhead && !rs.empty()) {
479 begin_reversible_command (_("nudge regions backward"));
481 for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
482 boost::shared_ptr<Region> r ((*i)->region());
484 distance = get_nudge_distance (r->position(), next_distance);
487 distance = next_distance;
492 if (r->position() > distance) {
493 r->set_position (r->position() - distance);
497 _session->add_command (new StatefulDiffCommand (r));
500 commit_reversible_command ();
502 } else if (!force_playhead && !selection->markers.empty()) {
505 bool in_command = false;
507 for (MarkerSelection::iterator i = selection->markers.begin(); i != selection->markers.end(); ++i) {
509 Location* loc = find_location_from_marker ((*i), is_start);
513 XMLNode& before (loc->get_state());
516 distance = get_nudge_distance (loc->start(), next_distance);
518 distance = next_distance;
520 if (distance < loc->start()) {
521 loc->set_start (loc->start() - distance, false, true, get_grid_music_divisions(0));
523 loc->set_start (0, false, true, get_grid_music_divisions(0));
526 distance = get_nudge_distance (loc->end(), next_distance);
529 distance = next_distance;
532 if (distance < loc->end() - loc->length()) {
533 loc->set_end (loc->end() - distance, false, true, get_grid_music_divisions(0));
535 loc->set_end (loc->length(), false, true, get_grid_music_divisions(0));
537 if (loc->is_session_range()) {
538 _session->set_session_range_is_free (false);
542 begin_reversible_command (_("nudge location forward"));
545 XMLNode& after (loc->get_state());
546 _session->add_command (new MementoCommand<Location>(*loc, &before, &after));
550 commit_reversible_command ();
555 distance = get_nudge_distance (playhead_cursor->current_sample (), next_distance);
557 if (playhead_cursor->current_sample () > distance) {
558 _session->request_locate (playhead_cursor->current_sample () - distance);
560 _session->goto_start();
566 Editor::nudge_forward_capture_offset ()
568 RegionSelection rs = get_regions_from_selection_and_entered ();
570 if (!_session || rs.empty()) {
574 begin_reversible_command (_("nudge forward"));
576 samplepos_t const distance = _session->worst_output_latency();
578 for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
579 boost::shared_ptr<Region> r ((*i)->region());
582 r->set_position (r->position() + distance);
583 _session->add_command(new StatefulDiffCommand (r));
586 commit_reversible_command ();
590 Editor::nudge_backward_capture_offset ()
592 RegionSelection rs = get_regions_from_selection_and_entered ();
594 if (!_session || rs.empty()) {
598 begin_reversible_command (_("nudge backward"));
600 samplepos_t const distance = _session->worst_output_latency();
602 for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
603 boost::shared_ptr<Region> r ((*i)->region());
607 if (r->position() > distance) {
608 r->set_position (r->position() - distance);
612 _session->add_command(new StatefulDiffCommand (r));
615 commit_reversible_command ();
618 struct RegionSelectionPositionSorter {
619 bool operator() (RegionView* a, RegionView* b) {
620 return a->region()->position() < b->region()->position();
625 Editor::sequence_regions ()
628 samplepos_t r_end_prev;
636 RegionSelection rs = get_regions_from_selection_and_entered ();
637 rs.sort(RegionSelectionPositionSorter());
641 bool in_command = false;
643 for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
644 boost::shared_ptr<Region> r ((*i)->region());
652 if(r->position_locked())
659 r->set_position(r_end_prev);
663 begin_reversible_command (_("sequence regions"));
666 _session->add_command (new StatefulDiffCommand (r));
668 r_end=r->position() + r->length();
674 commit_reversible_command ();
683 Editor::move_to_start ()
685 _session->goto_start ();
689 Editor::move_to_end ()
692 _session->request_locate (_session->current_end_sample());
696 Editor::build_region_boundary_cache ()
699 /* TODO: maybe set a timer so we don't recalutate when lots of changes are coming in */
700 /* TODO: maybe somehow defer this until session is fully loaded. */
702 if (!_region_boundary_cache_dirty)
706 vector<RegionPoint> interesting_points;
707 boost::shared_ptr<Region> r;
708 TrackViewList tracks;
711 region_boundary_cache.clear ();
717 bool maybe_first_sample = false;
719 if (UIConfiguration::instance().get_snap_to_region_start()) {
720 interesting_points.push_back (Start);
721 maybe_first_sample = true;
724 if (UIConfiguration::instance().get_snap_to_region_end()) {
725 interesting_points.push_back (End);
728 if (UIConfiguration::instance().get_snap_to_region_sync()) {
729 interesting_points.push_back (SyncPoint);
732 /* if no snap selections are set, boundary cache should be left empty */
733 if ( interesting_points.empty() ) {
734 _region_boundary_cache_dirty = false;
738 TimeAxisView *ontrack = 0;
741 tlist = track_views.filter_to_unique_playlists ();
743 if (maybe_first_sample) {
744 TrackViewList::const_iterator i;
745 for (i = tlist.begin(); i != tlist.end(); ++i) {
746 boost::shared_ptr<Playlist> pl = (*i)->playlist();
747 if (pl && pl->count_regions_at (0)) {
748 region_boundary_cache.push_back (0);
754 //allow regions to snap to the video start (if any) as if it were a "region"
755 if (ARDOUR_UI::instance()->video_timeline) {
756 region_boundary_cache.push_back (ARDOUR_UI::instance()->video_timeline->get_video_start_offset());
759 std::pair<samplepos_t, samplepos_t> ext = session_gui_extents (false);
760 samplepos_t session_end = ext.second;
762 while (pos < session_end && !at_end) {
765 samplepos_t lpos = session_end;
767 for (vector<RegionPoint>::iterator p = interesting_points.begin(); p != interesting_points.end(); ++p) {
769 if ((r = find_next_region (pos, *p, 1, tlist, &ontrack)) == 0) {
770 if (*p == interesting_points.back()) {
773 /* move to next point type */
779 rpos = r->first_sample();
783 rpos = r->last_sample();
787 rpos = r->sync_position ();
798 /* prevent duplicates, but we don't use set<> because we want to be able
802 vector<samplepos_t>::iterator ri;
804 for (ri = region_boundary_cache.begin(); ri != region_boundary_cache.end(); ++ri) {
810 if (ri == region_boundary_cache.end()) {
811 region_boundary_cache.push_back (rpos);
818 /* finally sort to be sure that the order is correct */
820 sort (region_boundary_cache.begin(), region_boundary_cache.end());
822 _region_boundary_cache_dirty = false;
825 boost::shared_ptr<Region>
826 Editor::find_next_region (samplepos_t sample, RegionPoint point, int32_t dir, TrackViewList& tracks, TimeAxisView **ontrack)
828 TrackViewList::iterator i;
829 samplepos_t closest = max_samplepos;
830 boost::shared_ptr<Region> ret;
831 samplepos_t rpos = 0;
833 samplepos_t track_sample;
835 for (i = tracks.begin(); i != tracks.end(); ++i) {
837 samplecnt_t distance;
838 boost::shared_ptr<Region> r;
840 track_sample = sample;
842 if ((r = (*i)->find_next_region (track_sample, point, dir)) == 0) {
848 rpos = r->first_sample ();
852 rpos = r->last_sample ();
856 rpos = r->sync_position ();
861 distance = rpos - sample;
863 distance = sample - rpos;
866 if (distance < closest) {
878 Editor::find_next_region_boundary (samplepos_t pos, int32_t dir, const TrackViewList& tracks)
880 samplecnt_t distance = max_samplepos;
881 samplepos_t current_nearest = -1;
883 for (TrackViewList::const_iterator i = tracks.begin(); i != tracks.end(); ++i) {
884 samplepos_t contender;
887 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (*i);
893 if ((contender = rtv->find_next_region_boundary (pos, dir)) < 0) {
897 d = ::llabs (pos - contender);
900 current_nearest = contender;
905 return current_nearest;
909 Editor::get_region_boundary (samplepos_t pos, int32_t dir, bool with_selection, bool only_onscreen)
914 if (with_selection && Config->get_region_boundaries_from_selected_tracks()) {
916 if (!selection->tracks.empty()) {
918 target = find_next_region_boundary (pos, dir, selection->tracks);
922 if (only_onscreen || Config->get_region_boundaries_from_onscreen_tracks()) {
923 get_onscreen_tracks (tvl);
924 target = find_next_region_boundary (pos, dir, tvl);
926 target = find_next_region_boundary (pos, dir, track_views);
932 if (only_onscreen || Config->get_region_boundaries_from_onscreen_tracks()) {
933 get_onscreen_tracks (tvl);
934 target = find_next_region_boundary (pos, dir, tvl);
936 target = find_next_region_boundary (pos, dir, track_views);
944 Editor::cursor_to_region_boundary (bool with_selection, int32_t dir)
946 samplepos_t pos = playhead_cursor->current_sample ();
953 // so we don't find the current region again..
954 if (dir > 0 || pos > 0) {
958 if ((target = get_region_boundary (pos, dir, with_selection, false)) < 0) {
962 _session->request_locate (target);
966 Editor::cursor_to_next_region_boundary (bool with_selection)
968 cursor_to_region_boundary (with_selection, 1);
972 Editor::cursor_to_previous_region_boundary (bool with_selection)
974 cursor_to_region_boundary (with_selection, -1);
978 Editor::cursor_to_region_point (EditorCursor* cursor, RegionPoint point, int32_t dir)
980 boost::shared_ptr<Region> r;
981 samplepos_t pos = cursor->current_sample ();
987 TimeAxisView *ontrack = 0;
989 // so we don't find the current region again..
993 if (!selection->tracks.empty()) {
995 r = find_next_region (pos, point, dir, selection->tracks, &ontrack);
997 } else if (clicked_axisview) {
1000 t.push_back (clicked_axisview);
1002 r = find_next_region (pos, point, dir, t, &ontrack);
1006 r = find_next_region (pos, point, dir, track_views, &ontrack);
1015 pos = r->first_sample ();
1019 pos = r->last_sample ();
1023 pos = r->sync_position ();
1027 if (cursor == playhead_cursor) {
1028 _session->request_locate (pos);
1030 cursor->set_position (pos);
1035 Editor::cursor_to_next_region_point (EditorCursor* cursor, RegionPoint point)
1037 cursor_to_region_point (cursor, point, 1);
1041 Editor::cursor_to_previous_region_point (EditorCursor* cursor, RegionPoint point)
1043 cursor_to_region_point (cursor, point, -1);
1047 Editor::cursor_to_selection_start (EditorCursor *cursor)
1049 samplepos_t pos = 0;
1051 switch (mouse_mode) {
1053 if (!selection->regions.empty()) {
1054 pos = selection->regions.start();
1059 if (!selection->time.empty()) {
1060 pos = selection->time.start ();
1068 if (cursor == playhead_cursor) {
1069 _session->request_locate (pos);
1071 cursor->set_position (pos);
1076 Editor::cursor_to_selection_end (EditorCursor *cursor)
1078 samplepos_t pos = 0;
1080 switch (mouse_mode) {
1082 if (!selection->regions.empty()) {
1083 pos = selection->regions.end_sample();
1088 if (!selection->time.empty()) {
1089 pos = selection->time.end_sample ();
1097 if (cursor == playhead_cursor) {
1098 _session->request_locate (pos);
1100 cursor->set_position (pos);
1105 Editor::selected_marker_to_region_boundary (bool with_selection, int32_t dir)
1115 if (selection->markers.empty()) {
1119 if (!mouse_sample (mouse, ignored)) {
1123 add_location_mark (mouse);
1126 if ((loc = find_location_from_marker (selection->markers.front(), ignored)) == 0) {
1130 samplepos_t pos = loc->start();
1132 // so we don't find the current region again..
1133 if (dir > 0 || pos > 0) {
1137 if ((target = get_region_boundary (pos, dir, with_selection, false)) < 0) {
1141 loc->move_to (target, 0);
1145 Editor::selected_marker_to_next_region_boundary (bool with_selection)
1147 selected_marker_to_region_boundary (with_selection, 1);
1151 Editor::selected_marker_to_previous_region_boundary (bool with_selection)
1153 selected_marker_to_region_boundary (with_selection, -1);
1157 Editor::selected_marker_to_region_point (RegionPoint point, int32_t dir)
1159 boost::shared_ptr<Region> r;
1164 if (!_session || selection->markers.empty()) {
1168 if ((loc = find_location_from_marker (selection->markers.front(), ignored)) == 0) {
1172 TimeAxisView *ontrack = 0;
1176 // so we don't find the current region again..
1180 if (!selection->tracks.empty()) {
1182 r = find_next_region (pos, point, dir, selection->tracks, &ontrack);
1186 r = find_next_region (pos, point, dir, track_views, &ontrack);
1195 pos = r->first_sample ();
1199 pos = r->last_sample ();
1203 pos = r->adjust_to_sync (r->first_sample());
1207 loc->move_to (pos, 0);
1211 Editor::selected_marker_to_next_region_point (RegionPoint point)
1213 selected_marker_to_region_point (point, 1);
1217 Editor::selected_marker_to_previous_region_point (RegionPoint point)
1219 selected_marker_to_region_point (point, -1);
1223 Editor::selected_marker_to_selection_start ()
1225 samplepos_t pos = 0;
1229 if (!_session || selection->markers.empty()) {
1233 if ((loc = find_location_from_marker (selection->markers.front(), ignored)) == 0) {
1237 switch (mouse_mode) {
1239 if (!selection->regions.empty()) {
1240 pos = selection->regions.start();
1245 if (!selection->time.empty()) {
1246 pos = selection->time.start ();
1254 loc->move_to (pos, 0);
1258 Editor::selected_marker_to_selection_end ()
1260 samplepos_t pos = 0;
1264 if (!_session || selection->markers.empty()) {
1268 if ((loc = find_location_from_marker (selection->markers.front(), ignored)) == 0) {
1272 switch (mouse_mode) {
1274 if (!selection->regions.empty()) {
1275 pos = selection->regions.end_sample();
1280 if (!selection->time.empty()) {
1281 pos = selection->time.end_sample ();
1289 loc->move_to (pos, 0);
1293 Editor::scroll_playhead (bool forward)
1295 samplepos_t pos = playhead_cursor->current_sample ();
1296 samplecnt_t delta = (samplecnt_t) floor (current_page_samples() / 0.8);
1299 if (pos == max_samplepos) {
1303 if (pos < max_samplepos - delta) {
1306 pos = max_samplepos;
1322 _session->request_locate (pos);
1326 Editor::cursor_align (bool playhead_to_edit)
1332 if (playhead_to_edit) {
1334 if (selection->markers.empty()) {
1338 _session->request_locate (selection->markers.front()->position(), _session->transport_rolling());
1341 const int32_t divisions = get_grid_music_divisions (0);
1342 /* move selected markers to playhead */
1344 for (MarkerSelection::iterator i = selection->markers.begin(); i != selection->markers.end(); ++i) {
1347 Location* loc = find_location_from_marker (*i, ignored);
1349 if (loc->is_mark()) {
1350 loc->set_start (playhead_cursor->current_sample (), false, true, divisions);
1352 loc->set (playhead_cursor->current_sample (),
1353 playhead_cursor->current_sample () + loc->length(), true, divisions);
1360 Editor::scroll_backward (float pages)
1362 samplepos_t const one_page = (samplepos_t) rint (_visible_canvas_width * samples_per_pixel);
1363 samplepos_t const cnt = (samplepos_t) floor (pages * one_page);
1366 if (_leftmost_sample < cnt) {
1369 sample = _leftmost_sample - cnt;
1372 reset_x_origin (sample);
1376 Editor::scroll_forward (float pages)
1378 samplepos_t const one_page = (samplepos_t) rint (_visible_canvas_width * samples_per_pixel);
1379 samplepos_t const cnt = (samplepos_t) floor (pages * one_page);
1382 if (max_samplepos - cnt < _leftmost_sample) {
1383 sample = max_samplepos - cnt;
1385 sample = _leftmost_sample + cnt;
1388 reset_x_origin (sample);
1392 Editor::scroll_tracks_down ()
1394 double vert_value = vertical_adjustment.get_value() + vertical_adjustment.get_page_size();
1395 if (vert_value > vertical_adjustment.get_upper() - _visible_canvas_height) {
1396 vert_value = vertical_adjustment.get_upper() - _visible_canvas_height;
1399 vertical_adjustment.set_value (vert_value);
1403 Editor::scroll_tracks_up ()
1405 vertical_adjustment.set_value (vertical_adjustment.get_value() - vertical_adjustment.get_page_size());
1409 Editor::scroll_tracks_down_line ()
1411 double vert_value = vertical_adjustment.get_value() + 60;
1413 if (vert_value > vertical_adjustment.get_upper() - _visible_canvas_height) {
1414 vert_value = vertical_adjustment.get_upper() - _visible_canvas_height;
1417 vertical_adjustment.set_value (vert_value);
1421 Editor::scroll_tracks_up_line ()
1423 reset_y_origin (vertical_adjustment.get_value() - 60);
1427 Editor::select_topmost_track ()
1429 const double top_of_trackviews = vertical_adjustment.get_value();
1430 for (TrackViewList::iterator t = track_views.begin(); t != track_views.end(); ++t) {
1431 if ((*t)->hidden()) {
1434 std::pair<TimeAxisView*,double> res = (*t)->covers_y_position (top_of_trackviews);
1436 selection->set (*t);
1443 Editor::scroll_down_one_track (bool skip_child_views)
1445 TrackViewList::reverse_iterator next = track_views.rend();
1446 const double top_of_trackviews = vertical_adjustment.get_value();
1448 for (TrackViewList::reverse_iterator t = track_views.rbegin(); t != track_views.rend(); ++t) {
1449 if ((*t)->hidden()) {
1453 /* If this is the upper-most visible trackview, we want to display
1454 * the one above it (next)
1456 * Note that covers_y_position() is recursive and includes child views
1458 std::pair<TimeAxisView*,double> res = (*t)->covers_y_position (top_of_trackviews);
1461 if (skip_child_views) {
1464 /* automation lane (one level, non-recursive)
1466 * - if no automation lane exists -> move to next tack
1467 * - if the first (here: bottom-most) matches -> move to next tack
1468 * - if no y-axis match is found -> the current track is at the top
1469 * -> move to last (here: top-most) automation lane
1471 TimeAxisView::Children kids = (*t)->get_child_list();
1472 TimeAxisView::Children::reverse_iterator nkid = kids.rend();
1474 for (TimeAxisView::Children::reverse_iterator ci = kids.rbegin(); ci != kids.rend(); ++ci) {
1475 if ((*ci)->hidden()) {
1479 std::pair<TimeAxisView*,double> dev;
1480 dev = (*ci)->covers_y_position (top_of_trackviews);
1482 /* some automation lane is currently at the top */
1483 if (ci == kids.rbegin()) {
1484 /* first (bottom-most) autmation lane is at the top.
1485 * -> move to next track
1494 if (nkid != kids.rend()) {
1495 ensure_time_axis_view_is_visible (**nkid, true);
1503 /* move to the track below the first one that covers the */
1505 if (next != track_views.rend()) {
1506 ensure_time_axis_view_is_visible (**next, true);
1514 Editor::scroll_up_one_track (bool skip_child_views)
1516 TrackViewList::iterator prev = track_views.end();
1517 double top_of_trackviews = vertical_adjustment.get_value ();
1519 for (TrackViewList::iterator t = track_views.begin(); t != track_views.end(); ++t) {
1521 if ((*t)->hidden()) {
1525 /* find the trackview at the top of the trackview group
1527 * Note that covers_y_position() is recursive and includes child views
1529 std::pair<TimeAxisView*,double> res = (*t)->covers_y_position (top_of_trackviews);
1532 if (skip_child_views) {
1535 /* automation lane (one level, non-recursive)
1537 * - if no automation lane exists -> move to prev tack
1538 * - if no y-axis match is found -> the current track is at the top -> move to prev track
1539 * (actually last automation lane of previous track, see below)
1540 * - if first (top-most) lane is at the top -> move to this track
1541 * - else move up one lane
1543 TimeAxisView::Children kids = (*t)->get_child_list();
1544 TimeAxisView::Children::iterator pkid = kids.end();
1546 for (TimeAxisView::Children::iterator ci = kids.begin(); ci != kids.end(); ++ci) {
1547 if ((*ci)->hidden()) {
1551 std::pair<TimeAxisView*,double> dev;
1552 dev = (*ci)->covers_y_position (top_of_trackviews);
1554 /* some automation lane is currently at the top */
1555 if (ci == kids.begin()) {
1556 /* first (top-most) autmation lane is at the top.
1557 * jump directly to this track's top
1559 ensure_time_axis_view_is_visible (**t, true);
1562 else if (pkid != kids.end()) {
1563 /* some other automation lane is at the top.
1564 * move up to prev automation lane.
1566 ensure_time_axis_view_is_visible (**pkid, true);
1569 assert(0); // not reached
1580 if (prev != track_views.end()) {
1581 // move to bottom-most automation-lane of the previous track
1582 TimeAxisView::Children kids = (*prev)->get_child_list();
1583 TimeAxisView::Children::reverse_iterator pkid = kids.rend();
1584 if (!skip_child_views) {
1585 // find the last visible lane
1586 for (TimeAxisView::Children::reverse_iterator ci = kids.rbegin(); ci != kids.rend(); ++ci) {
1587 if (!(*ci)->hidden()) {
1593 if (pkid != kids.rend()) {
1594 ensure_time_axis_view_is_visible (**pkid, true);
1596 ensure_time_axis_view_is_visible (**prev, true);
1605 Editor::scroll_left_step ()
1607 samplepos_t xdelta = (current_page_samples() / 8);
1609 if (_leftmost_sample > xdelta) {
1610 reset_x_origin (_leftmost_sample - xdelta);
1618 Editor::scroll_right_step ()
1620 samplepos_t xdelta = (current_page_samples() / 8);
1622 if (max_samplepos - xdelta > _leftmost_sample) {
1623 reset_x_origin (_leftmost_sample + xdelta);
1625 reset_x_origin (max_samplepos - current_page_samples());
1630 Editor::scroll_left_half_page ()
1632 samplepos_t xdelta = (current_page_samples() / 2);
1633 if (_leftmost_sample > xdelta) {
1634 reset_x_origin (_leftmost_sample - xdelta);
1641 Editor::scroll_right_half_page ()
1643 samplepos_t xdelta = (current_page_samples() / 2);
1644 if (max_samplepos - xdelta > _leftmost_sample) {
1645 reset_x_origin (_leftmost_sample + xdelta);
1647 reset_x_origin (max_samplepos - current_page_samples());
1654 Editor::tav_zoom_step (bool coarser)
1656 DisplaySuspender ds;
1660 if (selection->tracks.empty()) {
1663 ts = &selection->tracks;
1666 for (TrackViewList::iterator i = ts->begin(); i != ts->end(); ++i) {
1667 TimeAxisView *tv = (static_cast<TimeAxisView*>(*i));
1668 tv->step_height (coarser);
1673 Editor::tav_zoom_smooth (bool coarser, bool force_all)
1675 DisplaySuspender ds;
1679 if (selection->tracks.empty() || force_all) {
1682 ts = &selection->tracks;
1685 for (TrackViewList::iterator i = ts->begin(); i != ts->end(); ++i) {
1686 TimeAxisView *tv = (static_cast<TimeAxisView*>(*i));
1687 uint32_t h = tv->current_height ();
1692 if (h >= TimeAxisView::preset_height (HeightSmall)) {
1697 tv->set_height (h + 5);
1703 Editor::temporal_zoom_step_mouse_focus_scale (bool zoom_out, double scale)
1705 Editing::ZoomFocus temp_focus = zoom_focus;
1706 zoom_focus = Editing::ZoomFocusMouse;
1707 temporal_zoom_step_scale (zoom_out, scale);
1708 zoom_focus = temp_focus;
1712 Editor::temporal_zoom_step_mouse_focus (bool zoom_out)
1714 temporal_zoom_step_mouse_focus_scale (zoom_out, 2.0);
1718 Editor::temporal_zoom_step (bool zoom_out)
1720 temporal_zoom_step_scale (zoom_out, 2.0);
1724 Editor::temporal_zoom_step_scale (bool zoom_out, double scale)
1726 ENSURE_GUI_THREAD (*this, &Editor::temporal_zoom_step, zoom_out, scale)
1728 samplecnt_t nspp = samples_per_pixel;
1732 if (nspp == samples_per_pixel) {
1737 if (nspp == samples_per_pixel) {
1742 //zoom-behavior-tweaks
1743 //limit our maximum zoom to the session gui extents value
1744 std::pair<samplepos_t, samplepos_t> ext = session_gui_extents();
1745 samplecnt_t session_extents_pp = (ext.second - ext.first) / _visible_canvas_width;
1746 if (nspp > session_extents_pp)
1747 nspp = session_extents_pp;
1749 temporal_zoom (nspp);
1753 Editor::temporal_zoom (samplecnt_t fpp)
1759 samplepos_t current_page = current_page_samples();
1760 samplepos_t current_leftmost = _leftmost_sample;
1761 samplepos_t current_rightmost;
1762 samplepos_t current_center;
1763 samplepos_t new_page_size;
1764 samplepos_t half_page_size;
1765 samplepos_t leftmost_after_zoom = 0;
1767 bool in_track_canvas;
1768 bool use_mouse_sample = true;
1772 if (fpp == samples_per_pixel) {
1776 // Imposing an arbitrary limit to zoom out as too much zoom out produces
1777 // segfaults for lack of memory. If somebody decides this is not high enough I
1778 // believe it can be raisen to higher values but some limit must be in place.
1780 // This constant represents 1 day @ 48kHz on a 1600 pixel wide display
1781 // all of which is used for the editor track displays. The whole day
1782 // would be 4147200000 samples, so 2592000 samples per pixel.
1784 nfpp = min (fpp, (samplecnt_t) 2592000);
1785 nfpp = max ((samplecnt_t) 1, nfpp);
1787 new_page_size = (samplepos_t) floor (_visible_canvas_width * nfpp);
1788 half_page_size = new_page_size / 2;
1790 switch (zoom_focus) {
1792 leftmost_after_zoom = current_leftmost;
1795 case ZoomFocusRight:
1796 current_rightmost = _leftmost_sample + current_page;
1797 if (current_rightmost < new_page_size) {
1798 leftmost_after_zoom = 0;
1800 leftmost_after_zoom = current_rightmost - new_page_size;
1804 case ZoomFocusCenter:
1805 current_center = current_leftmost + (current_page/2);
1806 if (current_center < half_page_size) {
1807 leftmost_after_zoom = 0;
1809 leftmost_after_zoom = current_center - half_page_size;
1813 case ZoomFocusPlayhead:
1814 /* centre playhead */
1815 l = playhead_cursor->current_sample () - (new_page_size * 0.5);
1818 leftmost_after_zoom = 0;
1819 } else if (l > max_samplepos) {
1820 leftmost_after_zoom = max_samplepos - new_page_size;
1822 leftmost_after_zoom = (samplepos_t) l;
1826 case ZoomFocusMouse:
1827 /* try to keep the mouse over the same point in the display */
1829 if (_drags->active()) {
1830 where = _drags->current_pointer_sample ();
1831 } else if (!mouse_sample (where, in_track_canvas)) {
1832 use_mouse_sample = false;
1835 if (use_mouse_sample) {
1836 l = - ((new_page_size * ((where - current_leftmost)/(double)current_page)) - where);
1839 leftmost_after_zoom = 0;
1840 } else if (l > max_samplepos) {
1841 leftmost_after_zoom = max_samplepos - new_page_size;
1843 leftmost_after_zoom = (samplepos_t) l;
1846 /* use playhead instead */
1847 where = playhead_cursor->current_sample ();
1849 if (where < half_page_size) {
1850 leftmost_after_zoom = 0;
1852 leftmost_after_zoom = where - half_page_size;
1858 /* try to keep the edit point in the same place */
1859 where = get_preferred_edit_position ();
1863 double l = - ((new_page_size * ((where - current_leftmost)/(double)current_page)) - where);
1866 leftmost_after_zoom = 0;
1867 } else if (l > max_samplepos) {
1868 leftmost_after_zoom = max_samplepos - new_page_size;
1870 leftmost_after_zoom = (samplepos_t) l;
1874 /* edit point not defined */
1881 // leftmost_after_zoom = min (leftmost_after_zoom, _session->current_end_sample());
1883 reposition_and_zoom (leftmost_after_zoom, nfpp);
1887 Editor::calc_extra_zoom_edges(samplepos_t &start, samplepos_t &end)
1889 /* this func helps make sure we leave a little space
1890 at each end of the editor so that the zoom doesn't fit the region
1891 precisely to the screen.
1894 GdkScreen* screen = gdk_screen_get_default ();
1895 const gint pixwidth = gdk_screen_get_width (screen);
1896 const gint mmwidth = gdk_screen_get_width_mm (screen);
1897 const double pix_per_mm = (double) pixwidth/ (double) mmwidth;
1898 const double one_centimeter_in_pixels = pix_per_mm * 10.0;
1900 const samplepos_t range = end - start;
1901 const samplecnt_t new_fpp = (samplecnt_t) ceil ((double) range / (double) _visible_canvas_width);
1902 const samplepos_t extra_samples = (samplepos_t) floor (one_centimeter_in_pixels * new_fpp);
1904 if (start > extra_samples) {
1905 start -= extra_samples;
1910 if (max_samplepos - extra_samples > end) {
1911 end += extra_samples;
1913 end = max_samplepos;
1918 Editor::get_selection_extents (samplepos_t &start, samplepos_t &end) const
1920 start = max_samplepos;
1924 //ToDo: if notes are selected, set extents to that selection
1926 //ToDo: if control points are selected, set extents to that selection
1928 if (!selection->regions.empty()) {
1929 RegionSelection rs = get_regions_from_selection_and_entered ();
1931 for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
1933 if ((*i)->region()->position() < start) {
1934 start = (*i)->region()->position();
1937 if ((*i)->region()->last_sample() + 1 > end) {
1938 end = (*i)->region()->last_sample() + 1;
1942 } else if (!selection->time.empty()) {
1943 start = selection->time.start();
1944 end = selection->time.end_sample();
1946 ret = false; //no selection found
1949 if ((start == 0 && end == 0) || end < start) {
1958 Editor::temporal_zoom_selection (Editing::ZoomAxis axes)
1960 if (!selection) return;
1962 if (selection->regions.empty() && selection->time.empty()) {
1963 if (axes == Horizontal || axes == Both) {
1964 temporal_zoom_step(true);
1966 if (axes == Vertical || axes == Both) {
1967 if (!track_views.empty()) {
1971 //implicit hack: by extending the top & bottom check outside the current view limits, we include the trackviews immediately above & below what is visible
1972 const double top = vertical_adjustment.get_value() - 10;
1973 const double btm = top + _visible_canvas_height + 10;
1975 for (TrackViewList::iterator iter = track_views.begin(); iter != track_views.end(); ++iter) {
1976 if ((*iter)->covered_by_y_range (top, btm)) {
1977 tvl.push_back(*iter);
1987 //ToDo: if notes are selected, zoom to that
1989 //ToDo: if control points are selected, zoom to that
1991 if (axes == Horizontal || axes == Both) {
1993 samplepos_t start, end;
1994 if (get_selection_extents (start, end)) {
1995 calc_extra_zoom_edges (start, end);
1996 temporal_zoom_by_sample (start, end);
2000 if (axes == Vertical || axes == Both) {
2004 //normally, we don't do anything "automatic" to the user's selection.
2005 //but in this case, we will clear the selection after a zoom-to-selection.
2010 Editor::temporal_zoom_session ()
2012 ENSURE_GUI_THREAD (*this, &Editor::temporal_zoom_session)
2015 samplecnt_t start = _session->current_start_sample();
2016 samplecnt_t end = _session->current_end_sample();
2018 if (_session->actively_recording ()) {
2019 samplepos_t cur = playhead_cursor->current_sample ();
2021 /* recording beyond the end marker; zoom out
2022 * by 5 seconds more so that if 'follow
2023 * playhead' is active we don't immediately
2026 end = cur + _session->sample_rate() * 5;
2030 if ((start == 0 && end == 0) || end < start) {
2034 calc_extra_zoom_edges(start, end);
2036 temporal_zoom_by_sample (start, end);
2041 Editor::temporal_zoom_extents ()
2043 ENSURE_GUI_THREAD (*this, &Editor::temporal_zoom_extents)
2046 std::pair<samplepos_t, samplepos_t> ext = session_gui_extents (false); //in this case we want to zoom to the extents explicitly; ignore the users prefs for extra padding
2048 samplecnt_t start = ext.first;
2049 samplecnt_t end = ext.second;
2051 if (_session->actively_recording ()) {
2052 samplepos_t cur = playhead_cursor->current_sample ();
2054 /* recording beyond the end marker; zoom out
2055 * by 5 seconds more so that if 'follow
2056 * playhead' is active we don't immediately
2059 end = cur + _session->sample_rate() * 5;
2063 if ((start == 0 && end == 0) || end < start) {
2067 calc_extra_zoom_edges(start, end);
2069 temporal_zoom_by_sample (start, end);
2074 Editor::temporal_zoom_by_sample (samplepos_t start, samplepos_t end)
2076 if (!_session) return;
2078 if ((start == 0 && end == 0) || end < start) {
2082 samplepos_t range = end - start;
2084 const samplecnt_t new_fpp = (samplecnt_t) ceil ((double) range / (double) _visible_canvas_width);
2086 samplepos_t new_page = range;
2087 samplepos_t middle = (samplepos_t) floor ((double) start + ((double) range / 2.0f));
2088 samplepos_t new_leftmost = (samplepos_t) floor ((double) middle - ((double) new_page / 2.0f));
2090 if (new_leftmost > middle) {
2094 if (new_leftmost < 0) {
2098 reposition_and_zoom (new_leftmost, new_fpp);
2102 Editor::temporal_zoom_to_sample (bool coarser, samplepos_t sample)
2108 samplecnt_t range_before = sample - _leftmost_sample;
2109 samplecnt_t new_spp;
2112 if (samples_per_pixel <= 1) {
2115 new_spp = samples_per_pixel + (samples_per_pixel/2);
2117 range_before += range_before/2;
2119 if (samples_per_pixel >= 1) {
2120 new_spp = samples_per_pixel - (samples_per_pixel/2);
2122 /* could bail out here since we cannot zoom any finer,
2123 but leave that to the equality test below
2125 new_spp = samples_per_pixel;
2128 range_before -= range_before/2;
2131 if (new_spp == samples_per_pixel) {
2135 /* zoom focus is automatically taken as @param sample when this
2139 samplepos_t new_leftmost = sample - (samplepos_t)range_before;
2141 if (new_leftmost > sample) {
2145 if (new_leftmost < 0) {
2149 reposition_and_zoom (new_leftmost, new_spp);
2154 Editor::choose_new_marker_name(string &name, bool is_range) {
2156 if (!UIConfiguration::instance().get_name_new_markers()) {
2157 /* don't prompt user for a new name */
2161 Prompter dialog (true);
2163 dialog.set_prompt (_("New Name:"));
2166 dialog.set_title(_("New Range"));
2168 dialog.set_title (_("New Location Marker"));
2171 dialog.set_name ("MarkNameWindow");
2172 dialog.set_size_request (250, -1);
2173 dialog.set_position (Gtk::WIN_POS_MOUSE);
2175 dialog.add_button (Stock::OK, RESPONSE_ACCEPT);
2176 dialog.set_initial_text (name);
2180 switch (dialog.run ()) {
2181 case RESPONSE_ACCEPT:
2187 dialog.get_result(name);
2194 Editor::add_location_from_selection ()
2198 if (selection->time.empty()) {
2202 if (_session == 0 || clicked_axisview == 0) {
2206 samplepos_t start = selection->time[clicked_selection].start;
2207 samplepos_t end = selection->time[clicked_selection].end;
2209 _session->locations()->next_available_name(rangename,"selection");
2210 if (!choose_new_marker_name(rangename, true)) {
2213 Location *location = new Location (*_session, start, end, rangename, Location::IsRangeMarker, get_grid_music_divisions(0));
2215 begin_reversible_command (_("add marker"));
2217 XMLNode &before = _session->locations()->get_state();
2218 _session->locations()->add (location, true);
2219 XMLNode &after = _session->locations()->get_state();
2220 _session->add_command(new MementoCommand<Locations>(*(_session->locations()), &before, &after));
2222 commit_reversible_command ();
2226 Editor::add_location_mark (samplepos_t where)
2230 select_new_marker = true;
2232 _session->locations()->next_available_name(markername,"mark");
2233 if (!choose_new_marker_name(markername)) {
2236 Location *location = new Location (*_session, where, where, markername, Location::IsMark, get_grid_music_divisions (0));
2237 begin_reversible_command (_("add marker"));
2239 XMLNode &before = _session->locations()->get_state();
2240 _session->locations()->add (location, true);
2241 XMLNode &after = _session->locations()->get_state();
2242 _session->add_command(new MementoCommand<Locations>(*(_session->locations()), &before, &after));
2244 commit_reversible_command ();
2248 Editor::set_session_start_from_playhead ()
2254 if ((loc = _session->locations()->session_range_location()) == 0) { //should never happen
2255 _session->set_session_extents (_session->audible_sample(), _session->audible_sample());
2257 XMLNode &before = loc->get_state();
2259 _session->set_session_extents (_session->audible_sample(), loc->end());
2261 XMLNode &after = loc->get_state();
2263 begin_reversible_command (_("Set session start"));
2265 _session->add_command (new MementoCommand<Location>(*loc, &before, &after));
2267 commit_reversible_command ();
2270 _session->set_session_range_is_free (false);
2274 Editor::set_session_end_from_playhead ()
2280 if ((loc = _session->locations()->session_range_location()) == 0) { //should never happen
2281 _session->set_session_extents (_session->audible_sample(), _session->audible_sample());
2283 XMLNode &before = loc->get_state();
2285 _session->set_session_extents (loc->start(), _session->audible_sample());
2287 XMLNode &after = loc->get_state();
2289 begin_reversible_command (_("Set session start"));
2291 _session->add_command (new MementoCommand<Location>(*loc, &before, &after));
2293 commit_reversible_command ();
2296 _session->set_session_range_is_free (false);
2301 Editor::toggle_location_at_playhead_cursor ()
2303 if (!do_remove_location_at_playhead_cursor())
2305 add_location_from_playhead_cursor();
2310 Editor::add_location_from_playhead_cursor ()
2312 add_location_mark (_session->audible_sample());
2316 Editor::do_remove_location_at_playhead_cursor ()
2318 bool removed = false;
2321 XMLNode &before = _session->locations()->get_state();
2323 //find location(s) at this time
2324 Locations::LocationList locs;
2325 _session->locations()->find_all_between (_session->audible_sample(), _session->audible_sample()+1, locs, Location::Flags(0));
2326 for (Locations::LocationList::iterator i = locs.begin(); i != locs.end(); ++i) {
2327 if ((*i)->is_mark()) {
2328 _session->locations()->remove (*i);
2335 begin_reversible_command (_("remove marker"));
2336 XMLNode &after = _session->locations()->get_state();
2337 _session->add_command(new MementoCommand<Locations>(*(_session->locations()), &before, &after));
2338 commit_reversible_command ();
2345 Editor::remove_location_at_playhead_cursor ()
2347 do_remove_location_at_playhead_cursor ();
2350 /** Add a range marker around each selected region */
2352 Editor::add_locations_from_region ()
2354 RegionSelection rs = get_regions_from_selection_and_entered ();
2359 bool commit = false;
2361 XMLNode &before = _session->locations()->get_state();
2363 for (RegionSelection::iterator i = rs.begin (); i != rs.end (); ++i) {
2365 boost::shared_ptr<Region> region = (*i)->region ();
2367 Location *location = new Location (*_session, region->position(), region->last_sample(), region->name(), Location::IsRangeMarker, 0);
2369 _session->locations()->add (location, true);
2374 begin_reversible_command (selection->regions.size () > 1 ? _("add markers") : _("add marker"));
2375 XMLNode &after = _session->locations()->get_state();
2376 _session->add_command (new MementoCommand<Locations>(*(_session->locations()), &before, &after));
2377 commit_reversible_command ();
2381 /** Add a single range marker around all selected regions */
2383 Editor::add_location_from_region ()
2385 RegionSelection rs = get_regions_from_selection_and_entered ();
2391 XMLNode &before = _session->locations()->get_state();
2395 if (rs.size() > 1) {
2396 _session->locations()->next_available_name(markername, "regions");
2398 RegionView* rv = *(rs.begin());
2399 boost::shared_ptr<Region> region = rv->region();
2400 markername = region->name();
2403 if (!choose_new_marker_name(markername)) {
2407 // single range spanning all selected
2408 Location *location = new Location (*_session, selection->regions.start(), selection->regions.end_sample(), markername, Location::IsRangeMarker, 0);
2409 _session->locations()->add (location, true);
2411 begin_reversible_command (_("add marker"));
2412 XMLNode &after = _session->locations()->get_state();
2413 _session->add_command (new MementoCommand<Locations>(*(_session->locations()), &before, &after));
2414 commit_reversible_command ();
2420 Editor::jump_forward_to_mark ()
2426 samplepos_t pos = _session->locations()->first_mark_after (playhead_cursor->current_sample());
2432 _session->request_locate (pos, _session->transport_rolling());
2436 Editor::jump_backward_to_mark ()
2442 samplepos_t pos = _session->locations()->first_mark_before (playhead_cursor->current_sample());
2444 //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...
2445 if (_session->transport_rolling()) {
2446 if ((playhead_cursor->current_sample() - pos) < _session->sample_rate()/2) {
2447 samplepos_t prior = _session->locations()->first_mark_before (pos);
2456 _session->request_locate (pos, _session->transport_rolling());
2462 samplepos_t const pos = _session->audible_sample ();
2465 _session->locations()->next_available_name (markername, "mark");
2467 if (!choose_new_marker_name (markername)) {
2471 _session->locations()->add (new Location (*_session, pos, 0, markername, Location::IsMark, 0), true);
2475 Editor::clear_markers ()
2478 begin_reversible_command (_("clear markers"));
2480 XMLNode &before = _session->locations()->get_state();
2481 _session->locations()->clear_markers ();
2482 XMLNode &after = _session->locations()->get_state();
2483 _session->add_command(new MementoCommand<Locations>(*(_session->locations()), &before, &after));
2485 commit_reversible_command ();
2490 Editor::clear_ranges ()
2493 begin_reversible_command (_("clear ranges"));
2495 XMLNode &before = _session->locations()->get_state();
2497 _session->locations()->clear_ranges ();
2499 XMLNode &after = _session->locations()->get_state();
2500 _session->add_command(new MementoCommand<Locations>(*(_session->locations()), &before, &after));
2502 commit_reversible_command ();
2507 Editor::clear_locations ()
2509 begin_reversible_command (_("clear locations"));
2511 XMLNode &before = _session->locations()->get_state();
2512 _session->locations()->clear ();
2513 XMLNode &after = _session->locations()->get_state();
2514 _session->add_command(new MementoCommand<Locations>(*(_session->locations()), &before, &after));
2516 commit_reversible_command ();
2520 Editor::unhide_markers ()
2522 for (LocationMarkerMap::iterator i = location_markers.begin(); i != location_markers.end(); ++i) {
2523 Location *l = (*i).first;
2524 if (l->is_hidden() && l->is_mark()) {
2525 l->set_hidden(false, this);
2531 Editor::unhide_ranges ()
2533 for (LocationMarkerMap::iterator i = location_markers.begin(); i != location_markers.end(); ++i) {
2534 Location *l = (*i).first;
2535 if (l->is_hidden() && l->is_range_marker()) {
2536 l->set_hidden(false, this);
2541 /* INSERT/REPLACE */
2544 Editor::insert_region_list_selection (float times)
2546 RouteTimeAxisView *tv = 0;
2547 boost::shared_ptr<Playlist> playlist;
2549 if (clicked_routeview != 0) {
2550 tv = clicked_routeview;
2551 } else if (!selection->tracks.empty()) {
2552 if ((tv = dynamic_cast<RouteTimeAxisView*>(selection->tracks.front())) == 0) {
2555 } else if (entered_track != 0) {
2556 if ((tv = dynamic_cast<RouteTimeAxisView*>(entered_track)) == 0) {
2563 if ((playlist = tv->playlist()) == 0) {
2567 boost::shared_ptr<Region> region = _regions->get_single_selection ();
2572 begin_reversible_command (_("insert region"));
2573 playlist->clear_changes ();
2574 playlist->add_region ((RegionFactory::create (region, true)), get_preferred_edit_position(), times);
2575 if (Config->get_edit_mode() == Ripple)
2576 playlist->ripple (get_preferred_edit_position(), region->length() * times, boost::shared_ptr<Region>());
2578 _session->add_command(new StatefulDiffCommand (playlist));
2579 commit_reversible_command ();
2582 /* BUILT-IN EFFECTS */
2585 Editor::reverse_selection ()
2590 /* GAIN ENVELOPE EDITING */
2593 Editor::edit_envelope ()
2600 Editor::transition_to_rolling (bool fwd)
2606 if (_session->config.get_external_sync()) {
2607 switch (TransportMasterManager::instance().current()->type()) {
2611 /* transport controlled by the master */
2616 if (_session->is_auditioning()) {
2617 _session->cancel_audition ();
2621 _session->request_transport_speed (fwd ? 1.0f : -1.0f);
2625 Editor::play_from_start ()
2627 _session->request_locate (_session->current_start_sample(), true);
2631 Editor::play_from_edit_point ()
2633 _session->request_locate (get_preferred_edit_position(), true);
2637 Editor::play_from_edit_point_and_return ()
2639 samplepos_t start_sample;
2640 samplepos_t return_sample;
2642 start_sample = get_preferred_edit_position (EDIT_IGNORE_PHEAD);
2644 if (_session->transport_rolling()) {
2645 _session->request_locate (start_sample, false);
2649 /* don't reset the return sample if its already set */
2651 if ((return_sample = _session->requested_return_sample()) < 0) {
2652 return_sample = _session->audible_sample();
2655 if (start_sample >= 0) {
2656 _session->request_roll_at_and_return (start_sample, return_sample);
2661 Editor::play_selection ()
2663 samplepos_t start, end;
2664 if (!get_selection_extents (start, end))
2667 AudioRange ar (start, end, 0);
2668 list<AudioRange> lar;
2671 _session->request_play_range (&lar, true);
2676 Editor::maybe_locate_with_edit_preroll (samplepos_t location)
2678 if (_session->transport_rolling() || !UIConfiguration::instance().get_follow_edits() || _session->config.get_external_sync())
2681 location -= _session->preroll_samples (location);
2683 //don't try to locate before the beginning of time
2688 //if follow_playhead is on, keep the playhead on the screen
2689 if (_follow_playhead)
2690 if (location < _leftmost_sample)
2691 location = _leftmost_sample;
2693 _session->request_locate (location);
2697 Editor::play_with_preroll ()
2699 samplepos_t start, end;
2700 if (UIConfiguration::instance().get_follow_edits() && get_selection_extents (start, end)) {
2701 const samplepos_t preroll = _session->preroll_samples (start);
2703 samplepos_t ret = start;
2705 if (start > preroll) {
2706 start = start - preroll;
2709 end = end + preroll; //"post-roll"
2711 AudioRange ar (start, end, 0);
2712 list<AudioRange> lar;
2715 _session->request_play_range (&lar, true);
2716 _session->set_requested_return_sample (ret); //force auto-return to return to range start, without the preroll
2718 samplepos_t ph = playhead_cursor->current_sample ();
2719 const samplepos_t preroll = _session->preroll_samples (ph);
2722 start = ph - preroll;
2726 _session->request_locate (start, true);
2727 _session->set_requested_return_sample (ph); //force auto-return to return to playhead location, without the preroll
2732 Editor::rec_with_preroll ()
2734 samplepos_t ph = playhead_cursor->current_sample ();
2735 samplepos_t preroll = _session->preroll_samples (ph);
2736 _session->request_preroll_record_trim (ph, preroll);
2740 Editor::rec_with_count_in ()
2742 _session->request_count_in_record ();
2746 Editor::play_location (Location& location)
2748 if (location.start() <= location.end()) {
2752 _session->request_bounded_roll (location.start(), location.end());
2756 Editor::loop_location (Location& location)
2758 if (location.start() <= location.end()) {
2764 if ((tll = transport_loop_location()) != 0) {
2765 tll->set (location.start(), location.end());
2767 // enable looping, reposition and start rolling
2768 _session->request_locate (tll->start(), true);
2769 _session->request_play_loop (true);
2774 Editor::do_layer_operation (LayerOperation op)
2776 if (selection->regions.empty ()) {
2780 bool const multiple = selection->regions.size() > 1;
2784 begin_reversible_command (_("raise regions"));
2786 begin_reversible_command (_("raise region"));
2792 begin_reversible_command (_("raise regions to top"));
2794 begin_reversible_command (_("raise region to top"));
2800 begin_reversible_command (_("lower regions"));
2802 begin_reversible_command (_("lower region"));
2808 begin_reversible_command (_("lower regions to bottom"));
2810 begin_reversible_command (_("lower region"));
2815 set<boost::shared_ptr<Playlist> > playlists = selection->regions.playlists ();
2816 for (set<boost::shared_ptr<Playlist> >::iterator i = playlists.begin(); i != playlists.end(); ++i) {
2817 (*i)->clear_owned_changes ();
2820 for (RegionSelection::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
2821 boost::shared_ptr<Region> r = (*i)->region ();
2833 r->lower_to_bottom ();
2837 for (set<boost::shared_ptr<Playlist> >::iterator i = playlists.begin(); i != playlists.end(); ++i) {
2838 vector<Command*> cmds;
2840 _session->add_commands (cmds);
2843 commit_reversible_command ();
2847 Editor::raise_region ()
2849 do_layer_operation (Raise);
2853 Editor::raise_region_to_top ()
2855 do_layer_operation (RaiseToTop);
2859 Editor::lower_region ()
2861 do_layer_operation (Lower);
2865 Editor::lower_region_to_bottom ()
2867 do_layer_operation (LowerToBottom);
2870 /** Show the region editor for the selected regions */
2872 Editor::show_region_properties ()
2874 selection->foreach_regionview (&RegionView::show_region_editor);
2877 /** Show the midi list editor for the selected MIDI regions */
2879 Editor::show_midi_list_editor ()
2881 selection->foreach_midi_regionview (&MidiRegionView::show_list_editor);
2885 Editor::rename_region ()
2887 RegionSelection rs = get_regions_from_selection_and_entered ();
2893 ArdourDialog d (_("Rename Region"), true, false);
2895 Label label (_("New name:"));
2898 hbox.set_spacing (6);
2899 hbox.pack_start (label, false, false);
2900 hbox.pack_start (entry, true, true);
2902 d.get_vbox()->set_border_width (12);
2903 d.get_vbox()->pack_start (hbox, false, false);
2905 d.add_button(Gtk::Stock::CANCEL, Gtk::RESPONSE_CANCEL);
2906 d.add_button(Gtk::Stock::OK, Gtk::RESPONSE_OK);
2908 d.set_size_request (300, -1);
2910 entry.set_text (rs.front()->region()->name());
2911 entry.select_region (0, -1);
2913 entry.signal_activate().connect (sigc::bind (sigc::mem_fun (d, &Dialog::response), RESPONSE_OK));
2919 int const ret = d.run();
2923 if (ret != RESPONSE_OK) {
2927 std::string str = entry.get_text();
2928 strip_whitespace_edges (str);
2930 rs.front()->region()->set_name (str);
2931 _regions->redisplay ();
2935 /** Start an audition of the first selected region */
2937 Editor::play_edit_range ()
2939 samplepos_t start, end;
2941 if (get_edit_op_range (start, end)) {
2942 _session->request_bounded_roll (start, end);
2947 Editor::play_selected_region ()
2949 samplepos_t start = max_samplepos;
2950 samplepos_t end = 0;
2952 RegionSelection rs = get_regions_from_selection_and_entered ();
2958 for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
2959 if ((*i)->region()->position() < start) {
2960 start = (*i)->region()->position();
2962 if ((*i)->region()->last_sample() + 1 > end) {
2963 end = (*i)->region()->last_sample() + 1;
2967 _session->request_bounded_roll (start, end);
2971 Editor::audition_playlist_region_standalone (boost::shared_ptr<Region> region)
2973 _session->audition_region (region);
2977 Editor::region_from_selection ()
2979 if (clicked_axisview == 0) {
2983 if (selection->time.empty()) {
2987 samplepos_t start = selection->time[clicked_selection].start;
2988 samplepos_t end = selection->time[clicked_selection].end;
2990 TrackViewList tracks = get_tracks_for_range_action ();
2992 samplepos_t selection_cnt = end - start + 1;
2994 for (TrackSelection::iterator i = tracks.begin(); i != tracks.end(); ++i) {
2995 boost::shared_ptr<Region> current;
2996 boost::shared_ptr<Playlist> pl;
2997 samplepos_t internal_start;
3000 if ((pl = (*i)->playlist()) == 0) {
3004 if ((current = pl->top_region_at (start)) == 0) {
3008 internal_start = start - current->position();
3009 RegionFactory::region_name (new_name, current->name(), true);
3013 plist.add (ARDOUR::Properties::start, current->start() + internal_start);
3014 plist.add (ARDOUR::Properties::length, selection_cnt);
3015 plist.add (ARDOUR::Properties::name, new_name);
3016 plist.add (ARDOUR::Properties::layer, 0);
3018 boost::shared_ptr<Region> region (RegionFactory::create (current, plist));
3023 Editor::create_region_from_selection (vector<boost::shared_ptr<Region> >& new_regions)
3025 if (selection->time.empty() || selection->tracks.empty()) {
3029 samplepos_t start, end;
3030 if (clicked_selection) {
3031 start = selection->time[clicked_selection].start;
3032 end = selection->time[clicked_selection].end;
3034 start = selection->time.start();
3035 end = selection->time.end_sample();
3038 TrackViewList ts = selection->tracks.filter_to_unique_playlists ();
3039 sort_track_selection (ts);
3041 for (TrackSelection::iterator i = ts.begin(); i != ts.end(); ++i) {
3042 boost::shared_ptr<Region> current;
3043 boost::shared_ptr<Playlist> playlist;
3044 samplepos_t internal_start;
3047 if ((playlist = (*i)->playlist()) == 0) {
3051 if ((current = playlist->top_region_at(start)) == 0) {
3055 internal_start = start - current->position();
3056 RegionFactory::region_name (new_name, current->name(), true);
3060 plist.add (ARDOUR::Properties::start, current->start() + internal_start);
3061 plist.add (ARDOUR::Properties::length, end - start + 1);
3062 plist.add (ARDOUR::Properties::name, new_name);
3064 new_regions.push_back (RegionFactory::create (current, plist));
3069 Editor::split_multichannel_region ()
3071 RegionSelection rs = get_regions_from_selection_and_entered ();
3077 vector< boost::shared_ptr<Region> > v;
3079 for (list<RegionView*>::iterator x = rs.begin(); x != rs.end(); ++x) {
3080 (*x)->region()->separate_by_channel (v);
3085 Editor::new_region_from_selection ()
3087 region_from_selection ();
3088 cancel_selection ();
3092 add_if_covered (RegionView* rv, const AudioRange* ar, RegionSelection* rs)
3094 switch (rv->region()->coverage (ar->start, ar->end - 1)) {
3095 // n.b. -1 because AudioRange::end is one past the end, but coverage expects inclusive ranges
3096 case Evoral::OverlapNone:
3104 * - selected tracks, or if there are none...
3105 * - tracks containing selected regions, or if there are none...
3110 Editor::get_tracks_for_range_action () const
3114 if (selection->tracks.empty()) {
3116 /* use tracks with selected regions */
3118 RegionSelection rs = selection->regions;
3120 for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
3121 TimeAxisView* tv = &(*i)->get_time_axis_view();
3123 if (!t.contains (tv)) {
3129 /* no regions and no tracks: use all tracks */
3135 t = selection->tracks;
3138 return t.filter_to_unique_playlists();
3142 Editor::separate_regions_between (const TimeSelection& ts)
3144 bool in_command = false;
3145 boost::shared_ptr<Playlist> playlist;
3146 RegionSelection new_selection;
3148 TrackViewList tmptracks = get_tracks_for_range_action ();
3149 sort_track_selection (tmptracks);
3151 for (TrackSelection::iterator i = tmptracks.begin(); i != tmptracks.end(); ++i) {
3153 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> ((*i));
3159 if (!rtv->is_track()) {
3163 /* no edits to destructive tracks */
3165 if (rtv->track()->destructive()) {
3169 if ((playlist = rtv->playlist()) != 0) {
3171 playlist->clear_changes ();
3173 /* XXX need to consider musical time selections here at some point */
3175 for (list<AudioRange>::const_iterator t = ts.begin(); t != ts.end(); ++t) {
3177 sigc::connection c = rtv->view()->RegionViewAdded.connect (
3178 sigc::mem_fun(*this, &Editor::collect_new_region_view));
3180 latest_regionviews.clear ();
3182 playlist->partition ((*t).start, (*t).end, false);
3186 if (!latest_regionviews.empty()) {
3188 rtv->view()->foreach_regionview (sigc::bind (
3189 sigc::ptr_fun (add_if_covered),
3190 &(*t), &new_selection));
3193 begin_reversible_command (_("separate"));
3197 /* pick up changes to existing regions */
3199 vector<Command*> cmds;
3200 playlist->rdiff (cmds);
3201 _session->add_commands (cmds);
3203 /* pick up changes to the playlist itself (adds/removes)
3206 _session->add_command(new StatefulDiffCommand (playlist));
3214 RangeSelectionAfterSplit rsas = Config->get_range_selection_after_split();
3216 //if our config preference says to clear the selection, clear the Range selection
3217 if (rsas == ClearSel) {
3218 selection->clear_time();
3219 //but leave track selection intact
3220 } else if (rsas == ForceSel) {
3221 //note: forcing the regions to be selected *might* force a tool-change to Object here
3222 selection->set(new_selection);
3225 commit_reversible_command ();
3229 struct PlaylistState {
3230 boost::shared_ptr<Playlist> playlist;
3234 /** Take tracks from get_tracks_for_range_action and cut any regions
3235 * on those tracks so that the tracks are empty over the time
3239 Editor::separate_region_from_selection ()
3241 /* preferentially use *all* ranges in the time selection if we're in range mode
3242 to allow discontiguous operation, since get_edit_op_range() currently
3243 returns a single range.
3246 if (!selection->time.empty()) {
3248 separate_regions_between (selection->time);
3255 if (get_edit_op_range (start, end)) {
3257 AudioRange ar (start, end, 1);
3261 separate_regions_between (ts);
3267 Editor::separate_region_from_punch ()
3269 Location* loc = _session->locations()->auto_punch_location();
3271 separate_regions_using_location (*loc);
3276 Editor::separate_region_from_loop ()
3278 Location* loc = _session->locations()->auto_loop_location();
3280 separate_regions_using_location (*loc);
3285 Editor::separate_regions_using_location (Location& loc)
3287 if (loc.is_mark()) {
3291 AudioRange ar (loc.start(), loc.end(), 1);
3296 separate_regions_between (ts);
3299 /** Separate regions under the selected region */
3301 Editor::separate_under_selected_regions ()
3303 vector<PlaylistState> playlists;
3307 rs = get_regions_from_selection_and_entered();
3309 if (!_session || rs.empty()) {
3313 begin_reversible_command (_("separate region under"));
3315 list<boost::shared_ptr<Region> > regions_to_remove;
3317 for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
3318 // we can't just remove the region(s) in this loop because
3319 // this removes them from the RegionSelection, and they thus
3320 // disappear from underneath the iterator, and the ++i above
3321 // SEGVs in a puzzling fashion.
3323 // so, first iterate over the regions to be removed from rs and
3324 // add them to the regions_to_remove list, and then
3325 // iterate over the list to actually remove them.
3327 regions_to_remove.push_back ((*i)->region());
3330 for (list<boost::shared_ptr<Region> >::iterator rl = regions_to_remove.begin(); rl != regions_to_remove.end(); ++rl) {
3332 boost::shared_ptr<Playlist> playlist = (*rl)->playlist();
3335 // is this check necessary?
3339 vector<PlaylistState>::iterator i;
3341 //only take state if this is a new playlist.
3342 for (i = playlists.begin(); i != playlists.end(); ++i) {
3343 if ((*i).playlist == playlist) {
3348 if (i == playlists.end()) {
3350 PlaylistState before;
3351 before.playlist = playlist;
3352 before.before = &playlist->get_state();
3353 playlist->clear_changes ();
3354 playlist->freeze ();
3355 playlists.push_back(before);
3358 //Partition on the region bounds
3359 playlist->partition ((*rl)->first_sample() - 1, (*rl)->last_sample() + 1, true);
3361 //Re-add region that was just removed due to the partition operation
3362 playlist->add_region ((*rl), (*rl)->first_sample());
3365 vector<PlaylistState>::iterator pl;
3367 for (pl = playlists.begin(); pl != playlists.end(); ++pl) {
3368 (*pl).playlist->thaw ();
3369 _session->add_command(new MementoCommand<Playlist>(*(*pl).playlist, (*pl).before, &(*pl).playlist->get_state()));
3372 commit_reversible_command ();
3376 Editor::crop_region_to_selection ()
3378 if (!selection->time.empty()) {
3380 begin_reversible_command (_("Crop Regions to Time Selection"));
3381 for (std::list<AudioRange>::iterator i = selection->time.begin(); i != selection->time.end(); ++i) {
3382 crop_region_to ((*i).start, (*i).end);
3384 commit_reversible_command();
3390 if (get_edit_op_range (start, end)) {
3391 begin_reversible_command (_("Crop Regions to Edit Range"));
3393 crop_region_to (start, end);
3395 commit_reversible_command();
3402 Editor::crop_region_to (samplepos_t start, samplepos_t end)
3404 vector<boost::shared_ptr<Playlist> > playlists;
3405 boost::shared_ptr<Playlist> playlist;
3408 if (selection->tracks.empty()) {
3409 ts = track_views.filter_to_unique_playlists();
3411 ts = selection->tracks.filter_to_unique_playlists ();
3414 sort_track_selection (ts);
3416 for (TrackSelection::iterator i = ts.begin(); i != ts.end(); ++i) {
3418 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> ((*i));
3424 boost::shared_ptr<Track> t = rtv->track();
3426 if (t != 0 && ! t->destructive()) {
3428 if ((playlist = rtv->playlist()) != 0) {
3429 playlists.push_back (playlist);
3434 if (playlists.empty()) {
3439 samplepos_t new_start;
3440 samplepos_t new_end;
3441 samplecnt_t new_length;
3443 for (vector<boost::shared_ptr<Playlist> >::iterator i = playlists.begin(); i != playlists.end(); ++i) {
3445 /* Only the top regions at start and end have to be cropped */
3446 boost::shared_ptr<Region> region_at_start = (*i)->top_region_at(start);
3447 boost::shared_ptr<Region> region_at_end = (*i)->top_region_at(end);
3449 vector<boost::shared_ptr<Region> > regions;
3451 if (region_at_start != 0) {
3452 regions.push_back (region_at_start);
3454 if (region_at_end != 0) {
3455 regions.push_back (region_at_end);
3458 /* now adjust lengths */
3459 for (vector<boost::shared_ptr<Region> >::iterator i = regions.begin(); i != regions.end(); ++i) {
3461 pos = (*i)->position();
3462 new_start = max (start, pos);
3463 if (max_samplepos - pos > (*i)->length()) {
3464 new_end = pos + (*i)->length() - 1;
3466 new_end = max_samplepos;
3468 new_end = min (end, new_end);
3469 new_length = new_end - new_start + 1;
3471 (*i)->clear_changes ();
3472 (*i)->trim_to (new_start, new_length);
3473 _session->add_command (new StatefulDiffCommand (*i));
3479 Editor::region_fill_track ()
3481 boost::shared_ptr<Playlist> playlist;
3482 RegionSelection regions = get_regions_from_selection_and_entered ();
3483 RegionSelection foo;
3485 samplepos_t const end = _session->current_end_sample ();
3487 if (regions.empty () || regions.end_sample () + 1 >= end) {
3491 samplepos_t const start_sample = regions.start ();
3492 samplepos_t const end_sample = regions.end_sample ();
3493 samplecnt_t const gap = end_sample - start_sample + 1;
3495 begin_reversible_command (Operations::region_fill);
3497 selection->clear_regions ();
3499 for (RegionSelection::iterator i = regions.begin(); i != regions.end(); ++i) {
3501 boost::shared_ptr<Region> r ((*i)->region());
3503 TimeAxisView& tv = (*i)->get_time_axis_view();
3504 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (&tv);
3505 latest_regionviews.clear ();
3506 sigc::connection c = rtv->view()->RegionViewAdded.connect (sigc::mem_fun(*this, &Editor::collect_new_region_view));
3508 samplepos_t const position = end_sample + (r->first_sample() - start_sample + 1);
3509 playlist = (*i)->region()->playlist();
3510 playlist->clear_changes ();
3511 playlist->duplicate_until (r, position, gap, end);
3512 _session->add_command(new StatefulDiffCommand (playlist));
3516 foo.insert (foo.end(), latest_regionviews.begin(), latest_regionviews.end());
3520 selection->set (foo);
3523 commit_reversible_command ();
3527 Editor::set_region_sync_position ()
3529 set_sync_point (get_preferred_edit_position (), get_regions_from_selection_and_edit_point ());
3533 Editor::set_sync_point (samplepos_t where, const RegionSelection& rs)
3535 bool in_command = false;
3537 for (RegionSelection::const_iterator r = rs.begin(); r != rs.end(); ++r) {
3539 if (!(*r)->region()->covers (where)) {
3543 boost::shared_ptr<Region> region ((*r)->region());
3546 begin_reversible_command (_("set sync point"));
3550 region->clear_changes ();
3551 region->set_sync_position (where);
3552 _session->add_command(new StatefulDiffCommand (region));
3556 commit_reversible_command ();
3560 /** Remove the sync positions of the selection */
3562 Editor::remove_region_sync ()
3564 RegionSelection rs = get_regions_from_selection_and_entered ();
3570 begin_reversible_command (_("remove region sync"));
3572 for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
3574 (*i)->region()->clear_changes ();
3575 (*i)->region()->clear_sync_position ();
3576 _session->add_command(new StatefulDiffCommand ((*i)->region()));
3579 commit_reversible_command ();
3583 Editor::naturalize_region ()
3585 RegionSelection rs = get_regions_from_selection_and_entered ();
3591 if (rs.size() > 1) {
3592 begin_reversible_command (_("move regions to original position"));
3594 begin_reversible_command (_("move region to original position"));
3597 for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
3598 (*i)->region()->clear_changes ();
3599 (*i)->region()->move_to_natural_position ();
3600 _session->add_command (new StatefulDiffCommand ((*i)->region()));
3603 commit_reversible_command ();
3607 Editor::align_regions (RegionPoint what)
3609 RegionSelection const rs = get_regions_from_selection_and_edit_point ();
3615 begin_reversible_command (_("align selection"));
3617 samplepos_t const position = get_preferred_edit_position ();
3619 for (RegionSelection::const_iterator i = rs.begin(); i != rs.end(); ++i) {
3620 align_region_internal ((*i)->region(), what, position);
3623 commit_reversible_command ();
3626 struct RegionSortByTime {
3627 bool operator() (const RegionView* a, const RegionView* b) {
3628 return a->region()->position() < b->region()->position();
3633 Editor::align_regions_relative (RegionPoint point)
3635 RegionSelection const rs = get_regions_from_selection_and_edit_point ();
3641 samplepos_t const position = get_preferred_edit_position ();
3643 samplepos_t distance = 0;
3644 samplepos_t pos = 0;
3647 list<RegionView*> sorted;
3648 rs.by_position (sorted);
3650 boost::shared_ptr<Region> r ((*sorted.begin())->region());
3655 if (position > r->position()) {
3656 distance = position - r->position();
3658 distance = r->position() - position;
3664 if (position > r->last_sample()) {
3665 distance = position - r->last_sample();
3666 pos = r->position() + distance;
3668 distance = r->last_sample() - position;
3669 pos = r->position() - distance;
3675 pos = r->adjust_to_sync (position);
3676 if (pos > r->position()) {
3677 distance = pos - r->position();
3679 distance = r->position() - pos;
3685 if (pos == r->position()) {
3689 begin_reversible_command (_("align selection (relative)"));
3691 /* move first one specially */
3693 r->clear_changes ();
3694 r->set_position (pos);
3695 _session->add_command(new StatefulDiffCommand (r));
3697 /* move rest by the same amount */
3701 for (list<RegionView*>::iterator i = sorted.begin(); i != sorted.end(); ++i) {
3703 boost::shared_ptr<Region> region ((*i)->region());
3705 region->clear_changes ();
3708 region->set_position (region->position() + distance);
3710 region->set_position (region->position() - distance);
3713 _session->add_command(new StatefulDiffCommand (region));
3717 commit_reversible_command ();
3721 Editor::align_region (boost::shared_ptr<Region> region, RegionPoint point, samplepos_t position)
3723 begin_reversible_command (_("align region"));
3724 align_region_internal (region, point, position);
3725 commit_reversible_command ();
3729 Editor::align_region_internal (boost::shared_ptr<Region> region, RegionPoint point, samplepos_t position)
3731 region->clear_changes ();
3735 region->set_position (region->adjust_to_sync (position));
3739 if (position > region->length()) {
3740 region->set_position (position - region->length());
3745 region->set_position (position);
3749 _session->add_command(new StatefulDiffCommand (region));
3753 Editor::trim_region_front ()
3759 Editor::trim_region_back ()
3761 trim_region (false);
3765 Editor::trim_region (bool front)
3767 samplepos_t where = get_preferred_edit_position();
3768 RegionSelection rs = get_regions_from_selection_and_edit_point ();
3774 begin_reversible_command (front ? _("trim front") : _("trim back"));
3776 for (list<RegionView*>::const_iterator i = rs.by_layer().begin(); i != rs.by_layer().end(); ++i) {
3777 if (!(*i)->region()->locked()) {
3779 (*i)->region()->clear_changes ();
3782 (*i)->region()->trim_front (where);
3784 (*i)->region()->trim_end (where);
3787 _session->add_command (new StatefulDiffCommand ((*i)->region()));
3791 commit_reversible_command ();
3794 /** Trim the end of the selected regions to the position of the edit cursor */
3796 Editor::trim_region_to_loop ()
3798 Location* loc = _session->locations()->auto_loop_location();
3802 trim_region_to_location (*loc, _("trim to loop"));
3806 Editor::trim_region_to_punch ()
3808 Location* loc = _session->locations()->auto_punch_location();
3812 trim_region_to_location (*loc, _("trim to punch"));
3816 Editor::trim_region_to_location (const Location& loc, const char* str)
3818 RegionSelection rs = get_regions_from_selection_and_entered ();
3819 bool in_command = false;
3821 for (RegionSelection::iterator x = rs.begin(); x != rs.end(); ++x) {
3822 RegionView* rv = (*x);
3824 /* require region to span proposed trim */
3825 switch (rv->region()->coverage (loc.start(), loc.end())) {
3826 case Evoral::OverlapInternal:
3832 RouteTimeAxisView* tav = dynamic_cast<RouteTimeAxisView*> (&rv->get_time_axis_view());
3840 start = loc.start();
3843 rv->region()->clear_changes ();
3844 rv->region()->trim_to (start, (end - start));
3847 begin_reversible_command (str);
3850 _session->add_command(new StatefulDiffCommand (rv->region()));
3854 commit_reversible_command ();
3859 Editor::trim_region_to_previous_region_end ()
3861 return trim_to_region(false);
3865 Editor::trim_region_to_next_region_start ()
3867 return trim_to_region(true);
3871 Editor::trim_to_region(bool forward)
3873 RegionSelection rs = get_regions_from_selection_and_entered ();
3874 bool in_command = false;
3876 boost::shared_ptr<Region> next_region;
3878 for (RegionSelection::iterator x = rs.begin(); x != rs.end(); ++x) {
3880 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (*x);
3886 AudioTimeAxisView* atav = dynamic_cast<AudioTimeAxisView*> (&arv->get_time_axis_view());
3892 boost::shared_ptr<Region> region = arv->region();
3893 boost::shared_ptr<Playlist> playlist (region->playlist());
3895 region->clear_changes ();
3899 next_region = playlist->find_next_region (region->first_sample(), Start, 1);
3905 region->trim_end (next_region->first_sample() - 1);
3906 arv->region_changed (PropertyChange (ARDOUR::Properties::length));
3910 next_region = playlist->find_next_region (region->first_sample(), Start, 0);
3916 region->trim_front (next_region->last_sample() + 1);
3917 arv->region_changed (ARDOUR::bounds_change);
3921 begin_reversible_command (_("trim to region"));
3924 _session->add_command(new StatefulDiffCommand (region));
3928 commit_reversible_command ();
3933 Editor::unfreeze_route ()
3935 if (clicked_routeview == 0 || !clicked_routeview->is_track()) {
3939 clicked_routeview->track()->unfreeze ();
3943 Editor::_freeze_thread (void* arg)
3945 return static_cast<Editor*>(arg)->freeze_thread ();
3949 Editor::freeze_thread ()
3951 /* create event pool because we may need to talk to the session */
3952 SessionEvent::create_per_thread_pool ("freeze events", 64);
3953 /* create per-thread buffers for process() tree to use */
3954 clicked_routeview->audio_track()->freeze_me (*current_interthread_info);
3955 current_interthread_info->done = true;
3960 Editor::freeze_route ()
3966 /* stop transport before we start. this is important */
3968 _session->request_transport_speed (0.0);
3970 /* wait for just a little while, because the above call is asynchronous */
3972 Glib::usleep (250000);
3974 if (clicked_routeview == 0 || !clicked_routeview->is_audio_track()) {
3978 if (!clicked_routeview->track()->bounceable (clicked_routeview->track()->main_outs(), true)) {
3980 _("This track/bus cannot be frozen because the signal adds or loses channels before reaching the outputs.\n"
3981 "This is typically caused by plugins that generate stereo output from mono input or vice versa.")
3983 d.set_title (_("Cannot freeze"));
3988 if (clicked_routeview->track()->has_external_redirects()) {
3989 MessageDialog d (string_compose (_("<b>%1</b>\n\nThis track has at least one send/insert/return as part of its signal flow.\n\n"
3990 "Freezing will only process the signal as far as the first send/insert/return."),
3991 clicked_routeview->track()->name()), true, MESSAGE_INFO, BUTTONS_NONE, true);
3993 d.add_button (_("Freeze anyway"), Gtk::RESPONSE_OK);
3994 d.add_button (_("Don't freeze"), Gtk::RESPONSE_CANCEL);
3995 d.set_title (_("Freeze Limits"));
3997 int response = d.run ();
4000 case Gtk::RESPONSE_CANCEL:
4007 InterThreadInfo itt;
4008 current_interthread_info = &itt;
4010 InterthreadProgressWindow ipw (current_interthread_info, _("Freeze"), _("Cancel Freeze"));
4012 pthread_create_and_store (X_("freezer"), &itt.thread, _freeze_thread, this);
4014 CursorContext::Handle cursor_ctx = CursorContext::create(*this, _cursors->wait);
4016 while (!itt.done && !itt.cancel) {
4017 gtk_main_iteration ();
4020 pthread_join (itt.thread, 0);
4021 current_interthread_info = 0;
4025 Editor::bounce_range_selection (bool replace, bool enable_processing)
4027 if (selection->time.empty()) {
4031 TrackSelection views = selection->tracks;
4033 for (TrackViewList::iterator i = views.begin(); i != views.end(); ++i) {
4035 if (enable_processing) {
4037 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (*i);
4039 if (rtv && rtv->track() && replace && enable_processing && !rtv->track()->bounceable (rtv->track()->main_outs(), false)) {
4041 _("You can't perform this operation because the processing of the signal "
4042 "will cause one or more of the tracks to end up with a region with more channels than this track has inputs.\n\n"
4043 "You can do this without processing, which is a different operation.")
4045 d.set_title (_("Cannot bounce"));
4052 samplepos_t start = selection->time[clicked_selection].start;
4053 samplepos_t end = selection->time[clicked_selection].end;
4054 samplepos_t cnt = end - start + 1;
4055 bool in_command = false;
4057 for (TrackViewList::iterator i = views.begin(); i != views.end(); ++i) {
4059 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (*i);
4065 boost::shared_ptr<Playlist> playlist;
4067 if ((playlist = rtv->playlist()) == 0) {
4071 InterThreadInfo itt;
4073 playlist->clear_changes ();
4074 playlist->clear_owned_changes ();
4076 boost::shared_ptr<Region> r;
4078 if (enable_processing) {
4079 r = rtv->track()->bounce_range (start, start+cnt, itt, rtv->track()->main_outs(), false);
4081 r = rtv->track()->bounce_range (start, start+cnt, itt, boost::shared_ptr<Processor>(), false);
4089 list<AudioRange> ranges;
4090 ranges.push_back (AudioRange (start, start+cnt, 0));
4091 playlist->cut (ranges); // discard result
4092 playlist->add_region (r, start);
4096 begin_reversible_command (_("bounce range"));
4099 vector<Command*> cmds;
4100 playlist->rdiff (cmds);
4101 _session->add_commands (cmds);
4103 _session->add_command (new StatefulDiffCommand (playlist));
4107 commit_reversible_command ();
4111 /** Delete selected regions, automation points or a time range */
4115 //special case: if the user is pointing in the editor/mixer strip, they may be trying to delete a plugin.
4116 //we need this because the editor-mixer strip is in the editor window, so it doesn't get the bindings from the mix window
4117 bool deleted = false;
4118 if (current_mixer_strip && current_mixer_strip == MixerStrip::entered_mixer_strip())
4119 deleted = current_mixer_strip->delete_processors ();
4125 /** Cut selected regions, automation points or a time range */
4132 /** Copy selected regions, automation points or a time range */
4140 /** @return true if a Cut, Copy or Clear is possible */
4142 Editor::can_cut_copy () const
4144 if (!selection->time.empty() || !selection->regions.empty() || !selection->points.empty())
4151 /** Cut, copy or clear selected regions, automation points or a time range.
4152 * @param op Operation (Delete, Cut, Copy or Clear)
4155 Editor::cut_copy (CutCopyOp op)
4157 /* only cancel selection if cut/copy is successful.*/
4163 opname = _("delete");
4172 opname = _("clear");
4176 /* if we're deleting something, and the mouse is still pressed,
4177 the thing we started a drag for will be gone when we release
4178 the mouse button(s). avoid this. see part 2 at the end of
4182 if (op == Delete || op == Cut || op == Clear) {
4183 if (_drags->active ()) {
4188 if (op != Delete) { //"Delete" doesn't change copy/paste buf
4189 cut_buffer->clear ();
4192 if (entered_marker) {
4194 /* cut/delete op while pointing at a marker */
4197 Location* loc = find_location_from_marker (entered_marker, ignored);
4199 if (_session && loc) {
4200 entered_marker = NULL;
4201 Glib::signal_idle().connect (sigc::bind (sigc::mem_fun(*this, &Editor::really_remove_marker), loc));
4208 switch (mouse_mode) {
4211 begin_reversible_command (opname + ' ' + X_("MIDI"));
4213 commit_reversible_command ();
4219 bool did_edit = false;
4221 if (!selection->regions.empty() || !selection->points.empty()) {
4222 begin_reversible_command (opname + ' ' + _("objects"));
4225 if (!selection->regions.empty()) {
4226 cut_copy_regions (op, selection->regions);
4228 if (op == Cut || op == Delete) {
4229 selection->clear_regions ();
4233 if (!selection->points.empty()) {
4234 cut_copy_points (op);
4236 if (op == Cut || op == Delete) {
4237 selection->clear_points ();
4240 } else if (selection->time.empty()) {
4241 samplepos_t start, end;
4242 /* no time selection, see if we can get an edit range
4245 if (get_edit_op_range (start, end)) {
4246 selection->set (start, end);
4248 } else if (!selection->time.empty()) {
4249 begin_reversible_command (opname + ' ' + _("range"));
4252 cut_copy_ranges (op);
4254 if (op == Cut || op == Delete) {
4255 selection->clear_time ();
4260 /* reset repeated paste state */
4262 last_paste_pos = -1;
4263 commit_reversible_command ();
4266 if (op == Delete || op == Cut || op == Clear) {
4272 struct AutomationRecord {
4273 AutomationRecord () : state (0) , line(NULL) {}
4274 AutomationRecord (XMLNode* s, const AutomationLine* l) : state (s) , line (l) {}
4276 XMLNode* state; ///< state before any operation
4277 const AutomationLine* line; ///< line this came from
4278 boost::shared_ptr<Evoral::ControlList> copy; ///< copied events for the cut buffer
4281 struct PointsSelectionPositionSorter {
4282 bool operator() (ControlPoint* a, ControlPoint* b) {
4283 return (*(a->model()))->when < (*(b->model()))->when;
4287 /** Cut, copy or clear selected automation points.
4288 * @param op Operation (Cut, Copy or Clear)
4291 Editor::cut_copy_points (Editing::CutCopyOp op, Temporal::Beats earliest, bool midi)
4293 if (selection->points.empty ()) {
4297 /* XXX: not ideal, as there may be more than one track involved in the point selection */
4298 _last_cut_copy_source_track = &selection->points.front()->line().trackview;
4300 /* Keep a record of the AutomationLists that we end up using in this operation */
4301 typedef std::map<boost::shared_ptr<AutomationList>, AutomationRecord> Lists;
4304 /* user could select points in any order */
4305 selection->points.sort(PointsSelectionPositionSorter ());
4307 /* Go through all selected points, making an AutomationRecord for each distinct AutomationList */
4308 for (PointSelection::iterator sel_point = selection->points.begin(); sel_point != selection->points.end(); ++sel_point) {
4309 const AutomationLine& line = (*sel_point)->line();
4310 const boost::shared_ptr<AutomationList> al = line.the_list();
4311 if (lists.find (al) == lists.end ()) {
4312 /* We haven't seen this list yet, so make a record for it. This includes
4313 taking a copy of its current state, in case this is needed for undo later.
4315 lists[al] = AutomationRecord (&al->get_state (), &line);
4319 if (op == Cut || op == Copy) {
4320 /* This operation will involve putting things in the cut buffer, so create an empty
4321 ControlList for each of our source lists to put the cut buffer data in.
4323 for (Lists::iterator i = lists.begin(); i != lists.end(); ++i) {
4324 i->second.copy = i->first->create (i->first->parameter (), i->first->descriptor());
4327 /* Add all selected points to the relevant copy ControlLists */
4328 MusicSample start (std::numeric_limits<samplepos_t>::max(), 0);
4329 for (PointSelection::iterator sel_point = selection->points.begin(); sel_point != selection->points.end(); ++sel_point) {
4330 boost::shared_ptr<AutomationList> al = (*sel_point)->line().the_list();
4331 AutomationList::const_iterator ctrl_evt = (*sel_point)->model ();
4333 lists[al].copy->fast_simple_add ((*ctrl_evt)->when, (*ctrl_evt)->value);
4335 /* Update earliest MIDI start time in beats */
4336 earliest = std::min(earliest, Temporal::Beats((*ctrl_evt)->when));
4338 /* Update earliest session start time in samples */
4339 start.sample = std::min(start.sample, (*sel_point)->line().session_position(ctrl_evt));
4343 /* Snap start time backwards, so copy/paste is snap aligned. */
4345 if (earliest == std::numeric_limits<Temporal::Beats>::max()) {
4346 earliest = Temporal::Beats(); // Weird... don't offset
4348 earliest.round_down_to_beat();
4350 if (start.sample == std::numeric_limits<double>::max()) {
4351 start.sample = 0; // Weird... don't offset
4353 snap_to(start, RoundDownMaybe);
4356 const double line_offset = midi ? earliest.to_double() : start.sample;
4357 for (Lists::iterator i = lists.begin(); i != lists.end(); ++i) {
4358 /* Correct this copy list so that it is relative to the earliest
4359 start time, so relative ordering between points is preserved
4360 when copying from several lists and the paste starts at the
4361 earliest copied piece of data. */
4362 boost::shared_ptr<Evoral::ControlList> &al_cpy = i->second.copy;
4363 for (AutomationList::iterator ctrl_evt = al_cpy->begin(); ctrl_evt != al_cpy->end(); ++ctrl_evt) {
4364 (*ctrl_evt)->when -= line_offset;
4367 /* And add it to the cut buffer */
4368 cut_buffer->add (al_cpy);
4372 if (op == Delete || op == Cut) {
4373 /* This operation needs to remove things from the main AutomationList, so do that now */
4375 for (Lists::iterator i = lists.begin(); i != lists.end(); ++i) {
4376 i->first->freeze ();
4379 /* Remove each selected point from its AutomationList */
4380 for (PointSelection::iterator sel_point = selection->points.begin(); sel_point != selection->points.end(); ++sel_point) {
4381 AutomationLine& line = (*sel_point)->line ();
4382 boost::shared_ptr<AutomationList> al = line.the_list();
4386 if (dynamic_cast<AudioRegionGainLine*> (&line)) {
4387 /* removing of first and last gain point in region gain lines is prohibited*/
4388 if (line.is_last_point (*(*sel_point)) || line.is_first_point (*(*sel_point))) {
4394 al->erase ((*sel_point)->model ());
4398 /* Thaw the lists and add undo records for them */
4399 for (Lists::iterator i = lists.begin(); i != lists.end(); ++i) {
4400 boost::shared_ptr<AutomationList> al = i->first;
4402 _session->add_command (new MementoCommand<AutomationList> (*al.get(), i->second.state, &(al->get_state ())));
4407 /** Cut, copy or clear selected automation points.
4408 * @param op Operation (Cut, Copy or Clear)
4411 Editor::cut_copy_midi (CutCopyOp op)
4413 Temporal::Beats earliest = std::numeric_limits<Temporal::Beats>::max();
4414 for (MidiRegionSelection::iterator i = selection->midi_regions.begin(); i != selection->midi_regions.end(); ++i) {
4415 MidiRegionView* mrv = dynamic_cast<MidiRegionView*>(*i);
4417 if (!mrv->selection().empty()) {
4418 earliest = std::min(earliest, (*mrv->selection().begin())->note()->time());
4420 mrv->cut_copy_clear (op);
4422 /* XXX: not ideal, as there may be more than one track involved in the selection */
4423 _last_cut_copy_source_track = &mrv->get_time_axis_view();
4427 if (!selection->points.empty()) {
4428 cut_copy_points (op, earliest, true);
4429 if (op == Cut || op == Delete) {
4430 selection->clear_points ();
4435 struct lt_playlist {
4436 bool operator () (const PlaylistState& a, const PlaylistState& b) {
4437 return a.playlist < b.playlist;
4441 struct PlaylistMapping {
4443 boost::shared_ptr<Playlist> pl;
4445 PlaylistMapping (TimeAxisView* tvp) : tv (tvp) {}
4448 /** Remove `clicked_regionview' */
4450 Editor::remove_clicked_region ()
4452 if (clicked_routeview == 0 || clicked_regionview == 0) {
4456 begin_reversible_command (_("remove region"));
4458 boost::shared_ptr<Playlist> playlist = clicked_routeview->playlist();
4460 playlist->clear_changes ();
4461 playlist->clear_owned_changes ();
4462 playlist->remove_region (clicked_regionview->region());
4463 if (Config->get_edit_mode() == Ripple)
4464 playlist->ripple (clicked_regionview->region()->position(), -clicked_regionview->region()->length(), boost::shared_ptr<Region>());
4466 /* We might have removed regions, which alters other regions' layering_index,
4467 so we need to do a recursive diff here.
4469 vector<Command*> cmds;
4470 playlist->rdiff (cmds);
4471 _session->add_commands (cmds);
4473 _session->add_command(new StatefulDiffCommand (playlist));
4474 commit_reversible_command ();
4478 /** Remove the selected regions */
4480 Editor::remove_selected_regions ()
4482 RegionSelection rs = get_regions_from_selection_and_entered ();
4484 if (!_session || rs.empty()) {
4488 list<boost::shared_ptr<Region> > regions_to_remove;
4490 for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
4491 // we can't just remove the region(s) in this loop because
4492 // this removes them from the RegionSelection, and they thus
4493 // disappear from underneath the iterator, and the ++i above
4494 // SEGVs in a puzzling fashion.
4496 // so, first iterate over the regions to be removed from rs and
4497 // add them to the regions_to_remove list, and then
4498 // iterate over the list to actually remove them.
4500 regions_to_remove.push_back ((*i)->region());
4503 vector<boost::shared_ptr<Playlist> > playlists;
4505 for (list<boost::shared_ptr<Region> >::iterator rl = regions_to_remove.begin(); rl != regions_to_remove.end(); ++rl) {
4507 boost::shared_ptr<Playlist> playlist = (*rl)->playlist();
4510 // is this check necessary?
4514 /* get_regions_from_selection_and_entered() guarantees that
4515 the playlists involved are unique, so there is no need
4519 playlists.push_back (playlist);
4521 playlist->clear_changes ();
4522 playlist->clear_owned_changes ();
4523 playlist->freeze ();
4524 playlist->remove_region (*rl);
4525 if (Config->get_edit_mode() == Ripple)
4526 playlist->ripple ((*rl)->position(), -(*rl)->length(), boost::shared_ptr<Region>());
4530 vector<boost::shared_ptr<Playlist> >::iterator pl;
4531 bool in_command = false;
4533 for (pl = playlists.begin(); pl != playlists.end(); ++pl) {
4536 /* We might have removed regions, which alters other regions' layering_index,
4537 so we need to do a recursive diff here.
4541 begin_reversible_command (_("remove region"));
4544 vector<Command*> cmds;
4545 (*pl)->rdiff (cmds);
4546 _session->add_commands (cmds);
4548 _session->add_command(new StatefulDiffCommand (*pl));
4552 commit_reversible_command ();
4556 /** Cut, copy or clear selected regions.
4557 * @param op Operation (Cut, Copy or Clear)
4560 Editor::cut_copy_regions (CutCopyOp op, RegionSelection& rs)
4562 /* we can't use a std::map here because the ordering is important, and we can't trivially sort
4563 a map when we want ordered access to both elements. i think.
4566 vector<PlaylistMapping> pmap;
4568 samplepos_t first_position = max_samplepos;
4570 typedef set<boost::shared_ptr<Playlist> > FreezeList;
4571 FreezeList freezelist;
4573 /* get ordering correct before we cut/copy */
4575 rs.sort_by_position_and_track ();
4577 for (RegionSelection::iterator x = rs.begin(); x != rs.end(); ++x) {
4579 first_position = min ((samplepos_t) (*x)->region()->position(), first_position);
4581 if (op == Cut || op == Clear || op == Delete) {
4582 boost::shared_ptr<Playlist> pl = (*x)->region()->playlist();
4585 FreezeList::iterator fl;
4587 // only take state if this is a new playlist.
4588 for (fl = freezelist.begin(); fl != freezelist.end(); ++fl) {
4594 if (fl == freezelist.end()) {
4595 pl->clear_changes();
4596 pl->clear_owned_changes ();
4598 freezelist.insert (pl);
4603 TimeAxisView* tv = &(*x)->get_time_axis_view();
4604 vector<PlaylistMapping>::iterator z;
4606 for (z = pmap.begin(); z != pmap.end(); ++z) {
4607 if ((*z).tv == tv) {
4612 if (z == pmap.end()) {
4613 pmap.push_back (PlaylistMapping (tv));
4617 for (RegionSelection::iterator x = rs.begin(); x != rs.end(); ) {
4619 boost::shared_ptr<Playlist> pl = (*x)->region()->playlist();
4622 /* region not yet associated with a playlist (e.g. unfinished
4629 TimeAxisView& tv = (*x)->get_time_axis_view();
4630 boost::shared_ptr<Playlist> npl;
4631 RegionSelection::iterator tmp;
4638 vector<PlaylistMapping>::iterator z;
4640 for (z = pmap.begin(); z != pmap.end(); ++z) {
4641 if ((*z).tv == &tv) {
4646 assert (z != pmap.end());
4649 npl = PlaylistFactory::create (pl->data_type(), *_session, "cutlist", true);
4657 boost::shared_ptr<Region> r = (*x)->region();
4658 boost::shared_ptr<Region> _xx;
4664 pl->remove_region (r);
4665 if (Config->get_edit_mode() == Ripple)
4666 pl->ripple (r->position(), -r->length(), boost::shared_ptr<Region>());
4670 _xx = RegionFactory::create (r);
4671 npl->add_region (_xx, r->position() - first_position);
4672 pl->remove_region (r);
4673 if (Config->get_edit_mode() == Ripple)
4674 pl->ripple (r->position(), -r->length(), boost::shared_ptr<Region>());
4678 /* copy region before adding, so we're not putting same object into two different playlists */
4679 npl->add_region (RegionFactory::create (r), r->position() - first_position);
4683 pl->remove_region (r);
4684 if (Config->get_edit_mode() == Ripple)
4685 pl->ripple (r->position(), -r->length(), boost::shared_ptr<Region>());
4694 list<boost::shared_ptr<Playlist> > foo;
4696 /* the pmap is in the same order as the tracks in which selected regions occurred */
4698 for (vector<PlaylistMapping>::iterator i = pmap.begin(); i != pmap.end(); ++i) {
4701 foo.push_back ((*i).pl);
4706 cut_buffer->set (foo);
4710 _last_cut_copy_source_track = 0;
4712 _last_cut_copy_source_track = pmap.front().tv;
4716 for (FreezeList::iterator pl = freezelist.begin(); pl != freezelist.end(); ++pl) {
4719 /* We might have removed regions, which alters other regions' layering_index,
4720 so we need to do a recursive diff here.
4722 vector<Command*> cmds;
4723 (*pl)->rdiff (cmds);
4724 _session->add_commands (cmds);
4726 _session->add_command (new StatefulDiffCommand (*pl));
4731 Editor::cut_copy_ranges (CutCopyOp op)
4733 TrackViewList ts = selection->tracks.filter_to_unique_playlists ();
4735 /* Sort the track selection now, so that it if is used, the playlists
4736 selected by the calls below to cut_copy_clear are in the order that
4737 their tracks appear in the editor. This makes things like paste
4738 of ranges work properly.
4741 sort_track_selection (ts);
4744 if (!entered_track) {
4747 ts.push_back (entered_track);
4750 for (TrackViewList::iterator i = ts.begin(); i != ts.end(); ++i) {
4751 (*i)->cut_copy_clear (*selection, op);
4756 Editor::paste (float times, bool from_context)
4758 DEBUG_TRACE (DEBUG::CutNPaste, "paste to preferred edit pos\n");
4759 MusicSample where (get_preferred_edit_position (EDIT_IGNORE_NONE, from_context), 0);
4760 paste_internal (where.sample, times, 0);
4764 Editor::mouse_paste ()
4766 MusicSample where (0, 0);
4768 if (!mouse_sample (where.sample, ignored)) {
4773 paste_internal (where.sample, 1, where.division);
4777 Editor::paste_internal (samplepos_t position, float times, const int32_t sub_num)
4779 DEBUG_TRACE (DEBUG::CutNPaste, string_compose ("apparent paste position is %1\n", position));
4781 if (cut_buffer->empty(internal_editing())) {
4785 if (position == max_samplepos) {
4786 position = get_preferred_edit_position();
4787 DEBUG_TRACE (DEBUG::CutNPaste, string_compose ("preferred edit position is %1\n", position));
4790 if (position != last_paste_pos) {
4791 /* paste in new location, reset repeated paste state */
4793 last_paste_pos = position;
4796 /* get everything in the correct order */
4799 if (!selection->tracks.empty()) {
4800 /* If there is a track selection, paste into exactly those tracks and
4801 * only those tracks. This allows the user to be explicit and override
4802 * the below "do the reasonable thing" logic. */
4803 ts = selection->tracks.filter_to_unique_playlists ();
4804 sort_track_selection (ts);
4806 /* Figure out which track to base the paste at. */
4807 TimeAxisView* base_track = NULL;
4808 if (_edit_point == Editing::EditAtMouse && entered_track) {
4809 /* With the mouse edit point, paste onto the track under the mouse. */
4810 base_track = entered_track;
4811 } else if (_edit_point == Editing::EditAtMouse && entered_regionview) {
4812 /* With the mouse edit point, paste onto the track of the region under the mouse. */
4813 base_track = &entered_regionview->get_time_axis_view();
4814 } else if (_last_cut_copy_source_track) {
4815 /* Paste to the track that the cut/copy came from (see mantis #333). */
4816 base_track = _last_cut_copy_source_track;
4818 /* This is "impossible" since we've copied... well, do nothing. */
4822 /* Walk up to parent if necessary, so base track is a route. */
4823 while (base_track->get_parent()) {
4824 base_track = base_track->get_parent();
4827 /* Add base track and all tracks below it. The paste logic will select
4828 the appropriate object types from the cut buffer in relative order. */
4829 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
4830 if ((*i)->order() >= base_track->order()) {
4835 /* Sort tracks so the nth track of type T will pick the nth object of type T. */
4836 sort_track_selection (ts);
4838 /* Add automation children of each track in order, for pasting several lines. */
4839 for (TrackViewList::iterator i = ts.begin(); i != ts.end();) {
4840 /* Add any automation children for pasting several lines */
4841 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*>(*i++);
4846 typedef RouteTimeAxisView::AutomationTracks ATracks;
4847 const ATracks& atracks = rtv->automation_tracks();
4848 for (ATracks::const_iterator a = atracks.begin(); a != atracks.end(); ++a) {
4849 i = ts.insert(i, a->second.get());
4854 /* We now have a list of trackviews starting at base_track, including
4855 automation children, in the order shown in the editor, e.g. R1,
4856 R1.A1, R1.A2, R2, R2.A1, ... */
4859 begin_reversible_command (Operations::paste);
4861 if (ts.size() == 1 && cut_buffer->lines.size() == 1 &&
4862 dynamic_cast<AutomationTimeAxisView*>(ts.front())) {
4863 /* Only one line copied, and one automation track selected. Do a
4864 "greedy" paste from one automation type to another. */
4866 PasteContext ctx(paste_count, times, ItemCounts(), true);
4867 ts.front()->paste (position, *cut_buffer, ctx, sub_num);
4871 /* Paste into tracks */
4873 PasteContext ctx(paste_count, times, ItemCounts(), false);
4874 for (TrackViewList::iterator i = ts.begin(); i != ts.end(); ++i) {
4875 (*i)->paste (position, *cut_buffer, ctx, sub_num);
4881 commit_reversible_command ();
4885 Editor::duplicate_regions (float times)
4887 RegionSelection rs (get_regions_from_selection_and_entered());
4888 duplicate_some_regions (rs, times);
4892 Editor::duplicate_some_regions (RegionSelection& regions, float times)
4894 if (regions.empty ()) {
4898 boost::shared_ptr<Playlist> playlist;
4899 std::set<boost::shared_ptr<Playlist> > playlists; // list of unique playlists affected by duplication
4900 RegionSelection sel = regions; // clear (below) may clear the argument list if its the current region selection
4901 RegionSelection foo;
4903 samplepos_t const start_sample = regions.start ();
4904 samplepos_t const end_sample = regions.end_sample ();
4905 samplecnt_t const span = end_sample - start_sample + 1;
4907 begin_reversible_command (Operations::duplicate_region);
4909 selection->clear_regions ();
4911 /* ripple first so that we don't move the duplicates that will be added */
4913 if (Config->get_edit_mode() == Ripple) {
4915 /* convert RegionSelection into RegionList so that we can pass it to ripple and exclude the regions we will duplicate */
4919 for (RegionSelection::iterator i = sel.begin(); i != sel.end(); ++i) {
4920 exclude.push_back ((*i)->region());
4921 playlist = (*i)->region()->playlist();
4922 if (playlists.insert (playlist).second) {
4923 /* successfully inserted into set, so it's the first time we've seen this playlist */
4924 playlist->clear_changes ();
4928 for (set<boost::shared_ptr<Playlist> >::iterator p = playlists.begin(); p != playlists.end(); ++p) {
4929 (*p)->ripple (start_sample, span * times, &exclude);
4933 for (RegionSelection::iterator i = sel.begin(); i != sel.end(); ++i) {
4935 boost::shared_ptr<Region> r ((*i)->region());
4937 TimeAxisView& tv = (*i)->get_time_axis_view();
4938 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (&tv);
4939 latest_regionviews.clear ();
4940 sigc::connection c = rtv->view()->RegionViewAdded.connect (sigc::mem_fun(*this, &Editor::collect_new_region_view));
4942 samplepos_t const position = end_sample + (r->first_sample() - start_sample + 1);
4943 playlist = (*i)->region()->playlist();
4945 if (Config->get_edit_mode() != Ripple) {
4946 if (playlists.insert (playlist).second) {
4947 playlist->clear_changes ();
4951 playlist->duplicate (r, position, span, times);
4955 foo.insert (foo.end(), latest_regionviews.begin(), latest_regionviews.end());
4958 for (set<boost::shared_ptr<Playlist> >::iterator p = playlists.begin(); p != playlists.end(); ++p) {
4959 _session->add_command (new StatefulDiffCommand (*p));
4960 vector<Command*> cmds;
4962 _session->add_commands (cmds);
4966 selection->set (foo);
4969 commit_reversible_command ();
4973 Editor::duplicate_selection (float times)
4975 if (selection->time.empty() || selection->tracks.empty()) {
4979 boost::shared_ptr<Playlist> playlist;
4981 TrackViewList ts = selection->tracks.filter_to_unique_playlists ();
4983 bool in_command = false;
4985 for (TrackViewList::iterator i = ts.begin(); i != ts.end(); ++i) {
4986 if ((playlist = (*i)->playlist()) == 0) {
4989 playlist->clear_changes ();
4991 if (clicked_selection) {
4992 playlist->duplicate_range (selection->time[clicked_selection], times);
4994 playlist->duplicate_ranges (selection->time, times);
4998 begin_reversible_command (_("duplicate range selection"));
5001 _session->add_command (new StatefulDiffCommand (playlist));
5006 if (times == 1.0f) {
5007 // now "move" range selection to after the current range selection
5008 samplecnt_t distance = 0;
5010 if (clicked_selection) {
5012 selection->time[clicked_selection].end - selection->time[clicked_selection].start;
5014 distance = selection->time.end_sample () - selection->time.start ();
5017 selection->move_time (distance);
5019 commit_reversible_command ();
5023 /** Reset all selected points to the relevant default value */
5025 Editor::reset_point_selection ()
5027 for (PointSelection::iterator i = selection->points.begin(); i != selection->points.end(); ++i) {
5028 ARDOUR::AutomationList::iterator j = (*i)->model ();
5029 (*j)->value = (*i)->line().the_list()->descriptor ().normal;
5034 Editor::center_playhead ()
5036 float const page = _visible_canvas_width * samples_per_pixel;
5037 center_screen_internal (playhead_cursor->current_sample (), page);
5041 Editor::center_edit_point ()
5043 float const page = _visible_canvas_width * samples_per_pixel;
5044 center_screen_internal (get_preferred_edit_position(), page);
5047 /** Caller must begin and commit a reversible command */
5049 Editor::clear_playlist (boost::shared_ptr<Playlist> playlist)
5051 playlist->clear_changes ();
5053 _session->add_command (new StatefulDiffCommand (playlist));
5057 Editor::nudge_track (bool use_edit, bool forwards)
5059 boost::shared_ptr<Playlist> playlist;
5060 samplepos_t distance;
5061 samplepos_t next_distance;
5065 start = get_preferred_edit_position();
5070 if ((distance = get_nudge_distance (start, next_distance)) == 0) {
5074 if (selection->tracks.empty()) {
5078 TrackViewList ts = selection->tracks.filter_to_unique_playlists ();
5079 bool in_command = false;
5081 for (TrackViewList::iterator i = ts.begin(); i != ts.end(); ++i) {
5083 if ((playlist = (*i)->playlist()) == 0) {
5087 playlist->clear_changes ();
5088 playlist->clear_owned_changes ();
5090 playlist->nudge_after (start, distance, forwards);
5093 begin_reversible_command (_("nudge track"));
5096 vector<Command*> cmds;
5098 playlist->rdiff (cmds);
5099 _session->add_commands (cmds);
5101 _session->add_command (new StatefulDiffCommand (playlist));
5105 commit_reversible_command ();
5110 Editor::remove_last_capture ()
5112 vector<string> choices;
5119 if (Config->get_verify_remove_last_capture()) {
5120 prompt = _("Do you really want to destroy the last capture?"
5121 "\n(This is destructive and cannot be undone)");
5123 choices.push_back (_("No, do nothing."));
5124 choices.push_back (_("Yes, destroy it."));
5126 Choice prompter (_("Destroy last capture"), prompt, choices);
5128 if (prompter.run () == 1) {
5129 _session->remove_last_capture ();
5130 _regions->redisplay ();
5134 _session->remove_last_capture();
5135 _regions->redisplay ();
5140 Editor::normalize_region ()
5146 RegionSelection rs = get_regions_from_selection_and_entered ();
5152 NormalizeDialog dialog (rs.size() > 1);
5154 if (dialog.run () != RESPONSE_ACCEPT) {
5158 CursorContext::Handle cursor_ctx = CursorContext::create(*this, _cursors->wait);
5161 /* XXX: should really only count audio regions here */
5162 int const regions = rs.size ();
5164 /* Make a list of the selected audio regions' maximum amplitudes, and also
5165 obtain the maximum amplitude of them all.
5167 list<double> max_amps;
5168 list<double> rms_vals;
5171 bool use_rms = dialog.constrain_rms ();
5173 for (RegionSelection::const_iterator i = rs.begin(); i != rs.end(); ++i) {
5174 AudioRegionView const * arv = dynamic_cast<AudioRegionView const *> (*i);
5178 dialog.descend (1.0 / regions);
5179 double const a = arv->audio_region()->maximum_amplitude (&dialog);
5181 double r = arv->audio_region()->rms (&dialog);
5182 max_rms = max (max_rms, r);
5183 rms_vals.push_back (r);
5187 /* the user cancelled the operation */
5191 max_amps.push_back (a);
5192 max_amp = max (max_amp, a);
5196 list<double>::const_iterator a = max_amps.begin ();
5197 list<double>::const_iterator l = rms_vals.begin ();
5198 bool in_command = false;
5200 for (RegionSelection::iterator r = rs.begin(); r != rs.end(); ++r) {
5201 AudioRegionView* const arv = dynamic_cast<AudioRegionView*> (*r);
5206 arv->region()->clear_changes ();
5208 double amp = dialog.normalize_individually() ? *a : max_amp;
5209 double target = dialog.target_peak (); // dB
5212 double const amp_rms = dialog.normalize_individually() ? *l : max_rms;
5213 const double t_rms = dialog.target_rms ();
5214 const gain_t c_peak = dB_to_coefficient (target);
5215 const gain_t c_rms = dB_to_coefficient (t_rms);
5216 if ((amp_rms / c_rms) > (amp / c_peak)) {
5222 arv->audio_region()->normalize (amp, target);
5225 begin_reversible_command (_("normalize"));
5228 _session->add_command (new StatefulDiffCommand (arv->region()));
5235 commit_reversible_command ();
5241 Editor::reset_region_scale_amplitude ()
5247 RegionSelection rs = get_regions_from_selection_and_entered ();
5253 bool in_command = false;
5255 for (RegionSelection::iterator r = rs.begin(); r != rs.end(); ++r) {
5256 AudioRegionView* const arv = dynamic_cast<AudioRegionView*>(*r);
5259 arv->region()->clear_changes ();
5260 arv->audio_region()->set_scale_amplitude (1.0f);
5263 begin_reversible_command ("reset gain");
5266 _session->add_command (new StatefulDiffCommand (arv->region()));
5270 commit_reversible_command ();
5275 Editor::adjust_region_gain (bool up)
5277 RegionSelection rs = get_regions_from_selection_and_entered ();
5279 if (!_session || rs.empty()) {
5283 bool in_command = false;
5285 for (RegionSelection::iterator r = rs.begin(); r != rs.end(); ++r) {
5286 AudioRegionView* const arv = dynamic_cast<AudioRegionView*>(*r);
5291 arv->region()->clear_changes ();
5293 double dB = accurate_coefficient_to_dB (arv->audio_region()->scale_amplitude ());
5301 arv->audio_region()->set_scale_amplitude (dB_to_coefficient (dB));
5304 begin_reversible_command ("adjust region gain");
5307 _session->add_command (new StatefulDiffCommand (arv->region()));
5311 commit_reversible_command ();
5316 Editor::reset_region_gain ()
5318 RegionSelection rs = get_regions_from_selection_and_entered ();
5320 if (!_session || rs.empty()) {
5324 bool in_command = false;
5326 for (RegionSelection::iterator r = rs.begin(); r != rs.end(); ++r) {
5327 AudioRegionView* const arv = dynamic_cast<AudioRegionView*>(*r);
5332 arv->region()->clear_changes ();
5334 arv->audio_region()->set_scale_amplitude (1.0f);
5337 begin_reversible_command ("reset region gain");
5340 _session->add_command (new StatefulDiffCommand (arv->region()));
5344 commit_reversible_command ();
5349 Editor::reverse_region ()
5355 Reverse rev (*_session);
5356 apply_filter (rev, _("reverse regions"));
5360 Editor::strip_region_silence ()
5366 RegionSelection rs = get_regions_from_selection_and_entered ();
5372 std::list<RegionView*> audio_only;
5374 for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
5375 AudioRegionView* const arv = dynamic_cast<AudioRegionView*> (*i);
5377 audio_only.push_back (arv);
5381 assert (!audio_only.empty());
5383 StripSilenceDialog d (_session, audio_only);
5384 int const r = d.run ();
5388 if (r == Gtk::RESPONSE_OK) {
5389 ARDOUR::AudioIntervalMap silences;
5390 d.silences (silences);
5391 StripSilence s (*_session, silences, d.fade_length());
5393 apply_filter (s, _("strip silence"), &d);
5398 Editor::apply_midi_note_edit_op_to_region (MidiOperator& op, MidiRegionView& mrv)
5400 Evoral::Sequence<Temporal::Beats>::Notes selected;
5401 mrv.selection_as_notelist (selected, true);
5403 vector<Evoral::Sequence<Temporal::Beats>::Notes> v;
5404 v.push_back (selected);
5406 Temporal::Beats pos_beats = Temporal::Beats (mrv.midi_region()->beat()) - mrv.midi_region()->start_beats();
5408 return op (mrv.midi_region()->model(), pos_beats, v);
5412 Editor::apply_midi_note_edit_op (MidiOperator& op, const RegionSelection& rs)
5418 bool in_command = false;
5420 for (RegionSelection::const_iterator r = rs.begin(); r != rs.end(); ) {
5421 RegionSelection::const_iterator tmp = r;
5424 MidiRegionView* const mrv = dynamic_cast<MidiRegionView*> (*r);
5427 Command* cmd = apply_midi_note_edit_op_to_region (op, *mrv);
5430 begin_reversible_command (op.name ());
5434 _session->add_command (cmd);
5442 commit_reversible_command ();
5443 _session->set_dirty ();
5448 Editor::fork_region ()
5450 RegionSelection rs = get_regions_from_selection_and_entered ();
5456 CursorContext::Handle cursor_ctx = CursorContext::create(*this, _cursors->wait);
5457 bool in_command = false;
5461 for (RegionSelection::iterator r = rs.begin(); r != rs.end(); ) {
5462 RegionSelection::iterator tmp = r;
5465 MidiRegionView* const mrv = dynamic_cast<MidiRegionView*>(*r);
5469 boost::shared_ptr<Playlist> playlist = mrv->region()->playlist();
5470 boost::shared_ptr<MidiSource> new_source = _session->create_midi_source_by_stealing_name (mrv->midi_view()->track());
5471 boost::shared_ptr<MidiRegion> newregion = mrv->midi_region()->clone (new_source);
5474 begin_reversible_command (_("Fork Region(s)"));
5477 playlist->clear_changes ();
5478 playlist->replace_region (mrv->region(), newregion, mrv->region()->position());
5479 _session->add_command(new StatefulDiffCommand (playlist));
5481 error << string_compose (_("Could not unlink %1"), mrv->region()->name()) << endmsg;
5489 commit_reversible_command ();
5494 Editor::quantize_region ()
5497 quantize_regions(get_regions_from_selection_and_entered ());
5502 Editor::quantize_regions (const RegionSelection& rs)
5504 if (rs.n_midi_regions() == 0) {
5508 if (!quantize_dialog) {
5509 quantize_dialog = new QuantizeDialog (*this);
5512 if (quantize_dialog->is_mapped()) {
5513 /* in progress already */
5517 quantize_dialog->present ();
5518 const int r = quantize_dialog->run ();
5519 quantize_dialog->hide ();
5521 if (r == Gtk::RESPONSE_OK) {
5522 Quantize quant (quantize_dialog->snap_start(),
5523 quantize_dialog->snap_end(),
5524 quantize_dialog->start_grid_size(),
5525 quantize_dialog->end_grid_size(),
5526 quantize_dialog->strength(),
5527 quantize_dialog->swing(),
5528 quantize_dialog->threshold());
5530 apply_midi_note_edit_op (quant, rs);
5535 Editor::legatize_region (bool shrink_only)
5538 legatize_regions(get_regions_from_selection_and_entered (), shrink_only);
5543 Editor::legatize_regions (const RegionSelection& rs, bool shrink_only)
5545 if (rs.n_midi_regions() == 0) {
5549 Legatize legatize(shrink_only);
5550 apply_midi_note_edit_op (legatize, rs);
5554 Editor::transform_region ()
5557 transform_regions(get_regions_from_selection_and_entered ());
5562 Editor::transform_regions (const RegionSelection& rs)
5564 if (rs.n_midi_regions() == 0) {
5571 const int r = td.run();
5574 if (r == Gtk::RESPONSE_OK) {
5575 Transform transform(td.get());
5576 apply_midi_note_edit_op(transform, rs);
5581 Editor::transpose_region ()
5584 transpose_regions(get_regions_from_selection_and_entered ());
5589 Editor::transpose_regions (const RegionSelection& rs)
5591 if (rs.n_midi_regions() == 0) {
5596 int const r = d.run ();
5598 if (r == RESPONSE_ACCEPT) {
5599 Transpose transpose(d.semitones ());
5600 apply_midi_note_edit_op (transpose, rs);
5605 Editor::insert_patch_change (bool from_context)
5607 RegionSelection rs = get_regions_from_selection_and_entered ();
5613 const samplepos_t p = get_preferred_edit_position (EDIT_IGNORE_NONE, from_context);
5615 /* XXX: bit of a hack; use the MIDNAM from the first selected region;
5616 there may be more than one, but the PatchChangeDialog can only offer
5617 one set of patch menus.
5619 MidiRegionView* first = dynamic_cast<MidiRegionView*> (rs.front ());
5621 Evoral::PatchChange<Temporal::Beats> empty (Temporal::Beats(), 0, 0, 0);
5622 PatchChangeDialog d (0, _session, empty, first->instrument_info(), Gtk::Stock::ADD);
5624 if (d.run() == RESPONSE_CANCEL) {
5628 for (RegionSelection::iterator i = rs.begin (); i != rs.end(); ++i) {
5629 MidiRegionView* const mrv = dynamic_cast<MidiRegionView*> (*i);
5631 if (p >= mrv->region()->first_sample() && p <= mrv->region()->last_sample()) {
5632 mrv->add_patch_change (p - mrv->region()->position(), d.patch ());
5639 Editor::apply_filter (Filter& filter, string command, ProgressReporter* progress)
5641 RegionSelection rs = get_regions_from_selection_and_entered ();
5647 CursorContext::Handle cursor_ctx = CursorContext::create(*this, _cursors->wait);
5648 bool in_command = false;
5653 int const N = rs.size ();
5655 for (RegionSelection::iterator r = rs.begin(); r != rs.end(); ) {
5656 RegionSelection::iterator tmp = r;
5659 AudioRegionView* const arv = dynamic_cast<AudioRegionView*>(*r);
5661 boost::shared_ptr<Playlist> playlist = arv->region()->playlist();
5664 progress->descend (1.0 / N);
5667 if (arv->audio_region()->apply (filter, progress) == 0) {
5669 playlist->clear_changes ();
5670 playlist->clear_owned_changes ();
5673 begin_reversible_command (command);
5677 if (filter.results.empty ()) {
5679 /* no regions returned; remove the old one */
5680 playlist->remove_region (arv->region ());
5684 std::vector<boost::shared_ptr<Region> >::iterator res = filter.results.begin ();
5686 /* first region replaces the old one */
5687 playlist->replace_region (arv->region(), *res, (*res)->position());
5691 while (res != filter.results.end()) {
5692 playlist->add_region (*res, (*res)->position());
5698 /* We might have removed regions, which alters other regions' layering_index,
5699 so we need to do a recursive diff here.
5701 vector<Command*> cmds;
5702 playlist->rdiff (cmds);
5703 _session->add_commands (cmds);
5705 _session->add_command(new StatefulDiffCommand (playlist));
5709 progress->ascend ();
5718 commit_reversible_command ();
5723 Editor::external_edit_region ()
5729 Editor::reset_region_gain_envelopes ()
5731 RegionSelection rs = get_regions_from_selection_and_entered ();
5733 if (!_session || rs.empty()) {
5737 bool in_command = false;
5739 for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
5740 AudioRegionView* const arv = dynamic_cast<AudioRegionView*>(*i);
5742 boost::shared_ptr<AutomationList> alist (arv->audio_region()->envelope());
5743 XMLNode& before (alist->get_state());
5745 arv->audio_region()->set_default_envelope ();
5748 begin_reversible_command (_("reset region gain"));
5751 _session->add_command (new MementoCommand<AutomationList>(*arv->audio_region()->envelope().get(), &before, &alist->get_state()));
5756 commit_reversible_command ();
5761 Editor::set_region_gain_visibility (RegionView* rv)
5763 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (rv);
5765 arv->update_envelope_visibility();
5770 Editor::set_gain_envelope_visibility ()
5776 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
5777 AudioTimeAxisView* v = dynamic_cast<AudioTimeAxisView*>(*i);
5779 v->audio_view()->foreach_regionview (sigc::mem_fun (this, &Editor::set_region_gain_visibility));
5785 Editor::toggle_gain_envelope_active ()
5787 if (_ignore_region_action) {
5791 RegionSelection rs = get_regions_from_selection_and_entered ();
5793 if (!_session || rs.empty()) {
5797 bool in_command = false;
5799 for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
5800 AudioRegionView* const arv = dynamic_cast<AudioRegionView*>(*i);
5802 arv->region()->clear_changes ();
5803 arv->audio_region()->set_envelope_active (!arv->audio_region()->envelope_active());
5806 begin_reversible_command (_("region gain envelope active"));
5809 _session->add_command (new StatefulDiffCommand (arv->region()));
5814 commit_reversible_command ();
5819 Editor::toggle_region_lock ()
5821 if (_ignore_region_action) {
5825 RegionSelection rs = get_regions_from_selection_and_entered ();
5827 if (!_session || rs.empty()) {
5831 begin_reversible_command (_("toggle region lock"));
5833 for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
5834 (*i)->region()->clear_changes ();
5835 (*i)->region()->set_locked (!(*i)->region()->locked());
5836 _session->add_command (new StatefulDiffCommand ((*i)->region()));
5839 commit_reversible_command ();
5843 Editor::toggle_region_video_lock ()
5845 if (_ignore_region_action) {
5849 RegionSelection rs = get_regions_from_selection_and_entered ();
5851 if (!_session || rs.empty()) {
5855 begin_reversible_command (_("Toggle Video Lock"));
5857 for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
5858 (*i)->region()->clear_changes ();
5859 (*i)->region()->set_video_locked (!(*i)->region()->video_locked());
5860 _session->add_command (new StatefulDiffCommand ((*i)->region()));
5863 commit_reversible_command ();
5867 Editor::toggle_region_lock_style ()
5869 if (_ignore_region_action) {
5873 RegionSelection rs = get_regions_from_selection_and_entered ();
5875 if (!_session || rs.empty()) {
5879 Glib::RefPtr<ToggleAction> a = Glib::RefPtr<ToggleAction>::cast_dynamic (_region_actions->get_action("toggle-region-lock-style"));
5880 vector<Widget*> proxies = a->get_proxies();
5881 Gtk::CheckMenuItem* cmi = dynamic_cast<Gtk::CheckMenuItem*> (proxies.front());
5885 begin_reversible_command (_("toggle region lock style"));
5887 for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
5888 (*i)->region()->clear_changes ();
5889 PositionLockStyle const ns = ((*i)->region()->position_lock_style() == AudioTime && !cmi->get_inconsistent()) ? MusicTime : AudioTime;
5890 (*i)->region()->set_position_lock_style (ns);
5891 _session->add_command (new StatefulDiffCommand ((*i)->region()));
5894 commit_reversible_command ();
5898 Editor::toggle_opaque_region ()
5900 if (_ignore_region_action) {
5904 RegionSelection rs = get_regions_from_selection_and_entered ();
5906 if (!_session || rs.empty()) {
5910 begin_reversible_command (_("change region opacity"));
5912 for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
5913 (*i)->region()->clear_changes ();
5914 (*i)->region()->set_opaque (!(*i)->region()->opaque());
5915 _session->add_command (new StatefulDiffCommand ((*i)->region()));
5918 commit_reversible_command ();
5922 Editor::toggle_record_enable ()
5924 bool new_state = false;
5926 for (TrackSelection::iterator i = selection->tracks.begin(); i != selection->tracks.end(); ++i) {
5927 RouteTimeAxisView *rtav = dynamic_cast<RouteTimeAxisView *>(*i);
5930 if (!rtav->is_track())
5934 new_state = !rtav->track()->rec_enable_control()->get_value();
5938 rtav->track()->rec_enable_control()->set_value (new_state, Controllable::UseGroup);
5943 tracklist_to_stripables (TrackViewList list)
5947 for (TrackSelection::iterator i = list.begin(); i != list.end(); ++i) {
5948 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> ((*i));
5950 if (rtv && rtv->is_track()) {
5951 ret.push_back (rtv->track());
5959 Editor::play_solo_selection (bool restart)
5961 //note: session::solo_selection takes care of invalidating the region playlist
5963 if ((!selection->tracks.empty()) && selection->time.length() > 0) { //a range is selected; solo the tracks and roll
5965 StripableList sl = tracklist_to_stripables (selection->tracks);
5966 _session->solo_selection (sl, true);
5969 samplepos_t start = selection->time.start();
5970 samplepos_t end = selection->time.end_sample();
5971 _session->request_bounded_roll (start, end);
5973 } else if (! selection->tracks.empty()) { //no range is selected, but tracks are selected; solo the tracks and roll
5974 StripableList sl = tracklist_to_stripables (selection->tracks);
5975 _session->solo_selection (sl, true);
5976 _session->request_cancel_play_range();
5977 transition_to_rolling (true);
5979 } else if (! selection->regions.empty()) { //solo any tracks with selected regions, and roll
5980 StripableList sl = tracklist_to_stripables (get_tracks_for_range_action());
5981 _session->solo_selection (sl, true);
5982 _session->request_cancel_play_range();
5983 transition_to_rolling (true);
5985 _session->request_cancel_play_range();
5986 transition_to_rolling (true); //no selection. just roll.
5991 Editor::toggle_solo ()
5993 bool new_state = false;
5995 boost::shared_ptr<ControlList> cl (new ControlList);
5997 for (TrackSelection::iterator i = selection->tracks.begin(); i != selection->tracks.end(); ++i) {
5998 StripableTimeAxisView *stav = dynamic_cast<StripableTimeAxisView *>(*i);
6000 if (!stav || !stav->stripable()->solo_control()) {
6005 new_state = !stav->stripable()->solo_control()->soloed ();
6009 cl->push_back (stav->stripable()->solo_control());
6012 _session->set_controls (cl, new_state ? 1.0 : 0.0, Controllable::UseGroup);
6016 Editor::toggle_mute ()
6018 bool new_state = false;
6020 boost::shared_ptr<ControlList> cl (new ControlList);
6022 for (TrackSelection::iterator i = selection->tracks.begin(); i != selection->tracks.end(); ++i) {
6023 StripableTimeAxisView *stav = dynamic_cast<StripableTimeAxisView *>(*i);
6025 if (!stav || !stav->stripable()->mute_control()) {
6030 new_state = !stav->stripable()->mute_control()->muted();
6034 cl->push_back (stav->stripable()->mute_control());
6037 _session->set_controls (cl, new_state, Controllable::UseGroup);
6041 Editor::toggle_solo_isolate ()
6047 Editor::fade_range ()
6049 TrackViewList ts = selection->tracks.filter_to_unique_playlists ();
6051 begin_reversible_command (_("fade range"));
6053 for (TrackViewList::iterator i = ts.begin(); i != ts.end(); ++i) {
6054 (*i)->fade_range (selection->time);
6057 commit_reversible_command ();
6062 Editor::set_fade_length (bool in)
6064 RegionSelection rs = get_regions_from_selection_and_entered ();
6070 /* we need a region to measure the offset from the start */
6072 RegionView* rv = rs.front ();
6074 samplepos_t pos = get_preferred_edit_position();
6078 if (pos > rv->region()->last_sample() || pos < rv->region()->first_sample()) {
6079 /* edit point is outside the relevant region */
6084 if (pos <= rv->region()->position()) {
6088 len = pos - rv->region()->position();
6089 cmd = _("set fade in length");
6091 if (pos >= rv->region()->last_sample()) {
6095 len = rv->region()->last_sample() - pos;
6096 cmd = _("set fade out length");
6099 bool in_command = false;
6101 for (RegionSelection::iterator x = rs.begin(); x != rs.end(); ++x) {
6102 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (*x);
6108 boost::shared_ptr<AutomationList> alist;
6110 alist = tmp->audio_region()->fade_in();
6112 alist = tmp->audio_region()->fade_out();
6115 XMLNode &before = alist->get_state();
6118 tmp->audio_region()->set_fade_in_length (len);
6119 tmp->audio_region()->set_fade_in_active (true);
6121 tmp->audio_region()->set_fade_out_length (len);
6122 tmp->audio_region()->set_fade_out_active (true);
6126 begin_reversible_command (cmd);
6129 XMLNode &after = alist->get_state();
6130 _session->add_command(new MementoCommand<AutomationList>(*alist, &before, &after));
6134 commit_reversible_command ();
6139 Editor::set_fade_in_shape (FadeShape shape)
6141 RegionSelection rs = get_regions_from_selection_and_entered ();
6146 bool in_command = false;
6148 for (RegionSelection::iterator x = rs.begin(); x != rs.end(); ++x) {
6149 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (*x);
6155 boost::shared_ptr<AutomationList> alist = tmp->audio_region()->fade_in();
6156 XMLNode &before = alist->get_state();
6158 tmp->audio_region()->set_fade_in_shape (shape);
6161 begin_reversible_command (_("set fade in shape"));
6164 XMLNode &after = alist->get_state();
6165 _session->add_command(new MementoCommand<AutomationList>(*alist.get(), &before, &after));
6169 commit_reversible_command ();
6174 Editor::set_fade_out_shape (FadeShape shape)
6176 RegionSelection rs = get_regions_from_selection_and_entered ();
6181 bool in_command = false;
6183 for (RegionSelection::iterator x = rs.begin(); x != rs.end(); ++x) {
6184 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (*x);
6190 boost::shared_ptr<AutomationList> alist = tmp->audio_region()->fade_out();
6191 XMLNode &before = alist->get_state();
6193 tmp->audio_region()->set_fade_out_shape (shape);
6196 begin_reversible_command (_("set fade out shape"));
6199 XMLNode &after = alist->get_state();
6200 _session->add_command(new MementoCommand<AutomationList>(*alist.get(), &before, &after));
6204 commit_reversible_command ();
6209 Editor::set_fade_in_active (bool yn)
6211 RegionSelection rs = get_regions_from_selection_and_entered ();
6216 bool in_command = false;
6218 for (RegionSelection::iterator x = rs.begin(); x != rs.end(); ++x) {
6219 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (*x);
6226 boost::shared_ptr<AudioRegion> ar (tmp->audio_region());
6228 ar->clear_changes ();
6229 ar->set_fade_in_active (yn);
6232 begin_reversible_command (_("set fade in active"));
6235 _session->add_command (new StatefulDiffCommand (ar));
6239 commit_reversible_command ();
6244 Editor::set_fade_out_active (bool yn)
6246 RegionSelection rs = get_regions_from_selection_and_entered ();
6251 bool in_command = false;
6253 for (RegionSelection::iterator x = rs.begin(); x != rs.end(); ++x) {
6254 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (*x);
6260 boost::shared_ptr<AudioRegion> ar (tmp->audio_region());
6262 ar->clear_changes ();
6263 ar->set_fade_out_active (yn);
6266 begin_reversible_command (_("set fade out active"));
6269 _session->add_command(new StatefulDiffCommand (ar));
6273 commit_reversible_command ();
6278 Editor::toggle_region_fades (int dir)
6280 if (_ignore_region_action) {
6284 boost::shared_ptr<AudioRegion> ar;
6287 RegionSelection rs = get_regions_from_selection_and_entered ();
6293 RegionSelection::iterator i;
6294 for (i = rs.begin(); i != rs.end(); ++i) {
6295 if ((ar = boost::dynamic_pointer_cast<AudioRegion>((*i)->region())) != 0) {
6297 yn = ar->fade_out_active ();
6299 yn = ar->fade_in_active ();
6305 if (i == rs.end()) {
6309 /* XXX should this undo-able? */
6310 bool in_command = false;
6312 for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
6313 if ((ar = boost::dynamic_pointer_cast<AudioRegion>((*i)->region())) == 0) {
6316 ar->clear_changes ();
6318 if (dir == 1 || dir == 0) {
6319 ar->set_fade_in_active (!yn);
6322 if (dir == -1 || dir == 0) {
6323 ar->set_fade_out_active (!yn);
6326 begin_reversible_command (_("toggle fade active"));
6329 _session->add_command(new StatefulDiffCommand (ar));
6333 commit_reversible_command ();
6338 /** Update region fade visibility after its configuration has been changed */
6340 Editor::update_region_fade_visibility ()
6342 bool _fade_visibility = _session->config.get_show_region_fades ();
6344 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
6345 AudioTimeAxisView* v = dynamic_cast<AudioTimeAxisView*>(*i);
6347 if (_fade_visibility) {
6348 v->audio_view()->show_all_fades ();
6350 v->audio_view()->hide_all_fades ();
6357 Editor::set_edit_point ()
6360 MusicSample where (0, 0);
6362 if (!mouse_sample (where.sample, ignored)) {
6368 if (selection->markers.empty()) {
6370 mouse_add_new_marker (where.sample);
6375 Location* loc = find_location_from_marker (selection->markers.front(), ignored);
6378 loc->move_to (where.sample, where.division);
6384 Editor::set_playhead_cursor ()
6386 if (entered_marker) {
6387 _session->request_locate (entered_marker->position(), _session->transport_rolling());
6389 MusicSample where (0, 0);
6392 if (!mouse_sample (where.sample, ignored)) {
6399 _session->request_locate (where.sample, _session->transport_rolling());
6403 //not sure what this was for; remove it for now.
6404 // if (UIConfiguration::instance().get_follow_edits() && (!_session || !_session->config.get_external_sync())) {
6405 // cancel_time_selection();
6411 Editor::split_region ()
6413 if (_dragging_playhead) {
6415 } else if (_drags->active ()) {
6416 /*any other kind of drag, bail out so we avoid Undo snafu*/
6420 //if a range is selected, separate it
6421 if (!selection->time.empty()) {
6422 separate_regions_between (selection->time);
6426 //if no range was selected, try to find some regions to split
6427 if (current_mouse_mode() == MouseObject || current_mouse_mode() == MouseRange ) { //don't try this for Internal Edit, Stretch, Draw, etc.
6431 //new behavior: the Split action will prioritize the entered_regionview rather than selected regions.
6432 //this fixes the unexpected case where you point at a region, but
6433 // * nothing happens OR
6434 // * some other region (maybe off-screen) is split.
6435 //NOTE: if the entered_regionview is /part of the selection/ then we should operate on the selection as usual
6436 if (_edit_point == EditAtMouse && entered_regionview && !entered_regionview->selected()) {
6437 rs.add (entered_regionview);
6439 rs = selection->regions; //might be empty
6443 TrackViewList tracks = selection->tracks;
6445 if (!tracks.empty()) {
6446 /* no region selected or entered, but some selected tracks:
6447 * act on all regions on the selected tracks at the edit point
6449 samplepos_t const where = get_preferred_edit_position (Editing::EDIT_IGNORE_NONE, false, false);
6450 get_regions_at(rs, where, tracks);
6454 const samplepos_t pos = get_preferred_edit_position();
6455 const int32_t division = get_grid_music_divisions (0);
6456 MusicSample where (pos, division);
6462 split_regions_at (where, rs);
6467 Editor::select_next_stripable (bool routes_only)
6469 _session->selection().select_next_stripable (false, routes_only);
6473 Editor::select_prev_stripable (bool routes_only)
6475 _session->selection().select_prev_stripable (false, routes_only);
6479 Editor::set_loop_from_selection (bool play)
6481 if (_session == 0) {
6485 samplepos_t start, end;
6486 if (!get_selection_extents (start, end))
6489 set_loop_range (start, end, _("set loop range from selection"));
6492 _session->request_play_loop (true, true);
6497 Editor::set_loop_from_region (bool play)
6499 samplepos_t start, end;
6500 if (!get_selection_extents (start, end))
6503 set_loop_range (start, end, _("set loop range from region"));
6506 _session->request_locate (start, true);
6507 _session->request_play_loop (true);
6512 Editor::set_punch_from_selection ()
6514 if (_session == 0) {
6518 samplepos_t start, end;
6519 if (!get_selection_extents (start, end))
6522 set_punch_range (start, end, _("set punch range from selection"));
6526 Editor::set_auto_punch_range ()
6528 // auto punch in/out button from a single button
6529 // If Punch In is unset, set punch range from playhead to end, enable punch in
6530 // If Punch In is set, the next punch sets Punch Out, unless the playhead has been
6531 // rewound beyond the Punch In marker, in which case that marker will be moved back
6532 // to the current playhead position.
6533 // If punch out is set, it clears the punch range and Punch In/Out buttons
6535 if (_session == 0) {
6539 Location* tpl = transport_punch_location();
6540 samplepos_t now = playhead_cursor->current_sample();
6541 samplepos_t begin = now;
6542 samplepos_t end = _session->current_end_sample();
6544 if (!_session->config.get_punch_in()) {
6545 // First Press - set punch in and create range from here to eternity
6546 set_punch_range (begin, end, _("Auto Punch In"));
6547 _session->config.set_punch_in(true);
6548 } else if (tpl && !_session->config.get_punch_out()) {
6549 // Second press - update end range marker and set punch_out
6550 if (now < tpl->start()) {
6551 // playhead has been rewound - move start back and pretend nothing happened
6553 set_punch_range (begin, end, _("Auto Punch In/Out"));
6555 // normal case for 2nd press - set the punch out
6556 end = playhead_cursor->current_sample ();
6557 set_punch_range (tpl->start(), now, _("Auto Punch In/Out"));
6558 _session->config.set_punch_out(true);
6561 if (_session->config.get_punch_out()) {
6562 _session->config.set_punch_out(false);
6565 if (_session->config.get_punch_in()) {
6566 _session->config.set_punch_in(false);
6571 // third press - unset punch in/out and remove range
6572 _session->locations()->remove(tpl);
6579 Editor::set_session_extents_from_selection ()
6581 if (_session == 0) {
6585 samplepos_t start, end;
6586 if (!get_selection_extents (start, end))
6590 if ((loc = _session->locations()->session_range_location()) == 0) {
6591 _session->set_session_extents (start, end); // this will create a new session range; no need for UNDO
6593 XMLNode &before = loc->get_state();
6595 _session->set_session_extents (start, end);
6597 XMLNode &after = loc->get_state();
6599 begin_reversible_command (_("set session start/end from selection"));
6601 _session->add_command (new MementoCommand<Location>(*loc, &before, &after));
6603 commit_reversible_command ();
6606 _session->set_session_range_is_free (false);
6610 Editor::set_punch_start_from_edit_point ()
6614 MusicSample start (0, 0);
6615 samplepos_t end = max_samplepos;
6617 //use the existing punch end, if any
6618 Location* tpl = transport_punch_location();
6623 if ((_edit_point == EditAtPlayhead) && _session->transport_rolling()) {
6624 start.sample = _session->audible_sample();
6626 start.sample = get_preferred_edit_position();
6629 //if there's not already a sensible selection endpoint, go "forever"
6630 if (start.sample > end) {
6631 end = max_samplepos;
6634 set_punch_range (start.sample, end, _("set punch start from EP"));
6640 Editor::set_punch_end_from_edit_point ()
6644 samplepos_t start = 0;
6645 MusicSample end (max_samplepos, 0);
6647 //use the existing punch start, if any
6648 Location* tpl = transport_punch_location();
6650 start = tpl->start();
6653 if ((_edit_point == EditAtPlayhead) && _session->transport_rolling()) {
6654 end.sample = _session->audible_sample();
6656 end.sample = get_preferred_edit_position();
6659 set_punch_range (start, end.sample, _("set punch end from EP"));
6665 Editor::set_loop_start_from_edit_point ()
6669 MusicSample start (0, 0);
6670 samplepos_t end = max_samplepos;
6672 //use the existing loop end, if any
6673 Location* tpl = transport_loop_location();
6678 if ((_edit_point == EditAtPlayhead) && _session->transport_rolling()) {
6679 start.sample = _session->audible_sample();
6681 start.sample = get_preferred_edit_position();
6684 //if there's not already a sensible selection endpoint, go "forever"
6685 if (start.sample > end) {
6686 end = max_samplepos;
6689 set_loop_range (start.sample, end, _("set loop start from EP"));
6695 Editor::set_loop_end_from_edit_point ()
6699 samplepos_t start = 0;
6700 MusicSample end (max_samplepos, 0);
6702 //use the existing loop start, if any
6703 Location* tpl = transport_loop_location();
6705 start = tpl->start();
6708 if ((_edit_point == EditAtPlayhead) && _session->transport_rolling()) {
6709 end.sample = _session->audible_sample();
6711 end.sample = get_preferred_edit_position();
6714 set_loop_range (start, end.sample, _("set loop end from EP"));
6719 Editor::set_punch_from_region ()
6721 samplepos_t start, end;
6722 if (!get_selection_extents (start, end))
6725 set_punch_range (start, end, _("set punch range from region"));
6729 Editor::pitch_shift_region ()
6731 RegionSelection rs = get_regions_from_selection_and_entered ();
6733 RegionSelection audio_rs;
6734 for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
6735 if (dynamic_cast<AudioRegionView*> (*i)) {
6736 audio_rs.push_back (*i);
6740 if (audio_rs.empty()) {
6744 pitch_shift (audio_rs, 1.2);
6748 Editor::set_tempo_from_region ()
6750 RegionSelection rs = get_regions_from_selection_and_entered ();
6752 if (!_session || rs.empty()) {
6756 RegionView* rv = rs.front();
6758 define_one_bar (rv->region()->position(), rv->region()->last_sample() + 1);
6762 Editor::use_range_as_bar ()
6764 samplepos_t start, end;
6765 if (get_edit_op_range (start, end)) {
6766 define_one_bar (start, end);
6771 Editor::define_one_bar (samplepos_t start, samplepos_t end)
6773 samplepos_t length = end - start;
6775 const Meter& m (_session->tempo_map().meter_at_sample (start));
6777 /* length = 1 bar */
6779 /* We're going to deliver a constant tempo here,
6780 so we can use samples per beat to determine length.
6781 now we want samples per beat.
6782 we have samples per bar, and beats per bar, so ...
6785 /* XXXX METER MATH */
6787 double samples_per_beat = length / m.divisions_per_bar();
6789 /* beats per minute = */
6791 double beats_per_minute = (_session->sample_rate() * 60.0) / samples_per_beat;
6793 /* now decide whether to:
6795 (a) set global tempo
6796 (b) add a new tempo marker
6800 const TempoSection& t (_session->tempo_map().tempo_section_at_sample (start));
6802 bool do_global = false;
6804 if ((_session->tempo_map().n_tempos() == 1) && (_session->tempo_map().n_meters() == 1)) {
6806 /* only 1 tempo & 1 meter: ask if the user wants to set the tempo
6807 at the start, or create a new marker
6810 vector<string> options;
6811 options.push_back (_("Cancel"));
6812 options.push_back (_("Add new marker"));
6813 options.push_back (_("Set global tempo"));
6816 _("Define one bar"),
6817 _("Do you want to set the global tempo or add a new tempo marker?"),
6821 c.set_default_response (2);
6837 /* more than 1 tempo and/or meter section already, go ahead do the "usual":
6838 if the marker is at the region starter, change it, otherwise add
6843 begin_reversible_command (_("set tempo from region"));
6844 XMLNode& before (_session->tempo_map().get_state());
6847 _session->tempo_map().change_initial_tempo (beats_per_minute, t.note_type(), t.end_note_types_per_minute());
6848 } else if (t.sample() == start) {
6849 _session->tempo_map().change_existing_tempo_at (start, beats_per_minute, t.note_type(), t.end_note_types_per_minute());
6851 /* constant tempo */
6852 const Tempo tempo (beats_per_minute, t.note_type());
6853 _session->tempo_map().add_tempo (tempo, 0.0, start, AudioTime);
6856 XMLNode& after (_session->tempo_map().get_state());
6858 _session->add_command (new MementoCommand<TempoMap>(_session->tempo_map(), &before, &after));
6859 commit_reversible_command ();
6863 Editor::split_region_at_transients ()
6865 AnalysisFeatureList positions;
6867 RegionSelection rs = get_regions_from_selection_and_entered ();
6869 if (!_session || rs.empty()) {
6873 begin_reversible_command (_("split regions"));
6875 for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ) {
6877 RegionSelection::iterator tmp;
6882 boost::shared_ptr<AudioRegion> ar = boost::dynamic_pointer_cast<AudioRegion> ((*i)->region());
6885 ar->transients (positions);
6886 split_region_at_points ((*i)->region(), positions, true);
6893 commit_reversible_command ();
6898 Editor::split_region_at_points (boost::shared_ptr<Region> r, AnalysisFeatureList& positions, bool can_ferret, bool select_new)
6900 bool use_rhythmic_rodent = false;
6902 boost::shared_ptr<Playlist> pl = r->playlist();
6904 list<boost::shared_ptr<Region> > new_regions;
6910 if (positions.empty()) {
6914 if (positions.size() > 20 && can_ferret) {
6915 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);
6916 MessageDialog msg (msgstr,
6919 Gtk::BUTTONS_OK_CANCEL);
6922 msg.add_button (_("Call for the Ferret!"), RESPONSE_APPLY);
6923 msg.set_secondary_text (_("Press OK to continue with this split operation\nor ask the Ferret dialog to tune the analysis"));
6925 msg.set_secondary_text (_("Press OK to continue with this split operation"));
6928 msg.set_title (_("Excessive split?"));
6931 int response = msg.run();
6937 case RESPONSE_APPLY:
6938 use_rhythmic_rodent = true;
6945 if (use_rhythmic_rodent) {
6946 show_rhythm_ferret ();
6950 AnalysisFeatureList::const_iterator x;
6952 pl->clear_changes ();
6953 pl->clear_owned_changes ();
6955 x = positions.begin();
6957 if (x == positions.end()) {
6962 pl->remove_region (r);
6964 samplepos_t pos = 0;
6966 samplepos_t rstart = r->first_sample ();
6967 samplepos_t rend = r->last_sample ();
6969 while (x != positions.end()) {
6971 /* deal with positons that are out of scope of present region bounds */
6972 if (*x <= rstart || *x > rend) {
6977 /* file start = original start + how far we from the initial position ? */
6979 samplepos_t file_start = r->start() + pos;
6981 /* length = next position - current position */
6983 samplepos_t len = (*x) - pos - rstart;
6985 /* XXX we do we really want to allow even single-sample regions?
6986 * shouldn't we have some kind of lower limit on region size?
6995 if (RegionFactory::region_name (new_name, r->name())) {
6999 /* do NOT announce new regions 1 by one, just wait till they are all done */
7003 plist.add (ARDOUR::Properties::start, file_start);
7004 plist.add (ARDOUR::Properties::length, len);
7005 plist.add (ARDOUR::Properties::name, new_name);
7006 plist.add (ARDOUR::Properties::layer, 0);
7007 // TODO set transients_offset
7009 boost::shared_ptr<Region> nr = RegionFactory::create (r->sources(), plist, false);
7010 /* because we set annouce to false, manually add the new region to the
7013 RegionFactory::map_add (nr);
7015 pl->add_region (nr, rstart + pos);
7018 new_regions.push_front(nr);
7027 RegionFactory::region_name (new_name, r->name());
7029 /* Add the final region */
7032 plist.add (ARDOUR::Properties::start, r->start() + pos);
7033 plist.add (ARDOUR::Properties::length, r->last_sample() - (r->position() + pos) + 1);
7034 plist.add (ARDOUR::Properties::name, new_name);
7035 plist.add (ARDOUR::Properties::layer, 0);
7037 boost::shared_ptr<Region> nr = RegionFactory::create (r->sources(), plist, false);
7038 /* because we set annouce to false, manually add the new region to the
7041 RegionFactory::map_add (nr);
7042 pl->add_region (nr, r->position() + pos);
7045 new_regions.push_front(nr);
7050 /* We might have removed regions, which alters other regions' layering_index,
7051 so we need to do a recursive diff here.
7053 vector<Command*> cmds;
7055 _session->add_commands (cmds);
7057 _session->add_command (new StatefulDiffCommand (pl));
7061 for (list<boost::shared_ptr<Region> >::iterator i = new_regions.begin(); i != new_regions.end(); ++i){
7062 set_selected_regionview_from_region_list ((*i), Selection::Add);
7068 Editor::place_transient()
7074 RegionSelection rs = get_regions_from_selection_and_edit_point ();
7080 samplepos_t where = get_preferred_edit_position();
7082 begin_reversible_command (_("place transient"));
7084 for (RegionSelection::iterator r = rs.begin(); r != rs.end(); ++r) {
7085 (*r)->region()->add_transient(where);
7088 commit_reversible_command ();
7092 Editor::remove_transient(ArdourCanvas::Item* item)
7098 ArdourCanvas::Line* _line = reinterpret_cast<ArdourCanvas::Line*> (item);
7101 AudioRegionView* _arv = reinterpret_cast<AudioRegionView*> (item->get_data ("regionview"));
7102 _arv->remove_transient (*(float*) _line->get_data ("position"));
7106 Editor::snap_regions_to_grid ()
7108 list <boost::shared_ptr<Playlist > > used_playlists;
7110 RegionSelection rs = get_regions_from_selection_and_entered ();
7112 if (!_session || rs.empty()) {
7116 begin_reversible_command (_("snap regions to grid"));
7118 for (RegionSelection::iterator r = rs.begin(); r != rs.end(); ++r) {
7120 boost::shared_ptr<Playlist> pl = (*r)->region()->playlist();
7122 if (!pl->frozen()) {
7123 /* we haven't seen this playlist before */
7125 /* remember used playlists so we can thaw them later */
7126 used_playlists.push_back(pl);
7129 (*r)->region()->clear_changes ();
7131 MusicSample start ((*r)->region()->first_sample (), 0);
7132 snap_to (start, RoundNearest, SnapToGrid_Unscaled, true);
7133 (*r)->region()->set_position (start.sample, start.division);
7134 _session->add_command(new StatefulDiffCommand ((*r)->region()));
7137 while (used_playlists.size() > 0) {
7138 list <boost::shared_ptr<Playlist > >::iterator i = used_playlists.begin();
7140 used_playlists.pop_front();
7143 commit_reversible_command ();
7147 Editor::close_region_gaps ()
7149 list <boost::shared_ptr<Playlist > > used_playlists;
7151 RegionSelection rs = get_regions_from_selection_and_entered ();
7153 if (!_session || rs.empty()) {
7157 Dialog dialog (_("Close Region Gaps"));
7160 table.set_spacings (12);
7161 table.set_border_width (12);
7162 Label* l = manage (left_aligned_label (_("Crossfade length")));
7163 table.attach (*l, 0, 1, 0, 1);
7165 SpinButton spin_crossfade (1, 0);
7166 spin_crossfade.set_range (0, 15);
7167 spin_crossfade.set_increments (1, 1);
7168 spin_crossfade.set_value (5);
7169 table.attach (spin_crossfade, 1, 2, 0, 1);
7171 table.attach (*manage (new Label (_("ms"))), 2, 3, 0, 1);
7173 l = manage (left_aligned_label (_("Pull-back length")));
7174 table.attach (*l, 0, 1, 1, 2);
7176 SpinButton spin_pullback (1, 0);
7177 spin_pullback.set_range (0, 100);
7178 spin_pullback.set_increments (1, 1);
7179 spin_pullback.set_value(30);
7180 table.attach (spin_pullback, 1, 2, 1, 2);
7182 table.attach (*manage (new Label (_("ms"))), 2, 3, 1, 2);
7184 dialog.get_vbox()->pack_start (table);
7185 dialog.add_button (Stock::CANCEL, RESPONSE_CANCEL);
7186 dialog.add_button (_("Ok"), RESPONSE_ACCEPT);
7189 if (dialog.run () == RESPONSE_CANCEL) {
7193 samplepos_t crossfade_len = spin_crossfade.get_value();
7194 samplepos_t pull_back_samples = spin_pullback.get_value();
7196 crossfade_len = lrintf (crossfade_len * _session->sample_rate()/1000);
7197 pull_back_samples = lrintf (pull_back_samples * _session->sample_rate()/1000);
7199 /* Iterate over the region list and make adjacent regions overlap by crossfade_len_ms */
7201 begin_reversible_command (_("close region gaps"));
7204 boost::shared_ptr<Region> last_region;
7206 rs.sort_by_position_and_track();
7208 for (RegionSelection::iterator r = rs.begin(); r != rs.end(); ++r) {
7210 boost::shared_ptr<Playlist> pl = (*r)->region()->playlist();
7212 if (!pl->frozen()) {
7213 /* we haven't seen this playlist before */
7215 /* remember used playlists so we can thaw them later */
7216 used_playlists.push_back(pl);
7220 samplepos_t position = (*r)->region()->position();
7222 if (idx == 0 || position < last_region->position()){
7223 last_region = (*r)->region();
7228 (*r)->region()->clear_changes ();
7229 (*r)->region()->trim_front((position - pull_back_samples));
7231 last_region->clear_changes ();
7232 last_region->trim_end ((position - pull_back_samples + crossfade_len));
7234 _session->add_command (new StatefulDiffCommand ((*r)->region()));
7235 _session->add_command (new StatefulDiffCommand (last_region));
7237 last_region = (*r)->region();
7241 while (used_playlists.size() > 0) {
7242 list <boost::shared_ptr<Playlist > >::iterator i = used_playlists.begin();
7244 used_playlists.pop_front();
7247 commit_reversible_command ();
7251 Editor::tab_to_transient (bool forward)
7253 AnalysisFeatureList positions;
7255 RegionSelection rs = get_regions_from_selection_and_entered ();
7261 samplepos_t pos = _session->audible_sample ();
7263 if (!selection->tracks.empty()) {
7265 /* don't waste time searching for transients in duplicate playlists.
7268 TrackViewList ts = selection->tracks.filter_to_unique_playlists ();
7270 for (TrackViewList::iterator t = ts.begin(); t != ts.end(); ++t) {
7272 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (*t);
7275 boost::shared_ptr<Track> tr = rtv->track();
7277 boost::shared_ptr<Playlist> pl = tr->playlist ();
7279 samplepos_t result = pl->find_next_transient (pos, forward ? 1 : -1);
7282 positions.push_back (result);
7295 for (RegionSelection::iterator r = rs.begin(); r != rs.end(); ++r) {
7296 (*r)->region()->get_transients (positions);
7300 TransientDetector::cleanup_transients (positions, _session->sample_rate(), 3.0);
7303 AnalysisFeatureList::iterator x;
7305 for (x = positions.begin(); x != positions.end(); ++x) {
7311 if (x != positions.end ()) {
7312 _session->request_locate (*x);
7316 AnalysisFeatureList::reverse_iterator x;
7318 for (x = positions.rbegin(); x != positions.rend(); ++x) {
7324 if (x != positions.rend ()) {
7325 _session->request_locate (*x);
7331 Editor::playhead_forward_to_grid ()
7337 MusicSample pos (playhead_cursor->current_sample (), 0);
7339 if ( _grid_type == GridTypeNone) {
7340 if (pos.sample < max_samplepos - current_page_samples()*0.1) {
7341 pos.sample += current_page_samples()*0.1;
7342 _session->request_locate (pos.sample);
7344 _session->request_locate (0);
7348 if (pos.sample < max_samplepos - 1) {
7350 pos = snap_to_grid (pos, RoundUpAlways, SnapToGrid_Scaled);
7351 _session->request_locate (pos.sample);
7356 /* keep PH visible in window */
7357 if (pos.sample > (_leftmost_sample + current_page_samples() *0.9)) {
7358 reset_x_origin (pos.sample - (current_page_samples()*0.9));
7364 Editor::playhead_backward_to_grid ()
7370 MusicSample pos (playhead_cursor->current_sample (), 0);
7372 if ( _grid_type == GridTypeNone) {
7373 if ( pos.sample > current_page_samples()*0.1 ) {
7374 pos.sample -= current_page_samples()*0.1;
7375 _session->request_locate (pos.sample);
7377 _session->request_locate (0);
7381 if (pos.sample > 2) {
7383 pos = snap_to_grid (pos, RoundDownAlways, SnapToGrid_Scaled);
7386 //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...
7387 //also see: jump_backward_to_mark
7388 if (_session->transport_rolling()) {
7389 if ((playhead_cursor->current_sample() - pos.sample) < _session->sample_rate()/2) {
7390 pos = snap_to_grid (pos, RoundDownAlways, SnapToGrid_Scaled);
7394 _session->request_locate (pos.sample, _session->transport_rolling());
7397 /* keep PH visible in window */
7398 if (pos.sample < (_leftmost_sample + current_page_samples() *0.1)) {
7399 reset_x_origin (pos.sample - (current_page_samples()*0.1));
7404 Editor::set_track_height (Height h)
7406 TrackSelection& ts (selection->tracks);
7408 for (TrackSelection::iterator x = ts.begin(); x != ts.end(); ++x) {
7409 (*x)->set_height_enum (h);
7414 Editor::toggle_tracks_active ()
7416 TrackSelection& ts (selection->tracks);
7418 bool target = false;
7424 for (TrackSelection::iterator x = ts.begin(); x != ts.end(); ++x) {
7425 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*>(*x);
7429 target = !rtv->_route->active();
7432 rtv->_route->set_active (target, this);
7438 Editor::remove_tracks ()
7440 /* this will delete GUI objects that may be the subject of an event
7441 handler in which this method is called. Defer actual deletion to the
7442 next idle callback, when all event handling is finished.
7444 Glib::signal_idle().connect (sigc::mem_fun (*this, &Editor::idle_remove_tracks));
7448 Editor::idle_remove_tracks ()
7450 Session::StateProtector sp (_session);
7452 return false; /* do not call again */
7456 Editor::_remove_tracks ()
7458 TrackSelection& ts (selection->tracks);
7464 if (!ARDOUR_UI_UTILS::engine_is_running ()) {
7468 vector<string> choices;
7473 const char* trackstr;
7476 vector<boost::shared_ptr<Route> > routes;
7477 vector<boost::shared_ptr<VCA> > vcas;
7478 bool special_bus = false;
7480 for (TrackSelection::iterator x = ts.begin(); x != ts.end(); ++x) {
7481 VCATimeAxisView* vtv = dynamic_cast<VCATimeAxisView*> (*x);
7483 vcas.push_back (vtv->vca());
7487 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (*x);
7491 if (rtv->is_track()) {
7496 routes.push_back (rtv->_route);
7498 if (rtv->route()->is_master() || rtv->route()->is_monitor()) {
7503 if (special_bus && !Config->get_allow_special_bus_removal()) {
7504 MessageDialog msg (_("That would be bad news ...."),
7508 msg.set_secondary_text (string_compose (_(
7509 "Removing the master or monitor bus is such a bad idea\n\
7510 that %1 is not going to allow it.\n\
7512 If you really want to do this sort of thing\n\
7513 edit your ardour.rc file to set the\n\
7514 \"allow-special-bus-removal\" option to be \"yes\""), PROGRAM_NAME));
7521 if (ntracks + nbusses + nvcas == 0) {
7527 trackstr = P_("track", "tracks", ntracks);
7528 busstr = P_("bus", "busses", nbusses);
7529 vcastr = P_("VCA", "VCAs", nvcas);
7531 if (ntracks > 0 && nbusses > 0 && nvcas > 0) {
7532 title = _("Remove various strips");
7533 prompt = string_compose (_("Do you really want to remove %1 %2, %3 %4 and %5 %6?"),
7534 ntracks, trackstr, nbusses, busstr, nvcas, vcastr);
7536 else if (ntracks > 0 && nbusses > 0) {
7537 title = string_compose (_("Remove %1 and %2"), trackstr, busstr);
7538 prompt = string_compose (_("Do you really want to remove %1 %2 and %3 %4?"),
7539 ntracks, trackstr, nbusses, busstr);
7541 else if (ntracks > 0 && nvcas > 0) {
7542 title = string_compose (_("Remove %1 and %2"), trackstr, vcastr);
7543 prompt = string_compose (_("Do you really want to remove %1 %2 and %3 %4?"),
7544 ntracks, trackstr, nvcas, vcastr);
7546 else if (nbusses > 0 && nvcas > 0) {
7547 title = string_compose (_("Remove %1 and %2"), busstr, vcastr);
7548 prompt = string_compose (_("Do you really want to remove %1 %2 and %3 %4?"),
7549 nbusses, busstr, nvcas, vcastr);
7551 else if (ntracks > 0) {
7552 title = string_compose (_("Remove %1"), trackstr);
7553 prompt = string_compose (_("Do you really want to remove %1 %2?"),
7556 else if (nbusses > 0) {
7557 title = string_compose (_("Remove %1"), busstr);
7558 prompt = string_compose (_("Do you really want to remove %1 %2?"),
7561 else if (nvcas > 0) {
7562 title = string_compose (_("Remove %1"), vcastr);
7563 prompt = string_compose (_("Do you really want to remove %1 %2?"),
7571 prompt += "\n" + string_compose ("(You may also lose the playlists associated with the %1)", trackstr) + "\n";
7574 prompt += "\n" + string(_("This action cannot be undone, and the session file will be overwritten!"));
7576 choices.push_back (_("No, do nothing."));
7577 if (ntracks + nbusses + nvcas > 1) {
7578 choices.push_back (_("Yes, remove them."));
7580 choices.push_back (_("Yes, remove it."));
7583 Choice prompter (title, prompt, choices);
7585 if (prompter.run () != 1) {
7589 if (current_mixer_strip && routes.size () > 1 && std::find (routes.begin(), routes.end(), current_mixer_strip->route()) != routes.end ()) {
7590 /* Route deletion calls Editor::timeaxisview_deleted() iteratively (for each deleted
7591 * route). If the deleted route is currently displayed in the Editor-Mixer (highly
7592 * likely because deletion requires selection) this will call
7593 * Editor::set_selected_mixer_strip () which is expensive (MixerStrip::set_route()).
7594 * It's likewise likely that the route that has just been displayed in the
7595 * Editor-Mixer will be next in line for deletion.
7597 * So simply switch to the master-bus (if present)
7599 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
7600 if ((*i)->stripable ()->is_master ()) {
7601 set_selected_mixer_strip (*(*i));
7608 PresentationInfo::ChangeSuspender cs;
7609 DisplaySuspender ds;
7611 boost::shared_ptr<RouteList> rl (new RouteList);
7612 for (vector<boost::shared_ptr<Route> >::iterator x = routes.begin(); x != routes.end(); ++x) {
7615 _session->remove_routes (rl);
7617 for (vector<boost::shared_ptr<VCA> >::iterator x = vcas.begin(); x != vcas.end(); ++x) {
7618 _session->vca_manager().remove_vca (*x);
7622 /* TrackSelection and RouteList leave scope,
7623 * destructors are called,
7624 * diskstream drops references, save_state is called (again for every track)
7629 Editor::do_insert_time ()
7631 if (selection->tracks.empty()) {
7632 MessageDialog msg (_("You must first select some tracks to Insert Time."),
7633 true, MESSAGE_INFO, BUTTONS_OK, true);
7634 msg.set_position (WIN_POS_MOUSE);
7639 if (Config->get_edit_mode() == Lock) {
7640 MessageDialog msg (_("You cannot insert time in Lock Edit mode."),
7641 true, MESSAGE_INFO, BUTTONS_OK, true);
7642 msg.set_position (WIN_POS_MOUSE);
7647 InsertRemoveTimeDialog d (*this);
7648 int response = d.run ();
7650 if (response != RESPONSE_OK) {
7654 if (d.distance() == 0) {
7661 d.intersected_region_action (),
7665 d.move_glued_markers(),
7666 d.move_locked_markers(),
7672 Editor::insert_time (
7673 samplepos_t pos, samplecnt_t samples, InsertTimeOption opt,
7674 bool all_playlists, bool ignore_music_glue, bool markers_too, bool glued_markers_too, bool locked_markers_too, bool tempo_too
7678 if (Config->get_edit_mode() == Lock) {
7681 bool in_command = false;
7683 TrackViewList ts = selection->tracks.filter_to_unique_playlists ();
7685 for (TrackViewList::iterator x = ts.begin(); x != ts.end(); ++x) {
7689 /* don't operate on any playlist more than once, which could
7690 * happen if "all playlists" is enabled, but there is more
7691 * than 1 track using playlists "from" a given track.
7694 set<boost::shared_ptr<Playlist> > pl;
7696 if (all_playlists) {
7697 RouteTimeAxisView* rtav = dynamic_cast<RouteTimeAxisView*> (*x);
7698 if (rtav && rtav->track ()) {
7699 vector<boost::shared_ptr<Playlist> > all = _session->playlists->playlists_for_track (rtav->track ());
7700 for (vector<boost::shared_ptr<Playlist> >::iterator p = all.begin(); p != all.end(); ++p) {
7705 if ((*x)->playlist ()) {
7706 pl.insert ((*x)->playlist ());
7710 for (set<boost::shared_ptr<Playlist> >::iterator i = pl.begin(); i != pl.end(); ++i) {
7712 (*i)->clear_changes ();
7713 (*i)->clear_owned_changes ();
7716 begin_reversible_command (_("insert time"));
7720 if (opt == SplitIntersected) {
7721 /* non musical split */
7722 (*i)->split (MusicSample (pos, 0));
7725 (*i)->shift (pos, samples, (opt == MoveIntersected), ignore_music_glue);
7727 vector<Command*> cmds;
7729 _session->add_commands (cmds);
7731 _session->add_command (new StatefulDiffCommand (*i));
7735 RouteTimeAxisView* rtav = dynamic_cast<RouteTimeAxisView*> (*x);
7738 begin_reversible_command (_("insert time"));
7741 rtav->route ()->shift (pos, samples);
7748 const int32_t divisions = get_grid_music_divisions (0);
7749 XMLNode& before (_session->locations()->get_state());
7750 Locations::LocationList copy (_session->locations()->list());
7752 for (Locations::LocationList::iterator i = copy.begin(); i != copy.end(); ++i) {
7754 Locations::LocationList::const_iterator tmp;
7756 if ((*i)->position_lock_style() == AudioTime || glued_markers_too) {
7757 bool const was_locked = (*i)->locked ();
7758 if (locked_markers_too) {
7762 if ((*i)->start() >= pos) {
7763 // move end first, in case we're moving by more than the length of the range
7764 if (!(*i)->is_mark()) {
7765 (*i)->set_end ((*i)->end() + samples, false, true, divisions);
7767 (*i)->set_start ((*i)->start() + samples, false, true, divisions);
7779 begin_reversible_command (_("insert time"));
7782 XMLNode& after (_session->locations()->get_state());
7783 _session->add_command (new MementoCommand<Locations>(*_session->locations(), &before, &after));
7789 begin_reversible_command (_("insert time"));
7792 XMLNode& before (_session->tempo_map().get_state());
7793 _session->tempo_map().insert_time (pos, samples);
7794 XMLNode& after (_session->tempo_map().get_state());
7795 _session->add_command (new MementoCommand<TempoMap>(_session->tempo_map(), &before, &after));
7799 commit_reversible_command ();
7804 Editor::do_remove_time ()
7806 if (selection->tracks.empty()) {
7807 MessageDialog msg (_("You must first select some tracks to Remove Time."),
7808 true, MESSAGE_INFO, BUTTONS_OK, true);
7809 msg.set_position (WIN_POS_MOUSE);
7814 if (Config->get_edit_mode() == Lock) {
7815 MessageDialog msg (_("You cannot remove time in Lock Edit mode."),
7816 true, MESSAGE_INFO, BUTTONS_OK, true);
7817 msg.set_position (WIN_POS_MOUSE);
7822 InsertRemoveTimeDialog d (*this, true);
7824 int response = d.run ();
7826 if (response != RESPONSE_OK) {
7830 samplecnt_t distance = d.distance();
7832 if (distance == 0) {
7842 d.move_glued_markers(),
7843 d.move_locked_markers(),
7849 Editor::remove_time (samplepos_t pos, samplecnt_t samples, InsertTimeOption opt,
7850 bool ignore_music_glue, bool markers_too, bool glued_markers_too, bool locked_markers_too, bool tempo_too)
7852 if (Config->get_edit_mode() == Lock) {
7853 error << (_("Cannot insert or delete time when in Lock edit.")) << endmsg;
7856 bool in_command = false;
7858 for (TrackSelection::iterator x = selection->tracks.begin(); x != selection->tracks.end(); ++x) {
7860 boost::shared_ptr<Playlist> pl = (*x)->playlist();
7864 XMLNode &before = pl->get_state();
7867 begin_reversible_command (_("remove time"));
7871 std::list<AudioRange> rl;
7872 AudioRange ar(pos, pos+samples, 0);
7875 pl->shift (pos, -samples, true, ignore_music_glue);
7877 XMLNode &after = pl->get_state();
7879 _session->add_command (new MementoCommand<Playlist> (*pl, &before, &after));
7883 RouteTimeAxisView* rtav = dynamic_cast<RouteTimeAxisView*> (*x);
7886 begin_reversible_command (_("remove time"));
7889 rtav->route ()->shift (pos, -samples);
7893 const int32_t divisions = get_grid_music_divisions (0);
7894 std::list<Location*> loc_kill_list;
7899 XMLNode& before (_session->locations()->get_state());
7900 Locations::LocationList copy (_session->locations()->list());
7902 for (Locations::LocationList::iterator i = copy.begin(); i != copy.end(); ++i) {
7903 if ((*i)->position_lock_style() == AudioTime || glued_markers_too) {
7905 bool const was_locked = (*i)->locked ();
7906 if (locked_markers_too) {
7910 if (!(*i)->is_mark()) { // it's a range; have to handle both start and end
7911 if ((*i)->end() >= pos
7912 && (*i)->end() < pos+samples
7913 && (*i)->start() >= pos
7914 && (*i)->end() < pos+samples) { // range is completely enclosed; kill it
7916 loc_kill_list.push_back(*i);
7917 } else { // only start or end is included, try to do the right thing
7918 // move start before moving end, to avoid trying to move the end to before the start
7919 // if we're removing more time than the length of the range
7920 if ((*i)->start() >= pos && (*i)->start() < pos+samples) {
7921 // start is within cut
7922 (*i)->set_start (pos, false, true,divisions); // bring the start marker to the beginning of the cut
7924 } else if ((*i)->start() >= pos+samples) {
7925 // start (and thus entire range) lies beyond end of cut
7926 (*i)->set_start ((*i)->start() - samples, false, true, divisions); // slip the start marker back
7929 if ((*i)->end() >= pos && (*i)->end() < pos+samples) {
7930 // end is inside cut
7931 (*i)->set_end (pos, false, true, divisions); // bring the end to the cut
7933 } else if ((*i)->end() >= pos+samples) {
7934 // end is beyond end of cut
7935 (*i)->set_end ((*i)->end() - samples, false, true, divisions); // slip the end marker back
7940 } else if ((*i)->start() >= pos && (*i)->start() < pos+samples) {
7941 loc_kill_list.push_back(*i);
7943 } else if ((*i)->start() >= pos) {
7944 (*i)->set_start ((*i)->start() -samples, false, true, divisions);
7954 for (list<Location*>::iterator i = loc_kill_list.begin(); i != loc_kill_list.end(); ++i) {
7955 _session->locations()->remove (*i);
7960 begin_reversible_command (_("remove time"));
7963 XMLNode& after (_session->locations()->get_state());
7964 _session->add_command (new MementoCommand<Locations>(*_session->locations(), &before, &after));
7969 XMLNode& before (_session->tempo_map().get_state());
7971 if (_session->tempo_map().remove_time (pos, samples)) {
7973 begin_reversible_command (_("remove time"));
7976 XMLNode& after (_session->tempo_map().get_state());
7977 _session->add_command (new MementoCommand<TempoMap>(_session->tempo_map(), &before, &after));
7982 commit_reversible_command ();
7987 Editor::fit_selection ()
7989 if (!selection->tracks.empty()) {
7990 fit_tracks (selection->tracks);
7994 /* no selected tracks - use tracks with selected regions */
7996 if (!selection->regions.empty()) {
7997 for (RegionSelection::iterator r = selection->regions.begin(); r != selection->regions.end(); ++r) {
7998 tvl.push_back (&(*r)->get_time_axis_view ());
8004 } else if (internal_editing()) {
8005 /* no selected tracks, or regions, but in internal edit mode, so follow the mouse and use
8008 if (entered_track) {
8009 tvl.push_back (entered_track);
8017 Editor::fit_tracks (TrackViewList & tracks)
8019 if (tracks.empty()) {
8023 uint32_t child_heights = 0;
8024 int visible_tracks = 0;
8026 for (TrackSelection::iterator t = tracks.begin(); t != tracks.end(); ++t) {
8028 if (!(*t)->marked_for_display()) {
8032 child_heights += (*t)->effective_height() - (*t)->current_height();
8036 /* compute the per-track height from:
8038 * total canvas visible height
8039 * - height that will be taken by visible children of selected tracks
8040 * - height of the ruler/hscroll area
8042 uint32_t h = (uint32_t) floor ((trackviews_height() - child_heights) / visible_tracks);
8043 double first_y_pos = DBL_MAX;
8045 if (h < TimeAxisView::preset_height (HeightSmall)) {
8046 MessageDialog msg (_("There are too many tracks to fit in the current window"));
8047 /* too small to be displayed */
8051 undo_visual_stack.push_back (current_visual_state (true));
8052 PBD::Unwinder<bool> nsv (no_save_visual, true);
8054 /* build a list of all tracks, including children */
8057 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
8059 TimeAxisView::Children c = (*i)->get_child_list ();
8060 for (TimeAxisView::Children::iterator j = c.begin(); j != c.end(); ++j) {
8061 all.push_back (j->get());
8066 // find selection range.
8067 // if someone knows how to user TrackViewList::iterator for this
8069 int selected_top = -1;
8070 int selected_bottom = -1;
8072 for (TrackViewList::iterator t = all.begin(); t != all.end(); ++t, ++i) {
8073 if ((*t)->marked_for_display ()) {
8074 if (tracks.contains(*t)) {
8075 if (selected_top == -1) {
8078 selected_bottom = i;
8084 for (TrackViewList::iterator t = all.begin(); t != all.end(); ++t, ++i) {
8085 if ((*t)->marked_for_display ()) {
8086 if (tracks.contains(*t)) {
8087 (*t)->set_height (h);
8088 first_y_pos = std::min ((*t)->y_position (), first_y_pos);
8090 if (i > selected_top && i < selected_bottom) {
8091 hide_track_in_display (*t);
8098 set the controls_layout height now, because waiting for its size
8099 request signal handler will cause the vertical adjustment setting to fail
8102 controls_layout.property_height () = _full_canvas_height;
8103 vertical_adjustment.set_value (first_y_pos);
8105 redo_visual_stack.push_back (current_visual_state (true));
8107 visible_tracks_selector.set_text (_("Sel"));
8111 Editor::save_visual_state (uint32_t n)
8113 while (visual_states.size() <= n) {
8114 visual_states.push_back (0);
8117 if (visual_states[n] != 0) {
8118 delete visual_states[n];
8121 visual_states[n] = current_visual_state (true);
8126 Editor::goto_visual_state (uint32_t n)
8128 if (visual_states.size() <= n) {
8132 if (visual_states[n] == 0) {
8136 use_visual_state (*visual_states[n]);
8140 Editor::start_visual_state_op (uint32_t n)
8142 save_visual_state (n);
8144 PopUp* pup = new PopUp (WIN_POS_MOUSE, 1000, true);
8146 snprintf (buf, sizeof (buf), _("Saved view %u"), n+1);
8147 pup->set_text (buf);
8152 Editor::cancel_visual_state_op (uint32_t n)
8154 goto_visual_state (n);
8158 Editor::toggle_region_mute ()
8160 if (_ignore_region_action) {
8164 RegionSelection rs = get_regions_from_selection_and_entered ();
8170 if (rs.size() > 1) {
8171 begin_reversible_command (_("mute regions"));
8173 begin_reversible_command (_("mute region"));
8176 for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
8178 (*i)->region()->playlist()->clear_changes ();
8179 (*i)->region()->set_muted (!(*i)->region()->muted ());
8180 _session->add_command (new StatefulDiffCommand ((*i)->region()));
8184 commit_reversible_command ();
8188 Editor::combine_regions ()
8190 /* foreach track with selected regions, take all selected regions
8191 and join them into a new region containing the subregions (as a
8195 typedef set<RouteTimeAxisView*> RTVS;
8198 if (selection->regions.empty()) {
8202 for (RegionSelection::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
8203 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*>(&(*i)->get_time_axis_view());
8206 tracks.insert (rtv);
8210 begin_reversible_command (_("combine regions"));
8212 vector<RegionView*> new_selection;
8214 for (RTVS::iterator i = tracks.begin(); i != tracks.end(); ++i) {
8217 if ((rv = (*i)->combine_regions ()) != 0) {
8218 new_selection.push_back (rv);
8222 selection->clear_regions ();
8223 for (vector<RegionView*>::iterator i = new_selection.begin(); i != new_selection.end(); ++i) {
8224 selection->add (*i);
8227 commit_reversible_command ();
8231 Editor::uncombine_regions ()
8233 typedef set<RouteTimeAxisView*> RTVS;
8236 if (selection->regions.empty()) {
8240 for (RegionSelection::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
8241 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*>(&(*i)->get_time_axis_view());
8244 tracks.insert (rtv);
8248 begin_reversible_command (_("uncombine regions"));
8250 for (RTVS::iterator i = tracks.begin(); i != tracks.end(); ++i) {
8251 (*i)->uncombine_regions ();
8254 commit_reversible_command ();
8258 Editor::toggle_midi_input_active (bool flip_others)
8261 boost::shared_ptr<RouteList> rl (new RouteList);
8263 for (TrackSelection::iterator i = selection->tracks.begin(); i != selection->tracks.end(); ++i) {
8264 RouteTimeAxisView *rtav = dynamic_cast<RouteTimeAxisView *>(*i);
8270 boost::shared_ptr<MidiTrack> mt = rtav->midi_track();
8273 rl->push_back (rtav->route());
8274 onoff = !mt->input_active();
8278 _session->set_exclusive_input_active (rl, onoff, flip_others);
8281 static bool ok_fine (GdkEventAny*) { return true; }
8287 lock_dialog = new Gtk::Dialog (string_compose (_("%1: Locked"), PROGRAM_NAME), true);
8289 Gtk::Image* padlock = manage (new Gtk::Image (ARDOUR_UI_UTILS::get_icon ("padlock_closed")));
8290 lock_dialog->get_vbox()->pack_start (*padlock);
8291 lock_dialog->signal_delete_event ().connect (sigc::ptr_fun (ok_fine));
8293 ArdourButton* b = manage (new ArdourButton);
8294 b->set_name ("lock button");
8295 b->set_text (_("Click to unlock"));
8296 b->signal_clicked.connect (sigc::mem_fun (*this, &Editor::unlock));
8297 lock_dialog->get_vbox()->pack_start (*b);
8299 lock_dialog->get_vbox()->show_all ();
8300 lock_dialog->set_size_request (200, 200);
8303 delete _main_menu_disabler;
8304 _main_menu_disabler = new MainMenuDisabler;
8306 lock_dialog->present ();
8308 lock_dialog->get_window()->set_decorations (Gdk::WMDecoration (0));
8314 lock_dialog->hide ();
8316 delete _main_menu_disabler;
8317 _main_menu_disabler = 0;
8319 if (UIConfiguration::instance().get_lock_gui_after_seconds()) {
8320 start_lock_event_timing ();
8325 Editor::bring_in_callback (Gtk::Label* label, uint32_t n, uint32_t total, string name)
8327 Gtkmm2ext::UI::instance()->call_slot (invalidator (*this), boost::bind (&Editor::update_bring_in_message, this, label, n, total, name));
8331 Editor::update_bring_in_message (Gtk::Label* label, uint32_t n, uint32_t total, string name)
8333 Timers::TimerSuspender t;
8334 label->set_text (string_compose ("Copying %1, %2 of %3", name, n, total));
8335 Gtkmm2ext::UI::instance()->flush_pending (1);
8339 Editor::bring_all_sources_into_session ()
8346 ArdourDialog w (_("Moving embedded files into session folder"));
8347 w.get_vbox()->pack_start (msg);
8350 /* flush all pending GUI events because we're about to start copying
8354 Timers::TimerSuspender t;
8355 Gtkmm2ext::UI::instance()->flush_pending (3);
8359 _session->bring_all_sources_into_session (boost::bind (&Editor::bring_in_callback, this, &msg, _1, _2, _3));