use crate::model::{Bank, Pattern, Project}; use crate::state::{CopiedStepData, CopiedSteps}; fn annotate_copy_name(name: &Option) -> Option { match name { Some(n) if !n.ends_with(" (copy)") => Some(format!("{n} (copy)")), Some(n) => Some(n.clone()), None => Some("(copy)".to_string()), } } pub fn shift_patterns_up( project: &mut Project, bank: usize, range: std::ops::RangeInclusive, ) -> Option> { if *range.start() == 0 { return None; } let slice_start = range.start() - 1; let slice_end = *range.end(); project.banks[bank].patterns[slice_start..=slice_end].rotate_left(1); Some((slice_start..=slice_end).map(|p| (bank, p)).collect()) } pub fn shift_patterns_down( project: &mut Project, bank: usize, range: std::ops::RangeInclusive, ) -> Option> { if *range.end() >= crate::model::MAX_PATTERNS - 1 { return None; } let slice_start = *range.start(); let slice_end = range.end() + 1; project.banks[bank].patterns[slice_start..=slice_end].rotate_right(1); Some((slice_start..=slice_end).map(|p| (bank, p)).collect()) } pub fn copy_pattern(project: &Project, bank: usize, pattern: usize) -> Pattern { project.banks[bank].patterns[pattern].clone() } pub fn paste_pattern( project: &mut Project, bank: usize, pattern: usize, source: &Pattern, ) { let mut pat = source.clone(); pat.name = annotate_copy_name(&source.name); project.banks[bank].patterns[pattern] = pat; } pub fn copy_patterns(project: &Project, bank: usize, indices: &[usize]) -> Vec { indices .iter() .map(|&i| project.banks[bank].patterns[i].clone()) .collect() } pub fn paste_patterns( project: &mut Project, bank: usize, start: usize, sources: &[Pattern], ) -> usize { let mut count = 0; for (i, src) in sources.iter().enumerate() { let target = start + i; if target >= crate::model::MAX_PATTERNS { break; } let mut pat = src.clone(); pat.name = annotate_copy_name(&src.name); project.banks[bank].patterns[target] = pat; count += 1; } count } pub fn copy_bank(project: &Project, bank: usize) -> Bank { project.banks[bank].clone() } pub fn paste_bank(project: &mut Project, bank: usize, source: &Bank) -> usize { let mut b = source.clone(); b.name = annotate_copy_name(&source.name); project.banks[bank] = b; project.banks[bank].patterns.len() } pub fn copy_banks(project: &Project, indices: &[usize]) -> Vec { indices .iter() .map(|&i| project.banks[i].clone()) .collect() } pub fn paste_banks(project: &mut Project, start: usize, sources: &[Bank]) -> usize { let mut count = 0; for (i, src) in sources.iter().enumerate() { let target = start + i; if target >= crate::model::MAX_BANKS { break; } let mut b = src.clone(); b.name = annotate_copy_name(&src.name); project.banks[target] = b; count += 1; } count } pub fn copy_steps( project: &Project, bank: usize, pattern: usize, indices: &[usize], ) -> (CopiedSteps, Vec) { let pat = project.pattern_at(bank, pattern); let mut steps = Vec::new(); let mut scripts = Vec::new(); for &idx in indices { if let Some(step) = pat.step(idx) { let resolved = pat.resolve_script(idx).unwrap_or("").to_string(); scripts.push(resolved.clone()); steps.push(CopiedStepData { script: resolved, active: step.active, source: step.source, original_index: idx, name: step.name.clone(), }); } } let copied = CopiedSteps { bank, pattern, steps, }; (copied, scripts) } pub struct PasteResult { pub count: usize, pub compile_targets: Vec, } pub fn paste_steps( project: &mut Project, bank: usize, pattern: usize, cursor: usize, copied: &CopiedSteps, ) -> PasteResult { let pat_len = project.pattern_at(bank, pattern).length; let same_pattern = copied.bank == bank && copied.pattern == pattern; let mut compile_targets = Vec::new(); for (i, data) in copied.steps.iter().enumerate() { let target = cursor + i; if target >= pat_len { break; } if let Some(step) = project.pattern_at_mut(bank, pattern).step_mut(target) { let source = if same_pattern { data.source } else { None }; step.active = data.active; step.source = source; step.name = data.name.clone(); if source.is_some() { step.script.clear(); } else { step.script = data.script.clone(); } } compile_targets.push(target); } PasteResult { count: copied.steps.len(), compile_targets, } } pub fn link_paste_steps( project: &mut Project, bank: usize, pattern: usize, cursor: usize, copied: &CopiedSteps, ) -> Option { if copied.bank != bank || copied.pattern != pattern { return None; } let pat_len = project.pattern_at(bank, pattern).length; for (i, data) in copied.steps.iter().enumerate() { let target = cursor + i; if target >= pat_len { break; } let source_idx = if data.source.is_some() { data.source } else { Some(data.original_index as u8) }; if source_idx == Some(target as u8) { continue; } if let Some(step) = project.pattern_at_mut(bank, pattern).step_mut(target) { step.source = source_idx; step.script.clear(); } } Some(copied.steps.len()) } pub fn harden_steps( project: &mut Project, bank: usize, pattern: usize, indices: &[usize], ) -> usize { let pat = project.pattern_at(bank, pattern); let resolutions: Vec<(usize, String)> = indices .iter() .filter_map(|&idx| { let step = pat.step(idx)?; step.source?; let script = pat.resolve_script(idx)?.to_string(); Some((idx, script)) }) .collect(); let count = resolutions.len(); for (idx, script) in resolutions { if let Some(s) = project.pattern_at_mut(bank, pattern).step_mut(idx) { s.source = None; s.script = script; } } count } pub fn duplicate_steps( project: &mut Project, bank: usize, pattern: usize, indices: &[usize], ) -> PasteResult { let pat = project.pattern_at(bank, pattern); let pat_len = pat.length; let paste_at = *indices.last().unwrap() + 1; let dupe_data: Vec<(bool, String, Option)> = indices .iter() .filter_map(|&idx| { let step = pat.step(idx)?; let script = pat.resolve_script(idx).unwrap_or("").to_string(); let source = step.source; Some((step.active, script, source)) }) .collect(); let mut compile_targets = Vec::new(); for (i, (active, script, source)) in dupe_data.into_iter().enumerate() { let target = paste_at + i; if target >= pat_len { break; } if let Some(step) = project.pattern_at_mut(bank, pattern).step_mut(target) { step.active = active; step.source = source; if source.is_some() { step.script.clear(); } else { step.script = script; } } compile_targets.push(target); } PasteResult { count: indices.len(), compile_targets, } }