use crate::*; handle!(TuiIn: |self: MidiPool, input|{ //Ok(if let Some(command) = match self.mode() { //Some(PoolMode::Rename(..)) => self.keys_rename, //Some(PoolMode::Length(..)) => self.keys_length, //Some(PoolMode::Import(..)) | Some(PoolMode::Export(..)) => self.keys_file, //_ => self.keys //}.command::(self, input) { Ok(if let Some(command) = self.keys.command(self, input) { let _undo = command.execute(self)?; Some(true) } else { None }) }); provide!(bool: |self: MidiPool| {}); provide!(MidiClip: |self: MidiPool| { ":new-clip" => self.new_clip(), ":cloned-clip" => self.cloned_clip(), }); provide!(PathBuf: |self: MidiPool| {}); provide!(Arc: |self: MidiPool| {}); provide!(usize: |self: MidiPool| { ":current" => 0, ":after" => 0, ":previous" => 0, ":next" => 0 }); provide!(ItemColor: |self: MidiPool| { ":random-color" => ItemColor::random() }); #[derive(Clone, PartialEq, Debug)] pub enum PoolCommand { /// Toggle visibility of pool Show(bool), /// Select a clip from the clip pool Select(usize), /// Rename a clip Rename(ClipRenameCommand), /// Change the length of a clip Length(ClipLengthCommand), /// Import from file Import(FileBrowserCommand), /// Export to file Export(FileBrowserCommand), /// Update the contents of the clip pool Clip(PoolClipCommand), } atom_command!(PoolCommand: |state: MidiPool| { ("show" [a: bool] Some(Self::Show(a.expect("no flag")))) ("select" [i: usize] Some(Self::Select(i.expect("no index")))) ("rename" [,..a] ClipRenameCommand::try_from_expr(state, a).map(Self::Rename)) ("length" [,..a] ClipLengthCommand::try_from_expr(state, a).map(Self::Length)) ("import" [,..a] FileBrowserCommand::try_from_expr(state, a).map(Self::Import)) ("export" [,..a] FileBrowserCommand::try_from_expr(state, a).map(Self::Export)) ("clip" [,..a] PoolClipCommand::try_from_expr(state, a).map(Self::Clip)) }); command!(|self: PoolCommand, state: MidiPool|{ use PoolCommand::*; match self { Rename(ClipRenameCommand::Begin) => { state.begin_clip_rename(); None } Rename(command) => command.delegate(state, Rename)?, Length(ClipLengthCommand::Begin) => { state.begin_clip_length(); None }, Length(command) => command.delegate(state, Length)?, Import(FileBrowserCommand::Begin) => { state.begin_import()?; None }, Import(command) => command.delegate(state, Import)?, Export(FileBrowserCommand::Begin) => { state.begin_export()?; None }, Export(command) => command.delegate(state, Export)?, Clip(command) => command.execute(state)?.map(Clip), Show(visible) => { state.visible = visible; Some(Self::Show(!visible)) }, Select(clip) => { state.set_clip_index(clip); None }, } }); #[derive(Clone, Debug, PartialEq)] pub enum PoolClipCommand { Add(usize, MidiClip), Delete(usize), Swap(usize, usize), Import(usize, PathBuf), Export(usize, PathBuf), SetName(usize, Arc), SetLength(usize, usize), SetColor(usize, ItemColor), } atom_command!(PoolClipCommand: |state: MidiPool| { ("add" [i: usize, c: MidiClip] Some(Self::Add(i.expect("no index"), c.expect("no clip")))) ("delete" [i: usize] Some(Self::Delete(i.expect("no index")))) ("swap" [a: usize, b: usize] Some(Self::Swap(a.expect("no index"), b.expect("no index")))) ("import" [i: usize, p: PathBuf] Some(Self::Import(i.expect("no index"), p.expect("no path")))) ("export" [i: usize, p: PathBuf] Some(Self::Export(i.expect("no index"), p.expect("no path")))) ("set-name" [i: usize, n: Arc] Some(Self::SetName(i.expect("no index"), n.expect("no name")))) ("set-length" [i: usize, l: usize] Some(Self::SetLength(i.expect("no index"), l.expect("no length")))) ("set-color" [i: usize, c: ItemColor] Some(Self::SetColor(i.expect("no index"), c.expect("no color")))) }); impl Command for PoolClipCommand { fn execute (self, model: &mut T) -> Perhaps { use PoolClipCommand::*; Ok(match self { Add(mut index, clip) => { let clip = Arc::new(RwLock::new(clip)); let mut clips = model.clips_mut(); if index >= clips.len() { index = clips.len(); clips.push(clip) } else { clips.insert(index, clip); } Some(Self::Delete(index)) }, Delete(index) => { let clip = model.clips_mut().remove(index).read().unwrap().clone(); Some(Self::Add(index, clip)) }, Swap(index, other) => { model.clips_mut().swap(index, other); Some(Self::Swap(index, other)) }, Import(index, path) => { let bytes = std::fs::read(&path)?; let smf = Smf::parse(bytes.as_slice())?; let mut t = 0u32; let mut events = vec![]; for track in smf.tracks.iter() { for event in track.iter() { t += event.delta.as_int(); if let TrackEventKind::Midi { channel, message } = event.kind { events.push((t, channel.as_int(), message)); } } } let mut clip = MidiClip::new("imported", true, t as usize + 1, None, None); for event in events.iter() { clip.notes[event.0 as usize].push(event.2); } Self::Add(index, clip).execute(model)? }, Export(_index, _path) => { todo!("export clip to midi file"); }, SetName(index, name) => { let clip = &mut model.clips_mut()[index]; let old_name = clip.read().unwrap().name.clone(); clip.write().unwrap().name = name; Some(Self::SetName(index, old_name)) }, SetLength(index, length) => { let clip = &mut model.clips_mut()[index]; let old_len = clip.read().unwrap().length; clip.write().unwrap().length = length; Some(Self::SetLength(index, old_len)) }, SetColor(index, color) => { let mut color = ItemTheme::from(color); std::mem::swap(&mut color, &mut model.clips()[index].write().unwrap().color); Some(Self::SetColor(index, color.base)) }, }) } }