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();
4459 boost::shared_ptr<Region> region = clicked_regionview->region();
4461 playlist->clear_changes ();
4462 playlist->clear_owned_changes ();
4463 playlist->remove_region (region);
4465 if (Config->get_edit_mode() == Ripple) {
4466 playlist->ripple (region->position(), - region->length(), boost::shared_ptr<Region>());
4469 /* We might have removed regions, which alters other regions' layering_index,
4470 so we need to do a recursive diff here.
4472 vector<Command*> cmds;
4473 playlist->rdiff (cmds);
4474 _session->add_commands (cmds);
4476 _session->add_command(new StatefulDiffCommand (playlist));
4477 commit_reversible_command ();
4481 /** Remove the selected regions */
4483 Editor::remove_selected_regions ()
4485 RegionSelection rs = get_regions_from_selection_and_entered ();
4487 if (!_session || rs.empty()) {
4491 list<boost::shared_ptr<Region> > regions_to_remove;
4493 for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
4494 // we can't just remove the region(s) in this loop because
4495 // this removes them from the RegionSelection, and they thus
4496 // disappear from underneath the iterator, and the ++i above
4497 // SEGVs in a puzzling fashion.
4499 // so, first iterate over the regions to be removed from rs and
4500 // add them to the regions_to_remove list, and then
4501 // iterate over the list to actually remove them.
4503 regions_to_remove.push_back ((*i)->region());
4506 vector<boost::shared_ptr<Playlist> > playlists;
4508 for (list<boost::shared_ptr<Region> >::iterator rl = regions_to_remove.begin(); rl != regions_to_remove.end(); ++rl) {
4510 boost::shared_ptr<Playlist> playlist = (*rl)->playlist();
4513 // is this check necessary?
4517 /* get_regions_from_selection_and_entered() guarantees that
4518 the playlists involved are unique, so there is no need
4522 playlists.push_back (playlist);
4524 playlist->clear_changes ();
4525 playlist->clear_owned_changes ();
4526 playlist->freeze ();
4527 playlist->remove_region (*rl);
4528 if (Config->get_edit_mode() == Ripple)
4529 playlist->ripple ((*rl)->position(), -(*rl)->length(), boost::shared_ptr<Region>());
4533 vector<boost::shared_ptr<Playlist> >::iterator pl;
4534 bool in_command = false;
4536 for (pl = playlists.begin(); pl != playlists.end(); ++pl) {
4539 /* We might have removed regions, which alters other regions' layering_index,
4540 so we need to do a recursive diff here.
4544 begin_reversible_command (_("remove region"));
4547 vector<Command*> cmds;
4548 (*pl)->rdiff (cmds);
4549 _session->add_commands (cmds);
4551 _session->add_command(new StatefulDiffCommand (*pl));
4555 commit_reversible_command ();
4559 /** Cut, copy or clear selected regions.
4560 * @param op Operation (Cut, Copy or Clear)
4563 Editor::cut_copy_regions (CutCopyOp op, RegionSelection& rs)
4565 /* we can't use a std::map here because the ordering is important, and we can't trivially sort
4566 a map when we want ordered access to both elements. i think.
4569 vector<PlaylistMapping> pmap;
4571 samplepos_t first_position = max_samplepos;
4573 typedef set<boost::shared_ptr<Playlist> > FreezeList;
4574 FreezeList freezelist;
4576 /* get ordering correct before we cut/copy */
4578 rs.sort_by_position_and_track ();
4580 for (RegionSelection::iterator x = rs.begin(); x != rs.end(); ++x) {
4582 first_position = min ((samplepos_t) (*x)->region()->position(), first_position);
4584 if (op == Cut || op == Clear || op == Delete) {
4585 boost::shared_ptr<Playlist> pl = (*x)->region()->playlist();
4588 FreezeList::iterator fl;
4590 // only take state if this is a new playlist.
4591 for (fl = freezelist.begin(); fl != freezelist.end(); ++fl) {
4597 if (fl == freezelist.end()) {
4598 pl->clear_changes();
4599 pl->clear_owned_changes ();
4601 freezelist.insert (pl);
4606 TimeAxisView* tv = &(*x)->get_time_axis_view();
4607 vector<PlaylistMapping>::iterator z;
4609 for (z = pmap.begin(); z != pmap.end(); ++z) {
4610 if ((*z).tv == tv) {
4615 if (z == pmap.end()) {
4616 pmap.push_back (PlaylistMapping (tv));
4620 for (RegionSelection::iterator x = rs.begin(); x != rs.end(); ) {
4622 boost::shared_ptr<Playlist> pl = (*x)->region()->playlist();
4625 /* region not yet associated with a playlist (e.g. unfinished
4632 TimeAxisView& tv = (*x)->get_time_axis_view();
4633 boost::shared_ptr<Playlist> npl;
4634 RegionSelection::iterator tmp;
4641 vector<PlaylistMapping>::iterator z;
4643 for (z = pmap.begin(); z != pmap.end(); ++z) {
4644 if ((*z).tv == &tv) {
4649 assert (z != pmap.end());
4652 npl = PlaylistFactory::create (pl->data_type(), *_session, "cutlist", true);
4660 boost::shared_ptr<Region> r = (*x)->region();
4661 boost::shared_ptr<Region> _xx;
4667 pl->remove_region (r);
4668 if (Config->get_edit_mode() == Ripple)
4669 pl->ripple (r->position(), -r->length(), boost::shared_ptr<Region>());
4673 _xx = RegionFactory::create (r);
4674 npl->add_region (_xx, r->position() - first_position);
4675 pl->remove_region (r);
4676 if (Config->get_edit_mode() == Ripple)
4677 pl->ripple (r->position(), -r->length(), boost::shared_ptr<Region>());
4681 /* copy region before adding, so we're not putting same object into two different playlists */
4682 npl->add_region (RegionFactory::create (r), r->position() - first_position);
4686 pl->remove_region (r);
4687 if (Config->get_edit_mode() == Ripple)
4688 pl->ripple (r->position(), -r->length(), boost::shared_ptr<Region>());
4697 list<boost::shared_ptr<Playlist> > foo;
4699 /* the pmap is in the same order as the tracks in which selected regions occurred */
4701 for (vector<PlaylistMapping>::iterator i = pmap.begin(); i != pmap.end(); ++i) {
4704 foo.push_back ((*i).pl);
4709 cut_buffer->set (foo);
4713 _last_cut_copy_source_track = 0;
4715 _last_cut_copy_source_track = pmap.front().tv;
4719 for (FreezeList::iterator pl = freezelist.begin(); pl != freezelist.end(); ++pl) {
4722 /* We might have removed regions, which alters other regions' layering_index,
4723 so we need to do a recursive diff here.
4725 vector<Command*> cmds;
4726 (*pl)->rdiff (cmds);
4727 _session->add_commands (cmds);
4729 _session->add_command (new StatefulDiffCommand (*pl));
4734 Editor::cut_copy_ranges (CutCopyOp op)
4736 TrackViewList ts = selection->tracks.filter_to_unique_playlists ();
4738 /* Sort the track selection now, so that it if is used, the playlists
4739 selected by the calls below to cut_copy_clear are in the order that
4740 their tracks appear in the editor. This makes things like paste
4741 of ranges work properly.
4744 sort_track_selection (ts);
4747 if (!entered_track) {
4750 ts.push_back (entered_track);
4753 for (TrackViewList::iterator i = ts.begin(); i != ts.end(); ++i) {
4754 (*i)->cut_copy_clear (*selection, op);
4759 Editor::paste (float times, bool from_context)
4761 DEBUG_TRACE (DEBUG::CutNPaste, "paste to preferred edit pos\n");
4762 MusicSample where (get_preferred_edit_position (EDIT_IGNORE_NONE, from_context), 0);
4763 paste_internal (where.sample, times, 0);
4767 Editor::mouse_paste ()
4769 MusicSample where (0, 0);
4771 if (!mouse_sample (where.sample, ignored)) {
4776 paste_internal (where.sample, 1, where.division);
4780 Editor::paste_internal (samplepos_t position, float times, const int32_t sub_num)
4782 DEBUG_TRACE (DEBUG::CutNPaste, string_compose ("apparent paste position is %1\n", position));
4784 if (cut_buffer->empty(internal_editing())) {
4788 if (position == max_samplepos) {
4789 position = get_preferred_edit_position();
4790 DEBUG_TRACE (DEBUG::CutNPaste, string_compose ("preferred edit position is %1\n", position));
4793 if (position != last_paste_pos) {
4794 /* paste in new location, reset repeated paste state */
4796 last_paste_pos = position;
4799 /* get everything in the correct order */
4802 if (!selection->tracks.empty()) {
4803 /* If there is a track selection, paste into exactly those tracks and
4804 * only those tracks. This allows the user to be explicit and override
4805 * the below "do the reasonable thing" logic. */
4806 ts = selection->tracks.filter_to_unique_playlists ();
4807 sort_track_selection (ts);
4809 /* Figure out which track to base the paste at. */
4810 TimeAxisView* base_track = NULL;
4811 if (_edit_point == Editing::EditAtMouse && entered_track) {
4812 /* With the mouse edit point, paste onto the track under the mouse. */
4813 base_track = entered_track;
4814 } else if (_edit_point == Editing::EditAtMouse && entered_regionview) {
4815 /* With the mouse edit point, paste onto the track of the region under the mouse. */
4816 base_track = &entered_regionview->get_time_axis_view();
4817 } else if (_last_cut_copy_source_track) {
4818 /* Paste to the track that the cut/copy came from (see mantis #333). */
4819 base_track = _last_cut_copy_source_track;
4821 /* This is "impossible" since we've copied... well, do nothing. */
4825 /* Walk up to parent if necessary, so base track is a route. */
4826 while (base_track->get_parent()) {
4827 base_track = base_track->get_parent();
4830 /* Add base track and all tracks below it. The paste logic will select
4831 the appropriate object types from the cut buffer in relative order. */
4832 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
4833 if ((*i)->order() >= base_track->order()) {
4838 /* Sort tracks so the nth track of type T will pick the nth object of type T. */
4839 sort_track_selection (ts);
4841 /* Add automation children of each track in order, for pasting several lines. */
4842 for (TrackViewList::iterator i = ts.begin(); i != ts.end();) {
4843 /* Add any automation children for pasting several lines */
4844 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*>(*i++);
4849 typedef RouteTimeAxisView::AutomationTracks ATracks;
4850 const ATracks& atracks = rtv->automation_tracks();
4851 for (ATracks::const_iterator a = atracks.begin(); a != atracks.end(); ++a) {
4852 i = ts.insert(i, a->second.get());
4857 /* We now have a list of trackviews starting at base_track, including
4858 automation children, in the order shown in the editor, e.g. R1,
4859 R1.A1, R1.A2, R2, R2.A1, ... */
4862 begin_reversible_command (Operations::paste);
4864 if (ts.size() == 1 && cut_buffer->lines.size() == 1 &&
4865 dynamic_cast<AutomationTimeAxisView*>(ts.front())) {
4866 /* Only one line copied, and one automation track selected. Do a
4867 "greedy" paste from one automation type to another. */
4869 PasteContext ctx(paste_count, times, ItemCounts(), true);
4870 ts.front()->paste (position, *cut_buffer, ctx, sub_num);
4874 /* Paste into tracks */
4876 PasteContext ctx(paste_count, times, ItemCounts(), false);
4877 for (TrackViewList::iterator i = ts.begin(); i != ts.end(); ++i) {
4878 (*i)->paste (position, *cut_buffer, ctx, sub_num);
4884 commit_reversible_command ();
4888 Editor::duplicate_regions (float times)
4890 RegionSelection rs (get_regions_from_selection_and_entered());
4891 duplicate_some_regions (rs, times);
4895 Editor::duplicate_some_regions (RegionSelection& regions, float times)
4897 if (regions.empty ()) {
4901 boost::shared_ptr<Playlist> playlist;
4902 std::set<boost::shared_ptr<Playlist> > playlists; // list of unique playlists affected by duplication
4903 RegionSelection sel = regions; // clear (below) may clear the argument list if its the current region selection
4904 RegionSelection foo;
4906 samplepos_t const start_sample = regions.start ();
4907 samplepos_t const end_sample = regions.end_sample ();
4908 samplecnt_t const span = end_sample - start_sample + 1;
4910 begin_reversible_command (Operations::duplicate_region);
4912 selection->clear_regions ();
4914 /* ripple first so that we don't move the duplicates that will be added */
4916 if (Config->get_edit_mode() == Ripple) {
4918 /* convert RegionSelection into RegionList so that we can pass it to ripple and exclude the regions we will duplicate */
4922 for (RegionSelection::iterator i = sel.begin(); i != sel.end(); ++i) {
4923 exclude.push_back ((*i)->region());
4924 playlist = (*i)->region()->playlist();
4925 if (playlists.insert (playlist).second) {
4926 /* successfully inserted into set, so it's the first time we've seen this playlist */
4927 playlist->clear_changes ();
4931 for (set<boost::shared_ptr<Playlist> >::iterator p = playlists.begin(); p != playlists.end(); ++p) {
4932 (*p)->ripple (start_sample, span * times, &exclude);
4936 for (RegionSelection::iterator i = sel.begin(); i != sel.end(); ++i) {
4938 boost::shared_ptr<Region> r ((*i)->region());
4940 TimeAxisView& tv = (*i)->get_time_axis_view();
4941 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (&tv);
4942 latest_regionviews.clear ();
4943 sigc::connection c = rtv->view()->RegionViewAdded.connect (sigc::mem_fun(*this, &Editor::collect_new_region_view));
4945 samplepos_t const position = end_sample + (r->first_sample() - start_sample + 1);
4946 playlist = (*i)->region()->playlist();
4948 if (Config->get_edit_mode() != Ripple) {
4949 if (playlists.insert (playlist).second) {
4950 playlist->clear_changes ();
4954 playlist->duplicate (r, position, span, times);
4958 foo.insert (foo.end(), latest_regionviews.begin(), latest_regionviews.end());
4961 for (set<boost::shared_ptr<Playlist> >::iterator p = playlists.begin(); p != playlists.end(); ++p) {
4962 _session->add_command (new StatefulDiffCommand (*p));
4963 vector<Command*> cmds;
4965 _session->add_commands (cmds);
4969 selection->set (foo);
4972 commit_reversible_command ();
4976 Editor::duplicate_selection (float times)
4978 if (selection->time.empty() || selection->tracks.empty()) {
4982 boost::shared_ptr<Playlist> playlist;
4984 TrackViewList ts = selection->tracks.filter_to_unique_playlists ();
4986 bool in_command = false;
4988 for (TrackViewList::iterator i = ts.begin(); i != ts.end(); ++i) {
4989 if ((playlist = (*i)->playlist()) == 0) {
4992 playlist->clear_changes ();
4994 if (clicked_selection) {
4995 playlist->duplicate_range (selection->time[clicked_selection], times);
4997 playlist->duplicate_ranges (selection->time, times);
5001 begin_reversible_command (_("duplicate range selection"));
5004 _session->add_command (new StatefulDiffCommand (playlist));
5009 if (times == 1.0f) {
5010 // now "move" range selection to after the current range selection
5011 samplecnt_t distance = 0;
5013 if (clicked_selection) {
5015 selection->time[clicked_selection].end - selection->time[clicked_selection].start;
5017 distance = selection->time.end_sample () - selection->time.start ();
5020 selection->move_time (distance);
5022 commit_reversible_command ();
5026 /** Reset all selected points to the relevant default value */
5028 Editor::reset_point_selection ()
5030 for (PointSelection::iterator i = selection->points.begin(); i != selection->points.end(); ++i) {
5031 ARDOUR::AutomationList::iterator j = (*i)->model ();
5032 (*j)->value = (*i)->line().the_list()->descriptor ().normal;
5037 Editor::center_playhead ()
5039 float const page = _visible_canvas_width * samples_per_pixel;
5040 center_screen_internal (playhead_cursor->current_sample (), page);
5044 Editor::center_edit_point ()
5046 float const page = _visible_canvas_width * samples_per_pixel;
5047 center_screen_internal (get_preferred_edit_position(), page);
5050 /** Caller must begin and commit a reversible command */
5052 Editor::clear_playlist (boost::shared_ptr<Playlist> playlist)
5054 playlist->clear_changes ();
5056 _session->add_command (new StatefulDiffCommand (playlist));
5060 Editor::nudge_track (bool use_edit, bool forwards)
5062 boost::shared_ptr<Playlist> playlist;
5063 samplepos_t distance;
5064 samplepos_t next_distance;
5068 start = get_preferred_edit_position();
5073 if ((distance = get_nudge_distance (start, next_distance)) == 0) {
5077 if (selection->tracks.empty()) {
5081 TrackViewList ts = selection->tracks.filter_to_unique_playlists ();
5082 bool in_command = false;
5084 for (TrackViewList::iterator i = ts.begin(); i != ts.end(); ++i) {
5086 if ((playlist = (*i)->playlist()) == 0) {
5090 playlist->clear_changes ();
5091 playlist->clear_owned_changes ();
5093 playlist->nudge_after (start, distance, forwards);
5096 begin_reversible_command (_("nudge track"));
5099 vector<Command*> cmds;
5101 playlist->rdiff (cmds);
5102 _session->add_commands (cmds);
5104 _session->add_command (new StatefulDiffCommand (playlist));
5108 commit_reversible_command ();
5113 Editor::remove_last_capture ()
5115 vector<string> choices;
5122 if (Config->get_verify_remove_last_capture()) {
5123 prompt = _("Do you really want to destroy the last capture?"
5124 "\n(This is destructive and cannot be undone)");
5126 choices.push_back (_("No, do nothing."));
5127 choices.push_back (_("Yes, destroy it."));
5129 Choice prompter (_("Destroy last capture"), prompt, choices);
5131 if (prompter.run () == 1) {
5132 _session->remove_last_capture ();
5133 _regions->redisplay ();
5137 _session->remove_last_capture();
5138 _regions->redisplay ();
5143 Editor::normalize_region ()
5149 RegionSelection rs = get_regions_from_selection_and_entered ();
5155 NormalizeDialog dialog (rs.size() > 1);
5157 if (dialog.run () != RESPONSE_ACCEPT) {
5161 CursorContext::Handle cursor_ctx = CursorContext::create(*this, _cursors->wait);
5164 /* XXX: should really only count audio regions here */
5165 int const regions = rs.size ();
5167 /* Make a list of the selected audio regions' maximum amplitudes, and also
5168 obtain the maximum amplitude of them all.
5170 list<double> max_amps;
5171 list<double> rms_vals;
5174 bool use_rms = dialog.constrain_rms ();
5176 for (RegionSelection::const_iterator i = rs.begin(); i != rs.end(); ++i) {
5177 AudioRegionView const * arv = dynamic_cast<AudioRegionView const *> (*i);
5181 dialog.descend (1.0 / regions);
5182 double const a = arv->audio_region()->maximum_amplitude (&dialog);
5184 double r = arv->audio_region()->rms (&dialog);
5185 max_rms = max (max_rms, r);
5186 rms_vals.push_back (r);
5190 /* the user cancelled the operation */
5194 max_amps.push_back (a);
5195 max_amp = max (max_amp, a);
5199 list<double>::const_iterator a = max_amps.begin ();
5200 list<double>::const_iterator l = rms_vals.begin ();
5201 bool in_command = false;
5203 for (RegionSelection::iterator r = rs.begin(); r != rs.end(); ++r) {
5204 AudioRegionView* const arv = dynamic_cast<AudioRegionView*> (*r);
5209 arv->region()->clear_changes ();
5211 double amp = dialog.normalize_individually() ? *a : max_amp;
5212 double target = dialog.target_peak (); // dB
5215 double const amp_rms = dialog.normalize_individually() ? *l : max_rms;
5216 const double t_rms = dialog.target_rms ();
5217 const gain_t c_peak = dB_to_coefficient (target);
5218 const gain_t c_rms = dB_to_coefficient (t_rms);
5219 if ((amp_rms / c_rms) > (amp / c_peak)) {
5225 arv->audio_region()->normalize (amp, target);
5228 begin_reversible_command (_("normalize"));
5231 _session->add_command (new StatefulDiffCommand (arv->region()));
5238 commit_reversible_command ();
5244 Editor::reset_region_scale_amplitude ()
5250 RegionSelection rs = get_regions_from_selection_and_entered ();
5256 bool in_command = false;
5258 for (RegionSelection::iterator r = rs.begin(); r != rs.end(); ++r) {
5259 AudioRegionView* const arv = dynamic_cast<AudioRegionView*>(*r);
5262 arv->region()->clear_changes ();
5263 arv->audio_region()->set_scale_amplitude (1.0f);
5266 begin_reversible_command ("reset gain");
5269 _session->add_command (new StatefulDiffCommand (arv->region()));
5273 commit_reversible_command ();
5278 Editor::adjust_region_gain (bool up)
5280 RegionSelection rs = get_regions_from_selection_and_entered ();
5282 if (!_session || rs.empty()) {
5286 bool in_command = false;
5288 for (RegionSelection::iterator r = rs.begin(); r != rs.end(); ++r) {
5289 AudioRegionView* const arv = dynamic_cast<AudioRegionView*>(*r);
5294 arv->region()->clear_changes ();
5296 double dB = accurate_coefficient_to_dB (arv->audio_region()->scale_amplitude ());
5304 arv->audio_region()->set_scale_amplitude (dB_to_coefficient (dB));
5307 begin_reversible_command ("adjust region gain");
5310 _session->add_command (new StatefulDiffCommand (arv->region()));
5314 commit_reversible_command ();
5319 Editor::reset_region_gain ()
5321 RegionSelection rs = get_regions_from_selection_and_entered ();
5323 if (!_session || rs.empty()) {
5327 bool in_command = false;
5329 for (RegionSelection::iterator r = rs.begin(); r != rs.end(); ++r) {
5330 AudioRegionView* const arv = dynamic_cast<AudioRegionView*>(*r);
5335 arv->region()->clear_changes ();
5337 arv->audio_region()->set_scale_amplitude (1.0f);
5340 begin_reversible_command ("reset region gain");
5343 _session->add_command (new StatefulDiffCommand (arv->region()));
5347 commit_reversible_command ();
5352 Editor::reverse_region ()
5358 Reverse rev (*_session);
5359 apply_filter (rev, _("reverse regions"));
5363 Editor::strip_region_silence ()
5369 RegionSelection rs = get_regions_from_selection_and_entered ();
5375 std::list<RegionView*> audio_only;
5377 for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
5378 AudioRegionView* const arv = dynamic_cast<AudioRegionView*> (*i);
5380 audio_only.push_back (arv);
5384 assert (!audio_only.empty());
5386 StripSilenceDialog d (_session, audio_only);
5387 int const r = d.run ();
5391 if (r == Gtk::RESPONSE_OK) {
5392 ARDOUR::AudioIntervalMap silences;
5393 d.silences (silences);
5394 StripSilence s (*_session, silences, d.fade_length());
5396 apply_filter (s, _("strip silence"), &d);
5401 Editor::apply_midi_note_edit_op_to_region (MidiOperator& op, MidiRegionView& mrv)
5403 Evoral::Sequence<Temporal::Beats>::Notes selected;
5404 mrv.selection_as_notelist (selected, true);
5406 vector<Evoral::Sequence<Temporal::Beats>::Notes> v;
5407 v.push_back (selected);
5409 Temporal::Beats pos_beats = Temporal::Beats (mrv.midi_region()->beat()) - mrv.midi_region()->start_beats();
5411 return op (mrv.midi_region()->model(), pos_beats, v);
5415 Editor::apply_midi_note_edit_op (MidiOperator& op, const RegionSelection& rs)
5421 bool in_command = false;
5423 for (RegionSelection::const_iterator r = rs.begin(); r != rs.end(); ) {
5424 RegionSelection::const_iterator tmp = r;
5427 MidiRegionView* const mrv = dynamic_cast<MidiRegionView*> (*r);
5430 Command* cmd = apply_midi_note_edit_op_to_region (op, *mrv);
5433 begin_reversible_command (op.name ());
5437 _session->add_command (cmd);
5445 commit_reversible_command ();
5446 _session->set_dirty ();
5451 Editor::fork_region ()
5453 RegionSelection rs = get_regions_from_selection_and_entered ();
5459 CursorContext::Handle cursor_ctx = CursorContext::create(*this, _cursors->wait);
5460 bool in_command = false;
5464 for (RegionSelection::iterator r = rs.begin(); r != rs.end(); ) {
5465 RegionSelection::iterator tmp = r;
5468 MidiRegionView* const mrv = dynamic_cast<MidiRegionView*>(*r);
5472 boost::shared_ptr<Playlist> playlist = mrv->region()->playlist();
5473 boost::shared_ptr<MidiSource> new_source = _session->create_midi_source_by_stealing_name (mrv->midi_view()->track());
5474 boost::shared_ptr<MidiRegion> newregion = mrv->midi_region()->clone (new_source);
5477 begin_reversible_command (_("Fork Region(s)"));
5480 playlist->clear_changes ();
5481 playlist->replace_region (mrv->region(), newregion, mrv->region()->position());
5482 _session->add_command(new StatefulDiffCommand (playlist));
5484 error << string_compose (_("Could not unlink %1"), mrv->region()->name()) << endmsg;
5492 commit_reversible_command ();
5497 Editor::quantize_region ()
5500 quantize_regions(get_regions_from_selection_and_entered ());
5505 Editor::quantize_regions (const RegionSelection& rs)
5507 if (rs.n_midi_regions() == 0) {
5511 if (!quantize_dialog) {
5512 quantize_dialog = new QuantizeDialog (*this);
5515 if (quantize_dialog->is_mapped()) {
5516 /* in progress already */
5520 quantize_dialog->present ();
5521 const int r = quantize_dialog->run ();
5522 quantize_dialog->hide ();
5524 if (r == Gtk::RESPONSE_OK) {
5525 Quantize quant (quantize_dialog->snap_start(),
5526 quantize_dialog->snap_end(),
5527 quantize_dialog->start_grid_size(),
5528 quantize_dialog->end_grid_size(),
5529 quantize_dialog->strength(),
5530 quantize_dialog->swing(),
5531 quantize_dialog->threshold());
5533 apply_midi_note_edit_op (quant, rs);
5538 Editor::legatize_region (bool shrink_only)
5541 legatize_regions(get_regions_from_selection_and_entered (), shrink_only);
5546 Editor::legatize_regions (const RegionSelection& rs, bool shrink_only)
5548 if (rs.n_midi_regions() == 0) {
5552 Legatize legatize(shrink_only);
5553 apply_midi_note_edit_op (legatize, rs);
5557 Editor::transform_region ()
5560 transform_regions(get_regions_from_selection_and_entered ());
5565 Editor::transform_regions (const RegionSelection& rs)
5567 if (rs.n_midi_regions() == 0) {
5574 const int r = td.run();
5577 if (r == Gtk::RESPONSE_OK) {
5578 Transform transform(td.get());
5579 apply_midi_note_edit_op(transform, rs);
5584 Editor::transpose_region ()
5587 transpose_regions(get_regions_from_selection_and_entered ());
5592 Editor::transpose_regions (const RegionSelection& rs)
5594 if (rs.n_midi_regions() == 0) {
5599 int const r = d.run ();
5601 if (r == RESPONSE_ACCEPT) {
5602 Transpose transpose(d.semitones ());
5603 apply_midi_note_edit_op (transpose, rs);
5608 Editor::insert_patch_change (bool from_context)
5610 RegionSelection rs = get_regions_from_selection_and_entered ();
5616 const samplepos_t p = get_preferred_edit_position (EDIT_IGNORE_NONE, from_context);
5618 /* XXX: bit of a hack; use the MIDNAM from the first selected region;
5619 there may be more than one, but the PatchChangeDialog can only offer
5620 one set of patch menus.
5622 MidiRegionView* first = dynamic_cast<MidiRegionView*> (rs.front ());
5624 Evoral::PatchChange<Temporal::Beats> empty (Temporal::Beats(), 0, 0, 0);
5625 PatchChangeDialog d (0, _session, empty, first->instrument_info(), Gtk::Stock::ADD);
5627 if (d.run() == RESPONSE_CANCEL) {
5631 for (RegionSelection::iterator i = rs.begin (); i != rs.end(); ++i) {
5632 MidiRegionView* const mrv = dynamic_cast<MidiRegionView*> (*i);
5634 if (p >= mrv->region()->first_sample() && p <= mrv->region()->last_sample()) {
5635 mrv->add_patch_change (p - mrv->region()->position(), d.patch ());
5642 Editor::apply_filter (Filter& filter, string command, ProgressReporter* progress)
5644 RegionSelection rs = get_regions_from_selection_and_entered ();
5650 CursorContext::Handle cursor_ctx = CursorContext::create(*this, _cursors->wait);
5651 bool in_command = false;
5656 int const N = rs.size ();
5658 for (RegionSelection::iterator r = rs.begin(); r != rs.end(); ) {
5659 RegionSelection::iterator tmp = r;
5662 AudioRegionView* const arv = dynamic_cast<AudioRegionView*>(*r);
5664 boost::shared_ptr<Playlist> playlist = arv->region()->playlist();
5667 progress->descend (1.0 / N);
5670 if (arv->audio_region()->apply (filter, progress) == 0) {
5672 playlist->clear_changes ();
5673 playlist->clear_owned_changes ();
5676 begin_reversible_command (command);
5680 if (filter.results.empty ()) {
5682 /* no regions returned; remove the old one */
5683 playlist->remove_region (arv->region ());
5687 std::vector<boost::shared_ptr<Region> >::iterator res = filter.results.begin ();
5689 /* first region replaces the old one */
5690 playlist->replace_region (arv->region(), *res, (*res)->position());
5694 while (res != filter.results.end()) {
5695 playlist->add_region (*res, (*res)->position());
5701 /* We might have removed regions, which alters other regions' layering_index,
5702 so we need to do a recursive diff here.
5704 vector<Command*> cmds;
5705 playlist->rdiff (cmds);
5706 _session->add_commands (cmds);
5708 _session->add_command(new StatefulDiffCommand (playlist));
5712 progress->ascend ();
5721 commit_reversible_command ();
5726 Editor::external_edit_region ()
5732 Editor::reset_region_gain_envelopes ()
5734 RegionSelection rs = get_regions_from_selection_and_entered ();
5736 if (!_session || rs.empty()) {
5740 bool in_command = false;
5742 for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
5743 AudioRegionView* const arv = dynamic_cast<AudioRegionView*>(*i);
5745 boost::shared_ptr<AutomationList> alist (arv->audio_region()->envelope());
5746 XMLNode& before (alist->get_state());
5748 arv->audio_region()->set_default_envelope ();
5751 begin_reversible_command (_("reset region gain"));
5754 _session->add_command (new MementoCommand<AutomationList>(*arv->audio_region()->envelope().get(), &before, &alist->get_state()));
5759 commit_reversible_command ();
5764 Editor::set_region_gain_visibility (RegionView* rv)
5766 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (rv);
5768 arv->update_envelope_visibility();
5773 Editor::set_gain_envelope_visibility ()
5779 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
5780 AudioTimeAxisView* v = dynamic_cast<AudioTimeAxisView*>(*i);
5782 v->audio_view()->foreach_regionview (sigc::mem_fun (this, &Editor::set_region_gain_visibility));
5788 Editor::toggle_gain_envelope_active ()
5790 if (_ignore_region_action) {
5794 RegionSelection rs = get_regions_from_selection_and_entered ();
5796 if (!_session || rs.empty()) {
5800 bool in_command = false;
5802 for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
5803 AudioRegionView* const arv = dynamic_cast<AudioRegionView*>(*i);
5805 arv->region()->clear_changes ();
5806 arv->audio_region()->set_envelope_active (!arv->audio_region()->envelope_active());
5809 begin_reversible_command (_("region gain envelope active"));
5812 _session->add_command (new StatefulDiffCommand (arv->region()));
5817 commit_reversible_command ();
5822 Editor::toggle_region_lock ()
5824 if (_ignore_region_action) {
5828 RegionSelection rs = get_regions_from_selection_and_entered ();
5830 if (!_session || rs.empty()) {
5834 begin_reversible_command (_("toggle region lock"));
5836 for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
5837 (*i)->region()->clear_changes ();
5838 (*i)->region()->set_locked (!(*i)->region()->locked());
5839 _session->add_command (new StatefulDiffCommand ((*i)->region()));
5842 commit_reversible_command ();
5846 Editor::toggle_region_video_lock ()
5848 if (_ignore_region_action) {
5852 RegionSelection rs = get_regions_from_selection_and_entered ();
5854 if (!_session || rs.empty()) {
5858 begin_reversible_command (_("Toggle Video Lock"));
5860 for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
5861 (*i)->region()->clear_changes ();
5862 (*i)->region()->set_video_locked (!(*i)->region()->video_locked());
5863 _session->add_command (new StatefulDiffCommand ((*i)->region()));
5866 commit_reversible_command ();
5870 Editor::toggle_region_lock_style ()
5872 if (_ignore_region_action) {
5876 RegionSelection rs = get_regions_from_selection_and_entered ();
5878 if (!_session || rs.empty()) {
5882 Glib::RefPtr<ToggleAction> a = Glib::RefPtr<ToggleAction>::cast_dynamic (_region_actions->get_action("toggle-region-lock-style"));
5883 vector<Widget*> proxies = a->get_proxies();
5884 Gtk::CheckMenuItem* cmi = dynamic_cast<Gtk::CheckMenuItem*> (proxies.front());
5888 begin_reversible_command (_("toggle region lock style"));
5890 for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
5891 (*i)->region()->clear_changes ();
5892 PositionLockStyle const ns = ((*i)->region()->position_lock_style() == AudioTime && !cmi->get_inconsistent()) ? MusicTime : AudioTime;
5893 (*i)->region()->set_position_lock_style (ns);
5894 _session->add_command (new StatefulDiffCommand ((*i)->region()));
5897 commit_reversible_command ();
5901 Editor::toggle_opaque_region ()
5903 if (_ignore_region_action) {
5907 RegionSelection rs = get_regions_from_selection_and_entered ();
5909 if (!_session || rs.empty()) {
5913 begin_reversible_command (_("change region opacity"));
5915 for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
5916 (*i)->region()->clear_changes ();
5917 (*i)->region()->set_opaque (!(*i)->region()->opaque());
5918 _session->add_command (new StatefulDiffCommand ((*i)->region()));
5921 commit_reversible_command ();
5925 Editor::toggle_record_enable ()
5927 bool new_state = false;
5929 for (TrackSelection::iterator i = selection->tracks.begin(); i != selection->tracks.end(); ++i) {
5930 RouteTimeAxisView *rtav = dynamic_cast<RouteTimeAxisView *>(*i);
5933 if (!rtav->is_track())
5937 new_state = !rtav->track()->rec_enable_control()->get_value();
5941 rtav->track()->rec_enable_control()->set_value (new_state, Controllable::UseGroup);
5946 tracklist_to_stripables (TrackViewList list)
5950 for (TrackSelection::iterator i = list.begin(); i != list.end(); ++i) {
5951 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> ((*i));
5953 if (rtv && rtv->is_track()) {
5954 ret.push_back (rtv->track());
5962 Editor::play_solo_selection (bool restart)
5964 //note: session::solo_selection takes care of invalidating the region playlist
5966 if ((!selection->tracks.empty()) && selection->time.length() > 0) { //a range is selected; solo the tracks and roll
5968 StripableList sl = tracklist_to_stripables (selection->tracks);
5969 _session->solo_selection (sl, true);
5972 samplepos_t start = selection->time.start();
5973 samplepos_t end = selection->time.end_sample();
5974 _session->request_bounded_roll (start, end);
5976 } else if (! selection->tracks.empty()) { //no range is selected, but tracks are selected; solo the tracks and roll
5977 StripableList sl = tracklist_to_stripables (selection->tracks);
5978 _session->solo_selection (sl, true);
5979 _session->request_cancel_play_range();
5980 transition_to_rolling (true);
5982 } else if (! selection->regions.empty()) { //solo any tracks with selected regions, and roll
5983 StripableList sl = tracklist_to_stripables (get_tracks_for_range_action());
5984 _session->solo_selection (sl, true);
5985 _session->request_cancel_play_range();
5986 transition_to_rolling (true);
5988 _session->request_cancel_play_range();
5989 transition_to_rolling (true); //no selection. just roll.
5994 Editor::toggle_solo ()
5996 bool new_state = false;
5998 boost::shared_ptr<ControlList> cl (new ControlList);
6000 for (TrackSelection::iterator i = selection->tracks.begin(); i != selection->tracks.end(); ++i) {
6001 StripableTimeAxisView *stav = dynamic_cast<StripableTimeAxisView *>(*i);
6003 if (!stav || !stav->stripable()->solo_control()) {
6008 new_state = !stav->stripable()->solo_control()->soloed ();
6012 cl->push_back (stav->stripable()->solo_control());
6015 _session->set_controls (cl, new_state ? 1.0 : 0.0, Controllable::UseGroup);
6019 Editor::toggle_mute ()
6021 bool new_state = false;
6023 boost::shared_ptr<ControlList> cl (new ControlList);
6025 for (TrackSelection::iterator i = selection->tracks.begin(); i != selection->tracks.end(); ++i) {
6026 StripableTimeAxisView *stav = dynamic_cast<StripableTimeAxisView *>(*i);
6028 if (!stav || !stav->stripable()->mute_control()) {
6033 new_state = !stav->stripable()->mute_control()->muted();
6037 cl->push_back (stav->stripable()->mute_control());
6040 _session->set_controls (cl, new_state, Controllable::UseGroup);
6044 Editor::toggle_solo_isolate ()
6050 Editor::fade_range ()
6052 TrackViewList ts = selection->tracks.filter_to_unique_playlists ();
6054 begin_reversible_command (_("fade range"));
6056 for (TrackViewList::iterator i = ts.begin(); i != ts.end(); ++i) {
6057 (*i)->fade_range (selection->time);
6060 commit_reversible_command ();
6065 Editor::set_fade_length (bool in)
6067 RegionSelection rs = get_regions_from_selection_and_entered ();
6073 /* we need a region to measure the offset from the start */
6075 RegionView* rv = rs.front ();
6077 samplepos_t pos = get_preferred_edit_position();
6081 if (pos > rv->region()->last_sample() || pos < rv->region()->first_sample()) {
6082 /* edit point is outside the relevant region */
6087 if (pos <= rv->region()->position()) {
6091 len = pos - rv->region()->position();
6092 cmd = _("set fade in length");
6094 if (pos >= rv->region()->last_sample()) {
6098 len = rv->region()->last_sample() - pos;
6099 cmd = _("set fade out length");
6102 bool in_command = false;
6104 for (RegionSelection::iterator x = rs.begin(); x != rs.end(); ++x) {
6105 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (*x);
6111 boost::shared_ptr<AutomationList> alist;
6113 alist = tmp->audio_region()->fade_in();
6115 alist = tmp->audio_region()->fade_out();
6118 XMLNode &before = alist->get_state();
6121 tmp->audio_region()->set_fade_in_length (len);
6122 tmp->audio_region()->set_fade_in_active (true);
6124 tmp->audio_region()->set_fade_out_length (len);
6125 tmp->audio_region()->set_fade_out_active (true);
6129 begin_reversible_command (cmd);
6132 XMLNode &after = alist->get_state();
6133 _session->add_command(new MementoCommand<AutomationList>(*alist, &before, &after));
6137 commit_reversible_command ();
6142 Editor::set_fade_in_shape (FadeShape shape)
6144 RegionSelection rs = get_regions_from_selection_and_entered ();
6149 bool in_command = false;
6151 for (RegionSelection::iterator x = rs.begin(); x != rs.end(); ++x) {
6152 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (*x);
6158 boost::shared_ptr<AutomationList> alist = tmp->audio_region()->fade_in();
6159 XMLNode &before = alist->get_state();
6161 tmp->audio_region()->set_fade_in_shape (shape);
6164 begin_reversible_command (_("set fade in shape"));
6167 XMLNode &after = alist->get_state();
6168 _session->add_command(new MementoCommand<AutomationList>(*alist.get(), &before, &after));
6172 commit_reversible_command ();
6177 Editor::set_fade_out_shape (FadeShape shape)
6179 RegionSelection rs = get_regions_from_selection_and_entered ();
6184 bool in_command = false;
6186 for (RegionSelection::iterator x = rs.begin(); x != rs.end(); ++x) {
6187 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (*x);
6193 boost::shared_ptr<AutomationList> alist = tmp->audio_region()->fade_out();
6194 XMLNode &before = alist->get_state();
6196 tmp->audio_region()->set_fade_out_shape (shape);
6199 begin_reversible_command (_("set fade out shape"));
6202 XMLNode &after = alist->get_state();
6203 _session->add_command(new MementoCommand<AutomationList>(*alist.get(), &before, &after));
6207 commit_reversible_command ();
6212 Editor::set_fade_in_active (bool yn)
6214 RegionSelection rs = get_regions_from_selection_and_entered ();
6219 bool in_command = false;
6221 for (RegionSelection::iterator x = rs.begin(); x != rs.end(); ++x) {
6222 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (*x);
6229 boost::shared_ptr<AudioRegion> ar (tmp->audio_region());
6231 ar->clear_changes ();
6232 ar->set_fade_in_active (yn);
6235 begin_reversible_command (_("set fade in active"));
6238 _session->add_command (new StatefulDiffCommand (ar));
6242 commit_reversible_command ();
6247 Editor::set_fade_out_active (bool yn)
6249 RegionSelection rs = get_regions_from_selection_and_entered ();
6254 bool in_command = false;
6256 for (RegionSelection::iterator x = rs.begin(); x != rs.end(); ++x) {
6257 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (*x);
6263 boost::shared_ptr<AudioRegion> ar (tmp->audio_region());
6265 ar->clear_changes ();
6266 ar->set_fade_out_active (yn);
6269 begin_reversible_command (_("set fade out active"));
6272 _session->add_command(new StatefulDiffCommand (ar));
6276 commit_reversible_command ();
6281 Editor::toggle_region_fades (int dir)
6283 if (_ignore_region_action) {
6287 boost::shared_ptr<AudioRegion> ar;
6290 RegionSelection rs = get_regions_from_selection_and_entered ();
6296 RegionSelection::iterator i;
6297 for (i = rs.begin(); i != rs.end(); ++i) {
6298 if ((ar = boost::dynamic_pointer_cast<AudioRegion>((*i)->region())) != 0) {
6300 yn = ar->fade_out_active ();
6302 yn = ar->fade_in_active ();
6308 if (i == rs.end()) {
6312 /* XXX should this undo-able? */
6313 bool in_command = false;
6315 for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
6316 if ((ar = boost::dynamic_pointer_cast<AudioRegion>((*i)->region())) == 0) {
6319 ar->clear_changes ();
6321 if (dir == 1 || dir == 0) {
6322 ar->set_fade_in_active (!yn);
6325 if (dir == -1 || dir == 0) {
6326 ar->set_fade_out_active (!yn);
6329 begin_reversible_command (_("toggle fade active"));
6332 _session->add_command(new StatefulDiffCommand (ar));
6336 commit_reversible_command ();
6341 /** Update region fade visibility after its configuration has been changed */
6343 Editor::update_region_fade_visibility ()
6345 bool _fade_visibility = _session->config.get_show_region_fades ();
6347 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
6348 AudioTimeAxisView* v = dynamic_cast<AudioTimeAxisView*>(*i);
6350 if (_fade_visibility) {
6351 v->audio_view()->show_all_fades ();
6353 v->audio_view()->hide_all_fades ();
6360 Editor::set_edit_point ()
6363 MusicSample where (0, 0);
6365 if (!mouse_sample (where.sample, ignored)) {
6371 if (selection->markers.empty()) {
6373 mouse_add_new_marker (where.sample);
6378 Location* loc = find_location_from_marker (selection->markers.front(), ignored);
6381 loc->move_to (where.sample, where.division);
6387 Editor::set_playhead_cursor ()
6389 if (entered_marker) {
6390 _session->request_locate (entered_marker->position(), _session->transport_rolling());
6392 MusicSample where (0, 0);
6395 if (!mouse_sample (where.sample, ignored)) {
6402 _session->request_locate (where.sample, _session->transport_rolling());
6406 //not sure what this was for; remove it for now.
6407 // if (UIConfiguration::instance().get_follow_edits() && (!_session || !_session->config.get_external_sync())) {
6408 // cancel_time_selection();
6414 Editor::split_region ()
6416 if (_dragging_playhead) {
6418 } else if (_drags->active ()) {
6419 /*any other kind of drag, bail out so we avoid Undo snafu*/
6423 //if a range is selected, separate it
6424 if (!selection->time.empty()) {
6425 separate_regions_between (selection->time);
6429 //if no range was selected, try to find some regions to split
6430 if (current_mouse_mode() == MouseObject || current_mouse_mode() == MouseRange ) { //don't try this for Internal Edit, Stretch, Draw, etc.
6434 //new behavior: the Split action will prioritize the entered_regionview rather than selected regions.
6435 //this fixes the unexpected case where you point at a region, but
6436 // * nothing happens OR
6437 // * some other region (maybe off-screen) is split.
6438 //NOTE: if the entered_regionview is /part of the selection/ then we should operate on the selection as usual
6439 if (_edit_point == EditAtMouse && entered_regionview && !entered_regionview->selected()) {
6440 rs.add (entered_regionview);
6442 rs = selection->regions; //might be empty
6446 TrackViewList tracks = selection->tracks;
6448 if (!tracks.empty()) {
6449 /* no region selected or entered, but some selected tracks:
6450 * act on all regions on the selected tracks at the edit point
6452 samplepos_t const where = get_preferred_edit_position (Editing::EDIT_IGNORE_NONE, false, false);
6453 get_regions_at(rs, where, tracks);
6457 const samplepos_t pos = get_preferred_edit_position();
6458 const int32_t division = get_grid_music_divisions (0);
6459 MusicSample where (pos, division);
6465 split_regions_at (where, rs);
6470 Editor::select_next_stripable (bool routes_only)
6472 _session->selection().select_next_stripable (false, routes_only);
6476 Editor::select_prev_stripable (bool routes_only)
6478 _session->selection().select_prev_stripable (false, routes_only);
6482 Editor::set_loop_from_selection (bool play)
6484 if (_session == 0) {
6488 samplepos_t start, end;
6489 if (!get_selection_extents (start, end))
6492 set_loop_range (start, end, _("set loop range from selection"));
6495 _session->request_play_loop (true, true);
6500 Editor::set_loop_from_region (bool play)
6502 samplepos_t start, end;
6503 if (!get_selection_extents (start, end))
6506 set_loop_range (start, end, _("set loop range from region"));
6509 _session->request_locate (start, true);
6510 _session->request_play_loop (true);
6515 Editor::set_punch_from_selection ()
6517 if (_session == 0) {
6521 samplepos_t start, end;
6522 if (!get_selection_extents (start, end))
6525 set_punch_range (start, end, _("set punch range from selection"));
6529 Editor::set_auto_punch_range ()
6531 // auto punch in/out button from a single button
6532 // If Punch In is unset, set punch range from playhead to end, enable punch in
6533 // If Punch In is set, the next punch sets Punch Out, unless the playhead has been
6534 // rewound beyond the Punch In marker, in which case that marker will be moved back
6535 // to the current playhead position.
6536 // If punch out is set, it clears the punch range and Punch In/Out buttons
6538 if (_session == 0) {
6542 Location* tpl = transport_punch_location();
6543 samplepos_t now = playhead_cursor->current_sample();
6544 samplepos_t begin = now;
6545 samplepos_t end = _session->current_end_sample();
6547 if (!_session->config.get_punch_in()) {
6548 // First Press - set punch in and create range from here to eternity
6549 set_punch_range (begin, end, _("Auto Punch In"));
6550 _session->config.set_punch_in(true);
6551 } else if (tpl && !_session->config.get_punch_out()) {
6552 // Second press - update end range marker and set punch_out
6553 if (now < tpl->start()) {
6554 // playhead has been rewound - move start back and pretend nothing happened
6556 set_punch_range (begin, end, _("Auto Punch In/Out"));
6558 // normal case for 2nd press - set the punch out
6559 end = playhead_cursor->current_sample ();
6560 set_punch_range (tpl->start(), now, _("Auto Punch In/Out"));
6561 _session->config.set_punch_out(true);
6564 if (_session->config.get_punch_out()) {
6565 _session->config.set_punch_out(false);
6568 if (_session->config.get_punch_in()) {
6569 _session->config.set_punch_in(false);
6574 // third press - unset punch in/out and remove range
6575 _session->locations()->remove(tpl);
6582 Editor::set_session_extents_from_selection ()
6584 if (_session == 0) {
6588 samplepos_t start, end;
6589 if (!get_selection_extents (start, end))
6593 if ((loc = _session->locations()->session_range_location()) == 0) {
6594 _session->set_session_extents (start, end); // this will create a new session range; no need for UNDO
6596 XMLNode &before = loc->get_state();
6598 _session->set_session_extents (start, end);
6600 XMLNode &after = loc->get_state();
6602 begin_reversible_command (_("set session start/end from selection"));
6604 _session->add_command (new MementoCommand<Location>(*loc, &before, &after));
6606 commit_reversible_command ();
6609 _session->set_session_range_is_free (false);
6613 Editor::set_punch_start_from_edit_point ()
6617 MusicSample start (0, 0);
6618 samplepos_t end = max_samplepos;
6620 //use the existing punch end, if any
6621 Location* tpl = transport_punch_location();
6626 if ((_edit_point == EditAtPlayhead) && _session->transport_rolling()) {
6627 start.sample = _session->audible_sample();
6629 start.sample = get_preferred_edit_position();
6632 //if there's not already a sensible selection endpoint, go "forever"
6633 if (start.sample > end) {
6634 end = max_samplepos;
6637 set_punch_range (start.sample, end, _("set punch start from EP"));
6643 Editor::set_punch_end_from_edit_point ()
6647 samplepos_t start = 0;
6648 MusicSample end (max_samplepos, 0);
6650 //use the existing punch start, if any
6651 Location* tpl = transport_punch_location();
6653 start = tpl->start();
6656 if ((_edit_point == EditAtPlayhead) && _session->transport_rolling()) {
6657 end.sample = _session->audible_sample();
6659 end.sample = get_preferred_edit_position();
6662 set_punch_range (start, end.sample, _("set punch end from EP"));
6668 Editor::set_loop_start_from_edit_point ()
6672 MusicSample start (0, 0);
6673 samplepos_t end = max_samplepos;
6675 //use the existing loop end, if any
6676 Location* tpl = transport_loop_location();
6681 if ((_edit_point == EditAtPlayhead) && _session->transport_rolling()) {
6682 start.sample = _session->audible_sample();
6684 start.sample = get_preferred_edit_position();
6687 //if there's not already a sensible selection endpoint, go "forever"
6688 if (start.sample > end) {
6689 end = max_samplepos;
6692 set_loop_range (start.sample, end, _("set loop start from EP"));
6698 Editor::set_loop_end_from_edit_point ()
6702 samplepos_t start = 0;
6703 MusicSample end (max_samplepos, 0);
6705 //use the existing loop start, if any
6706 Location* tpl = transport_loop_location();
6708 start = tpl->start();
6711 if ((_edit_point == EditAtPlayhead) && _session->transport_rolling()) {
6712 end.sample = _session->audible_sample();
6714 end.sample = get_preferred_edit_position();
6717 set_loop_range (start, end.sample, _("set loop end from EP"));
6722 Editor::set_punch_from_region ()
6724 samplepos_t start, end;
6725 if (!get_selection_extents (start, end))
6728 set_punch_range (start, end, _("set punch range from region"));
6732 Editor::pitch_shift_region ()
6734 RegionSelection rs = get_regions_from_selection_and_entered ();
6736 RegionSelection audio_rs;
6737 for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
6738 if (dynamic_cast<AudioRegionView*> (*i)) {
6739 audio_rs.push_back (*i);
6743 if (audio_rs.empty()) {
6747 pitch_shift (audio_rs, 1.2);
6751 Editor::set_tempo_from_region ()
6753 RegionSelection rs = get_regions_from_selection_and_entered ();
6755 if (!_session || rs.empty()) {
6759 RegionView* rv = rs.front();
6761 define_one_bar (rv->region()->position(), rv->region()->last_sample() + 1);
6765 Editor::use_range_as_bar ()
6767 samplepos_t start, end;
6768 if (get_edit_op_range (start, end)) {
6769 define_one_bar (start, end);
6774 Editor::define_one_bar (samplepos_t start, samplepos_t end)
6776 samplepos_t length = end - start;
6778 const Meter& m (_session->tempo_map().meter_at_sample (start));
6780 /* length = 1 bar */
6782 /* We're going to deliver a constant tempo here,
6783 so we can use samples per beat to determine length.
6784 now we want samples per beat.
6785 we have samples per bar, and beats per bar, so ...
6788 /* XXXX METER MATH */
6790 double samples_per_beat = length / m.divisions_per_bar();
6792 /* beats per minute = */
6794 double beats_per_minute = (_session->sample_rate() * 60.0) / samples_per_beat;
6796 /* now decide whether to:
6798 (a) set global tempo
6799 (b) add a new tempo marker
6803 const TempoSection& t (_session->tempo_map().tempo_section_at_sample (start));
6805 bool do_global = false;
6807 if ((_session->tempo_map().n_tempos() == 1) && (_session->tempo_map().n_meters() == 1)) {
6809 /* only 1 tempo & 1 meter: ask if the user wants to set the tempo
6810 at the start, or create a new marker
6813 vector<string> options;
6814 options.push_back (_("Cancel"));
6815 options.push_back (_("Add new marker"));
6816 options.push_back (_("Set global tempo"));
6819 _("Define one bar"),
6820 _("Do you want to set the global tempo or add a new tempo marker?"),
6824 c.set_default_response (2);
6840 /* more than 1 tempo and/or meter section already, go ahead do the "usual":
6841 if the marker is at the region starter, change it, otherwise add
6846 begin_reversible_command (_("set tempo from region"));
6847 XMLNode& before (_session->tempo_map().get_state());
6850 _session->tempo_map().change_initial_tempo (beats_per_minute, t.note_type(), t.end_note_types_per_minute());
6851 } else if (t.sample() == start) {
6852 _session->tempo_map().change_existing_tempo_at (start, beats_per_minute, t.note_type(), t.end_note_types_per_minute());
6854 /* constant tempo */
6855 const Tempo tempo (beats_per_minute, t.note_type());
6856 _session->tempo_map().add_tempo (tempo, 0.0, start, AudioTime);
6859 XMLNode& after (_session->tempo_map().get_state());
6861 _session->add_command (new MementoCommand<TempoMap>(_session->tempo_map(), &before, &after));
6862 commit_reversible_command ();
6866 Editor::split_region_at_transients ()
6868 AnalysisFeatureList positions;
6870 RegionSelection rs = get_regions_from_selection_and_entered ();
6872 if (!_session || rs.empty()) {
6876 begin_reversible_command (_("split regions"));
6878 for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ) {
6880 RegionSelection::iterator tmp;
6885 boost::shared_ptr<AudioRegion> ar = boost::dynamic_pointer_cast<AudioRegion> ((*i)->region());
6888 ar->transients (positions);
6889 split_region_at_points ((*i)->region(), positions, true);
6896 commit_reversible_command ();
6901 Editor::split_region_at_points (boost::shared_ptr<Region> r, AnalysisFeatureList& positions, bool can_ferret, bool select_new)
6903 bool use_rhythmic_rodent = false;
6905 boost::shared_ptr<Playlist> pl = r->playlist();
6907 list<boost::shared_ptr<Region> > new_regions;
6913 if (positions.empty()) {
6917 if (positions.size() > 20 && can_ferret) {
6918 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);
6919 MessageDialog msg (msgstr,
6922 Gtk::BUTTONS_OK_CANCEL);
6925 msg.add_button (_("Call for the Ferret!"), RESPONSE_APPLY);
6926 msg.set_secondary_text (_("Press OK to continue with this split operation\nor ask the Ferret dialog to tune the analysis"));
6928 msg.set_secondary_text (_("Press OK to continue with this split operation"));
6931 msg.set_title (_("Excessive split?"));
6934 int response = msg.run();
6940 case RESPONSE_APPLY:
6941 use_rhythmic_rodent = true;
6948 if (use_rhythmic_rodent) {
6949 show_rhythm_ferret ();
6953 AnalysisFeatureList::const_iterator x;
6955 pl->clear_changes ();
6956 pl->clear_owned_changes ();
6958 x = positions.begin();
6960 if (x == positions.end()) {
6965 pl->remove_region (r);
6967 samplepos_t pos = 0;
6969 samplepos_t rstart = r->first_sample ();
6970 samplepos_t rend = r->last_sample ();
6972 while (x != positions.end()) {
6974 /* deal with positons that are out of scope of present region bounds */
6975 if (*x <= rstart || *x > rend) {
6980 /* file start = original start + how far we from the initial position ? */
6982 samplepos_t file_start = r->start() + pos;
6984 /* length = next position - current position */
6986 samplepos_t len = (*x) - pos - rstart;
6988 /* XXX we do we really want to allow even single-sample regions?
6989 * shouldn't we have some kind of lower limit on region size?
6998 if (RegionFactory::region_name (new_name, r->name())) {
7002 /* do NOT announce new regions 1 by one, just wait till they are all done */
7006 plist.add (ARDOUR::Properties::start, file_start);
7007 plist.add (ARDOUR::Properties::length, len);
7008 plist.add (ARDOUR::Properties::name, new_name);
7009 plist.add (ARDOUR::Properties::layer, 0);
7010 // TODO set transients_offset
7012 boost::shared_ptr<Region> nr = RegionFactory::create (r->sources(), plist, false);
7013 /* because we set annouce to false, manually add the new region to the
7016 RegionFactory::map_add (nr);
7018 pl->add_region (nr, rstart + pos);
7021 new_regions.push_front(nr);
7030 RegionFactory::region_name (new_name, r->name());
7032 /* Add the final region */
7035 plist.add (ARDOUR::Properties::start, r->start() + pos);
7036 plist.add (ARDOUR::Properties::length, r->last_sample() - (r->position() + pos) + 1);
7037 plist.add (ARDOUR::Properties::name, new_name);
7038 plist.add (ARDOUR::Properties::layer, 0);
7040 boost::shared_ptr<Region> nr = RegionFactory::create (r->sources(), plist, false);
7041 /* because we set annouce to false, manually add the new region to the
7044 RegionFactory::map_add (nr);
7045 pl->add_region (nr, r->position() + pos);
7048 new_regions.push_front(nr);
7053 /* We might have removed regions, which alters other regions' layering_index,
7054 so we need to do a recursive diff here.
7056 vector<Command*> cmds;
7058 _session->add_commands (cmds);
7060 _session->add_command (new StatefulDiffCommand (pl));
7064 for (list<boost::shared_ptr<Region> >::iterator i = new_regions.begin(); i != new_regions.end(); ++i){
7065 set_selected_regionview_from_region_list ((*i), Selection::Add);
7071 Editor::place_transient()
7077 RegionSelection rs = get_regions_from_selection_and_edit_point ();
7083 samplepos_t where = get_preferred_edit_position();
7085 begin_reversible_command (_("place transient"));
7087 for (RegionSelection::iterator r = rs.begin(); r != rs.end(); ++r) {
7088 (*r)->region()->add_transient(where);
7091 commit_reversible_command ();
7095 Editor::remove_transient(ArdourCanvas::Item* item)
7101 ArdourCanvas::Line* _line = reinterpret_cast<ArdourCanvas::Line*> (item);
7104 AudioRegionView* _arv = reinterpret_cast<AudioRegionView*> (item->get_data ("regionview"));
7105 _arv->remove_transient (*(float*) _line->get_data ("position"));
7109 Editor::snap_regions_to_grid ()
7111 list <boost::shared_ptr<Playlist > > used_playlists;
7113 RegionSelection rs = get_regions_from_selection_and_entered ();
7115 if (!_session || rs.empty()) {
7119 begin_reversible_command (_("snap regions to grid"));
7121 for (RegionSelection::iterator r = rs.begin(); r != rs.end(); ++r) {
7123 boost::shared_ptr<Playlist> pl = (*r)->region()->playlist();
7125 if (!pl->frozen()) {
7126 /* we haven't seen this playlist before */
7128 /* remember used playlists so we can thaw them later */
7129 used_playlists.push_back(pl);
7132 (*r)->region()->clear_changes ();
7134 MusicSample start ((*r)->region()->first_sample (), 0);
7135 snap_to (start, RoundNearest, SnapToGrid_Unscaled, true);
7136 (*r)->region()->set_position (start.sample, start.division);
7137 _session->add_command(new StatefulDiffCommand ((*r)->region()));
7140 while (used_playlists.size() > 0) {
7141 list <boost::shared_ptr<Playlist > >::iterator i = used_playlists.begin();
7143 used_playlists.pop_front();
7146 commit_reversible_command ();
7150 Editor::close_region_gaps ()
7152 list <boost::shared_ptr<Playlist > > used_playlists;
7154 RegionSelection rs = get_regions_from_selection_and_entered ();
7156 if (!_session || rs.empty()) {
7160 Dialog dialog (_("Close Region Gaps"));
7163 table.set_spacings (12);
7164 table.set_border_width (12);
7165 Label* l = manage (left_aligned_label (_("Crossfade length")));
7166 table.attach (*l, 0, 1, 0, 1);
7168 SpinButton spin_crossfade (1, 0);
7169 spin_crossfade.set_range (0, 15);
7170 spin_crossfade.set_increments (1, 1);
7171 spin_crossfade.set_value (5);
7172 table.attach (spin_crossfade, 1, 2, 0, 1);
7174 table.attach (*manage (new Label (_("ms"))), 2, 3, 0, 1);
7176 l = manage (left_aligned_label (_("Pull-back length")));
7177 table.attach (*l, 0, 1, 1, 2);
7179 SpinButton spin_pullback (1, 0);
7180 spin_pullback.set_range (0, 100);
7181 spin_pullback.set_increments (1, 1);
7182 spin_pullback.set_value(30);
7183 table.attach (spin_pullback, 1, 2, 1, 2);
7185 table.attach (*manage (new Label (_("ms"))), 2, 3, 1, 2);
7187 dialog.get_vbox()->pack_start (table);
7188 dialog.add_button (Stock::CANCEL, RESPONSE_CANCEL);
7189 dialog.add_button (_("Ok"), RESPONSE_ACCEPT);
7192 if (dialog.run () == RESPONSE_CANCEL) {
7196 samplepos_t crossfade_len = spin_crossfade.get_value();
7197 samplepos_t pull_back_samples = spin_pullback.get_value();
7199 crossfade_len = lrintf (crossfade_len * _session->sample_rate()/1000);
7200 pull_back_samples = lrintf (pull_back_samples * _session->sample_rate()/1000);
7202 /* Iterate over the region list and make adjacent regions overlap by crossfade_len_ms */
7204 begin_reversible_command (_("close region gaps"));
7207 boost::shared_ptr<Region> last_region;
7209 rs.sort_by_position_and_track();
7211 for (RegionSelection::iterator r = rs.begin(); r != rs.end(); ++r) {
7213 boost::shared_ptr<Playlist> pl = (*r)->region()->playlist();
7215 if (!pl->frozen()) {
7216 /* we haven't seen this playlist before */
7218 /* remember used playlists so we can thaw them later */
7219 used_playlists.push_back(pl);
7223 samplepos_t position = (*r)->region()->position();
7225 if (idx == 0 || position < last_region->position()){
7226 last_region = (*r)->region();
7231 (*r)->region()->clear_changes ();
7232 (*r)->region()->trim_front((position - pull_back_samples));
7234 last_region->clear_changes ();
7235 last_region->trim_end ((position - pull_back_samples + crossfade_len));
7237 _session->add_command (new StatefulDiffCommand ((*r)->region()));
7238 _session->add_command (new StatefulDiffCommand (last_region));
7240 last_region = (*r)->region();
7244 while (used_playlists.size() > 0) {
7245 list <boost::shared_ptr<Playlist > >::iterator i = used_playlists.begin();
7247 used_playlists.pop_front();
7250 commit_reversible_command ();
7254 Editor::tab_to_transient (bool forward)
7256 AnalysisFeatureList positions;
7258 RegionSelection rs = get_regions_from_selection_and_entered ();
7264 samplepos_t pos = _session->audible_sample ();
7266 if (!selection->tracks.empty()) {
7268 /* don't waste time searching for transients in duplicate playlists.
7271 TrackViewList ts = selection->tracks.filter_to_unique_playlists ();
7273 for (TrackViewList::iterator t = ts.begin(); t != ts.end(); ++t) {
7275 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (*t);
7278 boost::shared_ptr<Track> tr = rtv->track();
7280 boost::shared_ptr<Playlist> pl = tr->playlist ();
7282 samplepos_t result = pl->find_next_transient (pos, forward ? 1 : -1);
7285 positions.push_back (result);
7298 for (RegionSelection::iterator r = rs.begin(); r != rs.end(); ++r) {
7299 (*r)->region()->get_transients (positions);
7303 TransientDetector::cleanup_transients (positions, _session->sample_rate(), 3.0);
7306 AnalysisFeatureList::iterator x;
7308 for (x = positions.begin(); x != positions.end(); ++x) {
7314 if (x != positions.end ()) {
7315 _session->request_locate (*x);
7319 AnalysisFeatureList::reverse_iterator x;
7321 for (x = positions.rbegin(); x != positions.rend(); ++x) {
7327 if (x != positions.rend ()) {
7328 _session->request_locate (*x);
7334 Editor::playhead_forward_to_grid ()
7340 MusicSample pos (playhead_cursor->current_sample (), 0);
7342 if ( _grid_type == GridTypeNone) {
7343 if (pos.sample < max_samplepos - current_page_samples()*0.1) {
7344 pos.sample += current_page_samples()*0.1;
7345 _session->request_locate (pos.sample);
7347 _session->request_locate (0);
7351 if (pos.sample < max_samplepos - 1) {
7353 pos = snap_to_grid (pos, RoundUpAlways, SnapToGrid_Scaled);
7354 _session->request_locate (pos.sample);
7359 /* keep PH visible in window */
7360 if (pos.sample > (_leftmost_sample + current_page_samples() *0.9)) {
7361 reset_x_origin (pos.sample - (current_page_samples()*0.9));
7367 Editor::playhead_backward_to_grid ()
7373 MusicSample pos (playhead_cursor->current_sample (), 0);
7375 if ( _grid_type == GridTypeNone) {
7376 if ( pos.sample > current_page_samples()*0.1 ) {
7377 pos.sample -= current_page_samples()*0.1;
7378 _session->request_locate (pos.sample);
7380 _session->request_locate (0);
7384 if (pos.sample > 2) {
7386 pos = snap_to_grid (pos, RoundDownAlways, SnapToGrid_Scaled);
7389 //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...
7390 //also see: jump_backward_to_mark
7391 if (_session->transport_rolling()) {
7392 if ((playhead_cursor->current_sample() - pos.sample) < _session->sample_rate()/2) {
7393 pos = snap_to_grid (pos, RoundDownAlways, SnapToGrid_Scaled);
7397 _session->request_locate (pos.sample, _session->transport_rolling());
7400 /* keep PH visible in window */
7401 if (pos.sample < (_leftmost_sample + current_page_samples() *0.1)) {
7402 reset_x_origin (pos.sample - (current_page_samples()*0.1));
7407 Editor::set_track_height (Height h)
7409 TrackSelection& ts (selection->tracks);
7411 for (TrackSelection::iterator x = ts.begin(); x != ts.end(); ++x) {
7412 (*x)->set_height_enum (h);
7417 Editor::toggle_tracks_active ()
7419 TrackSelection& ts (selection->tracks);
7421 bool target = false;
7427 for (TrackSelection::iterator x = ts.begin(); x != ts.end(); ++x) {
7428 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*>(*x);
7432 target = !rtv->_route->active();
7435 rtv->_route->set_active (target, this);
7441 Editor::remove_tracks ()
7443 /* this will delete GUI objects that may be the subject of an event
7444 handler in which this method is called. Defer actual deletion to the
7445 next idle callback, when all event handling is finished.
7447 Glib::signal_idle().connect (sigc::mem_fun (*this, &Editor::idle_remove_tracks));
7451 Editor::idle_remove_tracks ()
7453 Session::StateProtector sp (_session);
7455 return false; /* do not call again */
7459 Editor::_remove_tracks ()
7461 TrackSelection& ts (selection->tracks);
7467 if (!ARDOUR_UI_UTILS::engine_is_running ()) {
7471 vector<string> choices;
7476 const char* trackstr;
7479 vector<boost::shared_ptr<Route> > routes;
7480 vector<boost::shared_ptr<VCA> > vcas;
7481 bool special_bus = false;
7483 for (TrackSelection::iterator x = ts.begin(); x != ts.end(); ++x) {
7484 VCATimeAxisView* vtv = dynamic_cast<VCATimeAxisView*> (*x);
7486 vcas.push_back (vtv->vca());
7490 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (*x);
7494 if (rtv->is_track()) {
7499 routes.push_back (rtv->_route);
7501 if (rtv->route()->is_master() || rtv->route()->is_monitor()) {
7506 if (special_bus && !Config->get_allow_special_bus_removal()) {
7507 MessageDialog msg (_("That would be bad news ...."),
7511 msg.set_secondary_text (string_compose (_(
7512 "Removing the master or monitor bus is such a bad idea\n\
7513 that %1 is not going to allow it.\n\
7515 If you really want to do this sort of thing\n\
7516 edit your ardour.rc file to set the\n\
7517 \"allow-special-bus-removal\" option to be \"yes\""), PROGRAM_NAME));
7524 if (ntracks + nbusses + nvcas == 0) {
7530 trackstr = P_("track", "tracks", ntracks);
7531 busstr = P_("bus", "busses", nbusses);
7532 vcastr = P_("VCA", "VCAs", nvcas);
7534 if (ntracks > 0 && nbusses > 0 && nvcas > 0) {
7535 title = _("Remove various strips");
7536 prompt = string_compose (_("Do you really want to remove %1 %2, %3 %4 and %5 %6?"),
7537 ntracks, trackstr, nbusses, busstr, nvcas, vcastr);
7539 else if (ntracks > 0 && nbusses > 0) {
7540 title = string_compose (_("Remove %1 and %2"), trackstr, busstr);
7541 prompt = string_compose (_("Do you really want to remove %1 %2 and %3 %4?"),
7542 ntracks, trackstr, nbusses, busstr);
7544 else if (ntracks > 0 && nvcas > 0) {
7545 title = string_compose (_("Remove %1 and %2"), trackstr, vcastr);
7546 prompt = string_compose (_("Do you really want to remove %1 %2 and %3 %4?"),
7547 ntracks, trackstr, nvcas, vcastr);
7549 else if (nbusses > 0 && nvcas > 0) {
7550 title = string_compose (_("Remove %1 and %2"), busstr, vcastr);
7551 prompt = string_compose (_("Do you really want to remove %1 %2 and %3 %4?"),
7552 nbusses, busstr, nvcas, vcastr);
7554 else if (ntracks > 0) {
7555 title = string_compose (_("Remove %1"), trackstr);
7556 prompt = string_compose (_("Do you really want to remove %1 %2?"),
7559 else if (nbusses > 0) {
7560 title = string_compose (_("Remove %1"), busstr);
7561 prompt = string_compose (_("Do you really want to remove %1 %2?"),
7564 else if (nvcas > 0) {
7565 title = string_compose (_("Remove %1"), vcastr);
7566 prompt = string_compose (_("Do you really want to remove %1 %2?"),
7574 prompt += "\n" + string_compose ("(You may also lose the playlists associated with the %1)", trackstr) + "\n";
7577 prompt += "\n" + string(_("This action cannot be undone, and the session file will be overwritten!"));
7579 choices.push_back (_("No, do nothing."));
7580 if (ntracks + nbusses + nvcas > 1) {
7581 choices.push_back (_("Yes, remove them."));
7583 choices.push_back (_("Yes, remove it."));
7586 Choice prompter (title, prompt, choices);
7588 if (prompter.run () != 1) {
7592 if (current_mixer_strip && routes.size () > 1 && std::find (routes.begin(), routes.end(), current_mixer_strip->route()) != routes.end ()) {
7593 /* Route deletion calls Editor::timeaxisview_deleted() iteratively (for each deleted
7594 * route). If the deleted route is currently displayed in the Editor-Mixer (highly
7595 * likely because deletion requires selection) this will call
7596 * Editor::set_selected_mixer_strip () which is expensive (MixerStrip::set_route()).
7597 * It's likewise likely that the route that has just been displayed in the
7598 * Editor-Mixer will be next in line for deletion.
7600 * So simply switch to the master-bus (if present)
7602 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
7603 if ((*i)->stripable ()->is_master ()) {
7604 set_selected_mixer_strip (*(*i));
7611 PresentationInfo::ChangeSuspender cs;
7612 DisplaySuspender ds;
7614 boost::shared_ptr<RouteList> rl (new RouteList);
7615 for (vector<boost::shared_ptr<Route> >::iterator x = routes.begin(); x != routes.end(); ++x) {
7618 _session->remove_routes (rl);
7620 for (vector<boost::shared_ptr<VCA> >::iterator x = vcas.begin(); x != vcas.end(); ++x) {
7621 _session->vca_manager().remove_vca (*x);
7625 /* TrackSelection and RouteList leave scope,
7626 * destructors are called,
7627 * diskstream drops references, save_state is called (again for every track)
7632 Editor::do_insert_time ()
7634 if (selection->tracks.empty()) {
7635 MessageDialog msg (_("You must first select some tracks to Insert Time."),
7636 true, MESSAGE_INFO, BUTTONS_OK, true);
7637 msg.set_position (WIN_POS_MOUSE);
7642 if (Config->get_edit_mode() == Lock) {
7643 MessageDialog msg (_("You cannot insert time in Lock Edit mode."),
7644 true, MESSAGE_INFO, BUTTONS_OK, true);
7645 msg.set_position (WIN_POS_MOUSE);
7650 InsertRemoveTimeDialog d (*this);
7651 int response = d.run ();
7653 if (response != RESPONSE_OK) {
7657 if (d.distance() == 0) {
7664 d.intersected_region_action (),
7668 d.move_glued_markers(),
7669 d.move_locked_markers(),
7675 Editor::insert_time (
7676 samplepos_t pos, samplecnt_t samples, InsertTimeOption opt,
7677 bool all_playlists, bool ignore_music_glue, bool markers_too, bool glued_markers_too, bool locked_markers_too, bool tempo_too
7681 if (Config->get_edit_mode() == Lock) {
7684 bool in_command = false;
7686 TrackViewList ts = selection->tracks.filter_to_unique_playlists ();
7688 for (TrackViewList::iterator x = ts.begin(); x != ts.end(); ++x) {
7692 /* don't operate on any playlist more than once, which could
7693 * happen if "all playlists" is enabled, but there is more
7694 * than 1 track using playlists "from" a given track.
7697 set<boost::shared_ptr<Playlist> > pl;
7699 if (all_playlists) {
7700 RouteTimeAxisView* rtav = dynamic_cast<RouteTimeAxisView*> (*x);
7701 if (rtav && rtav->track ()) {
7702 vector<boost::shared_ptr<Playlist> > all = _session->playlists()->playlists_for_track (rtav->track ());
7703 for (vector<boost::shared_ptr<Playlist> >::iterator p = all.begin(); p != all.end(); ++p) {
7708 if ((*x)->playlist ()) {
7709 pl.insert ((*x)->playlist ());
7713 for (set<boost::shared_ptr<Playlist> >::iterator i = pl.begin(); i != pl.end(); ++i) {
7715 (*i)->clear_changes ();
7716 (*i)->clear_owned_changes ();
7719 begin_reversible_command (_("insert time"));
7723 if (opt == SplitIntersected) {
7724 /* non musical split */
7725 (*i)->split (MusicSample (pos, 0));
7728 (*i)->shift (pos, samples, (opt == MoveIntersected), ignore_music_glue);
7730 vector<Command*> cmds;
7732 _session->add_commands (cmds);
7734 _session->add_command (new StatefulDiffCommand (*i));
7738 RouteTimeAxisView* rtav = dynamic_cast<RouteTimeAxisView*> (*x);
7741 begin_reversible_command (_("insert time"));
7744 rtav->route ()->shift (pos, samples);
7751 const int32_t divisions = get_grid_music_divisions (0);
7752 XMLNode& before (_session->locations()->get_state());
7753 Locations::LocationList copy (_session->locations()->list());
7755 for (Locations::LocationList::iterator i = copy.begin(); i != copy.end(); ++i) {
7757 Locations::LocationList::const_iterator tmp;
7759 if ((*i)->position_lock_style() == AudioTime || glued_markers_too) {
7760 bool const was_locked = (*i)->locked ();
7761 if (locked_markers_too) {
7765 if ((*i)->start() >= pos) {
7766 // move end first, in case we're moving by more than the length of the range
7767 if (!(*i)->is_mark()) {
7768 (*i)->set_end ((*i)->end() + samples, false, true, divisions);
7770 (*i)->set_start ((*i)->start() + samples, false, true, divisions);
7782 begin_reversible_command (_("insert time"));
7785 XMLNode& after (_session->locations()->get_state());
7786 _session->add_command (new MementoCommand<Locations>(*_session->locations(), &before, &after));
7792 begin_reversible_command (_("insert time"));
7795 XMLNode& before (_session->tempo_map().get_state());
7796 _session->tempo_map().insert_time (pos, samples);
7797 XMLNode& after (_session->tempo_map().get_state());
7798 _session->add_command (new MementoCommand<TempoMap>(_session->tempo_map(), &before, &after));
7802 commit_reversible_command ();
7807 Editor::do_remove_time ()
7809 if (selection->tracks.empty()) {
7810 MessageDialog msg (_("You must first select some tracks to Remove Time."),
7811 true, MESSAGE_INFO, BUTTONS_OK, true);
7812 msg.set_position (WIN_POS_MOUSE);
7817 if (Config->get_edit_mode() == Lock) {
7818 MessageDialog msg (_("You cannot remove time in Lock Edit mode."),
7819 true, MESSAGE_INFO, BUTTONS_OK, true);
7820 msg.set_position (WIN_POS_MOUSE);
7825 InsertRemoveTimeDialog d (*this, true);
7827 int response = d.run ();
7829 if (response != RESPONSE_OK) {
7833 samplecnt_t distance = d.distance();
7835 if (distance == 0) {
7845 d.move_glued_markers(),
7846 d.move_locked_markers(),
7852 Editor::remove_time (samplepos_t pos, samplecnt_t samples, InsertTimeOption opt,
7853 bool ignore_music_glue, bool markers_too, bool glued_markers_too, bool locked_markers_too, bool tempo_too)
7855 if (Config->get_edit_mode() == Lock) {
7856 error << (_("Cannot insert or delete time when in Lock edit.")) << endmsg;
7859 bool in_command = false;
7861 for (TrackSelection::iterator x = selection->tracks.begin(); x != selection->tracks.end(); ++x) {
7863 boost::shared_ptr<Playlist> pl = (*x)->playlist();
7867 XMLNode &before = pl->get_state();
7870 begin_reversible_command (_("remove time"));
7874 std::list<AudioRange> rl;
7875 AudioRange ar(pos, pos+samples, 0);
7878 pl->shift (pos, -samples, true, ignore_music_glue);
7880 XMLNode &after = pl->get_state();
7882 _session->add_command (new MementoCommand<Playlist> (*pl, &before, &after));
7886 RouteTimeAxisView* rtav = dynamic_cast<RouteTimeAxisView*> (*x);
7889 begin_reversible_command (_("remove time"));
7892 rtav->route ()->shift (pos, -samples);
7896 const int32_t divisions = get_grid_music_divisions (0);
7897 std::list<Location*> loc_kill_list;
7902 XMLNode& before (_session->locations()->get_state());
7903 Locations::LocationList copy (_session->locations()->list());
7905 for (Locations::LocationList::iterator i = copy.begin(); i != copy.end(); ++i) {
7906 if ((*i)->position_lock_style() == AudioTime || glued_markers_too) {
7908 bool const was_locked = (*i)->locked ();
7909 if (locked_markers_too) {
7913 if (!(*i)->is_mark()) { // it's a range; have to handle both start and end
7914 if ((*i)->end() >= pos
7915 && (*i)->end() < pos+samples
7916 && (*i)->start() >= pos
7917 && (*i)->end() < pos+samples) { // range is completely enclosed; kill it
7919 loc_kill_list.push_back(*i);
7920 } else { // only start or end is included, try to do the right thing
7921 // move start before moving end, to avoid trying to move the end to before the start
7922 // if we're removing more time than the length of the range
7923 if ((*i)->start() >= pos && (*i)->start() < pos+samples) {
7924 // start is within cut
7925 (*i)->set_start (pos, false, true,divisions); // bring the start marker to the beginning of the cut
7927 } else if ((*i)->start() >= pos+samples) {
7928 // start (and thus entire range) lies beyond end of cut
7929 (*i)->set_start ((*i)->start() - samples, false, true, divisions); // slip the start marker back
7932 if ((*i)->end() >= pos && (*i)->end() < pos+samples) {
7933 // end is inside cut
7934 (*i)->set_end (pos, false, true, divisions); // bring the end to the cut
7936 } else if ((*i)->end() >= pos+samples) {
7937 // end is beyond end of cut
7938 (*i)->set_end ((*i)->end() - samples, false, true, divisions); // slip the end marker back
7943 } else if ((*i)->start() >= pos && (*i)->start() < pos+samples) {
7944 loc_kill_list.push_back(*i);
7946 } else if ((*i)->start() >= pos) {
7947 (*i)->set_start ((*i)->start() -samples, false, true, divisions);
7957 for (list<Location*>::iterator i = loc_kill_list.begin(); i != loc_kill_list.end(); ++i) {
7958 _session->locations()->remove (*i);
7963 begin_reversible_command (_("remove time"));
7966 XMLNode& after (_session->locations()->get_state());
7967 _session->add_command (new MementoCommand<Locations>(*_session->locations(), &before, &after));
7972 XMLNode& before (_session->tempo_map().get_state());
7974 if (_session->tempo_map().remove_time (pos, samples)) {
7976 begin_reversible_command (_("remove time"));
7979 XMLNode& after (_session->tempo_map().get_state());
7980 _session->add_command (new MementoCommand<TempoMap>(_session->tempo_map(), &before, &after));
7985 commit_reversible_command ();
7990 Editor::fit_selection ()
7992 if (!selection->tracks.empty()) {
7993 fit_tracks (selection->tracks);
7997 /* no selected tracks - use tracks with selected regions */
7999 if (!selection->regions.empty()) {
8000 for (RegionSelection::iterator r = selection->regions.begin(); r != selection->regions.end(); ++r) {
8001 tvl.push_back (&(*r)->get_time_axis_view ());
8007 } else if (internal_editing()) {
8008 /* no selected tracks, or regions, but in internal edit mode, so follow the mouse and use
8011 if (entered_track) {
8012 tvl.push_back (entered_track);
8020 Editor::fit_tracks (TrackViewList & tracks)
8022 if (tracks.empty()) {
8026 uint32_t child_heights = 0;
8027 int visible_tracks = 0;
8029 for (TrackSelection::iterator t = tracks.begin(); t != tracks.end(); ++t) {
8031 if (!(*t)->marked_for_display()) {
8035 child_heights += (*t)->effective_height() - (*t)->current_height();
8039 /* compute the per-track height from:
8041 * total canvas visible height
8042 * - height that will be taken by visible children of selected tracks
8043 * - height of the ruler/hscroll area
8045 uint32_t h = (uint32_t) floor ((trackviews_height() - child_heights) / visible_tracks);
8046 double first_y_pos = DBL_MAX;
8048 if (h < TimeAxisView::preset_height (HeightSmall)) {
8049 MessageDialog msg (_("There are too many tracks to fit in the current window"));
8050 /* too small to be displayed */
8054 undo_visual_stack.push_back (current_visual_state (true));
8055 PBD::Unwinder<bool> nsv (no_save_visual, true);
8057 /* build a list of all tracks, including children */
8060 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
8062 TimeAxisView::Children c = (*i)->get_child_list ();
8063 for (TimeAxisView::Children::iterator j = c.begin(); j != c.end(); ++j) {
8064 all.push_back (j->get());
8069 // find selection range.
8070 // if someone knows how to user TrackViewList::iterator for this
8072 int selected_top = -1;
8073 int selected_bottom = -1;
8075 for (TrackViewList::iterator t = all.begin(); t != all.end(); ++t, ++i) {
8076 if ((*t)->marked_for_display ()) {
8077 if (tracks.contains(*t)) {
8078 if (selected_top == -1) {
8081 selected_bottom = i;
8087 for (TrackViewList::iterator t = all.begin(); t != all.end(); ++t, ++i) {
8088 if ((*t)->marked_for_display ()) {
8089 if (tracks.contains(*t)) {
8090 (*t)->set_height (h);
8091 first_y_pos = std::min ((*t)->y_position (), first_y_pos);
8093 if (i > selected_top && i < selected_bottom) {
8094 hide_track_in_display (*t);
8101 set the controls_layout height now, because waiting for its size
8102 request signal handler will cause the vertical adjustment setting to fail
8105 controls_layout.property_height () = _full_canvas_height;
8106 vertical_adjustment.set_value (first_y_pos);
8108 redo_visual_stack.push_back (current_visual_state (true));
8110 visible_tracks_selector.set_text (_("Sel"));
8114 Editor::save_visual_state (uint32_t n)
8116 while (visual_states.size() <= n) {
8117 visual_states.push_back (0);
8120 if (visual_states[n] != 0) {
8121 delete visual_states[n];
8124 visual_states[n] = current_visual_state (true);
8129 Editor::goto_visual_state (uint32_t n)
8131 if (visual_states.size() <= n) {
8135 if (visual_states[n] == 0) {
8139 use_visual_state (*visual_states[n]);
8143 Editor::start_visual_state_op (uint32_t n)
8145 save_visual_state (n);
8147 PopUp* pup = new PopUp (WIN_POS_MOUSE, 1000, true);
8149 snprintf (buf, sizeof (buf), _("Saved view %u"), n+1);
8150 pup->set_text (buf);
8155 Editor::cancel_visual_state_op (uint32_t n)
8157 goto_visual_state (n);
8161 Editor::toggle_region_mute ()
8163 if (_ignore_region_action) {
8167 RegionSelection rs = get_regions_from_selection_and_entered ();
8173 if (rs.size() > 1) {
8174 begin_reversible_command (_("mute regions"));
8176 begin_reversible_command (_("mute region"));
8179 for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
8181 (*i)->region()->playlist()->clear_changes ();
8182 (*i)->region()->set_muted (!(*i)->region()->muted ());
8183 _session->add_command (new StatefulDiffCommand ((*i)->region()));
8187 commit_reversible_command ();
8191 Editor::combine_regions ()
8193 /* foreach track with selected regions, take all selected regions
8194 and join them into a new region containing the subregions (as a
8198 typedef set<RouteTimeAxisView*> RTVS;
8201 if (selection->regions.empty()) {
8205 for (RegionSelection::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
8206 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*>(&(*i)->get_time_axis_view());
8209 tracks.insert (rtv);
8213 begin_reversible_command (_("combine regions"));
8215 vector<RegionView*> new_selection;
8217 for (RTVS::iterator i = tracks.begin(); i != tracks.end(); ++i) {
8220 if ((rv = (*i)->combine_regions ()) != 0) {
8221 new_selection.push_back (rv);
8225 selection->clear_regions ();
8226 for (vector<RegionView*>::iterator i = new_selection.begin(); i != new_selection.end(); ++i) {
8227 selection->add (*i);
8230 commit_reversible_command ();
8234 Editor::uncombine_regions ()
8236 typedef set<RouteTimeAxisView*> RTVS;
8239 if (selection->regions.empty()) {
8243 for (RegionSelection::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
8244 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*>(&(*i)->get_time_axis_view());
8247 tracks.insert (rtv);
8251 begin_reversible_command (_("uncombine regions"));
8253 for (RTVS::iterator i = tracks.begin(); i != tracks.end(); ++i) {
8254 (*i)->uncombine_regions ();
8257 commit_reversible_command ();
8261 Editor::toggle_midi_input_active (bool flip_others)
8264 boost::shared_ptr<RouteList> rl (new RouteList);
8266 for (TrackSelection::iterator i = selection->tracks.begin(); i != selection->tracks.end(); ++i) {
8267 RouteTimeAxisView *rtav = dynamic_cast<RouteTimeAxisView *>(*i);
8273 boost::shared_ptr<MidiTrack> mt = rtav->midi_track();
8276 rl->push_back (rtav->route());
8277 onoff = !mt->input_active();
8281 _session->set_exclusive_input_active (rl, onoff, flip_others);
8284 static bool ok_fine (GdkEventAny*) { return true; }
8290 lock_dialog = new Gtk::Dialog (string_compose (_("%1: Locked"), PROGRAM_NAME), true);
8292 Gtk::Image* padlock = manage (new Gtk::Image (ARDOUR_UI_UTILS::get_icon ("padlock_closed")));
8293 lock_dialog->get_vbox()->pack_start (*padlock);
8294 lock_dialog->signal_delete_event ().connect (sigc::ptr_fun (ok_fine));
8296 ArdourButton* b = manage (new ArdourButton);
8297 b->set_name ("lock button");
8298 b->set_text (_("Click to unlock"));
8299 b->signal_clicked.connect (sigc::mem_fun (*this, &Editor::unlock));
8300 lock_dialog->get_vbox()->pack_start (*b);
8302 lock_dialog->get_vbox()->show_all ();
8303 lock_dialog->set_size_request (200, 200);
8306 delete _main_menu_disabler;
8307 _main_menu_disabler = new MainMenuDisabler;
8309 lock_dialog->present ();
8311 lock_dialog->get_window()->set_decorations (Gdk::WMDecoration (0));
8317 lock_dialog->hide ();
8319 delete _main_menu_disabler;
8320 _main_menu_disabler = 0;
8322 if (UIConfiguration::instance().get_lock_gui_after_seconds()) {
8323 start_lock_event_timing ();
8328 Editor::bring_in_callback (Gtk::Label* label, uint32_t n, uint32_t total, string name)
8330 Gtkmm2ext::UI::instance()->call_slot (invalidator (*this), boost::bind (&Editor::update_bring_in_message, this, label, n, total, name));
8334 Editor::update_bring_in_message (Gtk::Label* label, uint32_t n, uint32_t total, string name)
8336 Timers::TimerSuspender t;
8337 label->set_text (string_compose ("Copying %1, %2 of %3", name, n, total));
8338 Gtkmm2ext::UI::instance()->flush_pending (1);
8342 Editor::bring_all_sources_into_session ()
8349 ArdourDialog w (_("Moving embedded files into session folder"));
8350 w.get_vbox()->pack_start (msg);
8353 /* flush all pending GUI events because we're about to start copying
8357 Timers::TimerSuspender t;
8358 Gtkmm2ext::UI::instance()->flush_pending (3);
8362 _session->bring_all_sources_into_session (boost::bind (&Editor::bring_in_callback, this, &msg, _1, _2, _3));