From 9ef16e1e6ceed0846b349c636ce0d8a1661234e6 Mon Sep 17 00:00:00 2001 From: Vikram Bhaskaran Date: Sat, 10 Oct 2026 16:21:47 +1100 Subject: [PATCH] Put Cut All Automation on the clipboard. The command cleared every automation lane in the selection and left the clipboard empty, so the cut could not be pasted. It now uses the same copy-and-clear path as Cut Pan Automation. --- crates/engine/src/cmd/edit.rs | 4 +++- crates/engine/src/cmd/edit_more.rs | 19 +++++++++++++++++++ 2 files changed, 22 insertions(+), 1 deletion(-) diff --git a/crates/engine/src/cmd/edit.rs b/crates/engine/src/cmd/edit.rs index 7e4f2d5..39add57 100644 --- a/crates/engine/src/cmd/edit.rs +++ b/crates/engine/src/cmd/edit.rs @@ -25,7 +25,9 @@ pub fn specs() -> Vec { p, "edit.clear_clip_gain" )), - cmd!("edit.cut_all_automation", "All Automation", ["Edit", "Cut Special"], None, "{}", has_range, |e, p| clear_automation(e, p, None)), + cmd!("edit.cut_all_automation", "All Automation", ["Edit", "Cut Special"], None, "{tracks?, start?, end?}", has_range, |e, p| { + super::edit_more::cut_all_automation(e, p) + }), cmd!("edit.clear_all_automation", "All Automation", ["Edit", "Clear Special"], None, "{}", has_range, |e, p| clear_automation(e, p, None)), cmd!("edit.clear_pan_automation", "Pan Automation", ["Edit", "Clear Special"], None, "{}", has_range, |e, p| clear_automation( e, diff --git a/crates/engine/src/cmd/edit_more.rs b/crates/engine/src/cmd/edit_more.rs index d27bb97..286d1d9 100644 --- a/crates/engine/src/cmd/edit_more.rs +++ b/crates/engine/src/cmd/edit_more.rs @@ -277,6 +277,10 @@ fn nonempty_range(e: &Engine, p: &Value, id: &str) -> Result { if r.is_empty() { Err(bad(id, "the selection is empty")) } else { Ok(r) } } +pub(super) fn cut_all_automation(e: &mut Engine, p: &Value) -> Result { + auto_copy(e, p, Lanes::All, true, "edit.cut_all_automation") +} + fn auto_copy(e: &mut Engine, p: &Value, which: Lanes, cut: bool, id: &str) -> Result { let tracks = tracks_required(e, id, p)?; let r = nonempty_range(e, p, id)?; @@ -982,6 +986,21 @@ mod tests { assert_eq!(starts(&e, t), vec![0, 900, 1000]); } + #[test] + fn cut_all_automation_can_be_pasted() { + let (mut e, t, _) = session_with_clips(&[(0, 1000)]); + e.execute("automation.set_point", &json!({"track": t.0, "param": "volume", "at": 100, "value": -6.0})).unwrap(); + e.execute("automation.set_point", &json!({"track": t.0, "param": "volume", "at": 400, "value": -12.0})).unwrap(); + let cut = e.execute("edit.cut_all_automation", &json!({"start": 50, "end": 250})).unwrap(); + assert!(cut["lanes"].as_u64().unwrap() >= 1, "{cut}"); + assert!(e.clipboard.automation.iter().any(|lanes| !lanes.is_empty())); + let lane = e.session().track(t).unwrap().lane(&AutoParam::Volume).unwrap().clone(); + assert!(lane.points.iter().all(|p| p.at != 100), "the cut point is gone: {:?}", lane.points); + e.execute("edit.paste_to_current_automation", &json!({"at": 1000})).unwrap(); + let vol = e.session().track(t).unwrap().lane(&AutoParam::Volume).unwrap().clone(); + assert!(vol.points.iter().any(|p| p.at >= 1000 && (p.value + 6.0).abs() < 1e-2), "paste wrote nothing at -6 dB: {:?}", vol.points); + } + #[test] fn copy_and_paste_automation_only() { let (mut e, t, _) = session_with_clips(&[(0, 1000)]);