diff options
author | qkzk <qu3nt1n@gmail.com> | 2022-12-26 00:39:05 +0100 |
---|---|---|
committer | qkzk <qu3nt1n@gmail.com> | 2022-12-26 00:39:05 +0100 |
commit | 6e2fbce3c01e1d877de5ef595b0d1cab88b02808 (patch) | |
tree | fc31a2550f54a4283abd7b7d468584d41f0a0e93 /src | |
parent | eab76cb61ee82e9433f568e56e2c65299d0aa415 (diff) |
trash: first working draft, very basic api
Diffstat (limited to 'src')
-rw-r--r-- | src/action_map.rs | 8 | ||||
-rw-r--r-- | src/completion.rs | 14 | ||||
-rw-r--r-- | src/constant_strings_paths.rs | 6 | ||||
-rw-r--r-- | src/event_dispatch.rs | 10 | ||||
-rw-r--r-- | src/event_exec.rs | 111 | ||||
-rw-r--r-- | src/keybindings.rs | 3 | ||||
-rw-r--r-- | src/marks.rs | 12 | ||||
-rw-r--r-- | src/mode.rs | 49 | ||||
-rw-r--r-- | src/status.rs | 5 | ||||
-rw-r--r-- | src/term_manager.rs | 48 | ||||
-rw-r--r-- | src/trash.rs | 154 | ||||
-rw-r--r-- | src/utils.rs | 11 |
12 files changed, 297 insertions, 134 deletions
diff --git a/src/action_map.rs b/src/action_map.rs index 05b92a3..810b019 100644 --- a/src/action_map.rs +++ b/src/action_map.rs @@ -67,6 +67,10 @@ pub enum ActionMap { ToggleDualPane, ToggleFlag, ToggleHidden, + TrashMoveFile, + TrashRestoreFile, + TrashEmpty, + TrashOpen, } impl ActionMap { @@ -131,6 +135,10 @@ impl ActionMap { ActionMap::ToggleDualPane => EventExec::event_toggle_dualpane(status), ActionMap::ToggleFlag => EventExec::event_toggle_flag(status), ActionMap::ToggleHidden => EventExec::event_toggle_hidden(current_tab), + ActionMap::TrashMoveFile => EventExec::event_trash_move_file(status), + ActionMap::TrashRestoreFile => EventExec::event_trash_restore_file(status), + ActionMap::TrashEmpty => EventExec::event_trash_empty(status), + ActionMap::TrashOpen => EventExec::event_trash_open(status), ActionMap::Nothing => Ok(()), } diff --git a/src/completion.rs b/src/completion.rs index cd6eabc..71190db 100644 --- a/src/completion.rs +++ b/src/completion.rs @@ -6,7 +6,7 @@ use crate::mode::Mode; /// Different kind of completions #[derive(Clone, Default)] -pub enum CompletionKind { +pub enum InputCompleted { /// No completion needed #[default] Nothing, @@ -22,7 +22,7 @@ pub enum CompletionKind { /// showing where the user is in the vec. #[derive(Clone, Default)] pub struct Completion { - pub kind: CompletionKind, + pub kind: InputCompleted, /// Possible completions pub proposals: Vec<String>, /// Which completion is selected by the user @@ -34,7 +34,7 @@ impl Completion { if let Mode::InputCompleted(completion_kind) = mode { self.kind = completion_kind.clone() } else { - self.kind = CompletionKind::Nothing + self.kind = InputCompleted::Nothing } } @@ -104,10 +104,10 @@ impl Completion { current_path: String, ) -> FmResult<()> { match self.kind { - CompletionKind::Exec => self.exec(input_string), - CompletionKind::Goto => self.goto(input_string, current_path), - CompletionKind::Search => self.search(input_string, path_content), - CompletionKind::Nothing => Ok(()), + InputCompleted::Exec => self.exec(input_string), + InputCompleted::Goto => self.goto(input_string, current_path), + InputCompleted::Search => self.search(input_string, path_content), + InputCompleted::Nothing => Ok(()), } } diff --git a/src/constant_strings_paths.rs b/src/constant_strings_paths.rs index 45794fb..2deecfb 100644 --- a/src/constant_strings_paths.rs +++ b/src/constant_strings_paths.rs @@ -51,3 +51,9 @@ pub static HARDCODED_SHORTCUTS: [&str; 9] = [ "/tmp", "/usr", ]; +/// Path to the trash folder +pub static TRASH_FOLDER: &str = "~/.config/fm/trash/"; +/// Path the to the file holding original places of trashed_files +pub static TRASH_FILE: &str = "~/.config/fm/trash_file"; +/// Path toe the temporary file used to write the trash file. +pub static TRASH_FILE_TEMP: &str = "~/tmp/trash_file_temp"; diff --git a/src/event_dispatch.rs b/src/event_dispatch.rs index 71bfae6..84e9e17 100644 --- a/src/event_dispatch.rs +++ b/src/event_dispatch.rs @@ -3,7 +3,7 @@ use tuikit::prelude::{Event, Key, MouseButton}; use crate::event_exec::EventExec; use crate::fm_error::FmResult; use crate::keybindings::Bindings; -use crate::mode::{InputKind, MarkAction, Mode}; +use crate::mode::{InputSimple, MarkAction, Mode}; use crate::status::Status; /// Struct which mutates `tabs.selected().. @@ -53,17 +53,17 @@ impl EventDispatcher { fn char(&self, status: &mut Status, key_char: Key) -> FmResult<()> { match key_char { Key::Char(c) => match status.selected_non_mut().mode { - Mode::InputSimple(InputKind::Marks(MarkAction::Jump)) => { + Mode::InputSimple(InputSimple::Marks(MarkAction::Jump)) => { EventExec::exec_marks_jump(status, c) } - Mode::InputSimple(InputKind::Marks(MarkAction::New)) => { + Mode::InputSimple(InputSimple::Marks(MarkAction::New)) => { EventExec::exec_marks_new(status, c) } - Mode::InputSimple(InputKind::Sort) => { + Mode::InputSimple(InputSimple::Sort) => { EventExec::event_leave_sort(status.selected(), c); Ok(()) } - Mode::InputSimple(InputKind::RegexMatch) => { + Mode::InputSimple(InputSimple::RegexMatch) => { EventExec::event_text_insertion(status.selected(), c); status.select_from_regex()?; Ok(()) diff --git a/src/event_exec.rs b/src/event_exec.rs index 84785fe..6864406 100644 --- a/src/event_exec.rs +++ b/src/event_exec.rs @@ -4,7 +4,7 @@ use std::fs; use std::path; use crate::bulkrename::Bulkrename; -use crate::completion::CompletionKind; +use crate::completion::InputCompleted; use crate::constant_strings_paths::DEFAULT_DRAGNDROP; use crate::constant_strings_paths::NVIM_RPC_SENDER; use crate::content_window::RESERVED_ROWS; @@ -13,7 +13,7 @@ use crate::fileinfo::{FileKind, PathContent}; use crate::filter::FilterKind; use crate::fm_error::{FmError, FmResult}; use crate::mode::Navigate; -use crate::mode::{ConfirmedAction, InputKind, MarkAction, Mode}; +use crate::mode::{InputSimple, MarkAction, Mode, NeedConfirmation}; use crate::opener::execute_in_child; use crate::preview::Preview; use crate::selectable_content::SelectableContent; @@ -104,7 +104,7 @@ impl EventExec { if status.selected().path_content.is_empty() { return Ok(()); } - status.selected().mode = Mode::InputSimple(InputKind::Chmod); + status.selected().mode = Mode::InputSimple(InputSimple::Chmod); if status.flagged.is_empty() { status.flagged.push( status.tabs[status.index] @@ -130,7 +130,7 @@ impl EventExec { /// Enter Marks new mode, allowing to bind a char to a path. pub fn event_marks_new(tab: &mut Tab) -> FmResult<()> { - tab.mode = Mode::InputSimple(InputKind::Marks(MarkAction::New)); + tab.mode = Mode::InputSimple(InputSimple::Marks(MarkAction::New)); Ok(()) } @@ -139,7 +139,7 @@ impl EventExec { if status.marks.is_empty() { return Ok(()); } - status.selected().mode = Mode::InputSimple(InputKind::Marks(MarkAction::Jump)); + status.selected().mode = Mode::InputSimple(InputSimple::Marks(MarkAction::Jump)); Ok(()) } @@ -256,7 +256,7 @@ impl EventExec { /// Execute a command requiring a confirmation (Delete, Move or Copy). pub fn exec_confirmed_action( status: &mut Status, - confirmed_action: ConfirmedAction, + confirmed_action: NeedConfirmation, ) -> FmResult<()> { Self::_exec_confirmed_action(status, confirmed_action)?; status.selected().mode = Mode::Normal; @@ -265,12 +265,13 @@ impl EventExec { fn _exec_confirmed_action( status: &mut Status, - confirmed_action: ConfirmedAction, + confirmed_action: NeedConfirmation, ) -> FmResult<()> { match confirmed_action { - ConfirmedAction::Delete => Self::exec_delete_files(status), - ConfirmedAction::Move => Self::exec_cut_paste(status), - ConfirmedAction::Copy => Self::exec_copy_paste(status), + NeedConfirmation::Delete => Self::exec_delete_files(status), + NeedConfirmation::Move => Self::exec_cut_paste(status), + NeedConfirmation::Copy => Self::exec_copy_paste(status), + NeedConfirmation::EmptyTrash => Self::exec_trash_empty(status), } } @@ -496,7 +497,7 @@ impl EventExec { if status.flagged.is_empty() { return Ok(()); } - status.selected().mode = Mode::NeedConfirmation(ConfirmedAction::Copy); + status.selected().mode = Mode::NeedConfirmation(NeedConfirmation::Copy); Ok(()) } @@ -508,26 +509,26 @@ impl EventExec { if status.flagged.is_empty() { return Ok(()); } - status.selected().mode = Mode::NeedConfirmation(ConfirmedAction::Move); + status.selected().mode = Mode::NeedConfirmation(NeedConfirmation::Move); Ok(()) } /// Enter the new dir mode. pub fn event_new_dir(tab: &mut Tab) -> FmResult<()> { - tab.mode = Mode::InputSimple(InputKind::Newdir); + tab.mode = Mode::InputSimple(InputSimple::Newdir); Ok(()) } /// Enter the new file mode. pub fn event_new_file(tab: &mut Tab) -> FmResult<()> { - tab.mode = Mode::InputSimple(InputKind::Newfile); + tab.mode = Mode::InputSimple(InputSimple::Newfile); Ok(()) } /// Enter the execute mode. Most commands must be executed to allow for /// a confirmation. pub fn event_exec(tab: &mut Tab) -> FmResult<()> { - tab.mode = Mode::InputCompleted(CompletionKind::Exec); + tab.mode = Mode::InputCompleted(InputCompleted::Exec); Ok(()) } @@ -556,7 +557,7 @@ impl EventExec { if status.flagged.is_empty() { return Ok(()); } - status.selected().mode = Mode::NeedConfirmation(ConfirmedAction::Delete); + status.selected().mode = Mode::NeedConfirmation(NeedConfirmation::Delete); Ok(()) } @@ -575,20 +576,20 @@ impl EventExec { /// Matching items are displayed as you type them. pub fn event_search(tab: &mut Tab) -> FmResult<()> { tab.searched = None; - tab.mode = Mode::InputCompleted(CompletionKind::Search); + tab.mode = Mode::InputCompleted(InputCompleted::Search); Ok(()) } /// Enter the regex mode. /// Every file matching the typed regex will be flagged. pub fn event_regex_match(tab: &mut Tab) -> FmResult<()> { - tab.mode = Mode::InputSimple(InputKind::RegexMatch); + tab.mode = Mode::InputSimple(InputSimple::RegexMatch); Ok(()) } /// Enter the sort mode, allowing the user to select a sort method. pub fn event_sort(tab: &mut Tab) -> FmResult<()> { - tab.mode = Mode::InputSimple(InputKind::Sort); + tab.mode = Mode::InputSimple(InputSimple::Sort); Ok(()) } @@ -662,13 +663,13 @@ impl EventExec { /// Enter the rename mode. pub fn event_rename(tab: &mut Tab) -> FmResult<()> { - tab.mode = Mode::InputSimple(InputKind::Rename); + tab.mode = Mode::InputSimple(InputSimple::Rename); Ok(()) } /// Enter the goto mode where an user can type a path to jump to. pub fn event_goto(tab: &mut Tab) -> FmResult<()> { - tab.mode = Mode::InputCompleted(CompletionKind::Goto); + tab.mode = Mode::InputCompleted(InputCompleted::Goto); tab.completion.reset(); Ok(()) } @@ -791,7 +792,7 @@ impl EventExec { /// Enter the filter mode, where you can filter. /// See `crate::filter::Filter` for more details. pub fn event_filter(tab: &mut Tab) -> FmResult<()> { - tab.mode = Mode::InputSimple(InputKind::Filter); + tab.mode = Mode::InputSimple(InputSimple::Filter); Ok(()) } @@ -1039,6 +1040,7 @@ impl EventExec { Mode::Normal | Mode::Preview => EventExec::event_up_one_row(status.selected()), Mode::Navigable(Navigate::Jump) => EventExec::event_jumplist_prev(status), Mode::Navigable(Navigate::History) => EventExec::event_history_prev(status.selected()), + Mode::Navigable(Navigate::Trash) => EventExec::event_trash_prev(status), Mode::Navigable(Navigate::Shortcut) => { EventExec::event_shortcut_prev(status.selected()) } @@ -1057,6 +1059,7 @@ impl EventExec { Mode::Normal | Mode::Preview => EventExec::event_down_one_row(status.selected()), Mode::Navigable(Navigate::Jump) => EventExec::event_jumplist_next(status), Mode::Navigable(Navigate::History) => EventExec::event_history_next(status.selected()), + Mode::Navigable(Navigate::Trash) => EventExec::event_trash_next(status), Mode::Navigable(Navigate::Shortcut) => { EventExec::event_shortcut_next(status.selected()) } @@ -1159,26 +1162,27 @@ impl EventExec { /// Reset to normal mode afterwards. pub fn enter(status: &mut Status) -> FmResult<()> { match status.selected().mode { - Mode::InputSimple(InputKind::Rename) => EventExec::exec_rename(status.selected())?, - Mode::InputSimple(InputKind::Newfile) => EventExec::exec_newfile(status.selected())?, - Mode::InputSimple(InputKind::Newdir) => EventExec::exec_newdir(status.selected())?, - Mode::InputSimple(InputKind::Chmod) => EventExec::exec_chmod(status)?, - Mode::InputSimple(InputKind::RegexMatch) => EventExec::exec_regex(status)?, - Mode::InputSimple(InputKind::Filter) => EventExec::exec_filter(status.selected())?, + Mode::InputSimple(InputSimple::Rename) => EventExec::exec_rename(status.selected())?, + Mode::InputSimple(InputSimple::Newfile) => EventExec::exec_newfile(status.selected())?, + Mode::InputSimple(InputSimple::Newdir) => EventExec::exec_newdir(status.selected())?, + Mode::InputSimple(InputSimple::Chmod) => EventExec::exec_chmod(status)?, + Mode::InputSimple(InputSimple::RegexMatch) => EventExec::exec_regex(status)?, + Mode::InputSimple(InputSimple::Filter) => EventExec::exec_filter(status.selected())?, Mode::Navigable(Navigate::Jump) => EventExec::exec_jump(status)?, Mode::Navigable(Navigate::History) => EventExec::exec_history(status.selected())?, Mode::Navigable(Navigate::Shortcut) => EventExec::exec_shortcut(status.selected())?, - Mode::InputCompleted(CompletionKind::Exec) => EventExec::exec_exec(status.selected())?, - Mode::InputCompleted(CompletionKind::Search) => { + Mode::Navigable(Navigate::Trash) => EventExec::event_trash_restore_file(status)?, + Mode::InputCompleted(InputCompleted::Exec) => EventExec::exec_exec(status.selected())?, + Mode::InputCompleted(InputCompleted::Search) => { EventExec::exec_search(status.selected()) } - Mode::InputCompleted(CompletionKind::Goto) => EventExec::exec_goto(status.selected())?, + Mode::InputCompleted(InputCompleted::Goto) => EventExec::exec_goto(status.selected())?, Mode::Normal => EventExec::exec_file(status)?, Mode::NeedConfirmation(_) | Mode::Preview - | Mode::InputCompleted(CompletionKind::Nothing) - | Mode::InputSimple(InputKind::Sort) - | Mode::InputSimple(InputKind::Marks(_)) => (), + | Mode::InputCompleted(InputCompleted::Nothing) + | Mode::InputSimple(InputSimple::Sort) + | Mode::InputSimple(InputSimple::Marks(_)) => (), }; status.selected().input.reset(); @@ -1264,6 +1268,45 @@ impl EventExec { fn row_to_index(row: u16) -> usize { row as usize - RESERVED_ROWS } + + pub fn event_trash_move_file(status: &mut Status) -> FmResult<()> { + for flagged in status.flagged.content.iter() { + status.trash.trash(flagged.to_owned())?; + } + Ok(()) + } + + pub fn event_trash_restore_file(status: &mut Status) -> FmResult<()> { + let (origin, _) = status.trash.selected().unwrap(); + status.trash.restore(origin.to_owned())?; + status.selected().refresh_view()?; + status.selected().mode = Mode::Normal; + Ok(()) + } + + pub fn event_trash_empty(status: &mut Status) -> FmResult<()> { + status.selected().mode = Mode::NeedConfirmation(NeedConfirmation::EmptyTrash); + Ok(()) + } + + pub fn exec_trash_empty(status: &mut Status) -> FmResult<()> { + status.trash.empty_trash()?; + status.clear_flags_and_reset_view()?; + Ok(()) + } + + pub fn event_trash_open(status: &mut Status) -> FmResult<()> { + status.selected().mode = Mode::Navigable(Navigate::Trash); + Ok(()) + } + + pub fn event_trash_next(status: &mut Status) { + status.trash.next(); + } + + pub fn event_trash_prev(status: &mut Status) { + status.trash.prev(); + } } fn string_to_path(path_string: String) -> FmResult<path::PathBuf> { diff --git a/src/keybindings.rs b/src/keybindings.rs index 5218090..52869f6 100644 --- a/src/keybindings.rs +++ b/src/keybindings.rs @@ -53,6 +53,7 @@ impl Bindings { (Key::Char('O'), ActionMap::Sort), (Key::Char('P'), ActionMap::Preview), (Key::Char('T'), ActionMap::Thumbnail), + (Key::Char('X'), ActionMap::TrashMoveFile), (Key::Char('a'), ActionMap::ToggleHidden), (Key::Char('c'), ActionMap::CopyPaste), (Key::Char('d'), ActionMap::NewDir), @@ -75,6 +76,8 @@ impl Bindings { (Key::Char('w'), ActionMap::RegexMatch), (Key::Char('x'), ActionMap::DeleteFile), (Key::Alt('d'), ActionMap::DragNDrop), + (Key::Alt('x'), ActionMap::TrashEmpty), + (Key::Alt('o'), ActionMap::TrashOpen), (Key::Ctrl('c'), ActionMap::CopyFilename), (Key::Ctrl('d'), ActionMap::Delete), (Key::Ctrl('e'), ActionMap::DisplayFull), diff --git a/src/marks.rs b/src/marks.rs index 58952ff..6b378c8 100644 --- a/src/marks.rs +++ b/src/marks.rs @@ -1,12 +1,12 @@ use std::collections::BTreeMap; -use std::fs::File; -use std::io::{self, BufRead, BufWriter, Write}; +use std::io::{self, BufWriter, Write}; use std::path::{Path, PathBuf}; use log::info; use crate::constant_strings_paths::MARKS_FILEPATH; use crate::fm_error::{FmError, FmResult}; +use crate::utils::read_lines; /// Holds the marks created by the user. /// It's an ordered map between any char (except :) and a PathBuf. @@ -117,11 +117,3 @@ impl Marks { format!("{} {}", ch, path.to_string_lossy()) } } - -fn read_lines<P>(filename: P) -> io::Result<io::Lines<io::BufReader<File>>> -where - P: AsRef<Path>, -{ - let file = File::open(filename)?; - Ok(io::BufReader::new(file).lines()) -} diff --git a/src/mode.rs b/src/mode.rs index 3293264..9fefeb7 100644 --- a/src/mode.rs +++ b/src/mode.rs @@ -1,6 +1,6 @@ use std::fmt; -use crate::completion::CompletionKind; +use crate::completion::InputCompleted; /// Different kind of mark actions. /// Either we jump to an existing mark or we save current path to a mark. @@ -16,16 +16,18 @@ pub enum MarkAction { /// 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 ConfirmedAction { +pub enum NeedConfirmation { /// Copy flagged files Copy, /// Delete flagged files Delete, /// Move flagged files Move, + /// Empty Trash + EmptyTrash, } -impl ConfirmedAction { +impl NeedConfirmation { /// Offset before the cursor. /// Since we ask the user confirmation, we need to know how much space /// is needed. @@ -34,16 +36,18 @@ impl ConfirmedAction { Self::Copy => 25, Self::Delete => 21, Self::Move => 25, + Self::EmptyTrash => 35, } } } -impl std::fmt::Display for ConfirmedAction { +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 ?"), } } } @@ -54,7 +58,7 @@ impl std::fmt::Display for ConfirmedAction { /// A regex to match all files in current directory, /// a kind of sort, a mark name, a new mark or a filter. #[derive(Clone)] -pub enum InputKind { +pub enum InputSimple { /// Rename the selected file Rename, /// Change permissions of the selected file @@ -83,6 +87,8 @@ pub enum Navigate { History, /// Navigate to a predefined shortcut Shortcut, + /// + Trash, } /// Different mode in which the application can be. @@ -93,37 +99,40 @@ pub enum Mode { Normal, /// We'll be able to complete the input string with /// different kind of completed items (exec, goto, search) - InputCompleted(CompletionKind), + InputCompleted(InputCompleted), /// Select a target and navigate to it Navigable(Navigate), /// Confirmation is required before modification is made to existing files : /// delete, move, copy - NeedConfirmation(ConfirmedAction), + NeedConfirmation(NeedConfirmation), /// Preview a file content Preview, /// Modes requiring an input that can't be completed - InputSimple(InputKind), + InputSimple(InputSimple), } impl fmt::Display for Mode { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { match *self { Mode::Normal => write!(f, "Normal: "), - Mode::InputSimple(InputKind::Rename) => write!(f, "Rename: "), - Mode::InputSimple(InputKind::Chmod) => write!(f, "Chmod: "), - Mode::InputSimple(InputKind::Newfile) => write!(f, "Newfile: "), - Mode::InputSimple(InputKind::Newdir) => write!(f, "Newdir: "), - Mode::InputSimple(InputKind::RegexMatch) => write!(f, "Regex: "), - Mode::InputSimple(InputKind::Sort) => write!(f, "Sort: Kind Name Modif Size Ext Rev :"), - Mode::InputSimple(InputKind::Marks(_)) => write!(f, "Marks jump:"), - Mode::InputSimple(InputKind::Filter) => write!(f, "Filter: "), - Mode::InputCompleted(CompletionKind::Exec) => write!(f, "Exec: "), - Mode::InputCompleted(CompletionKind::Goto) => write!(f, "Goto : "), - Mode::InputCompleted(CompletionKind::Search) => write!(f, "Search: "), - Mode::InputCompleted(CompletionKind::Nothing) => write!(f, "Nothing: "), + Mode::InputSimple(InputSimple::Rename) => write!(f, "Rename: "), + Mode::InputSimple(InputSimple::Chmod) => write!(f, "Chmod: "), + Mode::InputSimple(InputSimple::Newfile) => write!(f, "Newfile: "), + Mode::InputSimple(InputSimple::Newdir) => write!(f, "Newdir: "), + Mode::InputSimple(InputSimple::RegexMatch) => write!(f, "Regex: "), + Mode::InputSimple(InputSimple::Sort) => { + write!(f, "Sort: Kind Name Modif Size Ext Rev :") + } + Mode::InputSimple(InputSimple::Marks(_)) => write!(f, "Marks jump:"), + Mode::InputSimple(InputSimple::Filter) => write!(f, "Filter: "), + Mode::InputCompleted(InputCompleted::Exec) => write!(f, "Exec: "), + Mode::InputCompleted(InputCompleted::Goto) => write!(f, "Goto : "), + Mode::InputCompleted(InputCompleted::Search) => write!(f, "Search: "), + Mode::InputCompleted(InputCompleted::Nothing) => write!(f, "Nothing: "), Mode::Navigable(Navigate::Jump) => write!(f, "Jump : "), Mode::Navigable(Navigate::History) => write!(f, "History :"), Mode::Navigable(Navigate::Shortcut) => write!(f, "Shortcut :"), + Mode::Navigable(Navigate::Trash) => write!(f, "Trash :"), Mode::NeedConfirmation(_) => write!(f, "Y/N :"), Mode::Preview => write!(f, "Preview : "), } diff --git a/src/status.rs b/src/status.rs index 4b6fe43..c549ac9 100644 --- a/src/status.rs +++ b/src/status.rs @@ -19,6 +19,7 @@ use crate::marks::Marks; use crate::opener::{load_opener, Opener}; use crate::skim::Skimer; use crate::tab::Tab; +use crate::trash::Trash; use crate::utils::disk_space; /// Holds every mutable parameter of the application itself, except for @@ -53,6 +54,8 @@ pub struct Status { pub opener: Opener, /// The help string. pub help: String, + /// The trash + pub trash: Trash, } impl Status { @@ -75,6 +78,7 @@ impl Status { let mut tab = Tab::new(args, height)?; tab.shortcut .extend_with_mount_points(&Self::disks_mounts(sys.disks())); + let trash = Trash::parse_trash_file(term.clone())?; Ok(Self { tabs: [tab.clone(), tab], @@ -89,6 +93,7 @@ impl Status { display_full: true, opener, help, + trash, }) } diff --git a/src/term_manager.rs b/src/term_manager.rs index 071d91e..6351bdc 100644 --- a/src/term_manager.rs +++ b/src/term_manager.rs @@ -16,11 +16,12 @@ use crate::constant_strings_paths::{ use crate::content_window::ContentWindow; use crate::fileinfo::fileinfo_attr; use crate::fm_error::{FmError, FmResult}; -use crate::mode::{ConfirmedAction, InputKind, MarkAction, Mode, Navigate}; +use crate::mode::{InputSimple, MarkAction, Mode, Navigate, NeedConfirmation}; use crate::preview::{Preview, TextKind, Window}; use crate::selectable_content::SelectableContent; use crate::status::Status; use crate::tab::Tab; +use crate::trash::PathPair; /// At least 100 chars width to display 2 tabs. pub const MIN_WIDTH_FOR_DUAL_PANE: usize = 100; @@ -65,12 +66,13 @@ impl<'a> Draw for WinTab<'a> { Mode::Navigable(Navigate::Jump) => self.destination(canvas, &self.status.flagged), Mode::Navigable(Navigate::History) => self.destination(canvas, &self.tab.history), Mode::Navigable(Navigate::Shortcut) => self.destination(canvas, &self.tab.shortcut), + Mode::Navigable(Navigate::Trash) => self.trash(canvas, &self.status.trash), Mode::InputCompleted(_) => self.completion(self.tab, canvas), Mode::NeedConfirmation(confirmed_mode) => { self.confirmation(self.status, self.tab, confirmed_mode, canvas) } Mode::Preview => self.preview(self.tab, canvas), - Mode::InputSimple(InputKind::Marks(_)) => self.marks(self.status, self.tab, canvas), + Mode::InputSimple(InputSimple::Marks(_)) => self.marks(self.status, self.tab, canvas), _ => self.files(self.status, self.tab, canvas), }?; self.cursor(self.tab, canvas)?; @@ -135,7 +137,7 @@ impl<'a> WinTab<'a> { )?; } } - Mode::InputSimple(InputKind::Filter) => { + Mode::InputSimple(InputSimple::Filter) => { canvas.print_with_attr(1, 0, FILTER_PRESENTATION, Self::ATTR_YELLOW_BOLD)?; } _ => (), @@ -171,8 +173,12 @@ impl<'a> WinTab<'a> { } _ => Self::default_preview_first_line(tab), }, - Mode::InputSimple(InputKind::Marks(MarkAction::Jump)) => vec!["Jump to...".to_owned()], - Mode::InputSimple(InputKind::Marks(MarkAction::New)) => vec!["Save mark...".to_owned()], + Mode::InputSimple(InputSimple::Marks(MarkAction::Jump)) => { + vec!["Jump to...".to_owned()] + } + Mode::InputSimple(InputSimple::Marks(MarkAction::New)) => { + vec!["Save mark...".to_owned()] + } _ => { vec![ format!("{}", tab.mode.clone()), @@ -242,12 +248,12 @@ impl<'a> WinTab<'a> { fn cursor(&self, tab: &Tab, canvas: &mut dyn Canvas) -> FmResult<()> { match tab.mode { Mode::Normal - | Mode::InputSimple(InputKind::Marks(_)) + | Mode::InputSimple(InputSimple::Marks(_)) | Mode::Navigable(_) | Mode::Preview => { canvas.show_cursor(false)?; } - Mode::InputSimple(InputKind::Sort) => { + Mode::InputSimple(InputSimple::Sort) => { canvas.set_cursor(0, Self::SORT_CURSOR_OFFSET)?; } Mode::InputSimple(_) | Mode::InputCompleted(_) => { @@ -284,6 +290,25 @@ impl<'a> WinTab<'a> { Ok(()) } + fn trash( + &self, + canvas: &mut dyn Canvas, + selectable: &impl SelectableContent<PathPair>, + ) -> FmResult<()> { + canvas.print(0, 1, "Restore the selected file")?; + for (row, (origin, _dest)) in selectable.content().iter().enumerate() { + let mut attr = Attr::default(); + if row == selectable.index() { + attr.effect |= Effect::REVERSE; + } + let s = origin + .to_str() + .ok_or_else(|| FmError::custom("display", "Unreadable filename"))?; + let _ = canvas.print_with_attr(row + ContentWindow::WINDOW_MARGIN_TOP, 4, s, attr); + } + Ok(()) + } + /// Display the possible completion items. The currently selected one is /// reversed. fn completion(&self, tab: &Tab, canvas: &mut dyn Canvas) -> FmResult<()> { @@ -303,7 +328,7 @@ impl<'a> WinTab<'a> { &self, status: &Status, tab: &Tab, - confirmed_mode: ConfirmedAction, + confirmed_mode: NeedConfirmation, canvas: &mut dyn Canvas, ) -> FmResult<()> { for (row, path) in status.flagged.content.iter().enumerate() { @@ -317,16 +342,17 @@ impl<'a> WinTab<'a> { } info!("confirmed action: {:?}", confirmed_mode); let content = match confirmed_mode { - ConfirmedAction::Copy => { + NeedConfirmation::Copy => { format!( "Files will be copied to {}", tab.path_content.path_to_str()? ) } - ConfirmedAction::Delete => "Files will deleted permanently".to_owned(), - ConfirmedAction::Move => { + NeedConfirmation::Delete => "Files will deleted permanently".to_owned(), + NeedConfirmation::Move => { format!("Files will be moved to {}", tab.path_content.path_to_str()?) } + NeedConfirmation::EmptyTrash => "Trash will be emptied".to_owned(), }; canvas.print_with_attr(2, 3, &content, Self::ATTR_YELLOW_BOLD)?; |