From ea9d6d4d92a620a52efec96cd3310ecd690e7c40 Mon Sep 17 00:00:00 2001 From: rabite Date: Thu, 7 Feb 2019 16:32:27 +0100 Subject: multi-file selection --- src/files.rs | 19 ++++++++++++++---- src/listview.rs | 60 ++++++++++++++++++++++++++++++++++++++++++++++----------- src/preview.rs | 1 - src/window.rs | 3 ++- 4 files changed, 66 insertions(+), 17 deletions(-) diff --git a/src/files.rs b/src/files.rs index 728fb71..6cb3611 100644 --- a/src/files.rs +++ b/src/files.rs @@ -170,6 +170,10 @@ impl Files { pub fn len(&self) -> usize { self.files.len() } + + pub fn get_selected(&self) -> Vec<&File> { + self.files.iter().filter(|f| f.is_selected()).collect() + } } #[derive(Debug, Copy, Clone, PartialEq, Eq)] @@ -210,6 +214,7 @@ pub struct File { pub mode: u32, pub user: u32, pub group: u32, + pub selected: bool // flags: Option, } @@ -234,10 +239,8 @@ impl File { color: color, mode: mode, user: user, - group: group - // owner: None, - // group: None, - // flags: None, + group: group, + selected: false } } @@ -320,6 +323,14 @@ impl File { self.path.clone() } + pub fn toggle_selection(&mut self) { + self.selected = !self.selected + } + + pub fn is_selected(&self) -> bool { + self.selected + } + pub fn pretty_print_permissions(&self) -> String { let perms: usize = format!("{:o}", self.mode).parse().unwrap(); let perms: usize = perms % 800; diff --git a/src/listview.rs b/src/listview.rs index 4fbcc8e..5b102f3 100644 --- a/src/listview.rs +++ b/src/listview.rs @@ -20,8 +20,6 @@ where selection: usize, offset: usize, buffer: Vec, - // dimensions: (u16, u16), - // position: (u16, u16), coordinates: Coordinates, seeking: bool, } @@ -94,6 +92,12 @@ where let name = &file.name; let (size, unit) = file.calculate_size(); + let selection_gap = " ".to_string(); + let (name, selection_color) = if file.is_selected() { + (selection_gap + name, crate::term::color_yellow()) + } else { (name.clone(), "".to_string()) }; + + let xsize = self.get_size().xsize(); let sized_string = term::sized_string(&name, xsize); let size_pos = xsize - (size.to_string().len() as u16 @@ -105,13 +109,17 @@ where "{}{}{}{}{}{}{}", termion::cursor::Save, match &file.color { - Some(color) => format!("{}{:padding$}", + Some(color) => format!("{}{}{:padding$}{}", term::from_lscolor(color), + selection_color, &sized_string, + term::normal_color(), padding = padding as usize), - _ => format!("{}{:padding$}", + _ => format!("{}{}{:padding$}{}", term::normal_color(), + selection_color, &sized_string, + term::normal_color(), padding = padding as usize), } , termion::cursor::Restore, @@ -135,6 +143,12 @@ where file } + pub fn selected_file_mut(&mut self) -> &mut File { + let selection = self.selection; + let file = &mut self.content.files[selection]; + file + } + pub fn clone_selected_file(&self) -> File { let selection = self.selection; let file = self.content[selection].clone(); @@ -247,9 +261,9 @@ where } let file = self.clone_selected_file(); - // self.content.dirs_first = dir_settings; - // self.content.sort = sort_settings; - // self.content.sort(); + self.content.dirs_first = dir_settings; + self.content.sort = sort_settings; + self.content.sort(); self.select_file(&file); self.seeking = true; @@ -273,21 +287,44 @@ where self.show_status(&format!("Direcories first: {}", self.content.dirs_first)); } + fn multi_select_file(&mut self) { + let file = self.selected_file_mut(); + file.toggle_selection(); + self.move_down(); + self.refresh(); + } + fn exec_cmd(&mut self) { - match self.minibuffer("exec ($s for selected files)") { + let selected_files = self.content.get_selected(); + let file_names + = selected_files.iter().map(|f| f.name.clone()).collect::>(); + + match self.minibuffer("exec ($s for selected file(s))") { Some(cmd) => { self.show_status(&format!("Running: \"{}\"", &cmd)); let filename = self.selected_file().name.clone(); - let cmd = cmd.replace("$s", &format!("{}", &filename)); + + let cmd = if file_names.len() == 0 { + cmd.replace("$s", &format!("{}", &filename)) + } else { + let args = file_names.iter().map(|f| { + format!(" \"{}\" ", f) + }).collect::(); + let clean_cmd = cmd.replace("$s", ""); + + clean_cmd + &args + }; let status = std::process::Command::new("sh") .arg("-c") .arg(&cmd) .status(); match status { - Ok(status) => self.show_status(&format!("\"{}\" exited with {}", cmd, status)), - Err(err) => self.show_status(&format!("Can't run this \"{}\": {}", cmd, err)), + Ok(status) => self.show_status(&format!("\"{}\" exited with {}", + cmd, status)), + Err(err) => self.show_status(&format!("Can't run this \"{}\": {}", + cmd, err)), } } None => self.show_status(""), @@ -380,6 +417,7 @@ impl Widget for ListView { } Key::Left => self.goto_grand_parent(), Key::Right => self.goto_selected(), + Key::Char(' ') => self.multi_select_file(), Key::Char('h') => self.toggle_hidden(), Key::Char('r') => self.reverse_sort(), Key::Char('s') => self.cycle_sort(), diff --git a/src/preview.rs b/src/preview.rs index 59fa675..15638aa 100644 --- a/src/preview.rs +++ b/src/preview.rs @@ -17,7 +17,6 @@ lazy_static! { fn kill_procs() { let mut pids = PIDS.lock().unwrap(); for pid in &*pids { - let msg = format!("KILLING PROC: {}", pid); unsafe { libc::kill(*pid, 9); } } pids.clear(); diff --git a/src/window.rs b/src/window.rs index 6033c71..2903662 100644 --- a/src/window.rs +++ b/src/window.rs @@ -148,11 +148,12 @@ pub fn minibuffer(query: &str) -> Option { return Some(buffer); } } + Key::Char('\t') => buffer += "$s", Key::Backspace => { buffer.pop(); } Key::Char(key) => { - buffer = buffer + &format!("{}", key); + buffer += &format!("{}", key); } _ => {} }, -- cgit v1.2.3