diff options
Diffstat (limited to 'src/modes/mode.rs')
-rw-r--r-- | src/modes/mode.rs | 292 |
1 files changed, 292 insertions, 0 deletions
diff --git a/src/modes/mode.rs b/src/modes/mode.rs new file mode 100644 index 0000000..13817c2 --- /dev/null +++ b/src/modes/mode.rs @@ -0,0 +1,292 @@ +use std::fmt; + +use crate::common::{ + CHMOD_LINES, FILTER_LINES, NEWDIR_LINES, NEWFILE_LINES, NVIM_ADDRESS_LINES, + PASSWORD_LINES_DEVICE, PASSWORD_LINES_SUDO, REGEX_LINES, REMOTE_LINES, RENAME_LINES, + SHELL_LINES, SORT_LINES, +}; +use crate::modes::BlockDeviceAction; +use crate::modes::InputCompleted; +use crate::modes::{PasswordKind, PasswordUsage}; + +/// Different kind of mark actions. +/// Either we jump to an existing mark or we save current path to a mark. +/// In both case, we'll have to listen to the next char typed. +#[derive(Clone, Copy, Debug)] +pub enum MarkAction { + /// Jump to a selected mark (ie a path associated to a char) + Jump, + /// Creates a new mark (a path associated to a char) + New, +} + +/// Different kind of last edition command received requiring a confirmation. +/// Copy, move and delete require a confirmation to prevent big mistakes. +#[derive(Clone, Copy, Debug)] +pub enum NeedConfirmation { + /// Copy flagged files + Copy, + /// Delete flagged files + Delete, + /// Move flagged files + Move, + /// Empty Trash + EmptyTrash, +} + +impl NeedConfirmation { + /// Offset before the cursor. + /// Since we ask the user confirmation, we need to know how much space + /// is needed. + #[must_use] + pub fn cursor_offset(&self) -> usize { + self.to_string().len() + 9 + } + + /// A confirmation message to be displayed before executing the mode. + /// When files are moved or copied the destination is displayed. + #[must_use] + pub fn confirmation_string(&self, destination: &str) -> String { + match *self { + Self::Copy => { + format!("Files will be copied to {destination}") + } + Self::Delete | Self::EmptyTrash => "Files will be deleted permanently".to_owned(), + Self::Move => { + format!("Files will be moved to {destination}") + } + } + } +} + +impl std::fmt::Display for NeedConfirmation { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + match *self { + Self::Delete => write!(f, "Delete files :"), + Self::Move => write!(f, "Move files here :"), + Self::Copy => write!(f, "Copy files here :"), + Self::EmptyTrash => write!(f, "Empty the trash ?"), + } + } +} + +/// Different modes in which the user is expeted to type something. +/// It may be a new filename, a mode (aka an octal permission), +/// the name of a new file, of a new directory, +/// A regex to match all files in current directory, +/// a kind of sort, a mark name, a new mark or a filter. +#[derive(Clone, Copy)] +pub enum InputSimple { + /// Rename the selected file + Rename, + /// Change permissions of the selected file + Chmod, + /// Touch a new file + Newfile, + /// Mkdir a new directory + Newdir, + /// Flag files matching a regex + RegexMatch, + /// Change the type of sort + Sort, + /// Filter by extension, name, directory or no filter + Filter, + /// Set a new neovim RPC address + SetNvimAddr, + /// Input a password (chars a replaced by *) + Password(Option<BlockDeviceAction>, PasswordUsage), + /// Shell command execute as is + Shell, + /// Mount a remote directory with sshfs + Remote, +} + +impl fmt::Display for InputSimple { + fn fmt(&self, f: &mut std::fmt::Formatter) -> fmt::Result { + match *self { + Self::Rename => write!(f, "Rename: "), + Self::Chmod => write!(f, "Chmod: "), + Self::Newfile => write!(f, "Newfile: "), + Self::Newdir => write!(f, "Newdir: "), + Self::RegexMatch => write!(f, "Regex: "), + Self::SetNvimAddr => write!(f, "Neovim: "), + Self::Shell => write!(f, "Shell: "), + Self::Sort => { + write!(f, "Sort: Kind Name Modif Size Ext Rev :") + } + Self::Filter => write!(f, "Filter: "), + Self::Password(_, PasswordUsage::CRYPTSETUP(password_kind)) => { + write!(f, "{password_kind}") + } + Self::Password(_, _) => write!(f, " sudo: "), + Self::Remote => write!(f, "Remote: "), + } + } +} + +impl InputSimple { + const EDIT_BOX_OFFSET: usize = 11; + const SORT_CURSOR_OFFSET: usize = 39; + const PASSWORD_CURSOR_OFFSET: usize = 9; + + /// Returns a vector of static &str describing what + /// the mode does. + #[must_use] + pub const fn lines(&self) -> &'static [&'static str] { + match *self { + Self::Chmod => &CHMOD_LINES, + Self::Filter => &FILTER_LINES, + Self::Newdir => &NEWDIR_LINES, + Self::Newfile => &NEWFILE_LINES, + Self::Password(_, PasswordUsage::CRYPTSETUP(PasswordKind::SUDO)) => { + &PASSWORD_LINES_SUDO + } + Self::Password(_, PasswordUsage::CRYPTSETUP(PasswordKind::CRYPTSETUP)) => { + &PASSWORD_LINES_DEVICE + } + Self::Password(_, _) => &PASSWORD_LINES_SUDO, + Self::RegexMatch => ®EX_LINES, + Self::Rename => &RENAME_LINES, + Self::SetNvimAddr => &NVIM_ADDRESS_LINES, + Self::Shell => &SHELL_LINES, + Self::Sort => &SORT_LINES, + Self::Remote => &REMOTE_LINES, + } + } + + fn cursor_offset(&self) -> usize { + match *self { + Self::Sort => Self::SORT_CURSOR_OFFSET, + Self::Password(_, _) => Self::PASSWORD_CURSOR_OFFSET, + _ => Self::EDIT_BOX_OFFSET, + } + } +} + +/// Different modes in which we display a bunch of possible actions. +/// In all those mode we can select an action and execute it. +/// For some of them, it's just moving there, for some it acts on some file. +#[derive(Clone, Copy, Debug)] +pub enum Navigate { + /// Navigate to a flagged file + Jump, + /// Navigate back to a visited path + History, + /// Navigate to a predefined shortcut + Shortcut, + /// Manipulate trash files + Trash, + /// Manipulate an encrypted device + EncryptedDrive, + /// Removable devices + RemovableDevices, + /// Edit a mark or cd to it + Marks(MarkAction), + /// Pick a compression method + Compress, + /// Bulk rename, new files, new directories + Bulk, + /// Shell menu applications. Start a new shell with this application. + TuiApplication, + /// Cli info + CliApplication, + /// Context menu + Context, +} + +impl fmt::Display for Navigate { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + match *self { + Self::Marks(_) => write!(f, "Marks jump:"), + Self::Jump => write!( + f, + "Flagged files: <Enter> go to file -- <SPC> remove flag -- <u> unflag all -- <x> delete -- <X> trash" + ), + Self::History => write!(f, "History :"), + Self::Shortcut => write!(f, "Shortcut :"), + Self::Trash => write!(f, "Trash :"), + Self::TuiApplication => { + write!(f, "Start a new shell running a command:") + } + Self::Bulk => { + write!(f, "Bulk: rename flagged files or create new files") + } + Self::Compress => write!(f, "Compress :"), + Self::EncryptedDrive => { + write!(f, "Encrypted devices :") + } + Self::RemovableDevices => { + write!(f, "Removable devices :") + } + Self::CliApplication => write!(f, "Display infos :"), + Self::Context=> write!(f, "Context"), + } + } +} + +/// Different "edit" mode in which the application can be. +/// It dictates the reaction to event and what to display in the bottom window. +#[derive(Clone, Copy)] +pub enum Edit { + /// Do something that may be completed + /// Completion may come from : + /// - executable in $PATH, + /// - current directory or tree, + /// - directory in your file system, + /// - known actions. See [`crate::event::EventAction`], + InputCompleted(InputCompleted), + /// Do something that need typing : + /// - renaming a file or directory, + /// - creating a file or directory, + /// - typing a password (won't be displayed, will be dropped ASAP) + InputSimple(InputSimple), + /// Select something in a list and act on it + Navigate(Navigate), + /// Confirmation is required before modification is made to existing files : + /// delete, move, copy + NeedConfirmation(NeedConfirmation), + /// No action is currently performed + Nothing, +} + +impl fmt::Display for Edit { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + match *self { + Self::InputSimple(input_simple) => input_simple.fmt(f), + Self::InputCompleted(input_completed) => input_completed.fmt(f), + Self::Navigate(navigate) => navigate.fmt(f), + Self::NeedConfirmation(need_confirmation) => need_confirmation.fmt(f), + Self::Nothing => write!(f, ""), + } + } +} + +impl Edit { + /// Constant offset for the cursor. + /// In any mode, we display the mode used and then the cursor if needed. + pub fn cursor_offset(&self) -> usize { + match self { + Self::InputCompleted(input_completed) => input_completed.cursor_offset(), + Self::InputSimple(input_simple) => input_simple.cursor_offset(), + Self::Navigate(_) => 0, + Self::NeedConfirmation(confirmed_action) => confirmed_action.cursor_offset(), + Self::Nothing => 0, + } + } + + /// Does this mode requires a cursor ? + pub fn show_cursor(&self) -> bool { + self.cursor_offset() != 0 + } +} + +#[derive(Default)] +pub enum Display { + #[default] + /// Display the files like `ls -lh` does + Directory, + /// Display files like `tree` does + Tree, + /// Preview a file or directory + Preview, +} |