summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorqkzk <qu3nt1n@gmail.com>2022-12-26 00:39:05 +0100
committerqkzk <qu3nt1n@gmail.com>2022-12-26 00:39:05 +0100
commit6e2fbce3c01e1d877de5ef595b0d1cab88b02808 (patch)
treefc31a2550f54a4283abd7b7d468584d41f0a0e93
parenteab76cb61ee82e9433f568e56e2c65299d0aa415 (diff)
trash: first working draft, very basic api
-rw-r--r--Cargo.lock2
-rw-r--r--Cargo.toml2
-rw-r--r--development.md5
-rw-r--r--src/action_map.rs8
-rw-r--r--src/completion.rs14
-rw-r--r--src/constant_strings_paths.rs6
-rw-r--r--src/event_dispatch.rs10
-rw-r--r--src/event_exec.rs111
-rw-r--r--src/keybindings.rs3
-rw-r--r--src/marks.rs12
-rw-r--r--src/mode.rs49
-rw-r--r--src/status.rs5
-rw-r--r--src/term_manager.rs48
-rw-r--r--src/trash.rs154
-rw-r--r--src/utils.rs11
15 files changed, 304 insertions, 136 deletions
diff --git a/Cargo.lock b/Cargo.lock
index 186011d..36138ad 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -850,7 +850,7 @@ dependencies = [
[[package]]
name = "fm-tui"
-version = "0.1.6"
+version = "0.1.7"
dependencies = [
"chrono",
"clap 4.0.2",
diff --git a/Cargo.toml b/Cargo.toml
index 0303cc1..f6a8978 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -1,6 +1,6 @@
[package]
name = "fm-tui"
-version = "0.1.6"
+version = "0.1.7"
authors = ["Quentin Konieczko <qu3nt1n@gmail.com>"]
edition = "2021"
license-file = "LICENSE.txt"
diff --git a/development.md b/development.md
index 931eb2e..aa0ee15 100644
--- a/development.md
+++ b/development.md
@@ -242,6 +242,11 @@
- [x] Improve saved marks display by using a BTreeMap, allowing sorting by char.
- [x] Prevent entering jump marks mode if there's no mark to jump to
+### Version 0.1.7
+
+- [x] basic trash with custom format & spec
+- [ ] use [xdg trash](https://specifications.freedesktop.org/trash-spec/trashspec-latest.html)
+
## TODO
- [ ] remote control
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,
+