diff --git a/README.md b/README.md index 63c52c6..da00865 100644 --- a/README.md +++ b/README.md @@ -359,6 +359,7 @@ If left empty, the default formatter syntax will be used: `{NAME:24} {PEX} {USER | `` | Quit TermSCP | Quit | | `` | Rename file | Rename | | `` | Go to parent directory | Upper | +| `` | Execute a command | eXecute | | `` | Delete file | | | `` | Abort file transfer process | | diff --git a/src/ui/activities/filetransfer_activity/actions.rs b/src/ui/activities/filetransfer_activity/actions.rs index 2fac2ff..3a620d8 100644 --- a/src/ui/activities/filetransfer_activity/actions.rs +++ b/src/ui/activities/filetransfer_activity/actions.rs @@ -444,6 +444,47 @@ impl FileTransferActivity { } } + pub(super) fn action_local_exec(&mut self, input: String) { + match self.context.as_mut().unwrap().local.exec(input.as_str()) { + Ok(output) => { + // Reload files + self.log( + LogLevel::Info, + format!("\"{}\" output: \"{}\"", input, output).as_ref(), + ); + let wrkdir: PathBuf = self.local.wrkdir.clone(); + self.local_scan(wrkdir.as_path()); + } + Err(err) => { + // Report err + self.log_and_alert( + LogLevel::Error, + format!("Could not execute command \"{}\": {}", input, err), + ); + } + } + } + + pub(super) fn action_remote_exec(&mut self, input: String) { + match self.client.as_mut().exec(input.as_str()) { + Ok(output) => { + // Reload files + self.log( + LogLevel::Info, + format!("\"{}\" output: \"{}\"", input, output).as_ref(), + ); + self.reload_remote_dir(); + } + Err(err) => { + // Report err + self.log_and_alert( + LogLevel::Error, + format!("Could not execute command \"{}\": {}", input, err), + ); + } + } + } + /// ### get_local_file_entry /// /// Get local file entry diff --git a/src/ui/activities/filetransfer_activity/mod.rs b/src/ui/activities/filetransfer_activity/mod.rs index 919b8ea..724ae58 100644 --- a/src/ui/activities/filetransfer_activity/mod.rs +++ b/src/ui/activities/filetransfer_activity/mod.rs @@ -70,6 +70,7 @@ const COMPONENT_TEXT_ERROR: &str = "TEXT_ERROR"; const COMPONENT_TEXT_WAIT: &str = "TEXT_WAIT"; const COMPONENT_TEXT_FATAL: &str = "TEXT_FATAL"; const COMPONENT_INPUT_COPY: &str = "INPUT_COPY"; +const COMPONENT_INPUT_EXEC: &str = "INPUT_EXEC"; const COMPONENT_INPUT_MKDIR: &str = "INPUT_MKDIR"; const COMPONENT_INPUT_GOTO: &str = "INPUT_GOTO"; const COMPONENT_INPUT_SAVEAS: &str = "INPUT_SAVEAS"; diff --git a/src/ui/activities/filetransfer_activity/update.rs b/src/ui/activities/filetransfer_activity/update.rs index 3caf6f3..1d99172 100644 --- a/src/ui/activities/filetransfer_activity/update.rs +++ b/src/ui/activities/filetransfer_activity/update.rs @@ -28,8 +28,8 @@ extern crate bytesize; // locals use super::{ FileExplorerTab, FileTransferActivity, LogLevel, COMPONENT_EXPLORER_LOCAL, - COMPONENT_EXPLORER_REMOTE, COMPONENT_INPUT_COPY, COMPONENT_INPUT_GOTO, COMPONENT_INPUT_MKDIR, - COMPONENT_INPUT_NEWFILE, COMPONENT_INPUT_RENAME, COMPONENT_INPUT_SAVEAS, + COMPONENT_EXPLORER_REMOTE, COMPONENT_INPUT_COPY, COMPONENT_INPUT_EXEC, 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, @@ -343,10 +343,16 @@ impl FileTransferActivity { } (COMPONENT_EXPLORER_LOCAL, &MSG_KEY_CHAR_S) | (COMPONENT_EXPLORER_REMOTE, &MSG_KEY_CHAR_S) => { - // Mount rename + // Mount save as self.mount_saveas(); None } + (COMPONENT_EXPLORER_LOCAL, &MSG_KEY_CHAR_X) + | (COMPONENT_EXPLORER_REMOTE, &MSG_KEY_CHAR_X) => { + // Mount exec + self.mount_exec(); + None + } (COMPONENT_EXPLORER_LOCAL, &MSG_KEY_ESC) | (COMPONENT_EXPLORER_REMOTE, &MSG_KEY_ESC) | (COMPONENT_LOG_BOX, &MSG_KEY_ESC) => { @@ -389,6 +395,24 @@ impl FileTransferActivity { FileExplorerTab::Remote => self.update_remote_filelist(), } } + // -- exec popup + (COMPONENT_INPUT_EXEC, &MSG_KEY_ESC) => { + self.umount_exec(); + None + } + (COMPONENT_INPUT_EXEC, Msg::OnSubmit(Payload::Text(input))) => { + // Exex command + match self.tab { + FileExplorerTab::Local => self.action_local_exec(input.to_string()), + FileExplorerTab::Remote => self.action_remote_exec(input.to_string()), + } + self.umount_exec(); + // Reload files + match self.tab { + FileExplorerTab::Local => self.update_local_filelist(), + FileExplorerTab::Remote => self.update_remote_filelist(), + } + } // -- goto popup (COMPONENT_INPUT_GOTO, &MSG_KEY_ESC) => { self.umount_goto(); diff --git a/src/ui/activities/filetransfer_activity/view.rs b/src/ui/activities/filetransfer_activity/view.rs index ffef55f..bced2e7 100644 --- a/src/ui/activities/filetransfer_activity/view.rs +++ b/src/ui/activities/filetransfer_activity/view.rs @@ -186,6 +186,14 @@ impl FileTransferActivity { self.view.render(super::COMPONENT_INPUT_SAVEAS, f, popup); } } + if let Some(mut props) = self.view.get_props(super::COMPONENT_INPUT_EXEC) { + if props.build().visible { + let popup = draw_area_in(f.size(), 40, 10); + f.render_widget(Clear, popup); + // make popup + self.view.render(super::COMPONENT_INPUT_EXEC, f, popup); + } + } if let Some(mut props) = self.view.get_props(super::COMPONENT_LIST_FILEINFO) { if props.build().visible { let popup = draw_area_in(f.size(), 50, 50); @@ -411,6 +419,22 @@ impl FileTransferActivity { self.view.umount(super::COMPONENT_INPUT_COPY); } + pub(super) fn mount_exec(&mut self) { + self.view.mount( + super::COMPONENT_INPUT_EXEC, + Box::new(Input::new( + PropsBuilder::default() + .with_texts(TextParts::new(Some(String::from("Execute command")), None)) + .build(), + )), + ); + self.view.active(super::COMPONENT_INPUT_EXEC); + } + + pub(super) fn umount_exec(&mut self) { + self.view.umount(super::COMPONENT_INPUT_EXEC); + } + pub(super) fn mount_goto(&mut self) { self.view.mount( super::COMPONENT_INPUT_GOTO, diff --git a/src/ui/activities/keymap.rs b/src/ui/activities/keymap.rs index c86d502..6b5d938 100644 --- a/src/ui/activities/keymap.rs +++ b/src/ui/activities/keymap.rs @@ -174,10 +174,12 @@ pub const MSG_KEY_CHAR_W: Msg = Msg::OnKey(KeyEvent { code: KeyCode::Char('w'), modifiers: KeyModifiers::NONE, }); +*/ pub const MSG_KEY_CHAR_X: Msg = Msg::OnKey(KeyEvent { code: KeyCode::Char('x'), modifiers: KeyModifiers::NONE, }); +/* pub const MSG_KEY_CHAR_Y: Msg = Msg::OnKey(KeyEvent { code: KeyCode::Char('y'), modifiers: KeyModifiers::NONE,