diff --git a/src/ui/activities/filetransfer/actions/copy.rs b/src/ui/activities/filetransfer/actions/copy.rs index c614b14..bf28e5a 100644 --- a/src/ui/activities/filetransfer/actions/copy.rs +++ b/src/ui/activities/filetransfer/actions/copy.rs @@ -26,51 +26,34 @@ * SOFTWARE. */ // locals -use super::{FileTransferActivity, FsEntry, LogLevel}; -use std::path::PathBuf; -use tuirealm::{Payload, Value}; +use super::{FileTransferActivity, FsEntry, LogLevel, SelectedEntry}; +use std::path::{Path, PathBuf}; impl FileTransferActivity { /// ### action_local_copy /// /// Copy file on local pub(crate) fn action_local_copy(&mut self, input: String) { - match self.get_local_file_state() { - Some(Payload::One(Value::Usize(idx))) => { + match self.get_local_selected_entries() { + SelectedEntry::One(entry) => { let dest_path: PathBuf = PathBuf::from(input); - let entry: FsEntry = self.local().get(idx).unwrap().clone(); - match self.host.copy(&entry, dest_path.as_path()) { - Ok(_) => { - self.log( - LogLevel::Info, - format!( - "Copied \"{}\" to \"{}\"", - entry.get_abs_path().display(), - dest_path.display() - ), - ); - // Reload entries - let wrkdir: PathBuf = self.local().wrkdir.clone(); - self.local_scan(wrkdir.as_path()); - } - Err(err) => self.log_and_alert( - LogLevel::Error, - format!( - "Could not copy \"{}\" to \"{}\": {}", - entry.get_abs_path().display(), - dest_path.display(), - err - ), - ), + self.local_copy_file(&entry, dest_path.as_path()); + // Reload entries + self.reload_local_dir(); + } + SelectedEntry::Multi(entries) => { + // Try to copy each file to Input/{FILE_NAME} + let base_path: PathBuf = PathBuf::from(input); + // Iter files + for entry in entries.iter() { + let mut dest_path: PathBuf = base_path.clone(); + dest_path.push(entry.get_name()); + self.local_copy_file(entry, dest_path.as_path()); } + // Reload entries + self.reload_local_dir(); } - Some(Payload::Vec(_)) => { - self.log_and_alert( - LogLevel::Warn, - format!("Copy is not supported when using seleection"), - ); - } - _ => {} + SelectedEntry::None => {} } } @@ -78,40 +61,74 @@ impl FileTransferActivity { /// /// Copy file on remote pub(crate) fn action_remote_copy(&mut self, input: String) { - match self.get_local_file_state() { - Some(Payload::One(Value::Usize(idx))) => { + match self.get_remote_selected_entries() { + SelectedEntry::One(entry) => { let dest_path: PathBuf = PathBuf::from(input); - let entry: FsEntry = self.remote().get(idx).unwrap().clone(); - match self.client.as_mut().copy(&entry, dest_path.as_path()) { - Ok(_) => { - self.log( - LogLevel::Info, - format!( - "Copied \"{}\" to \"{}\"", - entry.get_abs_path().display(), - dest_path.display() - ), - ); - self.reload_remote_dir(); - } - Err(err) => self.log_and_alert( - LogLevel::Error, - format!( - "Could not copy \"{}\" to \"{}\": {}", - entry.get_abs_path().display(), - dest_path.display(), - err - ), - ), - } + self.remote_copy_file(&entry, dest_path.as_path()); + // Reload entries + self.reload_remote_dir(); } - Some(Payload::Vec(_)) => { - self.log_and_alert( - LogLevel::Warn, - format!("Copy is not supported when using seleection"), + SelectedEntry::Multi(entries) => { + // Try to copy each file to Input/{FILE_NAME} + let base_path: PathBuf = PathBuf::from(input); + // Iter files + for entry in entries.iter() { + let mut dest_path: PathBuf = base_path.clone(); + dest_path.push(entry.get_name()); + self.remote_copy_file(entry, dest_path.as_path()); + } + // Reload entries + self.reload_remote_dir(); + } + SelectedEntry::None => {} + } + } + + fn local_copy_file(&mut self, entry: &FsEntry, dest: &Path) { + match self.host.copy(entry, dest) { + Ok(_) => { + self.log( + LogLevel::Info, + format!( + "Copied \"{}\" to \"{}\"", + entry.get_abs_path().display(), + dest.display() + ), ); } - _ => {} + Err(err) => self.log_and_alert( + LogLevel::Error, + format!( + "Could not copy \"{}\" to \"{}\": {}", + entry.get_abs_path().display(), + dest.display(), + err + ), + ), + } + } + + fn remote_copy_file(&mut self, entry: &FsEntry, dest: &Path) { + match self.client.as_mut().copy(entry, dest) { + Ok(_) => { + self.log( + LogLevel::Info, + format!( + "Copied \"{}\" to \"{}\"", + entry.get_abs_path().display(), + dest.display() + ), + ); + } + Err(err) => self.log_and_alert( + LogLevel::Error, + format!( + "Could not copy \"{}\" to \"{}\": {}", + entry.get_abs_path().display(), + dest.display(), + err + ), + ), } } } diff --git a/src/ui/activities/filetransfer/actions/delete.rs b/src/ui/activities/filetransfer/actions/delete.rs index d231d18..b497d06 100644 --- a/src/ui/activities/filetransfer/actions/delete.rs +++ b/src/ui/activities/filetransfer/actions/delete.rs @@ -26,70 +26,90 @@ * SOFTWARE. */ // locals -use super::{FileTransferActivity, FsEntry, LogLevel}; -use std::path::PathBuf; -use tuirealm::{Payload, Value}; +use super::{FileTransferActivity, FsEntry, LogLevel, SelectedEntry}; impl FileTransferActivity { pub(crate) fn action_local_delete(&mut self) { - // Get selection - let selection: Vec = match self.get_local_file_state() { - Some(Payload::One(Value::Usize(idx))) => vec![idx], - Some(Payload::Vec(list)) => list.into_iter().map(|x| { - match x { - Value::Usize(x) => x, - _ => panic!("File selection contains non-usize value"), - } - }), - - } - let entry: Option = self.get_local_file_entry().cloned(); - if let Some(entry) = entry { - let full_path: PathBuf = entry.get_abs_path(); - // Delete file or directory and report status as popup - match self.host.remove(&entry) { - Ok(_) => { - // Reload files - let p: PathBuf = self.local().wrkdir.clone(); - self.local_scan(p.as_path()); - // Log - self.log( - LogLevel::Info, - format!("Removed file \"{}\"", full_path.display()), - ); - } - Err(err) => { - self.log_and_alert( - LogLevel::Error, - format!("Could not delete file \"{}\": {}", full_path.display(), err), - ); - } + match self.get_local_selected_entries() { + SelectedEntry::One(entry) => { + // Delete file + self.local_remove_file(&entry); + // Reload + self.reload_local_dir(); } + SelectedEntry::Multi(entries) => { + // Iter files + for entry in entries.iter() { + // Delete file + self.local_remove_file(entry); + } + // Reload entries + self.reload_local_dir(); + } + SelectedEntry::None => {} } } pub(crate) fn action_remote_delete(&mut self) { - if let Some(idx) = self.get_remote_file_state() { - // Check if file entry exists - let entry = self.remote().get(idx).cloned(); - if let Some(entry) = entry { - let full_path: PathBuf = entry.get_abs_path(); + match self.get_remote_selected_entries() { + SelectedEntry::One(entry) => { // Delete file - match self.client.remove(&entry) { - Ok(_) => { - self.reload_remote_dir(); - self.log( - LogLevel::Info, - format!("Removed file \"{}\"", full_path.display()), - ); - } - Err(err) => { - self.log_and_alert( - LogLevel::Error, - format!("Could not delete file \"{}\": {}", full_path.display(), err), - ); - } + self.remote_remove_file(&entry); + // Reload + self.reload_remote_dir(); + } + SelectedEntry::Multi(entries) => { + // Iter files + for entry in entries.iter() { + // Delete file + self.remote_remove_file(entry); } + // Reload entries + self.reload_remote_dir(); + } + SelectedEntry::None => {} + } + } + + pub(crate) fn local_remove_file(&mut self, entry: &FsEntry) { + match self.host.remove(&entry) { + Ok(_) => { + // Log + self.log( + LogLevel::Info, + format!("Removed file \"{}\"", entry.get_abs_path().display()), + ); + } + Err(err) => { + self.log_and_alert( + LogLevel::Error, + format!( + "Could not delete file \"{}\": {}", + entry.get_abs_path().display(), + err + ), + ); + } + } + } + + pub(crate) fn remote_remove_file(&mut self, entry: &FsEntry) { + match self.client.remove(&entry) { + Ok(_) => { + self.log( + LogLevel::Info, + format!("Removed file \"{}\"", entry.get_abs_path().display()), + ); + } + Err(err) => { + self.log_and_alert( + LogLevel::Error, + format!( + "Could not delete file \"{}\": {}", + entry.get_abs_path().display(), + err + ), + ); } } } diff --git a/src/ui/activities/filetransfer/actions/edit.rs b/src/ui/activities/filetransfer/actions/edit.rs index 3cb1543..6607376 100644 --- a/src/ui/activities/filetransfer/actions/edit.rs +++ b/src/ui/activities/filetransfer/actions/edit.rs @@ -26,51 +26,54 @@ * SOFTWARE. */ // locals -use super::{FileTransferActivity, FsEntry, LogLevel}; -use std::path::PathBuf; +use super::{FileTransferActivity, FsEntry, LogLevel, SelectedEntry}; impl FileTransferActivity { pub(crate) fn action_edit_local_file(&mut self) { - if self.get_local_file_entry().is_some() { - let fsentry: FsEntry = self.get_local_file_entry().unwrap().clone(); + let entries: Vec = match self.get_local_selected_entries() { + SelectedEntry::One(entry) => vec![entry], + SelectedEntry::Multi(entries) => entries, + SelectedEntry::None => vec![], + }; + // Edit all entries + for entry in entries.iter() { // Check if file - if fsentry.is_file() { + if entry.is_file() { self.log( LogLevel::Info, - format!("Opening file \"{}\"...", fsentry.get_abs_path().display()), + format!("Opening file \"{}\"...", entry.get_abs_path().display()), ); // Edit file - match self.edit_local_file(fsentry.get_abs_path().as_path()) { - Ok(_) => { - // Reload directory - let pwd: PathBuf = self.local().wrkdir.clone(); - self.local_scan(pwd.as_path()); - } - Err(err) => self.log_and_alert(LogLevel::Error, err), + if let Err(err) = self.edit_local_file(entry.get_abs_path().as_path()) { + self.log_and_alert(LogLevel::Error, err); } } } + // Reload entries + self.reload_local_dir(); } pub(crate) fn action_edit_remote_file(&mut self) { - if self.get_remote_file_entry().is_some() { - let fsentry: FsEntry = self.get_remote_file_entry().unwrap().clone(); + let entries: Vec = match self.get_remote_selected_entries() { + SelectedEntry::One(entry) => vec![entry], + SelectedEntry::Multi(entries) => entries, + SelectedEntry::None => vec![], + }; + // Edit all entries + for entry in entries.iter() { // Check if file - if let FsEntry::File(file) = fsentry.clone() { + if let FsEntry::File(file) = entry { self.log( LogLevel::Info, - format!("Opening file \"{}\"...", fsentry.get_abs_path().display()), + format!("Opening file \"{}\"...", entry.get_abs_path().display()), ); // Edit file - match self.edit_remote_file(&file) { - Ok(_) => { - // Reload directory - let pwd: PathBuf = self.remote().wrkdir.clone(); - self.remote_scan(pwd.as_path()); - } - Err(err) => self.log_and_alert(LogLevel::Error, err), + if let Err(err) = self.edit_remote_file(&file) { + self.log_and_alert(LogLevel::Error, err); } } } + // Reload entries + self.reload_remote_dir(); } } diff --git a/src/ui/activities/filetransfer/actions/exec.rs b/src/ui/activities/filetransfer/actions/exec.rs index 73ab3cb..ba897c2 100644 --- a/src/ui/activities/filetransfer/actions/exec.rs +++ b/src/ui/activities/filetransfer/actions/exec.rs @@ -27,7 +27,6 @@ */ // locals use super::{FileTransferActivity, LogLevel}; -use std::path::PathBuf; impl FileTransferActivity { pub(crate) fn action_local_exec(&mut self, input: String) { @@ -35,8 +34,8 @@ impl FileTransferActivity { Ok(output) => { // Reload files self.log(LogLevel::Info, format!("\"{}\": {}", input, output)); - let wrkdir: PathBuf = self.local().wrkdir.clone(); - self.local_scan(wrkdir.as_path()); + // Reload entries + self.reload_local_dir(); } Err(err) => { // Report err diff --git a/src/ui/activities/filetransfer/actions/find.rs b/src/ui/activities/filetransfer/actions/find.rs index 6268bfb..291c4f6 100644 --- a/src/ui/activities/filetransfer/actions/find.rs +++ b/src/ui/activities/filetransfer/actions/find.rs @@ -27,7 +27,7 @@ */ // locals use super::super::browser::FileExplorerTab; -use super::{FileTransferActivity, FsEntry, LogLevel}; +use super::{FileTransferActivity, FsEntry, SelectedEntry}; use std::path::PathBuf; @@ -46,12 +46,12 @@ impl FileTransferActivity { } } - pub(crate) fn action_find_changedir(&mut self, idx: usize) { + pub(crate) fn action_find_changedir(&mut self) { // Match entry - if let Some(entry) = self.found().as_ref().unwrap().get(idx) { + if let SelectedEntry::One(entry) = self.get_found_selected_entries() { // Get path: if a directory, use directory path; if it is a File, get parent path let path: PathBuf = match entry { - FsEntry::Directory(dir) => dir.abs_path.clone(), + FsEntry::Directory(dir) => dir.abs_path, FsEntry::File(file) => match file.abs_path.parent() { None => PathBuf::from("."), Some(p) => p.to_path_buf(), @@ -69,78 +69,60 @@ impl FileTransferActivity { } } - pub(crate) fn action_find_transfer(&mut self, idx: usize, name: Option) { - let entry: Option = self.found().as_ref().unwrap().get(idx).cloned(); - if let Some(entry) = entry { - // Download file - match self.browser.tab() { + pub(crate) fn action_find_transfer(&mut self, save_as: Option) { + let wrkdir: PathBuf = match self.browser.tab() { + FileExplorerTab::FindLocal | FileExplorerTab::Local => self.remote().wrkdir.clone(), + FileExplorerTab::FindRemote | FileExplorerTab::Remote => self.local().wrkdir.clone(), + }; + match self.get_found_selected_entries() { + SelectedEntry::One(entry) => match self.browser.tab() { FileExplorerTab::FindLocal | FileExplorerTab::Local => { - let wrkdir: PathBuf = self.remote().wrkdir.clone(); - self.filetransfer_send(&entry.get_realfile(), wrkdir.as_path(), name); + self.filetransfer_send(&entry.get_realfile(), wrkdir.as_path(), save_as); } FileExplorerTab::FindRemote | FileExplorerTab::Remote => { - let wrkdir: PathBuf = self.local().wrkdir.clone(); - self.filetransfer_recv(&entry.get_realfile(), wrkdir.as_path(), name); + self.filetransfer_recv(&entry.get_realfile(), wrkdir.as_path(), save_as); + } + }, + SelectedEntry::Multi(entries) => { + // In case of selection: save multiple files in wrkdir/input + let mut dest_path: PathBuf = wrkdir; + if let Some(save_as) = save_as { + dest_path.push(save_as); + } + // Iter files + for entry in entries.iter() { + self.filetransfer_recv(&entry.get_realfile(), dest_path.as_path(), None); } } + SelectedEntry::None => {} } } - pub(crate) fn action_find_delete(&mut self, idx: usize) { - let entry: Option = self.found().as_ref().unwrap().get(idx).cloned(); - if let Some(entry) = entry { - // Download file - match self.browser.tab() { - FileExplorerTab::FindLocal | FileExplorerTab::Local => { - let full_path: PathBuf = entry.get_abs_path(); - // Delete file or directory and report status as popup - match self.host.remove(&entry) { - Ok(_) => { - // Reload files - let p: PathBuf = self.local().wrkdir.clone(); - self.local_scan(p.as_path()); - // Log - self.log( - LogLevel::Info, - format!("Removed file \"{}\"", full_path.display()), - ); - } - Err(err) => { - self.log_and_alert( - LogLevel::Error, - format!( - "Could not delete file \"{}\": {}", - full_path.display(), - err - ), - ); - } - } - } - FileExplorerTab::FindRemote | FileExplorerTab::Remote => { - let full_path: PathBuf = entry.get_abs_path(); + pub(crate) fn action_find_delete(&mut self) { + match self.get_found_selected_entries() { + SelectedEntry::One(entry) => { + // Delete file + self.remove_found_file(&entry); + } + SelectedEntry::Multi(entries) => { + // Iter files + for entry in entries.iter() { // Delete file - match self.client.remove(&entry) { - Ok(_) => { - self.reload_remote_dir(); - self.log( - LogLevel::Info, - format!("Removed file \"{}\"", full_path.display()), - ); - } - Err(err) => { - self.log_and_alert( - LogLevel::Error, - format!( - "Could not delete file \"{}\": {}", - full_path.display(), - err - ), - ); - } - } + self.remove_found_file(entry); } } + SelectedEntry::None => {} + } + } + + fn remove_found_file(&mut self, entry: &FsEntry) { + match self.browser.tab() { + FileExplorerTab::FindLocal | FileExplorerTab::Local => { + self.local_remove_file(entry); + } + FileExplorerTab::FindRemote | FileExplorerTab::Remote => { + self.remote_remove_file(entry); + } } } } diff --git a/src/ui/activities/filetransfer/actions/mkdir.rs b/src/ui/activities/filetransfer/actions/mkdir.rs index 3f78823..3664920 100644 --- a/src/ui/activities/filetransfer/actions/mkdir.rs +++ b/src/ui/activities/filetransfer/actions/mkdir.rs @@ -35,8 +35,8 @@ impl FileTransferActivity { Ok(_) => { // Reload files self.log(LogLevel::Info, format!("Created directory \"{}\"", input)); - let wrkdir: PathBuf = self.local().wrkdir.clone(); - self.local_scan(wrkdir.as_path()); + // Reload entries + self.reload_local_dir(); } Err(err) => { // Report err diff --git a/src/ui/activities/filetransfer/actions/mod.rs b/src/ui/activities/filetransfer/actions/mod.rs index 23397ab..ef4a39d 100644 --- a/src/ui/activities/filetransfer/actions/mod.rs +++ b/src/ui/activities/filetransfer/actions/mod.rs @@ -40,40 +40,114 @@ pub(crate) mod newfile; pub(crate) mod rename; pub(crate) mod save; +pub(crate) enum SelectedEntry { + One(FsEntry), + Multi(Vec), + None, +} + +enum SelectedEntryIndex { + One(usize), + Multi(Vec), + None, +} + +impl From> for SelectedEntry { + fn from(opt: Option<&FsEntry>) -> Self { + match opt { + Some(e) => SelectedEntry::One(e.clone()), + None => SelectedEntry::None, + } + } +} + +impl From> for SelectedEntry { + fn from(files: Vec<&FsEntry>) -> Self { + SelectedEntry::Multi(files.into_iter().cloned().collect()) + } +} + impl FileTransferActivity { - /// ### get_local_file_entry + /// ### get_local_selected_entries /// /// Get local file entry - pub(crate) fn get_local_file_entry(&self) -> Option<&FsEntry> { - match self.get_local_file_state() { - Some(Payload::One(Value::Usize(idx))) => self.local().get(idx), - _ => None, + pub(crate) fn get_local_selected_entries(&self) -> SelectedEntry { + match self.get_selected_index(super::COMPONENT_EXPLORER_LOCAL) { + SelectedEntryIndex::One(idx) => SelectedEntry::from(self.local().get(idx)), + SelectedEntryIndex::Multi(files) => { + let files: Vec<&FsEntry> = files + .iter() + .map(|x| self.local().get(*x)) // Usize to Option + .filter(|x| x.is_some()) // Get only some values + .map(|x| x.unwrap()) // Option to FsEntry + .collect(); + SelectedEntry::from(files) + } + SelectedEntryIndex::None => SelectedEntry::None, } } - /// ### get_remote_file_entry + /// ### get_remote_selected_entries /// /// Get remote file entry - pub(crate) fn get_remote_file_entry(&self) -> Option<&FsEntry> { - match self.get_remote_file_state() { - Some(Payload::One(Value::Usize(idx))) => self.remote().get(idx), - _ => None, + pub(crate) fn get_remote_selected_entries(&self) -> SelectedEntry { + match self.get_selected_index(super::COMPONENT_EXPLORER_REMOTE) { + SelectedEntryIndex::One(idx) => SelectedEntry::from(self.remote().get(idx)), + SelectedEntryIndex::Multi(files) => { + let files: Vec<&FsEntry> = files + .iter() + .map(|x| self.remote().get(*x)) // Usize to Option + .filter(|x| x.is_some()) // Get only some values + .map(|x| x.unwrap()) // Option to FsEntry + .collect(); + SelectedEntry::from(files) + } + SelectedEntryIndex::None => SelectedEntry::None, + } + } + + /// ### get_remote_selected_entries + /// + /// Get remote file entry + pub(crate) fn get_found_selected_entries(&self) -> SelectedEntry { + match self.get_selected_index(super::COMPONENT_EXPLORER_FIND) { + SelectedEntryIndex::One(idx) => { + SelectedEntry::from(self.found().as_ref().unwrap().get(idx)) + } + SelectedEntryIndex::Multi(files) => { + let files: Vec<&FsEntry> = files + .iter() + .map(|x| self.found().as_ref().unwrap().get(*x)) // Usize to Option + .filter(|x| x.is_some()) // Get only some values + .map(|x| x.unwrap()) // Option to FsEntry + .collect(); + SelectedEntry::from(files) + } + SelectedEntryIndex::None => SelectedEntry::None, } } // -- private - /// ### get_local_file_state - /// - /// Get index of selected file in the local tab - fn get_local_file_state(&self) -> Option { - self.view.get_state(super::COMPONENT_EXPLORER_LOCAL) - } - - /// ### get_remote_file_state - /// - /// Get index of selected file in the remote file - fn get_remote_file_state(&self) -> Option { - self.view.get_state(super::COMPONENT_EXPLORER_REMOTE) + fn get_selected_index(&self, component: &str) -> SelectedEntryIndex { + eprintln!( + "INDEX FOR {}: {:?}", + component, + self.view.get_state(component) + ); + match self.view.get_state(component) { + Some(Payload::One(Value::Usize(idx))) => SelectedEntryIndex::One(idx), + Some(Payload::Vec(files)) => { + let list: Vec = files + .iter() + .map(|x| match x { + Value::Usize(v) => *v, + _ => 0, + }) + .collect(); + SelectedEntryIndex::Multi(list) + } + _ => SelectedEntryIndex::None, + } } } diff --git a/src/ui/activities/filetransfer/actions/newfile.rs b/src/ui/activities/filetransfer/actions/newfile.rs index f09baa8..ac65f79 100644 --- a/src/ui/activities/filetransfer/actions/newfile.rs +++ b/src/ui/activities/filetransfer/actions/newfile.rs @@ -59,8 +59,7 @@ impl FileTransferActivity { ); } // Reload files - let path: PathBuf = self.local().wrkdir.clone(); - self.local_scan(path.as_path()); + self.reload_local_dir(); } pub(crate) fn action_remote_newfile(&mut self, input: String) { @@ -119,8 +118,7 @@ impl FileTransferActivity { ); } // Reload files - let path: PathBuf = self.remote().wrkdir.clone(); - self.remote_scan(path.as_path()); + self.reload_remote_dir(); } } } diff --git a/src/ui/activities/filetransfer/actions/rename.rs b/src/ui/activities/filetransfer/actions/rename.rs index 5cae46d..9e18b39 100644 --- a/src/ui/activities/filetransfer/actions/rename.rs +++ b/src/ui/activities/filetransfer/actions/rename.rs @@ -26,77 +26,103 @@ * SOFTWARE. */ // locals -use super::{FileTransferActivity, FsEntry, LogLevel}; -use std::path::PathBuf; +use super::{FileTransferActivity, FsEntry, LogLevel, SelectedEntry}; +use std::path::{Path, PathBuf}; impl FileTransferActivity { pub(crate) fn action_local_rename(&mut self, input: String) { - let entry: Option = self.get_local_file_entry().cloned(); - if let Some(entry) = entry { - let mut dst_path: PathBuf = PathBuf::from(input); - // Check if path is relative - if dst_path.as_path().is_relative() { - let mut wrkdir: PathBuf = self.local().wrkdir.clone(); - wrkdir.push(dst_path); - dst_path = wrkdir; + match self.get_local_selected_entries() { + SelectedEntry::One(entry) => { + let dest_path: PathBuf = PathBuf::from(input); + self.local_rename_file(&entry, dest_path.as_path()); + // Reload entries + self.reload_local_dir(); } - let full_path: PathBuf = entry.get_abs_path(); - // Rename file or directory and report status as popup - match self.host.rename(&entry, dst_path.as_path()) { - Ok(_) => { - // Reload files - let path: PathBuf = self.local().wrkdir.clone(); - self.local_scan(path.as_path()); - // Log - self.log( - LogLevel::Info, - format!( - "Renamed file \"{}\" to \"{}\"", - full_path.display(), - dst_path.display() - ), - ); - } - Err(err) => { - self.log_and_alert( - LogLevel::Error, - format!("Could not rename file \"{}\": {}", full_path.display(), err), - ); + SelectedEntry::Multi(entries) => { + // Try to copy each file to Input/{FILE_NAME} + let base_path: PathBuf = PathBuf::from(input); + // Iter files + for entry in entries.iter() { + let mut dest_path: PathBuf = base_path.clone(); + dest_path.push(entry.get_name()); + self.local_rename_file(entry, dest_path.as_path()); } + // Reload entries + self.reload_local_dir(); } + SelectedEntry::None => {} } } pub(crate) fn action_remote_rename(&mut self, input: String) { - if let Some(idx) = self.get_remote_file_state() { - let entry = self.remote().get(idx).cloned(); - if let Some(entry) = entry { - let dst_path: PathBuf = PathBuf::from(input); - let full_path: PathBuf = entry.get_abs_path(); - // Rename file or directory and report status as popup - match self.client.as_mut().rename(&entry, dst_path.as_path()) { - Ok(_) => { - // Reload files - let path: PathBuf = self.remote().wrkdir.clone(); - self.remote_scan(path.as_path()); - // Log - self.log( - LogLevel::Info, - format!( - "Renamed file \"{}\" to \"{}\"", - full_path.display(), - dst_path.display() - ), - ); - } - Err(err) => { - self.log_and_alert( - LogLevel::Error, - format!("Could not rename file \"{}\": {}", full_path.display(), err), - ); - } - } + match self.get_remote_selected_entries() { + SelectedEntry::One(entry) => { + let dest_path: PathBuf = PathBuf::from(input); + self.remote_rename_file(&entry, dest_path.as_path()); + // Reload entries + self.reload_remote_dir(); } + SelectedEntry::Multi(entries) => { + // Try to copy each file to Input/{FILE_NAME} + let base_path: PathBuf = PathBuf::from(input); + // Iter files + for entry in entries.iter() { + let mut dest_path: PathBuf = base_path.clone(); + dest_path.push(entry.get_name()); + self.remote_rename_file(entry, dest_path.as_path()); + } + // Reload entries + self.reload_remote_dir(); + } + SelectedEntry::None => {} + } + } + + fn local_rename_file(&mut self, entry: &FsEntry, dest: &Path) { + match self.host.rename(entry, dest) { + Ok(_) => { + self.log( + LogLevel::Info, + format!( + "Moved \"{}\" to \"{}\"", + entry.get_abs_path().display(), + dest.display() + ), + ); + } + Err(err) => self.log_and_alert( + LogLevel::Error, + format!( + "Could not move \"{}\" to \"{}\": {}", + entry.get_abs_path().display(), + dest.display(), + err + ), + ), + } + } + + fn remote_rename_file(&mut self, entry: &FsEntry, dest: &Path) { + match self.client.as_mut().rename(entry, dest) { + Ok(_) => { + self.log( + LogLevel::Info, + format!( + "Moved \"{}\" to \"{}\"", + entry.get_abs_path().display(), + dest.display() + ), + ); + } + Err(err) => self.log_and_alert( + LogLevel::Error, + format!( + "Could not move \"{}\" to \"{}\": {}", + entry.get_abs_path().display(), + dest.display(), + err + ), + ), } } } diff --git a/src/ui/activities/filetransfer/actions/save.rs b/src/ui/activities/filetransfer/actions/save.rs index 4a34079..3956e17 100644 --- a/src/ui/activities/filetransfer/actions/save.rs +++ b/src/ui/activities/filetransfer/actions/save.rs @@ -26,31 +26,65 @@ * SOFTWARE. */ // locals -use super::{FileTransferActivity, FsEntry}; +use super::{FileTransferActivity, SelectedEntry}; use std::path::PathBuf; impl FileTransferActivity { pub(crate) fn action_local_saveas(&mut self, input: String) { - if let Some(idx) = self.get_local_file_state() { - // Get pwd - let wrkdir: PathBuf = self.remote().wrkdir.clone(); - if self.local().get(idx).is_some() { - let file: FsEntry = self.local().get(idx).unwrap().clone(); - // Call upload; pass realfile, keep link name - self.filetransfer_send(&file.get_realfile(), wrkdir.as_path(), Some(input)); - } - } + self.action_local_send_file(Some(input)); } pub(crate) fn action_remote_saveas(&mut self, input: String) { - if let Some(idx) = self.get_remote_file_state() { - // Get pwd - let wrkdir: PathBuf = self.local().wrkdir.clone(); - if self.remote().get(idx).is_some() { - let file: FsEntry = self.remote().get(idx).unwrap().clone(); - // Call upload; pass realfile, keep link name - self.filetransfer_recv(&file.get_realfile(), wrkdir.as_path(), Some(input)); + self.action_remote_recv_file(Some(input)); + } + + pub(crate) fn action_local_send(&mut self) { + self.action_local_send_file(None); + } + + pub(crate) fn action_remote_recv(&mut self) { + self.action_remote_recv_file(None); + } + + fn action_local_send_file(&mut self, save_as: Option) { + let wrkdir: PathBuf = self.remote().wrkdir.clone(); + match self.get_local_selected_entries() { + SelectedEntry::One(entry) => { + self.filetransfer_send(&entry.get_realfile(), wrkdir.as_path(), save_as); } + SelectedEntry::Multi(entries) => { + // In case of selection: save multiple files in wrkdir/input + let mut dest_path: PathBuf = wrkdir; + if let Some(save_as) = save_as { + dest_path.push(save_as); + } + // Iter files + for entry in entries.iter() { + self.filetransfer_send(&entry.get_realfile(), dest_path.as_path(), None); + } + } + SelectedEntry::None => {} + } + } + + fn action_remote_recv_file(&mut self, save_as: Option) { + let wrkdir: PathBuf = self.local().wrkdir.clone(); + match self.get_remote_selected_entries() { + SelectedEntry::One(entry) => { + self.filetransfer_recv(&entry.get_realfile(), wrkdir.as_path(), save_as); + } + SelectedEntry::Multi(entries) => { + // In case of selection: save multiple files in wrkdir/input + let mut dest_path: PathBuf = wrkdir; + if let Some(save_as) = save_as { + dest_path.push(save_as); + } + // Iter files + for entry in entries.iter() { + self.filetransfer_recv(&entry.get_realfile(), dest_path.as_path(), None); + } + } + SelectedEntry::None => {} } } } diff --git a/src/ui/activities/filetransfer/session.rs b/src/ui/activities/filetransfer/session.rs index d1c66d1..481a572 100644 --- a/src/ui/activities/filetransfer/session.rs +++ b/src/ui/activities/filetransfer/session.rs @@ -145,6 +145,11 @@ impl FileTransferActivity { } } + pub(super) fn reload_local_dir(&mut self) { + let wrkdir: PathBuf = self.local().wrkdir.clone(); + self.local_scan(wrkdir.as_path()); + } + /// ### filetransfer_send /// /// Send fs entry to remote. @@ -257,8 +262,7 @@ impl FileTransferActivity { } } // Scan dir on remote - let path: PathBuf = self.remote().wrkdir.clone(); - self.remote_scan(path.as_path()); + self.reload_remote_dir(); // If aborted; show popup if self.transfer.aborted { // Log abort diff --git a/src/ui/activities/filetransfer/update.rs b/src/ui/activities/filetransfer/update.rs index d7bd6d0..26e4316 100644 --- a/src/ui/activities/filetransfer/update.rs +++ b/src/ui/activities/filetransfer/update.rs @@ -29,10 +29,10 @@ extern crate bytesize; // locals use super::{ - browser::FileExplorerTab, FileTransferActivity, LogLevel, COMPONENT_EXPLORER_FIND, - COMPONENT_EXPLORER_LOCAL, COMPONENT_EXPLORER_REMOTE, COMPONENT_INPUT_COPY, - COMPONENT_INPUT_EXEC, COMPONENT_INPUT_FIND, COMPONENT_INPUT_GOTO, COMPONENT_INPUT_MKDIR, - COMPONENT_INPUT_NEWFILE, COMPONENT_INPUT_RENAME, COMPONENT_INPUT_SAVEAS, + actions::SelectedEntry, browser::FileExplorerTab, FileTransferActivity, LogLevel, + COMPONENT_EXPLORER_FIND, COMPONENT_EXPLORER_LOCAL, COMPONENT_EXPLORER_REMOTE, + COMPONENT_INPUT_COPY, COMPONENT_INPUT_EXEC, COMPONENT_INPUT_FIND, COMPONENT_INPUT_GOTO, + COMPONENT_INPUT_MKDIR, COMPONENT_INPUT_NEWFILE, COMPONENT_INPUT_RENAME, COMPONENT_INPUT_SAVEAS, COMPONENT_LIST_FILEINFO, COMPONENT_LOG_BOX, COMPONENT_PROGRESS_BAR, COMPONENT_RADIO_DELETE, COMPONENT_RADIO_DISCONNECT, COMPONENT_RADIO_QUIT, COMPONENT_RADIO_SORTING, COMPONENT_TEXT_ERROR, COMPONENT_TEXT_FATAL, COMPONENT_TEXT_HELP, @@ -101,18 +101,8 @@ impl FileTransferActivity { } } (COMPONENT_EXPLORER_LOCAL, &MSG_KEY_SPACE) => { - // Get pwd - let wrkdir: PathBuf = self.remote().wrkdir.clone(); - // Get file and clone (due to mutable / immutable stuff...) - if self.get_local_file_entry().is_some() { - let file: FsEntry = self.get_local_file_entry().unwrap().clone(); - let name: String = file.get_name().to_string(); - // Call upload; pass realfile, keep link name - self.filetransfer_send(&file.get_realfile(), wrkdir.as_path(), Some(name)); - self.update_remote_filelist() - } else { - None - } + self.action_local_send(); + self.update_remote_filelist() } (COMPONENT_EXPLORER_LOCAL, &MSG_KEY_CHAR_A) => { // Toggle hidden files @@ -121,8 +111,7 @@ impl FileTransferActivity { self.update_local_filelist() } (COMPONENT_EXPLORER_LOCAL, &MSG_KEY_CHAR_I) => { - let file: Option = self.get_local_file_entry().cloned(); - if let Some(file) = file { + if let SelectedEntry::One(file) = self.get_local_selected_entries() { self.mount_file_info(&file); } None @@ -175,17 +164,8 @@ impl FileTransferActivity { } } (COMPONENT_EXPLORER_REMOTE, &MSG_KEY_SPACE) => { - // Get file and clone (due to mutable / immutable stuff...) - if self.get_remote_file_entry().is_some() { - let file: FsEntry = self.get_remote_file_entry().unwrap().clone(); - let name: String = file.get_name().to_string(); - // Call upload; pass realfile, keep link name - let wrkdir: PathBuf = self.local().wrkdir.clone(); - self.filetransfer_recv(&file.get_realfile(), wrkdir.as_path(), Some(name)); - self.update_local_filelist() - } else { - None - } + self.action_remote_recv(); + self.update_local_filelist() } (COMPONENT_EXPLORER_REMOTE, &MSG_KEY_BACKSPACE) => { // Go to previous directory @@ -204,8 +184,7 @@ impl FileTransferActivity { self.update_remote_filelist() } (COMPONENT_EXPLORER_REMOTE, &MSG_KEY_CHAR_I) => { - let file: Option = self.get_remote_file_entry().cloned(); - if let Some(file) = file { + if let SelectedEntry::One(file) = self.get_remote_selected_entries() { self.mount_file_info(&file); } None @@ -324,9 +303,9 @@ impl FileTransferActivity { self.finalize_find(); None } - (COMPONENT_EXPLORER_FIND, Msg::OnSubmit(Payload::One(Value::Usize(idx)))) => { + (COMPONENT_EXPLORER_FIND, Msg::OnSubmit(_)) => { // Find changedir - self.action_find_changedir(*idx); + self.action_find_changedir(); // Umount find self.umount_find(); // Finalize find @@ -340,17 +319,12 @@ impl FileTransferActivity { } (COMPONENT_EXPLORER_FIND, &MSG_KEY_SPACE) => { // Get entry - match self.view.get_state(COMPONENT_EXPLORER_FIND) { - Some(Payload::One(Value::Usize(idx))) => { - self.action_find_transfer(idx, None); - // Reload files - match self.browser.tab() { - // NOTE: swapped by purpose - FileExplorerTab::FindLocal => self.update_remote_filelist(), - FileExplorerTab::FindRemote => self.update_local_filelist(), - _ => None, - } - } + self.action_find_transfer(None); + // Reload files + match self.browser.tab() { + // NOTE: swapped by purpose + FileExplorerTab::FindLocal => self.update_remote_filelist(), + FileExplorerTab::FindRemote => self.update_local_filelist(), _ => None, } } @@ -540,11 +514,7 @@ impl FileTransferActivity { FileExplorerTab::Remote => self.action_remote_saveas(input.to_string()), FileExplorerTab::FindLocal | FileExplorerTab::FindRemote => { // Get entry - if let Some(Payload::One(Value::Usize(idx))) = - self.view.get_state(COMPONENT_EXPLORER_FIND) - { - self.action_find_transfer(idx, Some(input.to_string())); - } + self.action_find_transfer(Some(input.to_string())); } } self.umount_saveas(); @@ -579,7 +549,7 @@ impl FileTransferActivity { if let Some(Payload::One(Value::Usize(idx))) = self.view.get_state(COMPONENT_EXPLORER_FIND) { - self.action_find_delete(idx); + self.action_find_delete(); // Reload entries self.found_mut().unwrap().del_entry(idx); self.update_find_list();