use std::cmp::{max, min};
use std::collections::HashSet;
use std::fmt;
use std::fs;
use std::os::unix::fs::PermissionsExt;
use std::process::Command;
use std::{env, path, process};
use tuikit::attr::*;
use tuikit::event::{Event, Key};
use tuikit::key::MouseButton;
use tuikit::term::{Term, TermHeight};
use fm::args::Args;
use fm::config::{load_file, str_to_tuikit, Colors, Keybindings};
use fm::fileinfo::{FileInfo, FileKind, PathContent};
pub mod fileinfo;
const WINDOW_PADDING: usize = 4;
const WINDOW_MARGIN_TOP: usize = 1;
const EDIT_BOX_OFFSET: usize = 10;
const MAX_PERMISSIONS: u32 = 0o777;
static CONFIG_FILE: &str = "/home/quentin/gclem/dev/rust/fm/config.yaml";
static USAGE: &str = "
FM: dired inspired File Manager
dired [flags] [path]
flags:
-a display hidden files
-h show help and exit
";
static HELP_LINES: &str = "
Default key bindings:
q: quit
?: help
- Navigation -
←: cd to parent directory
→: cd to child directory
↑: one line up
↓: one line down
Home: go to first line
End: go to last line
PgUp: 10 lines up
PgDown: 10 lines down
a: toggle hidden
s: shell in current directory
o: xdg-open this file
- Action on flagged files -
space: toggle flag on a file
*: flag all
u: clear flags
v: reverse flags
c: copy to current dir
p: move to current dir
x: delete flagged files
- MODES -
m: CHMOD
e: EXEC
d: NEWDIR
n: NEWFILE
r: RENAME
g: GOTO
Enter: Execute mode then NORMAL
Esc: NORMAL
";
struct FilesWindow {
top: usize,
bottom: usize,
len: usize,
height: usize,
}
impl FilesWindow {
fn new(len: usize, height: usize) -> Self {
FilesWindow {
top: 0,
bottom: min(len, height - 3),
len,
height: height - 3,
}
}
fn scroll_up_one(&mut self, index: usize) {
if index < self.top + WINDOW_PADDING && self.top > 0 {
self.top -= 1;
self.bottom -= 1;
}
}
fn scroll_down_one(&mut self, index: usize) {
if self.len < self.height {
return;
}
if index > self.bottom - WINDOW_PADDING && self.bottom < self.len - WINDOW_MARGIN_TOP {
self.top += 1;
self.bottom += 1;
}
}
fn reset(&mut self, len: usize) {
self.len = len;
self.top = 0;
self.bottom = min(len, self.height);
}
fn scroll_to(&mut self, index: usize) {
if index < self.top || index > self.bottom {
self.top = max(index, WINDOW_PADDING) - WINDOW_PADDING;
self.bottom = self.top + min(self.len, self.height - 3);
}
}
}
fn fileinfo_attr(fileinfo: &FileInfo, colors: &Colors) -> Attr {
let fg = match fileinfo.file_kind {
FileKind::Directory => str_to_tuikit(&colors.directory),
FileKind::BlockDevice => str_to_tuikit(&colors.block),
FileKind::CharDevice => str_to_tuikit(&colors.char),
FileKind::Fifo => str_to_tuikit(&colors.fifo),
FileKind::Socket => str_to_tuikit(&colors.socket),
FileKind::SymbolicLink => str_to_tuikit(&colors.symlink),
_ => str_to_tuikit(&colors.file),
};
let effect = if fileinfo.is_selected {
Effect::REVERSE
} else {
Effect::empty()
};
Attr {
fg,
bg: Color::default(),
effect,
}
}
#[derive(Clone)]
enum Mode {
Normal,
Rename,
Chmod,
Newfile,
Newdir,
Exec,
Help,
Search,
Goto,
}
impl fmt::Debug for Mode {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match *self {
Mode::Normal => write!(f, "Normal: "),
Mode::Rename => write!(f, "Rename: "),
Mode::Chmod =>