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::tag_regions (RegionList regions)
5145 ArdourDialog d (_("Tag Last Capture"), true, false);
5147 Label label (_("Tag:"));
5150 hbox.set_spacing (6);
5151 hbox.pack_start (label, false, false);
5152 hbox.pack_start (entry, true, true);
5154 d.get_vbox()->set_border_width (12);
5155 d.get_vbox()->pack_start (hbox, false, false);
5157 d.add_button(Gtk::Stock::CANCEL, Gtk::RESPONSE_CANCEL);
5158 d.add_button(Gtk::Stock::OK, Gtk::RESPONSE_OK);
5160 d.set_size_request (300, -1);
5162 entry.set_text (_("Good"));
5163 entry.select_region (0, -1);
5165 entry.signal_activate().connect (sigc::bind (sigc::mem_fun (d, &Dialog::response), RESPONSE_OK));
5171 int const ret = d.run();
5175 if (ret != RESPONSE_OK) {
5179 std::string tagstr = entry.get_text();
5180 strip_whitespace_edges (tagstr);
5182 if (!tagstr.empty()) {
5183 for (RegionList::iterator r = regions.begin(); r != regions.end(); r++) {
5184 (*r)->set_tags(tagstr);
5187 _regions->redisplay ();
5192 Editor::tag_selected_region ()
5194 std::list<boost::shared_ptr<Region> > rlist;
5196 RegionSelection rs = get_regions_from_selection_and_entered ();
5197 for (RegionSelection::iterator r = rs.begin(); r != rs.end(); r++) {
5198 rlist.push_back((*r)->region());
5205 Editor::tag_last_capture ()
5211 std::list<boost::shared_ptr<Region> > rlist;
5213 std::list<boost::shared_ptr<Source> > srcs;
5214 _session->get_last_capture_sources (srcs);
5215 for (std::list<boost::shared_ptr<Source> >::iterator i = srcs.begin(); i != srcs.end(); ++i) {
5216 boost::shared_ptr<ARDOUR::Source> source = (*i);
5219 set<boost::shared_ptr<Region> > regions;
5220 RegionFactory::get_regions_using_source (source, regions);
5221 for (set<boost::shared_ptr<Region> >::iterator r = regions.begin(); r != regions.end(); r++) {
5222 rlist.push_back(*r);
5232 Editor::normalize_region ()
5238 RegionSelection rs = get_regions_from_selection_and_entered ();
5244 NormalizeDialog dialog (rs.size() > 1);
5246 if (dialog.run () != RESPONSE_ACCEPT) {
5250 CursorContext::Handle cursor_ctx = CursorContext::create(*this, _cursors->wait);
5253 /* XXX: should really only count audio regions here */
5254 int const regions = rs.size ();
5256 /* Make a list of the selected audio regions' maximum amplitudes, and also
5257 obtain the maximum amplitude of them all.
5259 list<double> max_amps;
5260 list<double> rms_vals;
5263 bool use_rms = dialog.constrain_rms ();
5265 for (RegionSelection::const_iterator i = rs.begin(); i != rs.end(); ++i) {
5266 AudioRegionView const * arv = dynamic_cast<AudioRegionView const *> (*i);
5270 dialog.descend (1.0 / regions);
5271 double const a = arv->audio_region()->maximum_amplitude (&dialog);
5273 double r = arv->audio_region()->rms (&dialog);
5274 max_rms = max (max_rms, r);
5275 rms_vals.push_back (r);
5279 /* the user cancelled the operation */
5283 max_amps.push_back (a);
5284 max_amp = max (max_amp, a);
5288 list<double>::const_iterator a = max_amps.begin ();
5289 list<double>::const_iterator l = rms_vals.begin ();
5290 bool in_command = false;
5292 for (RegionSelection::iterator r = rs.begin(); r != rs.end(); ++r) {
5293 AudioRegionView* const arv = dynamic_cast<AudioRegionView*> (*r);
5298 arv->region()->clear_changes ();
5300 double amp = dialog.normalize_individually() ? *a : max_amp;
5301 double target = dialog.target_peak (); // dB
5304 double const amp_rms = dialog.normalize_individually() ? *l : max_rms;
5305 const double t_rms = dialog.target_rms ();
5306 const gain_t c_peak = dB_to_coefficient (target);
5307 const gain_t c_rms = dB_to_coefficient (t_rms);
5308 if ((amp_rms / c_rms) > (amp / c_peak)) {
5314 arv->audio_region()->normalize (amp, target);
5317 begin_reversible_command (_("normalize"));
5320 _session->add_command (new StatefulDiffCommand (arv->region()));
5327 commit_reversible_command ();
5333 Editor::reset_region_scale_amplitude ()
5339 RegionSelection rs = get_regions_from_selection_and_entered ();
5345 bool in_command = false;
5347 for (RegionSelection::iterator r = rs.begin(); r != rs.end(); ++r) {
5348 AudioRegionView* const arv = dynamic_cast<AudioRegionView*>(*r);
5351 arv->region()->clear_changes ();
5352 arv->audio_region()->set_scale_amplitude (1.0f);
5355 begin_reversible_command ("reset gain");
5358 _session->add_command (new StatefulDiffCommand (arv->region()));
5362 commit_reversible_command ();
5367 Editor::adjust_region_gain (bool up)
5369 RegionSelection rs = get_regions_from_selection_and_entered ();
5371 if (!_session || rs.empty()) {
5375 bool in_command = false;
5377 for (RegionSelection::iterator r = rs.begin(); r != rs.end(); ++r) {
5378 AudioRegionView* const arv = dynamic_cast<AudioRegionView*>(*r);
5383 arv->region()->clear_changes ();
5385 double dB = accurate_coefficient_to_dB (arv->audio_region()->scale_amplitude ());
5393 arv->audio_region()->set_scale_amplitude (dB_to_coefficient (dB));
5396 begin_reversible_command ("adjust region gain");
5399 _session->add_command (new StatefulDiffCommand (arv->region()));
5403 commit_reversible_command ();
5408 Editor::reset_region_gain ()
5410 RegionSelection rs = get_regions_from_selection_and_entered ();
5412 if (!_session || rs.empty()) {
5416 bool in_command = false;
5418 for (RegionSelection::iterator r = rs.begin(); r != rs.end(); ++r) {
5419 AudioRegionView* const arv = dynamic_cast<AudioRegionView*>(*r);
5424 arv->region()->clear_changes ();
5426 arv->audio_region()->set_scale_amplitude (1.0f);
5429 begin_reversible_command ("reset region gain");
5432 _session->add_command (new StatefulDiffCommand (arv->region()));
5436 commit_reversible_command ();
5441 Editor::reverse_region ()
5447 Reverse rev (*_session);
5448 apply_filter (rev, _("reverse regions"));
5452 Editor::strip_region_silence ()
5458 RegionSelection rs = get_regions_from_selection_and_entered ();
5464 std::list<RegionView*> audio_only;
5466 for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
5467 AudioRegionView* const arv = dynamic_cast<AudioRegionView*> (*i);
5469 audio_only.push_back (arv);
5473 assert (!audio_only.empty());
5475 StripSilenceDialog d (_session, audio_only);
5476 int const r = d.run ();
5480 if (r == Gtk::RESPONSE_OK) {
5481 ARDOUR::AudioIntervalMap silences;
5482 d.silences (silences);
5483 StripSilence s (*_session, silences, d.fade_length());
5485 apply_filter (s, _("strip silence"), &d);
5490 Editor::apply_midi_note_edit_op_to_region (MidiOperator& op, MidiRegionView& mrv)
5492 Evoral::Sequence<Temporal::Beats>::Notes selected;
5493 mrv.selection_as_notelist (selected, true);
5495 vector<Evoral::Sequence<Temporal::Beats>::Notes> v;
5496 v.push_back (selected);
5498 Temporal::Beats pos_beats = Temporal::Beats (mrv.midi_region()->beat()) - mrv.midi_region()->start_beats();
5500 return op (mrv.midi_region()->model(), pos_beats, v);
5504 Editor::apply_midi_note_edit_op (MidiOperator& op, const RegionSelection& rs)
5510 bool in_command = false;
5512 for (RegionSelection::const_iterator r = rs.begin(); r != rs.end(); ) {
5513 RegionSelection::const_iterator tmp = r;
5516 MidiRegionView* const mrv = dynamic_cast<MidiRegionView*> (*r);
5519 Command* cmd = apply_midi_note_edit_op_to_region (op, *mrv);
5522 begin_reversible_command (op.name ());
5526 _session->add_command (cmd);
5534 commit_reversible_command ();
5535 _session->set_dirty ();
5540 Editor::fork_region ()
5542 RegionSelection rs = get_regions_from_selection_and_entered ();
5548 CursorContext::Handle cursor_ctx = CursorContext::create(*this, _cursors->wait);
5549 bool in_command = false;
5553 for (RegionSelection::iterator r = rs.begin(); r != rs.end(); ) {
5554 RegionSelection::iterator tmp = r;
5557 MidiRegionView* const mrv = dynamic_cast<MidiRegionView*>(*r);
5561 boost::shared_ptr<Playlist> playlist = mrv->region()->playlist();
5562 boost::shared_ptr<MidiSource> new_source = _session->create_midi_source_by_stealing_name (mrv->midi_view()->track());
5563 boost::shared_ptr<MidiRegion> newregion = mrv->midi_region()->clone (new_source);
5566 begin_reversible_command (_("Fork Region(s)"));
5569 playlist->clear_changes ();
5570 playlist->replace_region (mrv->region(), newregion, mrv->region()->position());
5571 _session->add_command(new StatefulDiffCommand (playlist));
5573 error << string_compose (_("Could not unlink %1"), mrv->region()->name()) << endmsg;
5581 commit_reversible_command ();
5586 Editor::quantize_region ()
5589 quantize_regions(get_regions_from_selection_and_entered ());
5594 Editor::quantize_regions (const RegionSelection& rs)
5596 if (rs.n_midi_regions() == 0) {
5600 if (!quantize_dialog) {
5601 quantize_dialog = new QuantizeDialog (*this);
5604 if (quantize_dialog->is_mapped()) {
5605 /* in progress already */
5609 quantize_dialog->present ();
5610 const int r = quantize_dialog->run ();
5611 quantize_dialog->hide ();
5613 if (r == Gtk::RESPONSE_OK) {
5614 Quantize quant (quantize_dialog->snap_start(),
5615 quantize_dialog->snap_end(),
5616 quantize_dialog->start_grid_size(),
5617 quantize_dialog->end_grid_size(),
5618 quantize_dialog->strength(),
5619 quantize_dialog->swing(),
5620 quantize_dialog->threshold());
5622 apply_midi_note_edit_op (quant, rs);
5627 Editor::legatize_region (bool shrink_only)
5630 legatize_regions(get_regions_from_selection_and_entered (), shrink_only);
5635 Editor::legatize_regions (const RegionSelection& rs, bool shrink_only)
5637 if (rs.n_midi_regions() == 0) {
5641 Legatize legatize(shrink_only);
5642 apply_midi_note_edit_op (legatize, rs);
5646 Editor::transform_region ()
5649 transform_regions(get_regions_from_selection_and_entered ());
5654 Editor::transform_regions (const RegionSelection& rs)
5656 if (rs.n_midi_regions() == 0) {
5663 const int r = td.run();
5666 if (r == Gtk::RESPONSE_OK) {
5667 Transform transform(td.get());
5668 apply_midi_note_edit_op(transform, rs);
5673 Editor::transpose_region ()
5676 transpose_regions(get_regions_from_selection_and_entered ());
5681 Editor::transpose_regions (const RegionSelection& rs)
5683 if (rs.n_midi_regions() == 0) {
5688 int const r = d.run ();
5690 if (r == RESPONSE_ACCEPT) {
5691 Transpose transpose(d.semitones ());
5692 apply_midi_note_edit_op (transpose, rs);
5697 Editor::insert_patch_change (bool from_context)
5699 RegionSelection rs = get_regions_from_selection_and_entered ();
5705 const samplepos_t p = get_preferred_edit_position (EDIT_IGNORE_NONE, from_context);
5707 /* XXX: bit of a hack; use the MIDNAM from the first selected region;
5708 there may be more than one, but the PatchChangeDialog can only offer
5709 one set of patch menus.
5711 MidiRegionView* first = dynamic_cast<MidiRegionView*> (rs.front ());
5713 Evoral::PatchChange<Temporal::Beats> empty (Temporal::Beats(), 0, 0, 0);
5714 PatchChangeDialog d (0, _session, empty, first->instrument_info(), Gtk::Stock::ADD);
5716 if (d.run() == RESPONSE_CANCEL) {
5720 for (RegionSelection::iterator i = rs.begin (); i != rs.end(); ++i) {
5721 MidiRegionView* const mrv = dynamic_cast<MidiRegionView*> (*i);
5723 if (p >= mrv->region()->first_sample() && p <= mrv->region()->last_sample()) {
5724 mrv->add_patch_change (p - mrv->region()->position(), d.patch ());
5731 Editor::apply_filter (Filter& filter, string command, ProgressReporter* progress)
5733 RegionSelection rs = get_regions_from_selection_and_entered ();
5739 CursorContext::Handle cursor_ctx = CursorContext::create(*this, _cursors->wait);
5740 bool in_command = false;
5745 int const N = rs.size ();
5747 for (RegionSelection::iterator r = rs.begin(); r != rs.end(); ) {
5748 RegionSelection::iterator tmp = r;
5751 AudioRegionView* const arv = dynamic_cast<AudioRegionView*>(*r);
5753 boost::shared_ptr<Playlist> playlist = arv->region()->playlist();
5756 progress->descend (1.0 / N);
5759 if (arv->audio_region()->apply (filter, progress) == 0) {
5761 playlist->clear_changes ();
5762 playlist->clear_owned_changes ();
5765 begin_reversible_command (command);
5769 if (filter.results.empty ()) {
5771 /* no regions returned; remove the old one */
5772 playlist->remove_region (arv->region ());
5776 std::vector<boost::shared_ptr<Region> >::iterator res = filter.results.begin ();
5778 /* first region replaces the old one */
5779 playlist->replace_region (arv->region(), *res, (*res)->position());
5783 while (res != filter.results.end()) {
5784 playlist->add_region (*res, (*res)->position());
5790 /* We might have removed regions, which alters other regions' layering_index,
5791 so we need to do a recursive diff here.
5793 vector<Command*> cmds;
5794 playlist->rdiff (cmds);
5795 _session->add_commands (cmds);
5797 _session->add_command(new StatefulDiffCommand (playlist));
5801 progress->ascend ();
5810 commit_reversible_command ();
5815 Editor::external_edit_region ()
5821 Editor::reset_region_gain_envelopes ()
5823 RegionSelection rs = get_regions_from_selection_and_entered ();
5825 if (!_session || rs.empty()) {
5829 bool in_command = false;
5831 for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
5832 AudioRegionView* const arv = dynamic_cast<AudioRegionView*>(*i);
5834 boost::shared_ptr<AutomationList> alist (arv->audio_region()->envelope());
5835 XMLNode& before (alist->get_state());
5837 arv->audio_region()->set_default_envelope ();
5840 begin_reversible_command (_("reset region gain"));
5843 _session->add_command (new MementoCommand<AutomationList>(*arv->audio_region()->envelope().get(), &before, &alist->get_state()));
5848 commit_reversible_command ();
5853 Editor::set_region_gain_visibility (RegionView* rv)
5855 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (rv);
5857 arv->update_envelope_visibility();
5862 Editor::set_gain_envelope_visibility ()
5868 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
5869 AudioTimeAxisView* v = dynamic_cast<AudioTimeAxisView*>(*i);
5871 v->audio_view()->foreach_regionview (sigc::mem_fun (this, &Editor::set_region_gain_visibility));
5877 Editor::toggle_gain_envelope_active ()
5879 if (_ignore_region_action) {
5883 RegionSelection rs = get_regions_from_selection_and_entered ();
5885 if (!_session || rs.empty()) {
5889 bool in_command = false;
5891 for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
5892 AudioRegionView* const arv = dynamic_cast<AudioRegionView*>(*i);
5894 arv->region()->clear_changes ();
5895 arv->audio_region()->set_envelope_active (!arv->audio_region()->envelope_active());
5898 begin_reversible_command (_("region gain envelope active"));
5901 _session->add_command (new StatefulDiffCommand (arv->region()));
5906 commit_reversible_command ();
5911 Editor::toggle_region_lock ()
5913 if (_ignore_region_action) {
5917 RegionSelection rs = get_regions_from_selection_and_entered ();
5919 if (!_session || rs.empty()) {
5923 begin_reversible_command (_("toggle region lock"));
5925 for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
5926 (*i)->region()->clear_changes ();
5927 (*i)->region()->set_locked (!(*i)->region()->locked());
5928 _session->add_command (new StatefulDiffCommand ((*i)->region()));
5931 commit_reversible_command ();
5935 Editor::toggle_region_video_lock ()
5937 if (_ignore_region_action) {
5941 RegionSelection rs = get_regions_from_selection_and_entered ();
5943 if (!_session || rs.empty()) {
5947 begin_reversible_command (_("Toggle Video Lock"));
5949 for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
5950 (*i)->region()->clear_changes ();
5951 (*i)->region()->set_video_locked (!(*i)->region()->video_locked());
5952 _session->add_command (new StatefulDiffCommand ((*i)->region()));
5955 commit_reversible_command ();
5959 Editor::toggle_region_lock_style ()
5961 if (_ignore_region_action) {
5965 RegionSelection rs = get_regions_from_selection_and_entered ();
5967 if (!_session || rs.empty()) {
5971 Glib::RefPtr<ToggleAction> a = Glib::RefPtr<ToggleAction>::cast_dynamic (_region_actions->get_action("toggle-region-lock-style"));
5972 vector<Widget*> proxies = a->get_proxies();
5973 Gtk::CheckMenuItem* cmi = dynamic_cast<Gtk::CheckMenuItem*> (proxies.front());
5977 begin_reversible_command (_("toggle region lock style"));
5979 for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
5980 (*i)->region()->clear_changes ();
5981 PositionLockStyle const ns = ((*i)->region()->position_lock_style() == AudioTime && !cmi->get_inconsistent()) ? MusicTime : AudioTime;
5982 (*i)->region()->set_position_lock_style (ns);
5983 _session->add_command (new StatefulDiffCommand ((*i)->region()));
5986 commit_reversible_command ();
5990 Editor::toggle_opaque_region ()
5992 if (_ignore_region_action) {
5996 RegionSelection rs = get_regions_from_selection_and_entered ();
5998 if (!_session || rs.empty()) {
6002 begin_reversible_command (_("change region opacity"));
6004 for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
6005 (*i)->region()->clear_changes ();
6006 (*i)->region()->set_opaque (!(*i)->region()->opaque());
6007 _session->add_command (new StatefulDiffCommand ((*i)->region()));
6010 commit_reversible_command ();
6014 Editor::toggle_record_enable ()
6016 bool new_state = false;
6018 for (TrackSelection::iterator i = selection->tracks.begin(); i != selection->tracks.end(); ++i) {
6019 RouteTimeAxisView *rtav = dynamic_cast<RouteTimeAxisView *>(*i);
6022 if (!rtav->is_track())
6026 new_state = !rtav->track()->rec_enable_control()->get_value();
6030 rtav->track()->rec_enable_control()->set_value (new_state, Controllable::UseGroup);
6035 tracklist_to_stripables (TrackViewList list)
6039 for (TrackSelection::iterator i = list.begin(); i != list.end(); ++i) {
6040 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> ((*i));
6042 if (rtv && rtv->is_track()) {
6043 ret.push_back (rtv->track());
6051 Editor::play_solo_selection (bool restart)
6053 //note: session::solo_selection takes care of invalidating the region playlist
6055 if ((!selection->tracks.empty()) && selection->time.length() > 0) { //a range is selected; solo the tracks and roll
6057 StripableList sl = tracklist_to_stripables (selection->tracks);
6058 _session->solo_selection (sl, true);
6061 samplepos_t start = selection->time.start();
6062 samplepos_t end = selection->time.end_sample();
6063 _session->request_bounded_roll (start, end);
6065 } else if (! selection->tracks.empty()) { //no range is selected, but tracks are selected; solo the tracks and roll
6066 StripableList sl = tracklist_to_stripables (selection->tracks);
6067 _session->solo_selection (sl, true);
6068 _session->request_cancel_play_range();
6069 transition_to_rolling (true);
6071 } else if (! selection->regions.empty()) { //solo any tracks with selected regions, and roll
6072 StripableList sl = tracklist_to_stripables (get_tracks_for_range_action());
6073 _session->solo_selection (sl, true);
6074 _session->request_cancel_play_range();
6075 transition_to_rolling (true);
6077 _session->request_cancel_play_range();
6078 transition_to_rolling (true); //no selection. just roll.
6083 Editor::toggle_solo ()
6085 bool new_state = false;
6087 boost::shared_ptr<ControlList> cl (new ControlList);
6089 for (TrackSelection::iterator i = selection->tracks.begin(); i != selection->tracks.end(); ++i) {
6090 StripableTimeAxisView *stav = dynamic_cast<StripableTimeAxisView *>(*i);
6092 if (!stav || !stav->stripable()->solo_control()) {
6097 new_state = !stav->stripable()->solo_control()->soloed ();
6101 cl->push_back (stav->stripable()->solo_control());
6104 _session->set_controls (cl, new_state ? 1.0 : 0.0, Controllable::UseGroup);
6108 Editor::toggle_mute ()
6110 bool new_state = false;
6112 boost::shared_ptr<ControlList> cl (new ControlList);
6114 for (TrackSelection::iterator i = selection->tracks.begin(); i != selection->tracks.end(); ++i) {
6115 StripableTimeAxisView *stav = dynamic_cast<StripableTimeAxisView *>(*i);
6117 if (!stav || !stav->stripable()->mute_control()) {
6122 new_state = !stav->stripable()->mute_control()->muted();
6126 cl->push_back (stav->stripable()->mute_control());
6129 _session->set_controls (cl, new_state, Controllable::UseGroup);
6133 Editor::toggle_solo_isolate ()
6139 Editor::fade_range ()
6141 TrackViewList ts = selection->tracks.filter_to_unique_playlists ();
6143 begin_reversible_command (_("fade range"));
6145 for (TrackViewList::iterator i = ts.begin(); i != ts.end(); ++i) {
6146 (*i)->fade_range (selection->time);
6149 commit_reversible_command ();
6154 Editor::set_fade_length (bool in)
6156 RegionSelection rs = get_regions_from_selection_and_entered ();
6162 /* we need a region to measure the offset from the start */
6164 RegionView* rv = rs.front ();
6166 samplepos_t pos = get_preferred_edit_position();
6170 if (pos > rv->region()->last_sample() || pos < rv->region()->first_sample()) {
6171 /* edit point is outside the relevant region */
6176 if (pos <= rv->region()->position()) {
6180 len = pos - rv->region()->position();
6181 cmd = _("set fade in length");
6183 if (pos >= rv->region()->last_sample()) {
6187 len = rv->region()->last_sample() - pos;
6188 cmd = _("set fade out length");
6191 bool in_command = false;
6193 for (RegionSelection::iterator x = rs.begin(); x != rs.end(); ++x) {
6194 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (*x);
6200 boost::shared_ptr<AutomationList> alist;
6202 alist = tmp->audio_region()->fade_in();
6204 alist = tmp->audio_region()->fade_out();
6207 XMLNode &before = alist->get_state();
6210 tmp->audio_region()->set_fade_in_length (len);
6211 tmp->audio_region()->set_fade_in_active (true);
6213 tmp->audio_region()->set_fade_out_length (len);
6214 tmp->audio_region()->set_fade_out_active (true);
6218 begin_reversible_command (cmd);
6221 XMLNode &after = alist->get_state();
6222 _session->add_command(new MementoCommand<AutomationList>(*alist, &before, &after));
6226 commit_reversible_command ();
6231 Editor::set_fade_in_shape (FadeShape shape)
6233 RegionSelection rs = get_regions_from_selection_and_entered ();
6238 bool in_command = false;
6240 for (RegionSelection::iterator x = rs.begin(); x != rs.end(); ++x) {
6241 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (*x);
6247 boost::shared_ptr<AutomationList> alist = tmp->audio_region()->fade_in();
6248 XMLNode &before = alist->get_state();
6250 tmp->audio_region()->set_fade_in_shape (shape);
6253 begin_reversible_command (_("set fade in shape"));
6256 XMLNode &after = alist->get_state();
6257 _session->add_command(new MementoCommand<AutomationList>(*alist.get(), &before, &after));
6261 commit_reversible_command ();
6266 Editor::set_fade_out_shape (FadeShape shape)
6268 RegionSelection rs = get_regions_from_selection_and_entered ();
6273 bool in_command = false;
6275 for (RegionSelection::iterator x = rs.begin(); x != rs.end(); ++x) {
6276 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (*x);
6282 boost::shared_ptr<AutomationList> alist = tmp->audio_region()->fade_out();
6283 XMLNode &before = alist->get_state();
6285 tmp->audio_region()->set_fade_out_shape (shape);
6288 begin_reversible_command (_("set fade out shape"));
6291 XMLNode &after = alist->get_state();
6292 _session->add_command(new MementoCommand<AutomationList>(*alist.get(), &before, &after));
6296 commit_reversible_command ();
6301 Editor::set_fade_in_active (bool yn)
6303 RegionSelection rs = get_regions_from_selection_and_entered ();
6308 bool in_command = false;
6310 for (RegionSelection::iterator x = rs.begin(); x != rs.end(); ++x) {
6311 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (*x);
6318 boost::shared_ptr<AudioRegion> ar (tmp->audio_region());
6320 ar->clear_changes ();
6321 ar->set_fade_in_active (yn);
6324 begin_reversible_command (_("set fade in active"));
6327 _session->add_command (new StatefulDiffCommand (ar));
6331 commit_reversible_command ();
6336 Editor::set_fade_out_active (bool yn)
6338 RegionSelection rs = get_regions_from_selection_and_entered ();
6343 bool in_command = false;
6345 for (RegionSelection::iterator x = rs.begin(); x != rs.end(); ++x) {
6346 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (*x);
6352 boost::shared_ptr<AudioRegion> ar (tmp->audio_region());
6354 ar->clear_changes ();
6355 ar->set_fade_out_active (yn);
6358 begin_reversible_command (_("set fade out active"));
6361 _session->add_command(new StatefulDiffCommand (ar));
6365 commit_reversible_command ();
6370 Editor::toggle_region_fades (int dir)
6372 if (_ignore_region_action) {
6376 boost::shared_ptr<AudioRegion> ar;
6379 RegionSelection rs = get_regions_from_selection_and_entered ();
6385 RegionSelection::iterator i;
6386 for (i = rs.begin(); i != rs.end(); ++i) {
6387 if ((ar = boost::dynamic_pointer_cast<AudioRegion>((*i)->region())) != 0) {
6389 yn = ar->fade_out_active ();
6391 yn = ar->fade_in_active ();
6397 if (i == rs.end()) {
6401 /* XXX should this undo-able? */
6402 bool in_command = false;
6404 for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
6405 if ((ar = boost::dynamic_pointer_cast<AudioRegion>((*i)->region())) == 0) {
6408 ar->clear_changes ();
6410 if (dir == 1 || dir == 0) {
6411 ar->set_fade_in_active (!yn);
6414 if (dir == -1 || dir == 0) {
6415 ar->set_fade_out_active (!yn);
6418 begin_reversible_command (_("toggle fade active"));
6421 _session->add_command(new StatefulDiffCommand (ar));
6425 commit_reversible_command ();
6430 /** Update region fade visibility after its configuration has been changed */
6432 Editor::update_region_fade_visibility ()
6434 bool _fade_visibility = _session->config.get_show_region_fades ();
6436 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
6437 AudioTimeAxisView* v = dynamic_cast<AudioTimeAxisView*>(*i);
6439 if (_fade_visibility) {
6440 v->audio_view()->show_all_fades ();
6442 v->audio_view()->hide_all_fades ();
6449 Editor::set_edit_point ()
6452 MusicSample where (0, 0);
6454 if (!mouse_sample (where.sample, ignored)) {
6460 if (selection->markers.empty()) {
6462 mouse_add_new_marker (where.sample);
6467 Location* loc = find_location_from_marker (selection->markers.front(), ignored);
6470 loc->move_to (where.sample, where.division);
6476 Editor::set_playhead_cursor ()
6478 if (entered_marker) {
6479 _session->request_locate (entered_marker->position(), _session->transport_rolling());
6481 MusicSample where (0, 0);
6484 if (!mouse_sample (where.sample, ignored)) {
6491 _session->request_locate (where.sample, _session->transport_rolling());
6495 //not sure what this was for; remove it for now.
6496 // if (UIConfiguration::instance().get_follow_edits() && (!_session || !_session->config.get_external_sync())) {
6497 // cancel_time_selection();
6503 Editor::split_region ()
6505 if (_dragging_playhead) {
6507 } else if (_drags->active ()) {
6508 /*any other kind of drag, bail out so we avoid Undo snafu*/
6512 //if a range is selected, separate it
6513 if (!selection->time.empty()) {
6514 separate_regions_between (selection->time);
6518 //if no range was selected, try to find some regions to split
6519 if (current_mouse_mode() == MouseObject || current_mouse_mode() == MouseRange ) { //don't try this for Internal Edit, Stretch, Draw, etc.
6523 //new behavior: the Split action will prioritize the entered_regionview rather than selected regions.
6524 //this fixes the unexpected case where you point at a region, but
6525 // * nothing happens OR
6526 // * some other region (maybe off-screen) is split.
6527 //NOTE: if the entered_regionview is /part of the selection/ then we should operate on the selection as usual
6528 if (_edit_point == EditAtMouse && entered_regionview && !entered_regionview->selected()) {
6529 rs.add (entered_regionview);
6531 rs = selection->regions; //might be empty
6535 TrackViewList tracks = selection->tracks;
6537 if (!tracks.empty()) {
6538 /* no region selected or entered, but some selected tracks:
6539 * act on all regions on the selected tracks at the edit point
6541 samplepos_t const where = get_preferred_edit_position (Editing::EDIT_IGNORE_NONE, false, false);
6542 get_regions_at(rs, where, tracks);
6546 const samplepos_t pos = get_preferred_edit_position();
6547 const int32_t division = get_grid_music_divisions (0);
6548 MusicSample where (pos, division);
6554 split_regions_at (where, rs);
6559 Editor::select_next_stripable (bool routes_only)
6561 _session->selection().select_next_stripable (false, routes_only);
6565 Editor::select_prev_stripable (bool routes_only)
6567 _session->selection().select_prev_stripable (false, routes_only);
6571 Editor::set_loop_from_selection (bool play)
6573 if (_session == 0) {
6577 samplepos_t start, end;
6578 if (!get_selection_extents (start, end))
6581 set_loop_range (start, end, _("set loop range from selection"));
6584 _session->request_play_loop (true, true);
6589 Editor::set_loop_from_region (bool play)
6591 samplepos_t start, end;
6592 if (!get_selection_extents (start, end))
6595 set_loop_range (start, end, _("set loop range from region"));
6598 _session->request_locate (start, true);
6599 _session->request_play_loop (true);
6604 Editor::set_punch_from_selection ()
6606 if (_session == 0) {
6610 samplepos_t start, end;
6611 if (!get_selection_extents (start, end))
6614 set_punch_range (start, end, _("set punch range from selection"));
6618 Editor::set_auto_punch_range ()
6620 // auto punch in/out button from a single button
6621 // If Punch In is unset, set punch range from playhead to end, enable punch in
6622 // If Punch In is set, the next punch sets Punch Out, unless the playhead has been
6623 // rewound beyond the Punch In marker, in which case that marker will be moved back
6624 // to the current playhead position.
6625 // If punch out is set, it clears the punch range and Punch In/Out buttons
6627 if (_session == 0) {
6631 Location* tpl = transport_punch_location();
6632 samplepos_t now = playhead_cursor->current_sample();
6633 samplepos_t begin = now;
6634 samplepos_t end = _session->current_end_sample();
6636 if (!_session->config.get_punch_in()) {
6637 // First Press - set punch in and create range from here to eternity
6638 set_punch_range (begin, end, _("Auto Punch In"));
6639 _session->config.set_punch_in(true);
6640 } else if (tpl && !_session->config.get_punch_out()) {
6641 // Second press - update end range marker and set punch_out
6642 if (now < tpl->start()) {
6643 // playhead has been rewound - move start back and pretend nothing happened
6645 set_punch_range (begin, end, _("Auto Punch In/Out"));
6647 // normal case for 2nd press - set the punch out
6648 end = playhead_cursor->current_sample ();
6649 set_punch_range (tpl->start(), now, _("Auto Punch In/Out"));
6650 _session->config.set_punch_out(true);
6653 if (_session->config.get_punch_out()) {
6654 _session->config.set_punch_out(false);
6657 if (_session->config.get_punch_in()) {
6658 _session->config.set_punch_in(false);
6663 // third press - unset punch in/out and remove range
6664 _session->locations()->remove(tpl);
6671 Editor::set_session_extents_from_selection ()
6673 if (_session == 0) {
6677 samplepos_t start, end;
6678 if (!get_selection_extents (start, end))
6682 if ((loc = _session->locations()->session_range_location()) == 0) {
6683 _session->set_session_extents (start, end); // this will create a new session range; no need for UNDO
6685 XMLNode &before = loc->get_state();
6687 _session->set_session_extents (start, end);
6689 XMLNode &after = loc->get_state();
6691 begin_reversible_command (_("set session start/end from selection"));
6693 _session->add_command (new MementoCommand<Location>(*loc, &before, &after));
6695 commit_reversible_command ();
6698 _session->set_session_range_is_free (false);
6702 Editor::set_punch_start_from_edit_point ()
6706 MusicSample start (0, 0);
6707 samplepos_t end = max_samplepos;
6709 //use the existing punch end, if any
6710 Location* tpl = transport_punch_location();
6715 if ((_edit_point == EditAtPlayhead) && _session->transport_rolling()) {
6716 start.sample = _session->audible_sample();
6718 start.sample = get_preferred_edit_position();
6721 //if there's not already a sensible selection endpoint, go "forever"
6722 if (start.sample > end) {
6723 end = max_samplepos;
6726 set_punch_range (start.sample, end, _("set punch start from EP"));
6732 Editor::set_punch_end_from_edit_point ()
6736 samplepos_t start = 0;
6737 MusicSample end (max_samplepos, 0);
6739 //use the existing punch start, if any
6740 Location* tpl = transport_punch_location();
6742 start = tpl->start();
6745 if ((_edit_point == EditAtPlayhead) && _session->transport_rolling()) {
6746 end.sample = _session->audible_sample();
6748 end.sample = get_preferred_edit_position();
6751 set_punch_range (start, end.sample, _("set punch end from EP"));
6757 Editor::set_loop_start_from_edit_point ()
6761 MusicSample start (0, 0);
6762 samplepos_t end = max_samplepos;
6764 //use the existing loop end, if any
6765 Location* tpl = transport_loop_location();
6770 if ((_edit_point == EditAtPlayhead) && _session->transport_rolling()) {
6771 start.sample = _session->audible_sample();
6773 start.sample = get_preferred_edit_position();
6776 //if there's not already a sensible selection endpoint, go "forever"
6777 if (start.sample > end) {
6778 end = max_samplepos;
6781 set_loop_range (start.sample, end, _("set loop start from EP"));
6787 Editor::set_loop_end_from_edit_point ()
6791 samplepos_t start = 0;
6792 MusicSample end (max_samplepos, 0);
6794 //use the existing loop start, if any
6795 Location* tpl = transport_loop_location();
6797 start = tpl->start();
6800 if ((_edit_point == EditAtPlayhead) && _session->transport_rolling()) {
6801 end.sample = _session->audible_sample();
6803 end.sample = get_preferred_edit_position();
6806 set_loop_range (start, end.sample, _("set loop end from EP"));
6811 Editor::set_punch_from_region ()
6813 samplepos_t start, end;
6814 if (!get_selection_extents (start, end))
6817 set_punch_range (start, end, _("set punch range from region"));
6821 Editor::pitch_shift_region ()
6823 RegionSelection rs = get_regions_from_selection_and_entered ();
6825 RegionSelection audio_rs;
6826 for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
6827 if (dynamic_cast<AudioRegionView*> (*i)) {
6828 audio_rs.push_back (*i);
6832 if (audio_rs.empty()) {
6836 pitch_shift (audio_rs, 1.2);
6840 Editor::set_tempo_from_region ()
6842 RegionSelection rs = get_regions_from_selection_and_entered ();
6844 if (!_session || rs.empty()) {
6848 RegionView* rv = rs.front();
6850 define_one_bar (rv->region()->position(), rv->region()->last_sample() + 1);
6854 Editor::use_range_as_bar ()
6856 samplepos_t start, end;
6857 if (get_edit_op_range (start, end)) {
6858 define_one_bar (start, end);
6863 Editor::define_one_bar (samplepos_t start, samplepos_t end)
6865 samplepos_t length = end - start;
6867 const Meter& m (_session->tempo_map().meter_at_sample (start));
6869 /* length = 1 bar */
6871 /* We're going to deliver a constant tempo here,
6872 so we can use samples per beat to determine length.
6873 now we want samples per beat.
6874 we have samples per bar, and beats per bar, so ...
6877 /* XXXX METER MATH */
6879 double samples_per_beat = length / m.divisions_per_bar();
6881 /* beats per minute = */
6883 double beats_per_minute = (_session->sample_rate() * 60.0) / samples_per_beat;
6885 /* now decide whether to:
6887 (a) set global tempo
6888 (b) add a new tempo marker
6892 const TempoSection& t (_session->tempo_map().tempo_section_at_sample (start));
6894 bool do_global = false;
6896 if ((_session->tempo_map().n_tempos() == 1) && (_session->tempo_map().n_meters() == 1)) {
6898 /* only 1 tempo & 1 meter: ask if the user wants to set the tempo
6899 at the start, or create a new marker
6902 vector<string> options;
6903 options.push_back (_("Cancel"));
6904 options.push_back (_("Add new marker"));
6905 options.push_back (_("Set global tempo"));
6908 _("Define one bar"),
6909 _("Do you want to set the global tempo or add a new tempo marker?"),
6913 c.set_default_response (2);
6929 /* more than 1 tempo and/or meter section already, go ahead do the "usual":
6930 if the marker is at the region starter, change it, otherwise add
6935 begin_reversible_command (_("set tempo from region"));
6936 XMLNode& before (_session->tempo_map().get_state());
6939 _session->tempo_map().change_initial_tempo (beats_per_minute, t.note_type(), t.end_note_types_per_minute());
6940 } else if (t.sample() == start) {
6941 _session->tempo_map().change_existing_tempo_at (start, beats_per_minute, t.note_type(), t.end_note_types_per_minute());
6943 /* constant tempo */
6944 const Tempo tempo (beats_per_minute, t.note_type());
6945 _session->tempo_map().add_tempo (tempo, 0.0, start, AudioTime);
6948 XMLNode& after (_session->tempo_map().get_state());
6950 _session->add_command (new MementoCommand<TempoMap>(_session->tempo_map(), &before, &after));
6951 commit_reversible_command ();
6955 Editor::split_region_at_transients ()
6957 AnalysisFeatureList positions;
6959 RegionSelection rs = get_regions_from_selection_and_entered ();
6961 if (!_session || rs.empty()) {
6965 begin_reversible_command (_("split regions"));
6967 for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ) {
6969 RegionSelection::iterator tmp;
6974 boost::shared_ptr<AudioRegion> ar = boost::dynamic_pointer_cast<AudioRegion> ((*i)->region());
6977 ar->transients (positions);
6978 split_region_at_points ((*i)->region(), positions, true);
6985 commit_reversible_command ();
6990 Editor::split_region_at_points (boost::shared_ptr<Region> r, AnalysisFeatureList& positions, bool can_ferret, bool select_new)
6992 bool use_rhythmic_rodent = false;
6994 boost::shared_ptr<Playlist> pl = r->playlist();
6996 list<boost::shared_ptr<Region> > new_regions;
7002 if (positions.empty()) {
7006 if (positions.size() > 20 && can_ferret) {
7007 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);
7008 MessageDialog msg (msgstr,
7011 Gtk::BUTTONS_OK_CANCEL);
7014 msg.add_button (_("Call for the Ferret!"), RESPONSE_APPLY);
7015 msg.set_secondary_text (_("Press OK to continue with this split operation\nor ask the Ferret dialog to tune the analysis"));
7017 msg.set_secondary_text (_("Press OK to continue with this split operation"));
7020 msg.set_title (_("Excessive split?"));
7023 int response = msg.run();
7029 case RESPONSE_APPLY:
7030 use_rhythmic_rodent = true;
7037 if (use_rhythmic_rodent) {
7038 show_rhythm_ferret ();
7042 AnalysisFeatureList::const_iterator x;
7044 pl->clear_changes ();
7045 pl->clear_owned_changes ();
7047 x = positions.begin();
7049 if (x == positions.end()) {
7054 pl->remove_region (r);
7056 samplepos_t pos = 0;
7058 samplepos_t rstart = r->first_sample ();
7059 samplepos_t rend = r->last_sample ();
7061 while (x != positions.end()) {
7063 /* deal with positons that are out of scope of present region bounds */
7064 if (*x <= rstart || *x > rend) {
7069 /* file start = original start + how far we from the initial position ? */
7071 samplepos_t file_start = r->start() + pos;
7073 /* length = next position - current position */
7075 samplepos_t len = (*x) - pos - rstart;
7077 /* XXX we do we really want to allow even single-sample regions?
7078 * shouldn't we have some kind of lower limit on region size?
7087 if (RegionFactory::region_name (new_name, r->name())) {
7091 /* do NOT announce new regions 1 by one, just wait till they are all done */
7095 plist.add (ARDOUR::Properties::start, file_start);
7096 plist.add (ARDOUR::Properties::length, len);
7097 plist.add (ARDOUR::Properties::name, new_name);
7098 plist.add (ARDOUR::Properties::layer, 0);
7099 // TODO set transients_offset
7101 boost::shared_ptr<Region> nr = RegionFactory::create (r->sources(), plist, false);
7102 /* because we set annouce to false, manually add the new region to the
7105 RegionFactory::map_add (nr);
7107 pl->add_region (nr, rstart + pos);
7110 new_regions.push_front(nr);
7119 RegionFactory::region_name (new_name, r->name());
7121 /* Add the final region */
7124 plist.add (ARDOUR::Properties::start, r->start() + pos);
7125 plist.add (ARDOUR::Properties::length, r->last_sample() - (r->position() + pos) + 1);
7126 plist.add (ARDOUR::Properties::name, new_name);
7127 plist.add (ARDOUR::Properties::layer, 0);
7129 boost::shared_ptr<Region> nr = RegionFactory::create (r->sources(), plist, false);
7130 /* because we set annouce to false, manually add the new region to the
7133 RegionFactory::map_add (nr);
7134 pl->add_region (nr, r->position() + pos);
7137 new_regions.push_front(nr);
7142 /* We might have removed regions, which alters other regions' layering_index,
7143 so we need to do a recursive diff here.
7145 vector<Command*> cmds;
7147 _session->add_commands (cmds);
7149 _session->add_command (new StatefulDiffCommand (pl));
7153 for (list<boost::shared_ptr<Region> >::iterator i = new_regions.begin(); i != new_regions.end(); ++i){
7154 set_selected_regionview_from_region_list ((*i), Selection::Add);
7160 Editor::place_transient()
7166 RegionSelection rs = get_regions_from_selection_and_edit_point ();
7172 samplepos_t where = get_preferred_edit_position();
7174 begin_reversible_command (_("place transient"));
7176 for (RegionSelection::iterator r = rs.begin(); r != rs.end(); ++r) {
7177 (*r)->region()->add_transient(where);
7180 commit_reversible_command ();
7184 Editor::remove_transient(ArdourCanvas::Item* item)
7190 ArdourCanvas::Line* _line = reinterpret_cast<ArdourCanvas::Line*> (item);
7193 AudioRegionView* _arv = reinterpret_cast<AudioRegionView*> (item->get_data ("regionview"));
7194 _arv->remove_transient (*(float*) _line->get_data ("position"));
7198 Editor::snap_regions_to_grid ()
7200 list <boost::shared_ptr<Playlist > > used_playlists;
7202 RegionSelection rs = get_regions_from_selection_and_entered ();
7204 if (!_session || rs.empty()) {
7208 begin_reversible_command (_("snap regions to grid"));
7210 for (RegionSelection::iterator r = rs.begin(); r != rs.end(); ++r) {
7212 boost::shared_ptr<Playlist> pl = (*r)->region()->playlist();
7214 if (!pl->frozen()) {
7215 /* we haven't seen this playlist before */
7217 /* remember used playlists so we can thaw them later */
7218 used_playlists.push_back(pl);
7221 (*r)->region()->clear_changes ();
7223 MusicSample start ((*r)->region()->first_sample (), 0);
7224 snap_to (start, RoundNearest, SnapToGrid_Unscaled, true);
7225 (*r)->region()->set_position (start.sample, start.division);
7226 _session->add_command(new StatefulDiffCommand ((*r)->region()));
7229 while (used_playlists.size() > 0) {
7230 list <boost::shared_ptr<Playlist > >::iterator i = used_playlists.begin();
7232 used_playlists.pop_front();
7235 commit_reversible_command ();
7239 Editor::close_region_gaps ()
7241 list <boost::shared_ptr<Playlist > > used_playlists;
7243 RegionSelection rs = get_regions_from_selection_and_entered ();
7245 if (!_session || rs.empty()) {
7249 Dialog dialog (_("Close Region Gaps"));
7252 table.set_spacings (12);
7253 table.set_border_width (12);
7254 Label* l = manage (left_aligned_label (_("Crossfade length")));
7255 table.attach (*l, 0, 1, 0, 1);
7257 SpinButton spin_crossfade (1, 0);
7258 spin_crossfade.set_range (0, 15);
7259 spin_crossfade.set_increments (1, 1);
7260 spin_crossfade.set_value (5);
7261 table.attach (spin_crossfade, 1, 2, 0, 1);
7263 table.attach (*manage (new Label (_("ms"))), 2, 3, 0, 1);
7265 l = manage (left_aligned_label (_("Pull-back length")));
7266 table.attach (*l, 0, 1, 1, 2);
7268 SpinButton spin_pullback (1, 0);
7269 spin_pullback.set_range (0, 100);
7270 spin_pullback.set_increments (1, 1);
7271 spin_pullback.set_value(30);
7272 table.attach (spin_pullback, 1, 2, 1, 2);
7274 table.attach (*manage (new Label (_("ms"))), 2, 3, 1, 2);
7276 dialog.get_vbox()->pack_start (table);
7277 dialog.add_button (Stock::CANCEL, RESPONSE_CANCEL);
7278 dialog.add_button (_("Ok"), RESPONSE_ACCEPT);
7281 if (dialog.run () == RESPONSE_CANCEL) {
7285 samplepos_t crossfade_len = spin_crossfade.get_value();
7286 samplepos_t pull_back_samples = spin_pullback.get_value();
7288 crossfade_len = lrintf (crossfade_len * _session->sample_rate()/1000);
7289 pull_back_samples = lrintf (pull_back_samples * _session->sample_rate()/1000);
7291 /* Iterate over the region list and make adjacent regions overlap by crossfade_len_ms */
7293 begin_reversible_command (_("close region gaps"));
7296 boost::shared_ptr<Region> last_region;
7298 rs.sort_by_position_and_track();
7300 for (RegionSelection::iterator r = rs.begin(); r != rs.end(); ++r) {
7302 boost::shared_ptr<Playlist> pl = (*r)->region()->playlist();
7304 if (!pl->frozen()) {
7305 /* we haven't seen this playlist before */
7307 /* remember used playlists so we can thaw them later */
7308 used_playlists.push_back(pl);
7312 samplepos_t position = (*r)->region()->position();
7314 if (idx == 0 || position < last_region->position()){
7315 last_region = (*r)->region();
7320 (*r)->region()->clear_changes ();
7321 (*r)->region()->trim_front((position - pull_back_samples));
7323 last_region->clear_changes ();
7324 last_region->trim_end ((position - pull_back_samples + crossfade_len));
7326 _session->add_command (new StatefulDiffCommand ((*r)->region()));
7327 _session->add_command (new StatefulDiffCommand (last_region));
7329 last_region = (*r)->region();
7333 while (used_playlists.size() > 0) {
7334 list <boost::shared_ptr<Playlist > >::iterator i = used_playlists.begin();
7336 used_playlists.pop_front();
7339 commit_reversible_command ();
7343 Editor::tab_to_transient (bool forward)
7345 AnalysisFeatureList positions;
7347 RegionSelection rs = get_regions_from_selection_and_entered ();
7353 samplepos_t pos = _session->audible_sample ();
7355 if (!selection->tracks.empty()) {
7357 /* don't waste time searching for transients in duplicate playlists.
7360 TrackViewList ts = selection->tracks.filter_to_unique_playlists ();
7362 for (TrackViewList::iterator t = ts.begin(); t != ts.end(); ++t) {
7364 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (*t);
7367 boost::shared_ptr<Track> tr = rtv->track();
7369 boost::shared_ptr<Playlist> pl = tr->playlist ();
7371 samplepos_t result = pl->find_next_transient (pos, forward ? 1 : -1);
7374 positions.push_back (result);
7387 for (RegionSelection::iterator r = rs.begin(); r != rs.end(); ++r) {
7388 (*r)->region()->get_transients (positions);
7392 TransientDetector::cleanup_transients (positions, _session->sample_rate(), 3.0);
7395 AnalysisFeatureList::iterator x;
7397 for (x = positions.begin(); x != positions.end(); ++x) {
7403 if (x != positions.end ()) {
7404 _session->request_locate (*x);
7408 AnalysisFeatureList::reverse_iterator x;
7410 for (x = positions.rbegin(); x != positions.rend(); ++x) {
7416 if (x != positions.rend ()) {
7417 _session->request_locate (*x);
7423 Editor::playhead_forward_to_grid ()
7429 MusicSample pos (playhead_cursor->current_sample (), 0);
7431 if ( _grid_type == GridTypeNone) {
7432 if (pos.sample < max_samplepos - current_page_samples()*0.1) {
7433 pos.sample += current_page_samples()*0.1;
7434 _session->request_locate (pos.sample);
7436 _session->request_locate (0);
7440 if (pos.sample < max_samplepos - 1) {
7442 pos = snap_to_grid (pos, RoundUpAlways, SnapToGrid_Scaled);
7443 _session->request_locate (pos.sample);
7448 /* keep PH visible in window */
7449 if (pos.sample > (_leftmost_sample + current_page_samples() *0.9)) {
7450 reset_x_origin (pos.sample - (current_page_samples()*0.9));
7456 Editor::playhead_backward_to_grid ()
7462 MusicSample pos (playhead_cursor->current_sample (), 0);
7464 if ( _grid_type == GridTypeNone) {
7465 if ( pos.sample > current_page_samples()*0.1 ) {
7466 pos.sample -= current_page_samples()*0.1;
7467 _session->request_locate (pos.sample);
7469 _session->request_locate (0);
7473 if (pos.sample > 2) {
7475 pos = snap_to_grid (pos, RoundDownAlways, SnapToGrid_Scaled);
7478 //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...
7479 //also see: jump_backward_to_mark
7480 if (_session->transport_rolling()) {
7481 if ((playhead_cursor->current_sample() - pos.sample) < _session->sample_rate()/2) {
7482 pos = snap_to_grid (pos, RoundDownAlways, SnapToGrid_Scaled);
7486 _session->request_locate (pos.sample, _session->transport_rolling());
7489 /* keep PH visible in window */
7490 if (pos.sample < (_leftmost_sample + current_page_samples() *0.1)) {
7491 reset_x_origin (pos.sample - (current_page_samples()*0.1));
7496 Editor::set_track_height (Height h)
7498 TrackSelection& ts (selection->tracks);
7500 for (TrackSelection::iterator x = ts.begin(); x != ts.end(); ++x) {
7501 (*x)->set_height_enum (h);
7506 Editor::toggle_tracks_active ()
7508 TrackSelection& ts (selection->tracks);
7510 bool target = false;
7516 for (TrackSelection::iterator x = ts.begin(); x != ts.end(); ++x) {
7517 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*>(*x);
7521 target = !rtv->_route->active();
7524 rtv->_route->set_active (target, this);
7530 Editor::remove_tracks ()
7532 /* this will delete GUI objects that may be the subject of an event
7533 handler in which this method is called. Defer actual deletion to the
7534 next idle callback, when all event handling is finished.
7536 Glib::signal_idle().connect (sigc::mem_fun (*this, &Editor::idle_remove_tracks));
7540 Editor::idle_remove_tracks ()
7542 Session::StateProtector sp (_session);
7544 return false; /* do not call again */
7548 Editor::_remove_tracks ()
7550 TrackSelection& ts (selection->tracks);
7556 if (!ARDOUR_UI_UTILS::engine_is_running ()) {
7560 vector<string> choices;
7565 const char* trackstr;
7568 vector<boost::shared_ptr<Route> > routes;
7569 vector<boost::shared_ptr<VCA> > vcas;
7570 bool special_bus = false;
7572 for (TrackSelection::iterator x = ts.begin(); x != ts.end(); ++x) {
7573 VCATimeAxisView* vtv = dynamic_cast<VCATimeAxisView*> (*x);
7575 vcas.push_back (vtv->vca());
7579 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (*x);
7583 if (rtv->is_track()) {
7588 routes.push_back (rtv->_route);
7590 if (rtv->route()->is_master() || rtv->route()->is_monitor()) {
7595 if (special_bus && !Config->get_allow_special_bus_removal()) {
7596 MessageDialog msg (_("That would be bad news ...."),
7600 msg.set_secondary_text (string_compose (_(
7601 "Removing the master or monitor bus is such a bad idea\n\
7602 that %1 is not going to allow it.\n\
7604 If you really want to do this sort of thing\n\
7605 edit your ardour.rc file to set the\n\
7606 \"allow-special-bus-removal\" option to be \"yes\""), PROGRAM_NAME));
7613 if (ntracks + nbusses + nvcas == 0) {
7619 trackstr = P_("track", "tracks", ntracks);
7620 busstr = P_("bus", "busses", nbusses);
7621 vcastr = P_("VCA", "VCAs", nvcas);
7623 if (ntracks > 0 && nbusses > 0 && nvcas > 0) {
7624 title = _("Remove various strips");
7625 prompt = string_compose (_("Do you really want to remove %1 %2, %3 %4 and %5 %6?"),
7626 ntracks, trackstr, nbusses, busstr, nvcas, vcastr);
7628 else if (ntracks > 0 && nbusses > 0) {
7629 title = string_compose (_("Remove %1 and %2"), trackstr, busstr);
7630 prompt = string_compose (_("Do you really want to remove %1 %2 and %3 %4?"),
7631 ntracks, trackstr, nbusses, busstr);
7633 else if (ntracks > 0 && nvcas > 0) {
7634 title = string_compose (_("Remove %1 and %2"), trackstr, vcastr);
7635 prompt = string_compose (_("Do you really want to remove %1 %2 and %3 %4?"),
7636 ntracks, trackstr, nvcas, vcastr);
7638 else if (nbusses > 0 && nvcas > 0) {
7639 title = string_compose (_("Remove %1 and %2"), busstr, vcastr);
7640 prompt = string_compose (_("Do you really want to remove %1 %2 and %3 %4?"),
7641 nbusses, busstr, nvcas, vcastr);
7643 else if (ntracks > 0) {
7644 title = string_compose (_("Remove %1"), trackstr);
7645 prompt = string_compose (_("Do you really want to remove %1 %2?"),
7648 else if (nbusses > 0) {
7649 title = string_compose (_("Remove %1"), busstr);
7650 prompt = string_compose (_("Do you really want to remove %1 %2?"),
7653 else if (nvcas > 0) {
7654 title = string_compose (_("Remove %1"), vcastr);
7655 prompt = string_compose (_("Do you really want to remove %1 %2?"),
7663 prompt += "\n" + string_compose ("(You may also lose the playlists associated with the %1)", trackstr) + "\n";
7666 prompt += "\n" + string(_("This action cannot be undone, and the session file will be overwritten!"));
7668 choices.push_back (_("No, do nothing."));
7669 if (ntracks + nbusses + nvcas > 1) {
7670 choices.push_back (_("Yes, remove them."));
7672 choices.push_back (_("Yes, remove it."));
7675 Choice prompter (title, prompt, choices);
7677 if (prompter.run () != 1) {
7681 if (current_mixer_strip && routes.size () > 1 && std::find (routes.begin(), routes.end(), current_mixer_strip->route()) != routes.end ()) {
7682 /* Route deletion calls Editor::timeaxisview_deleted() iteratively (for each deleted
7683 * route). If the deleted route is currently displayed in the Editor-Mixer (highly
7684 * likely because deletion requires selection) this will call
7685 * Editor::set_selected_mixer_strip () which is expensive (MixerStrip::set_route()).
7686 * It's likewise likely that the route that has just been displayed in the
7687 * Editor-Mixer will be next in line for deletion.
7689 * So simply switch to the master-bus (if present)
7691 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
7692 if ((*i)->stripable ()->is_master ()) {
7693 set_selected_mixer_strip (*(*i));
7700 PresentationInfo::ChangeSuspender cs;
7701 DisplaySuspender ds;
7703 boost::shared_ptr<RouteList> rl (new RouteList);
7704 for (vector<boost::shared_ptr<Route> >::iterator x = routes.begin(); x != routes.end(); ++x) {
7707 _session->remove_routes (rl);
7709 for (vector<boost::shared_ptr<VCA> >::iterator x = vcas.begin(); x != vcas.end(); ++x) {
7710 _session->vca_manager().remove_vca (*x);
7714 /* TrackSelection and RouteList leave scope,
7715 * destructors are called,
7716 * diskstream drops references, save_state is called (again for every track)
7721 Editor::do_insert_time ()
7723 if (selection->tracks.empty()) {
7724 MessageDialog msg (_("You must first select some tracks to Insert Time."),
7725 true, MESSAGE_INFO, BUTTONS_OK, true);
7726 msg.set_position (WIN_POS_MOUSE);
7731 if (Config->get_edit_mode() == Lock) {
7732 MessageDialog msg (_("You cannot insert time in Lock Edit mode."),
7733 true, MESSAGE_INFO, BUTTONS_OK, true);
7734 msg.set_position (WIN_POS_MOUSE);
7739 InsertRemoveTimeDialog d (*this);
7740 int response = d.run ();
7742 if (response != RESPONSE_OK) {
7746 if (d.distance() == 0) {
7753 d.intersected_region_action (),
7757 d.move_glued_markers(),
7758 d.move_locked_markers(),
7764 Editor::insert_time (
7765 samplepos_t pos, samplecnt_t samples, InsertTimeOption opt,
7766 bool all_playlists, bool ignore_music_glue, bool markers_too, bool glued_markers_too, bool locked_markers_too, bool tempo_too
7770 if (Config->get_edit_mode() == Lock) {
7773 bool in_command = false;
7775 TrackViewList ts = selection->tracks.filter_to_unique_playlists ();
7777 for (TrackViewList::iterator x = ts.begin(); x != ts.end(); ++x) {
7781 /* don't operate on any playlist more than once, which could
7782 * happen if "all playlists" is enabled, but there is more
7783 * than 1 track using playlists "from" a given track.
7786 set<boost::shared_ptr<Playlist> > pl;
7788 if (all_playlists) {
7789 RouteTimeAxisView* rtav = dynamic_cast<RouteTimeAxisView*> (*x);
7790 if (rtav && rtav->track ()) {
7791 vector<boost::shared_ptr<Playlist> > all = _session->playlists()->playlists_for_track (rtav->track ());
7792 for (vector<boost::shared_ptr<Playlist> >::iterator p = all.begin(); p != all.end(); ++p) {
7797 if ((*x)->playlist ()) {
7798 pl.insert ((*x)->playlist ());
7802 for (set<boost::shared_ptr<Playlist> >::iterator i = pl.begin(); i != pl.end(); ++i) {
7804 (*i)->clear_changes ();
7805 (*i)->clear_owned_changes ();
7808 begin_reversible_command (_("insert time"));
7812 if (opt == SplitIntersected) {
7813 /* non musical split */
7814 (*i)->split (MusicSample (pos, 0));
7817 (*i)->shift (pos, samples, (opt == MoveIntersected), ignore_music_glue);
7819 vector<Command*> cmds;
7821 _session->add_commands (cmds);
7823 _session->add_command (new StatefulDiffCommand (*i));
7827 RouteTimeAxisView* rtav = dynamic_cast<RouteTimeAxisView*> (*x);
7830 begin_reversible_command (_("insert time"));
7833 rtav->route ()->shift (pos, samples);
7840 const int32_t divisions = get_grid_music_divisions (0);
7841 XMLNode& before (_session->locations()->get_state());
7842 Locations::LocationList copy (_session->locations()->list());
7844 for (Locations::LocationList::iterator i = copy.begin(); i != copy.end(); ++i) {
7846 Locations::LocationList::const_iterator tmp;
7848 if ((*i)->position_lock_style() == AudioTime || glued_markers_too) {
7849 bool const was_locked = (*i)->locked ();
7850 if (locked_markers_too) {
7854 if ((*i)->start() >= pos) {
7855 // move end first, in case we're moving by more than the length of the range
7856 if (!(*i)->is_mark()) {
7857 (*i)->set_end ((*i)->end() + samples, false, true, divisions);
7859 (*i)->set_start ((*i)->start() + samples, false, true, divisions);
7871 begin_reversible_command (_("insert time"));
7874 XMLNode& after (_session->locations()->get_state());
7875 _session->add_command (new MementoCommand<Locations>(*_session->locations(), &before, &after));
7881 begin_reversible_command (_("insert time"));
7884 XMLNode& before (_session->tempo_map().get_state());
7885 _session->tempo_map().insert_time (pos, samples);
7886 XMLNode& after (_session->tempo_map().get_state());
7887 _session->add_command (new MementoCommand<TempoMap>(_session->tempo_map(), &before, &after));
7891 commit_reversible_command ();
7896 Editor::do_remove_time ()
7898 if (selection->tracks.empty()) {
7899 MessageDialog msg (_("You must first select some tracks to Remove Time."),
7900 true, MESSAGE_INFO, BUTTONS_OK, true);
7901 msg.set_position (WIN_POS_MOUSE);
7906 if (Config->get_edit_mode() == Lock) {
7907 MessageDialog msg (_("You cannot remove time in Lock Edit mode."),
7908 true, MESSAGE_INFO, BUTTONS_OK, true);
7909 msg.set_position (WIN_POS_MOUSE);
7914 InsertRemoveTimeDialog d (*this, true);
7916 int response = d.run ();
7918 if (response != RESPONSE_OK) {
7922 samplecnt_t distance = d.distance();
7924 if (distance == 0) {
7934 d.move_glued_markers(),
7935 d.move_locked_markers(),
7941 Editor::remove_time (samplepos_t pos, samplecnt_t samples, InsertTimeOption opt,
7942 bool ignore_music_glue, bool markers_too, bool glued_markers_too, bool locked_markers_too, bool tempo_too)
7944 if (Config->get_edit_mode() == Lock) {
7945 error << (_("Cannot insert or delete time when in Lock edit.")) << endmsg;
7948 bool in_command = false;
7950 for (TrackSelection::iterator x = selection->tracks.begin(); x != selection->tracks.end(); ++x) {
7952 boost::shared_ptr<Playlist> pl = (*x)->playlist();
7956 XMLNode &before = pl->get_state();
7959 begin_reversible_command (_("remove time"));
7963 std::list<AudioRange> rl;
7964 AudioRange ar(pos, pos+samples, 0);
7967 pl->shift (pos, -samples, true, ignore_music_glue);
7969 XMLNode &after = pl->get_state();
7971 _session->add_command (new MementoCommand<Playlist> (*pl, &before, &after));
7975 RouteTimeAxisView* rtav = dynamic_cast<RouteTimeAxisView*> (*x);
7978 begin_reversible_command (_("remove time"));
7981 rtav->route ()->shift (pos, -samples);
7985 const int32_t divisions = get_grid_music_divisions (0);
7986 std::list<Location*> loc_kill_list;
7991 XMLNode& before (_session->locations()->get_state());
7992 Locations::LocationList copy (_session->locations()->list());
7994 for (Locations::LocationList::iterator i = copy.begin(); i != copy.end(); ++i) {
7995 if ((*i)->position_lock_style() == AudioTime || glued_markers_too) {
7997 bool const was_locked = (*i)->locked ();
7998 if (locked_markers_too) {
8002 if (!(*i)->is_mark()) { // it's a range; have to handle both start and end
8003 if ((*i)->end() >= pos
8004 && (*i)->end() < pos+samples
8005 && (*i)->start() >= pos
8006 && (*i)->end() < pos+samples) { // range is completely enclosed; kill it
8008 loc_kill_list.push_back(*i);
8009 } else { // only start or end is included, try to do the right thing
8010 // move start before moving end, to avoid trying to move the end to before the start
8011 // if we're removing more time than the length of the range
8012 if ((*i)->start() >= pos && (*i)->start() < pos+samples) {
8013 // start is within cut
8014 (*i)->set_start (pos, false, true,divisions); // bring the start marker to the beginning of the cut
8016 } else if ((*i)->start() >= pos+samples) {
8017 // start (and thus entire range) lies beyond end of cut
8018 (*i)->set_start ((*i)->start() - samples, false, true, divisions); // slip the start marker back
8021 if ((*i)->end() >= pos && (*i)->end() < pos+samples) {
8022 // end is inside cut
8023 (*i)->set_end (pos, false, true, divisions); // bring the end to the cut
8025 } else if ((*i)->end() >= pos+samples) {
8026 // end is beyond end of cut
8027 (*i)->set_end ((*i)->end() - samples, false, true, divisions); // slip the end marker back
8032 } else if ((*i)->start() >= pos && (*i)->start() < pos+samples) {
8033 loc_kill_list.push_back(*i);
8035 } else if ((*i)->start() >= pos) {
8036 (*i)->set_start ((*i)->start() -samples, false, true, divisions);
8046 for (list<Location*>::iterator i = loc_kill_list.begin(); i != loc_kill_list.end(); ++i) {
8047 _session->locations()->remove (*i);
8052 begin_reversible_command (_("remove time"));
8055 XMLNode& after (_session->locations()->get_state());
8056 _session->add_command (new MementoCommand<Locations>(*_session->locations(), &before, &after));
8061 XMLNode& before (_session->tempo_map().get_state());
8063 if (_session->tempo_map().remove_time (pos, samples)) {
8065 begin_reversible_command (_("remove time"));
8068 XMLNode& after (_session->tempo_map().get_state());
8069 _session->add_command (new MementoCommand<TempoMap>(_session->tempo_map(), &before, &after));
8074 commit_reversible_command ();
8079 Editor::fit_selection ()
8081 if (!selection->tracks.empty()) {
8082 fit_tracks (selection->tracks);
8086 /* no selected tracks - use tracks with selected regions */
8088 if (!selection->regions.empty()) {
8089 for (RegionSelection::iterator r = selection->regions.begin(); r != selection->regions.end(); ++r) {
8090 tvl.push_back (&(*r)->get_time_axis_view ());
8096 } else if (internal_editing()) {
8097 /* no selected tracks, or regions, but in internal edit mode, so follow the mouse and use
8100 if (entered_track) {
8101 tvl.push_back (entered_track);
8109 Editor::fit_tracks (TrackViewList & tracks)
8111 if (tracks.empty()) {
8115 uint32_t child_heights = 0;
8116 int visible_tracks = 0;
8118 for (TrackSelection::iterator t = tracks.begin(); t != tracks.end(); ++t) {
8120 if (!(*t)->marked_for_display()) {
8124 child_heights += (*t)->effective_height() - (*t)->current_height();
8128 /* compute the per-track height from:
8130 * total canvas visible height
8131 * - height that will be taken by visible children of selected tracks
8132 * - height of the ruler/hscroll area
8134 uint32_t h = (uint32_t) floor ((trackviews_height() - child_heights) / visible_tracks);
8135 double first_y_pos = DBL_MAX;
8137 if (h < TimeAxisView::preset_height (HeightSmall)) {
8138 MessageDialog msg (_("There are too many tracks to fit in the current window"));
8139 /* too small to be displayed */
8143 undo_visual_stack.push_back (current_visual_state (true));
8144 PBD::Unwinder<bool> nsv (no_save_visual, true);
8146 /* build a list of all tracks, including children */
8149 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
8151 TimeAxisView::Children c = (*i)->get_child_list ();
8152 for (TimeAxisView::Children::iterator j = c.begin(); j != c.end(); ++j) {
8153 all.push_back (j->get());
8158 // find selection range.
8159 // if someone knows how to user TrackViewList::iterator for this
8161 int selected_top = -1;
8162 int selected_bottom = -1;
8164 for (TrackViewList::iterator t = all.begin(); t != all.end(); ++t, ++i) {
8165 if ((*t)->marked_for_display ()) {
8166 if (tracks.contains(*t)) {
8167 if (selected_top == -1) {
8170 selected_bottom = i;
8176 for (TrackViewList::iterator t = all.begin(); t != all.end(); ++t, ++i) {
8177 if ((*t)->marked_for_display ()) {
8178 if (tracks.contains(*t)) {
8179 (*t)->set_height (h);
8180 first_y_pos = std::min ((*t)->y_position (), first_y_pos);
8182 if (i > selected_top && i < selected_bottom) {
8183 hide_track_in_display (*t);
8190 set the controls_layout height now, because waiting for its size
8191 request signal handler will cause the vertical adjustment setting to fail
8194 controls_layout.property_height () = _full_canvas_height;
8195 vertical_adjustment.set_value (first_y_pos);
8197 redo_visual_stack.push_back (current_visual_state (true));
8199 visible_tracks_selector.set_text (_("Sel"));
8203 Editor::save_visual_state (uint32_t n)
8205 while (visual_states.size() <= n) {
8206 visual_states.push_back (0);
8209 if (visual_states[n] != 0) {
8210 delete visual_states[n];
8213 visual_states[n] = current_visual_state (true);
8218 Editor::goto_visual_state (uint32_t n)
8220 if (visual_states.size() <= n) {
8224 if (visual_states[n] == 0) {
8228 use_visual_state (*visual_states[n]);
8232 Editor::start_visual_state_op (uint32_t n)
8234 save_visual_state (n);
8236 PopUp* pup = new PopUp (WIN_POS_MOUSE, 1000, true);
8238 snprintf (buf, sizeof (buf), _("Saved view %u"), n+1);
8239 pup->set_text (buf);
8244 Editor::cancel_visual_state_op (uint32_t n)
8246 goto_visual_state (n);
8250 Editor::toggle_region_mute ()
8252 if (_ignore_region_action) {
8256 RegionSelection rs = get_regions_from_selection_and_entered ();
8262 if (rs.size() > 1) {
8263 begin_reversible_command (_("mute regions"));
8265 begin_reversible_command (_("mute region"));
8268 for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
8270 (*i)->region()->playlist()->clear_changes ();
8271 (*i)->region()->set_muted (!(*i)->region()->muted ());
8272 _session->add_command (new StatefulDiffCommand ((*i)->region()));
8276 commit_reversible_command ();
8280 Editor::combine_regions ()
8282 /* foreach track with selected regions, take all selected regions
8283 and join them into a new region containing the subregions (as a
8287 typedef set<RouteTimeAxisView*> RTVS;
8290 if (selection->regions.empty()) {
8294 for (RegionSelection::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
8295 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*>(&(*i)->get_time_axis_view());
8298 tracks.insert (rtv);
8302 begin_reversible_command (_("combine regions"));
8304 vector<RegionView*> new_selection;
8306 for (RTVS::iterator i = tracks.begin(); i != tracks.end(); ++i) {
8309 if ((rv = (*i)->combine_regions ()) != 0) {
8310 new_selection.push_back (rv);
8314 selection->clear_regions ();
8315 for (vector<RegionView*>::iterator i = new_selection.begin(); i != new_selection.end(); ++i) {
8316 selection->add (*i);
8319 commit_reversible_command ();
8323 Editor::uncombine_regions ()
8325 typedef set<RouteTimeAxisView*> RTVS;
8328 if (selection->regions.empty()) {
8332 for (RegionSelection::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
8333 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*>(&(*i)->get_time_axis_view());
8336 tracks.insert (rtv);
8340 begin_reversible_command (_("uncombine regions"));
8342 for (RTVS::iterator i = tracks.begin(); i != tracks.end(); ++i) {
8343 (*i)->uncombine_regions ();
8346 commit_reversible_command ();
8350 Editor::toggle_midi_input_active (bool flip_others)
8353 boost::shared_ptr<RouteList> rl (new RouteList);
8355 for (TrackSelection::iterator i = selection->tracks.begin(); i != selection->tracks.end(); ++i) {
8356 RouteTimeAxisView *rtav = dynamic_cast<RouteTimeAxisView *>(*i);
8362 boost::shared_ptr<MidiTrack> mt = rtav->midi_track();
8365 rl->push_back (rtav->route());
8366 onoff = !mt->input_active();
8370 _session->set_exclusive_input_active (rl, onoff, flip_others);
8373 static bool ok_fine (GdkEventAny*) { return true; }
8379 lock_dialog = new Gtk::Dialog (string_compose (_("%1: Locked"), PROGRAM_NAME), true);
8381 Gtk::Image* padlock = manage (new Gtk::Image (ARDOUR_UI_UTILS::get_icon ("padlock_closed")));
8382 lock_dialog->get_vbox()->pack_start (*padlock);
8383 lock_dialog->signal_delete_event ().connect (sigc::ptr_fun (ok_fine));
8385 ArdourButton* b = manage (new ArdourButton);
8386 b->set_name ("lock button");
8387 b->set_text (_("Click to unlock"));
8388 b->signal_clicked.connect (sigc::mem_fun (*this, &Editor::unlock));
8389 lock_dialog->get_vbox()->pack_start (*b);
8391 lock_dialog->get_vbox()->show_all ();
8392 lock_dialog->set_size_request (200, 200);
8395 delete _main_menu_disabler;
8396 _main_menu_disabler = new MainMenuDisabler;
8398 lock_dialog->present ();
8400 lock_dialog->get_window()->set_decorations (Gdk::WMDecoration (0));
8406 lock_dialog->hide ();
8408 delete _main_menu_disabler;
8409 _main_menu_disabler = 0;
8411 if (UIConfiguration::instance().get_lock_gui_after_seconds()) {
8412 start_lock_event_timing ();
8417 Editor::bring_in_callback (Gtk::Label* label, uint32_t n, uint32_t total, string name)
8419 Gtkmm2ext::UI::instance()->call_slot (invalidator (*this), boost::bind (&Editor::update_bring_in_message, this, label, n, total, name));
8423 Editor::update_bring_in_message (Gtk::Label* label, uint32_t n, uint32_t total, string name)
8425 Timers::TimerSuspender t;
8426 label->set_text (string_compose ("Copying %1, %2 of %3", name, n, total));
8427 Gtkmm2ext::UI::instance()->flush_pending (1);
8431 Editor::bring_all_sources_into_session ()
8438 ArdourDialog w (_("Moving embedded files into session folder"));
8439 w.get_vbox()->pack_start (msg);
8442 /* flush all pending GUI events because we're about to start copying
8446 Timers::TimerSuspender t;
8447 Gtkmm2ext::UI::instance()->flush_pending (3);
8451 _session->bring_all_sources_into_session (boost::bind (&Editor::bring_in_callback, this, &msg, _1, _2, _3));