termscp/src/ui/activities/filetransfer/update.rs

971 lines
43 KiB
Rust

//! ## FileTransferActivity
//!
//! `filetransfer_activiy` is the module which implements the Filetransfer activity, which is the main activity afterall
/**
* MIT License
*
* termscp - Copyright (c) 2021 Christian Visintin
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
// locals
use super::{
actions::SelectedEntry, browser::FileExplorerTab, FileTransferActivity, LogLevel, TransferOpts,
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_OPEN_WITH,
COMPONENT_INPUT_RENAME, COMPONENT_INPUT_SAVEAS, COMPONENT_LIST_FILEINFO,
COMPONENT_LIST_REPLACING_FILES, COMPONENT_LOG_BOX, COMPONENT_PROGRESS_BAR_FULL,
COMPONENT_PROGRESS_BAR_PARTIAL, COMPONENT_RADIO_DELETE, COMPONENT_RADIO_DISCONNECT,
COMPONENT_RADIO_QUIT, COMPONENT_RADIO_REPLACE, COMPONENT_RADIO_SORTING, COMPONENT_TEXT_ERROR,
COMPONENT_TEXT_FATAL, COMPONENT_TEXT_HELP,
};
use crate::fs::explorer::FileSorting;
use crate::fs::FsEntry;
use crate::ui::components::{file_list::FileListPropsBuilder, logbox::LogboxPropsBuilder};
use crate::ui::keymap::*;
use crate::utils::fmt::fmt_path_elide_ex;
// externals
use tui_realm_stdlib::ProgressBarPropsBuilder;
use tuirealm::{
props::{Alignment, PropsBuilder, TableBuilder, TextSpan},
tui::style::Color,
Msg, Payload, Update, Value,
};
impl Update for FileTransferActivity {
// -- update
/// ### update
///
/// Update auth activity model based on msg
/// The function exits when returns None
fn update(&mut self, msg: Option<(String, Msg)>) -> Option<(String, Msg)> {
let ref_msg: Option<(&str, &Msg)> = msg.as_ref().map(|(s, msg)| (s.as_str(), msg));
// Match msg
match ref_msg {
None => None, // Exit after None
Some(msg) => match msg {
// -- local tab
(COMPONENT_EXPLORER_LOCAL, key) if key == &MSG_KEY_RIGHT => {
// Change tab
self.view.active(COMPONENT_EXPLORER_REMOTE);
self.browser.change_tab(FileExplorerTab::Remote);
None
}
(COMPONENT_EXPLORER_LOCAL, key) if key == &MSG_KEY_BACKSPACE => {
// Go to previous directory
self.action_go_to_previous_local_dir(false);
if self.browser.sync_browsing {
let _ = self.update_remote_filelist();
}
// Reload file list component
self.update_local_filelist()
}
(COMPONENT_EXPLORER_LOCAL, Msg::OnSubmit(Payload::One(Value::Usize(idx)))) => {
// Match selected file
let mut entry: Option<FsEntry> = None;
if let Some(e) = self.local().get(*idx) {
entry = Some(e.clone());
}
if let Some(entry) = entry {
if self.action_submit_local(entry) {
// Update file list if sync
if self.browser.sync_browsing {
let _ = self.update_remote_filelist();
}
self.update_local_filelist()
} else {
None
}
} else {
None
}
}
(COMPONENT_EXPLORER_LOCAL, key) if key == &MSG_KEY_SPACE => {
self.action_local_send();
self.update_remote_filelist()
}
(COMPONENT_EXPLORER_LOCAL, key) if key == &MSG_KEY_CHAR_A => {
// Toggle hidden files
self.local_mut().toggle_hidden_files();
// Update status bar
self.refresh_local_status_bar();
// Reload file list component
self.update_local_filelist()
}
(COMPONENT_EXPLORER_LOCAL, key) if key == &MSG_KEY_CHAR_I => {
if let SelectedEntry::One(file) = self.get_local_selected_entries() {
self.mount_file_info(&file);
}
None
}
(COMPONENT_EXPLORER_LOCAL, key) if key == &MSG_KEY_CHAR_L => {
// Reload directory
self.reload_local_dir();
// Reload file list component
self.update_local_filelist()
}
(COMPONENT_EXPLORER_LOCAL, key) if key == &MSG_KEY_CHAR_O => {
self.action_edit_local_file();
// Reload file list component
self.update_local_filelist()
}
(COMPONENT_EXPLORER_LOCAL, key) if key == &MSG_KEY_CHAR_U => {
self.action_go_to_local_upper_dir(false);
if self.browser.sync_browsing {
let _ = self.update_remote_filelist();
}
// Reload file list component
self.update_local_filelist()
}
// -- remote tab
(COMPONENT_EXPLORER_REMOTE, key) if key == &MSG_KEY_LEFT => {
// Change tab
self.view.active(COMPONENT_EXPLORER_LOCAL);
self.browser.change_tab(FileExplorerTab::Local);
None
}
(COMPONENT_EXPLORER_REMOTE, Msg::OnSubmit(Payload::One(Value::Usize(idx)))) => {
// Match selected file
let mut entry: Option<FsEntry> = None;
if let Some(e) = self.remote().get(*idx) {
entry = Some(e.clone());
}
if let Some(entry) = entry {
if self.action_submit_remote(entry) {
// Update file list if sync
if self.browser.sync_browsing {
let _ = self.update_local_filelist();
}
self.update_remote_filelist()
} else {
None
}
} else {
None
}
}
(COMPONENT_EXPLORER_REMOTE, key) if key == &MSG_KEY_SPACE => {
self.action_remote_recv();
self.update_local_filelist()
}
(COMPONENT_EXPLORER_REMOTE, key) if key == &MSG_KEY_BACKSPACE => {
// Go to previous directory
self.action_go_to_previous_remote_dir(false);
// If sync is enabled update local too
if self.browser.sync_browsing {
let _ = self.update_local_filelist();
}
// Reload file list component
self.update_remote_filelist()
}
(COMPONENT_EXPLORER_REMOTE, key) if key == &MSG_KEY_CHAR_A => {
// Toggle hidden files
self.remote_mut().toggle_hidden_files();
// Update status bar
self.refresh_remote_status_bar();
// Reload file list component
self.update_remote_filelist()
}
(COMPONENT_EXPLORER_REMOTE, key) if key == &MSG_KEY_CHAR_I => {
if let SelectedEntry::One(file) = self.get_remote_selected_entries() {
self.mount_file_info(&file);
}
None
}
(COMPONENT_EXPLORER_REMOTE, key) if key == &MSG_KEY_CHAR_L => {
// Reload directory
self.reload_remote_dir();
// Reload file list component
self.update_remote_filelist()
}
(COMPONENT_EXPLORER_REMOTE, key) if key == &MSG_KEY_CHAR_O => {
// Edit file
self.action_edit_remote_file();
// Reload file list component
self.update_remote_filelist()
}
(COMPONENT_EXPLORER_REMOTE, key) if key == &MSG_KEY_CHAR_U => {
self.action_go_to_remote_upper_dir(false);
if self.browser.sync_browsing {
let _ = self.update_local_filelist();
}
// Reload file list component
self.update_remote_filelist()
}
// -- common explorer keys
(COMPONENT_EXPLORER_LOCAL, key) | (COMPONENT_EXPLORER_REMOTE, key)
if key == &MSG_KEY_CHAR_B =>
{
// Show sorting file
self.mount_file_sorting();
None
}
(COMPONENT_EXPLORER_LOCAL, key) | (COMPONENT_EXPLORER_REMOTE, key)
if key == &MSG_KEY_CHAR_C =>
{
self.mount_copy();
None
}
(COMPONENT_EXPLORER_LOCAL, key) | (COMPONENT_EXPLORER_REMOTE, key)
if key == &MSG_KEY_CHAR_D =>
{
self.mount_mkdir();
None
}
(COMPONENT_EXPLORER_LOCAL, key) | (COMPONENT_EXPLORER_REMOTE, key)
if key == &MSG_KEY_CHAR_F =>
{
self.mount_find_input();
None
}
(COMPONENT_EXPLORER_LOCAL, key) | (COMPONENT_EXPLORER_REMOTE, key)
if key == &MSG_KEY_CHAR_G =>
{
self.mount_goto();
None
}
(COMPONENT_EXPLORER_LOCAL, key) | (COMPONENT_EXPLORER_REMOTE, key)
if key == &MSG_KEY_CHAR_H =>
{
self.mount_help();
None
}
(COMPONENT_EXPLORER_LOCAL, key) | (COMPONENT_EXPLORER_REMOTE, key)
if key == &MSG_KEY_CHAR_N =>
{
self.mount_newfile();
None
}
(COMPONENT_EXPLORER_LOCAL, key)
| (COMPONENT_EXPLORER_REMOTE, key)
| (COMPONENT_LOG_BOX, key)
if key == &MSG_KEY_CHAR_Q =>
{
self.mount_quit();
None
}
(COMPONENT_EXPLORER_LOCAL, key) | (COMPONENT_EXPLORER_REMOTE, key)
if key == &MSG_KEY_CHAR_R =>
{
// Mount rename
self.mount_rename();
None
}
(COMPONENT_EXPLORER_LOCAL, key)
| (COMPONENT_EXPLORER_REMOTE, key)
| (COMPONENT_EXPLORER_FIND, key)
if key == &MSG_KEY_CHAR_S =>
{
// Mount save as
self.mount_saveas();
None
}
(COMPONENT_EXPLORER_LOCAL, key)
| (COMPONENT_EXPLORER_REMOTE, key)
| (COMPONENT_EXPLORER_FIND, key)
if key == &MSG_KEY_CHAR_V =>
{
// View
match self.browser.tab() {
FileExplorerTab::Local => self.action_open_local(),
FileExplorerTab::Remote => self.action_open_remote(),
FileExplorerTab::FindLocal | FileExplorerTab::FindRemote => {
self.action_find_open()
}
}
None
}
(COMPONENT_EXPLORER_LOCAL, key)
| (COMPONENT_EXPLORER_REMOTE, key)
| (COMPONENT_EXPLORER_FIND, key)
if key == &MSG_KEY_CHAR_W =>
{
// Open with
self.mount_openwith();
None
}
(COMPONENT_EXPLORER_LOCAL, key) | (COMPONENT_EXPLORER_REMOTE, key)
if key == &MSG_KEY_CHAR_X =>
{
// Mount exec
self.mount_exec();
None
}
(COMPONENT_EXPLORER_LOCAL, key) | (COMPONENT_EXPLORER_REMOTE, key)
if key == &MSG_KEY_CHAR_Y =>
{
// Toggle browser sync
self.browser.toggle_sync_browsing();
// Update status bar
self.refresh_remote_status_bar();
None
}
(COMPONENT_EXPLORER_LOCAL, key)
| (COMPONENT_EXPLORER_REMOTE, key)
| (COMPONENT_LOG_BOX, key)
if key == &MSG_KEY_ESC =>
{
self.mount_disconnect();
None
}
(COMPONENT_EXPLORER_LOCAL, key)
| (COMPONENT_EXPLORER_REMOTE, key)
| (COMPONENT_EXPLORER_FIND, key)
if key == &MSG_KEY_CHAR_E || key == &MSG_KEY_DEL =>
{
self.mount_radio_delete();
None
}
// -- find result explorer
(COMPONENT_EXPLORER_FIND, key) if key == &MSG_KEY_ESC => {
// Umount find
self.umount_find();
// Finalize find
self.finalize_find();
None
}
(COMPONENT_EXPLORER_FIND, Msg::OnSubmit(_)) => {
// Find changedir
self.action_find_changedir();
// Umount find
self.umount_find();
// Finalize find
self.finalize_find();
// Reload files
match self.browser.tab() {
FileExplorerTab::Local => self.update_local_filelist(),
FileExplorerTab::Remote => self.update_remote_filelist(),
_ => None,
}
}
(COMPONENT_EXPLORER_FIND, key) if key == &MSG_KEY_SPACE => {
// Get entry
self.action_find_transfer(TransferOpts::default());
// Reload files
match self.browser.tab() {
// NOTE: swapped by purpose
FileExplorerTab::FindLocal => self.update_remote_filelist(),
FileExplorerTab::FindRemote => self.update_local_filelist(),
_ => None,
}
}
// -- switch to log
(COMPONENT_EXPLORER_LOCAL, key) | (COMPONENT_EXPLORER_REMOTE, key)
if key == &MSG_KEY_TAB =>
{
self.view.active(COMPONENT_LOG_BOX); // Active log box
None
}
// -- Log box
(COMPONENT_LOG_BOX, key) if key == &MSG_KEY_TAB => {
self.view.blur(); // Blur log box
None
}
// -- copy popup
(COMPONENT_INPUT_COPY, key) if key == &MSG_KEY_ESC => {
self.umount_copy();
None
}
(COMPONENT_INPUT_COPY, Msg::OnSubmit(Payload::One(Value::Str(input)))) => {
// Copy file
self.umount_copy();
self.mount_blocking_wait("Copying file(s)…");
match self.browser.tab() {
FileExplorerTab::Local => self.action_local_copy(input.to_string()),
FileExplorerTab::Remote => self.action_remote_copy(input.to_string()),
_ => panic!("Found tab doesn't support COPY"),
}
self.umount_wait();
// Reload files
match self.browser.tab() {
FileExplorerTab::Local => self.update_local_filelist(),
FileExplorerTab::Remote => self.update_remote_filelist(),
_ => None,
}
}
(COMPONENT_INPUT_COPY, _) => None,
// -- exec popup
(COMPONENT_INPUT_EXEC, key) if key == &MSG_KEY_ESC => {
self.umount_exec();
None
}
(COMPONENT_INPUT_EXEC, Msg::OnSubmit(Payload::One(Value::Str(input)))) => {
// Exex command
self.umount_exec();
self.mount_blocking_wait(format!("Executing '{}'…", input).as_str());
match self.browser.tab() {
FileExplorerTab::Local => self.action_local_exec(input.to_string()),
FileExplorerTab::Remote => self.action_remote_exec(input.to_string()),
_ => panic!("Found tab doesn't support EXEC"),
}
self.umount_wait();
// Reload files
match self.browser.tab() {
FileExplorerTab::Local => self.update_local_filelist(),
FileExplorerTab::Remote => self.update_remote_filelist(),
_ => None,
}
}
(COMPONENT_INPUT_EXEC, _) => None,
// -- find popup
(COMPONENT_INPUT_FIND, key) if key == &MSG_KEY_ESC => {
self.umount_find_input();
None
}
(COMPONENT_INPUT_FIND, Msg::OnSubmit(Payload::One(Value::Str(input)))) => {
self.umount_find_input();
// Find
let res: Result<Vec<FsEntry>, String> = match self.browser.tab() {
FileExplorerTab::Local => self.action_local_find(input.to_string()),
FileExplorerTab::Remote => self.action_remote_find(input.to_string()),
_ => panic!("Trying to search for files, while already in a find result"),
};
// Match result
match res {
Err(err) => {
// Mount error
self.mount_error(err.as_str());
}
Ok(files) => {
// Create explorer and load files
self.browser.set_found(files);
// Mount result widget
self.mount_find(input);
self.update_find_list();
// Initialize tab
self.browser.change_tab(match self.browser.tab() {
FileExplorerTab::Local => FileExplorerTab::FindLocal,
FileExplorerTab::Remote => FileExplorerTab::FindRemote,
_ => FileExplorerTab::FindLocal,
});
}
}
None
}
// -- goto popup
(COMPONENT_INPUT_GOTO, key) if key == &MSG_KEY_ESC => {
self.umount_goto();
None
}
(COMPONENT_INPUT_GOTO, Msg::OnSubmit(Payload::One(Value::Str(input)))) => {
match self.browser.tab() {
FileExplorerTab::Local => {
self.action_change_local_dir(input.to_string(), false)
}
FileExplorerTab::Remote => {
self.action_change_remote_dir(input.to_string(), false)
}
_ => panic!("Found tab doesn't support GOTO"),
}
// Umount
self.umount_goto();
// Reload files if sync
if self.browser.sync_browsing {
match self.browser.tab() {
FileExplorerTab::Remote => self.update_local_filelist(),
FileExplorerTab::Local => self.update_remote_filelist(),
_ => None,
};
}
// Reload files
match self.browser.tab() {
FileExplorerTab::Local => self.update_local_filelist(),
FileExplorerTab::Remote => self.update_remote_filelist(),
_ => None,
}
}
(COMPONENT_INPUT_GOTO, _) => None,
// -- make directory
(COMPONENT_INPUT_MKDIR, key) if key == &MSG_KEY_ESC => {
self.umount_mkdir();
None
}
(COMPONENT_INPUT_MKDIR, Msg::OnSubmit(Payload::One(Value::Str(input)))) => {
match self.browser.tab() {
FileExplorerTab::Local => self.action_local_mkdir(input.to_string()),
FileExplorerTab::Remote => self.action_remote_mkdir(input.to_string()),
_ => panic!("Found tab doesn't support MKDIR"),
}
self.umount_mkdir();
// Reload files
match self.browser.tab() {
FileExplorerTab::Local => self.update_local_filelist(),
FileExplorerTab::Remote => self.update_remote_filelist(),
_ => None,
}
}
(COMPONENT_INPUT_MKDIR, _) => None,
// -- new file
(COMPONENT_INPUT_NEWFILE, key) if key == &MSG_KEY_ESC => {
self.umount_newfile();
None
}
(COMPONENT_INPUT_NEWFILE, Msg::OnSubmit(Payload::One(Value::Str(input)))) => {
match self.browser.tab() {
FileExplorerTab::Local => self.action_local_newfile(input.to_string()),
FileExplorerTab::Remote => self.action_remote_newfile(input.to_string()),
_ => panic!("Found tab doesn't support NEWFILE"),
}
self.umount_newfile();
// Reload files
match self.browser.tab() {
FileExplorerTab::Local => self.update_local_filelist(),
FileExplorerTab::Remote => self.update_remote_filelist(),
_ => None,
}
}
(COMPONENT_INPUT_NEWFILE, _) => None,
// -- open with
(COMPONENT_INPUT_OPEN_WITH, key) if key == &MSG_KEY_ESC => {
self.umount_openwith();
None
}
(COMPONENT_INPUT_OPEN_WITH, Msg::OnSubmit(Payload::One(Value::Str(input)))) => {
match self.browser.tab() {
FileExplorerTab::Local => self.action_local_open_with(input),
FileExplorerTab::Remote => self.action_remote_open_with(input),
FileExplorerTab::FindLocal | FileExplorerTab::FindRemote => {
self.action_find_open_with(input)
}
}
self.umount_openwith();
None
}
(COMPONENT_INPUT_OPEN_WITH, _) => None,
// -- rename
(COMPONENT_INPUT_RENAME, key) if key == &MSG_KEY_ESC => {
self.umount_rename();
None
}
(COMPONENT_INPUT_RENAME, Msg::OnSubmit(Payload::One(Value::Str(input)))) => {
self.umount_rename();
self.mount_blocking_wait("Moving file(s)…");
match self.browser.tab() {
FileExplorerTab::Local => self.action_local_rename(input.to_string()),
FileExplorerTab::Remote => self.action_remote_rename(input.to_string()),
_ => panic!("Found tab doesn't support RENAME"),
}
self.umount_wait();
// Reload files
match self.browser.tab() {
FileExplorerTab::Local => self.update_local_filelist(),
FileExplorerTab::Remote => self.update_remote_filelist(),
_ => None,
}
}
(COMPONENT_INPUT_RENAME, _) => None,
// -- save as
(COMPONENT_INPUT_SAVEAS, key) if key == &MSG_KEY_ESC => {
self.umount_saveas();
None
}
(COMPONENT_INPUT_SAVEAS, Msg::OnSubmit(Payload::One(Value::Str(input)))) => {
match self.browser.tab() {
FileExplorerTab::Local => self.action_local_saveas(input.to_string()),
FileExplorerTab::Remote => self.action_remote_saveas(input.to_string()),
FileExplorerTab::FindLocal | FileExplorerTab::FindRemote => {
// Get entry
self.action_find_transfer(TransferOpts::default().save_as(Some(input)));
}
}
self.umount_saveas();
// Reload files
match self.browser.tab() {
// NOTE: Swapped is intentional
FileExplorerTab::Local => self.update_remote_filelist(),
FileExplorerTab::Remote => self.update_local_filelist(),
FileExplorerTab::FindLocal => self.update_remote_filelist(),
FileExplorerTab::FindRemote => self.update_local_filelist(),
}
}
(COMPONENT_INPUT_SAVEAS, _) => None,
// -- fileinfo
(COMPONENT_LIST_FILEINFO, key) | (COMPONENT_LIST_FILEINFO, key)
if key == &MSG_KEY_ENTER || key == &MSG_KEY_ESC =>
{
self.umount_file_info();
None
}
(COMPONENT_LIST_FILEINFO, _) => None,
// -- delete
(COMPONENT_RADIO_DELETE, key)
if key == &MSG_KEY_ESC
|| key == &Msg::OnSubmit(Payload::One(Value::Usize(1))) =>
{
self.umount_radio_delete();
None
}
(COMPONENT_RADIO_DELETE, Msg::OnSubmit(Payload::One(Value::Usize(0)))) => {
// Choice is 'YES'
self.umount_radio_delete();
self.mount_blocking_wait("Removing file(s)…");
match self.browser.tab() {
FileExplorerTab::Local => self.action_local_delete(),
FileExplorerTab::Remote => self.action_remote_delete(),
FileExplorerTab::FindLocal | FileExplorerTab::FindRemote => {
// Get entry
self.action_find_delete();
// Delete entries
match self.view.get_state(COMPONENT_EXPLORER_FIND) {
Some(Payload::One(Value::Usize(idx))) => {
// Reload entries
self.found_mut().unwrap().del_entry(idx);
}
Some(Payload::Vec(values)) => {
values
.iter()
.map(|x| match x {
Value::Usize(v) => *v,
_ => 0,
})
.for_each(|x| self.found_mut().unwrap().del_entry(x));
}
_ => {}
}
self.update_find_list();
}
}
self.umount_wait();
// Reload files
match self.browser.tab() {
FileExplorerTab::Local => self.update_local_filelist(),
FileExplorerTab::Remote => self.update_remote_filelist(),
FileExplorerTab::FindLocal => self.update_local_filelist(),
FileExplorerTab::FindRemote => self.update_remote_filelist(),
}
}
(COMPONENT_RADIO_DELETE, _) => None,
// -- replace
(COMPONENT_RADIO_REPLACE, key)
if key == &MSG_KEY_ESC
|| key == &Msg::OnSubmit(Payload::One(Value::Usize(1))) =>
{
self.umount_radio_replace();
None
}
(COMPONENT_RADIO_REPLACE, key) if key == &MSG_KEY_TAB => {
if self.is_radio_replace_extended() {
self.view.active(COMPONENT_LIST_REPLACING_FILES);
}
None
}
(COMPONENT_RADIO_REPLACE, Msg::OnSubmit(Payload::One(Value::Usize(0)))) => {
// Choice is 'YES'
self.umount_radio_replace();
self.action_finalize_pending_transfer();
None
}
(COMPONENT_RADIO_REPLACE, _) => None,
(COMPONENT_LIST_REPLACING_FILES, key) if key == &MSG_KEY_TAB => {
self.view.active(COMPONENT_RADIO_REPLACE);
None
}
(COMPONENT_LIST_REPLACING_FILES, key) if key == &MSG_KEY_ESC => {
self.umount_radio_replace();
None
}
(COMPONENT_LIST_REPLACING_FILES, _) => None,
// -- disconnect
(COMPONENT_RADIO_DISCONNECT, key)
if key == &MSG_KEY_ESC
|| key == &Msg::OnSubmit(Payload::One(Value::Usize(1))) =>
{
self.umount_disconnect();
None
}
(COMPONENT_RADIO_DISCONNECT, Msg::OnSubmit(Payload::One(Value::Usize(0)))) => {
self.disconnect();
self.umount_disconnect();
None
}
(COMPONENT_RADIO_DISCONNECT, _) => None,
// -- quit
(COMPONENT_RADIO_QUIT, key)
if key == &MSG_KEY_ESC
|| key == &Msg::OnSubmit(Payload::One(Value::Usize(1))) =>
{
self.umount_quit();
None
}
(COMPONENT_RADIO_QUIT, Msg::OnSubmit(Payload::One(Value::Usize(0)))) => {
self.disconnect_and_quit();
self.umount_quit();
None
}
(COMPONENT_RADIO_QUIT, _) => None,
// -- sorting
(COMPONENT_RADIO_SORTING, key) if key == &MSG_KEY_ESC => {
self.umount_file_sorting();
None
}
(COMPONENT_RADIO_SORTING, Msg::OnSubmit(_)) => {
self.umount_file_sorting();
None
}
(COMPONENT_RADIO_SORTING, Msg::OnChange(Payload::One(Value::Usize(mode)))) => {
// Get sorting mode
let sorting: FileSorting = match mode {
1 => FileSorting::ModifyTime,
2 => FileSorting::CreationTime,
3 => FileSorting::Size,
_ => FileSorting::Name,
};
match self.browser.tab() {
FileExplorerTab::Local => self.local_mut().sort_by(sorting),
FileExplorerTab::Remote => self.remote_mut().sort_by(sorting),
_ => panic!("Found result doesn't support SORTING"),
}
// Update status bar
match self.browser.tab() {
FileExplorerTab::Local => self.refresh_local_status_bar(),
FileExplorerTab::Remote => self.refresh_remote_status_bar(),
_ => panic!("Found result doesn't support SORTING"),
};
// Reload files
match self.browser.tab() {
FileExplorerTab::Local => self.update_local_filelist(),
FileExplorerTab::Remote => self.update_remote_filelist(),
_ => None,
}
}
(COMPONENT_RADIO_SORTING, _) => None,
// -- error
(COMPONENT_TEXT_ERROR, key) | (COMPONENT_TEXT_ERROR, key)
if key == &MSG_KEY_ESC || key == &MSG_KEY_ENTER =>
{
self.umount_error();
None
}
(COMPONENT_TEXT_ERROR, _) => None,
// -- fatal
(COMPONENT_TEXT_FATAL, key) | (COMPONENT_TEXT_FATAL, key)
if key == &MSG_KEY_ESC || key == &MSG_KEY_ENTER =>
{
self.exit_reason = Some(super::ExitReason::Disconnect);
None
}
(COMPONENT_TEXT_FATAL, _) => None,
// -- help
(COMPONENT_TEXT_HELP, key) | (COMPONENT_TEXT_HELP, key)
if key == &MSG_KEY_ESC || key == &MSG_KEY_ENTER =>
{
self.umount_help();
None
}
(COMPONENT_TEXT_HELP, _) => None,
// -- progress bar
(COMPONENT_PROGRESS_BAR_PARTIAL, key) if key == &MSG_KEY_CTRL_C => {
// Set transfer aborted to True
self.transfer.abort();
None
}
(COMPONENT_PROGRESS_BAR_PARTIAL, _) => None,
// -- fallback
(_, _) => None, // Nothing to do
},
}
}
}
impl FileTransferActivity {
/// ### update_local_filelist
///
/// Update local file list
pub(super) fn update_local_filelist(&mut self) -> Option<(String, Msg)> {
match self.view.get_props(super::COMPONENT_EXPLORER_LOCAL) {
Some(props) => {
// Get width
let width: usize = self
.context()
.store()
.get_unsigned(super::STORAGE_EXPLORER_WIDTH)
.unwrap_or(256);
let hostname: String = match hostname::get() {
Ok(h) => {
let hostname: String = h.as_os_str().to_string_lossy().to_string();
let tokens: Vec<&str> = hostname.split('.').collect();
String::from(*tokens.get(0).unwrap_or(&"localhost"))
}
Err(_) => String::from("localhost"),
};
let hostname: String = format!(
"{}:{} ",
hostname,
fmt_path_elide_ex(self.local().wrkdir.as_path(), width, hostname.len() + 3) // 3 because of '/…/'
);
let files: Vec<String> = self
.local()
.iter_files()
.map(|x: &FsEntry| self.local().fmt_file(x))
.collect();
// Update
let props = FileListPropsBuilder::from(props)
.with_files(files)
.with_title(hostname, Alignment::Left)
.build();
// Update
self.view.update(super::COMPONENT_EXPLORER_LOCAL, props)
}
None => None,
}
}
/// ### update_remote_filelist
///
/// Update remote file list
pub(super) fn update_remote_filelist(&mut self) -> Option<(String, Msg)> {
match self.view.get_props(super::COMPONENT_EXPLORER_REMOTE) {
Some(props) => {
// Get width
let width: usize = self
.context()
.store()
.get_unsigned(super::STORAGE_EXPLORER_WIDTH)
.unwrap_or(256);
let hostname = self.get_remote_hostname();
let hostname: String = format!(
"{}:{} ",
hostname,
fmt_path_elide_ex(
self.remote().wrkdir.as_path(),
width,
hostname.len() + 3 // 3 because of '/…/'
)
);
let files: Vec<String> = self
.remote()
.iter_files()
.map(|x: &FsEntry| self.remote().fmt_file(x))
.collect();
// Update
let props = FileListPropsBuilder::from(props)
.with_files(files)
.with_title(hostname, Alignment::Left)
.build();
self.view.update(super::COMPONENT_EXPLORER_REMOTE, props)
}
None => None,
}
}
/// ### update_logbox
///
/// Update log box
pub(super) fn update_logbox(&mut self) -> Option<(String, Msg)> {
match self.view.get_props(super::COMPONENT_LOG_BOX) {
Some(props) => {
// Make log entries
let mut table: TableBuilder = TableBuilder::default();
for (idx, record) in self.log_records.iter().enumerate() {
// Add row if not first row
if idx > 0 {
table.add_row();
}
let fg = match record.level {
LogLevel::Error => Color::Red,
LogLevel::Warn => Color::Yellow,
LogLevel::Info => Color::Green,
};
table
.add_col(TextSpan::from(format!(
"{}",
record.time.format("%Y-%m-%dT%H:%M:%S%Z")
)))
.add_col(TextSpan::from(" ["))
.add_col(
TextSpan::new(
format!(
"{:5}",
match record.level {
LogLevel::Error => "ERROR",
LogLevel::Warn => "WARN",
LogLevel::Info => "INFO",
}
)
.as_str(),
)
.fg(fg),
)
.add_col(TextSpan::from("]: "))
.add_col(TextSpan::from(record.msg.as_ref()));
}
let table = table.build();
let props = LogboxPropsBuilder::from(props).with_log(table).build();
self.view.update(super::COMPONENT_LOG_BOX, props)
}
None => None,
}
}
pub(super) fn update_progress_bar(&mut self, filename: String) -> Option<(String, Msg)> {
if let Some(props) = self.view.get_props(COMPONENT_PROGRESS_BAR_FULL) {
let props = ProgressBarPropsBuilder::from(props)
.with_label(self.transfer.full.to_string())
.with_progress(self.transfer.full.calc_progress())
.build();
let _ = self.view.update(COMPONENT_PROGRESS_BAR_FULL, props);
}
match self.view.get_props(COMPONENT_PROGRESS_BAR_PARTIAL) {
Some(props) => {
let props = ProgressBarPropsBuilder::from(props)
.with_title(filename, Alignment::Center)
.with_label(self.transfer.partial.to_string())
.with_progress(self.transfer.partial.calc_progress())
.build();
self.view.update(COMPONENT_PROGRESS_BAR_PARTIAL, props)
}
None => None,
}
}
/// ### finalize_find
///
/// Finalize find process
fn finalize_find(&mut self) {
// Set found to none
self.browser.del_found();
// Restore tab
self.browser.change_tab(match self.browser.tab() {
FileExplorerTab::FindLocal => FileExplorerTab::Local,
FileExplorerTab::FindRemote => FileExplorerTab::Remote,
_ => FileExplorerTab::Local,
});
}
fn update_find_list(&mut self) -> Option<(String, Msg)> {
match self.view.get_props(COMPONENT_EXPLORER_FIND) {
None => None,
Some(props) => {
// Prepare files
let files: Vec<String> = self
.found()
.unwrap()
.iter_files()
.map(|x: &FsEntry| self.found().unwrap().fmt_file(x))
.collect();
let props = FileListPropsBuilder::from(props).with_files(files).build();
self.view.update(COMPONENT_EXPLORER_FIND, props)
}
}
}
}