summaryrefslogtreecommitdiffstats
path: root/src/preview.rs
diff options
context:
space:
mode:
Diffstat (limited to 'src/preview.rs')
-rw-r--r--src/preview.rs185
1 files changed, 140 insertions, 45 deletions
diff --git a/src/preview.rs b/src/preview.rs
index 7ae7cca..fab6ff6 100644
--- a/src/preview.rs
+++ b/src/preview.rs
@@ -4,6 +4,7 @@ use std::io::{BufRead, BufReader, Read};
use std::iter::{Enumerate, Skip, Take};
use std::panic;
use std::path::{Path, PathBuf};
+use std::rc::Rc;
use std::slice::Iter;
use content_inspector::{inspect, ContentType};
@@ -13,12 +14,16 @@ use pdf_extract;
use syntect::easy::HighlightLines;
use syntect::highlighting::{Style, ThemeSet};
use syntect::parsing::{SyntaxReference, SyntaxSet};
-use termtree::Tree;
use tuikit::attr::{Attr, Color};
+use users::UsersCache;
use crate::compress::list_files;
+use crate::config::Colors;
use crate::fileinfo::{FileInfo, FileKind};
+use crate::filter::FilterKind;
use crate::fm_error::{FmError, FmResult};
+use crate::status::Status;
+use crate::tree::{ColoredString, Tree};
/// Different kind of preview used to display some informaitons
/// About the file.
@@ -51,9 +56,19 @@ impl Preview {
/// the file.
/// Sometimes it reads the content of the file, sometimes it delegates
/// it to the display method.
- pub fn new(file_info: &FileInfo) -> FmResult<Self> {
+ pub fn new(
+ file_info: &FileInfo,
+ users_cache: &Rc<UsersCache>,
+ status: &Status,
+ ) -> FmResult<Self> {
match file_info.file_kind {
- FileKind::Directory => Ok(Self::Directory(Directory::new(&file_info.path)?)),
+ FileKind::Directory => Ok(Self::Directory(Directory::new(
+ &file_info.path,
+ users_cache,
+ &status.config_colors,
+ &status.selected_non_mut().filter,
+ status.selected_non_mut().show_hidden,
+ )?)),
FileKind::NormalFile => match file_info.extension.to_lowercase().as_str() {
e if is_ext_zip(e) => Ok(Self::Archive(ZipContent::new(&file_info.path)?)),
e if is_ext_pdf(e) => Ok(Self::Pdf(PdfContent::new(&file_info.path))),
@@ -493,28 +508,135 @@ impl Pixels {
/// if the directory has a lot of children.
#[derive(Clone, Debug)]
pub struct Directory {
- pub content: Vec<String>,
+ pub content: Vec<(String, ColoredString)>,
+ pub tree: Tree,
len: usize,
+ pub selected_index: usize,
}
impl Directory {
- fn new(path: &Path) -> FmResult<Self> {
- let tree = tree_view(
- path.to_str().ok_or_else(|| {
- FmError::custom("Directory Preview", "Can't parse the filename to str")
- })?,
- 10,
+ /// Creates a new tree view of the directory.
+ /// We only hold the result here, since the tree itself has now usage atm.
+ pub fn new(
+ path: &Path,
+ users_cache: &Rc<UsersCache>,
+ colors: &Colors,
+ filter_kind: &FilterKind,
+ show_hidden: bool,
+ ) -> FmResult<Self> {
+ let mut tree = Tree::from_path(
+ path,
+ Tree::MAX_DEPTH,
+ users_cache,
+ filter_kind,
+ show_hidden,
+ vec![0],
)?;
- let tree_str = format!("{}", tree);
- let content: Vec<String> = tree_str.lines().map(|s| s.to_owned()).collect();
- let len = content.len();
+ tree.select_root();
+ let (selected_index, content) = tree.into_navigable_content(colors);
+ Ok(Self {
+ tree,
+ len: content.len(),
+ content,
+ selected_index,
+ })
+ }
- Ok(Self { content, len })
+ /// Creates an empty directory preview.
+ pub fn empty(path: &Path, users_cache: &Rc<UsersCache>) -> FmResult<Self> {
+ Ok(Self {
+ tree: Tree::empty(path, users_cache)?,
+ len: 0,
+ content: vec![],
+ selected_index: 0,
+ })
}
- fn len(&self) -> usize {
+ /// Number of displayed lines.
+ pub fn len(&self) -> usize {
self.len
}
+
+ /// True if there's no lines in preview.
+ pub fn is_empty(&self) -> bool {
+ self.len == 0
+ }
+
+ /// Select the root node and reset the view.
+ pub fn select_root(&mut self, colors: &Colors) -> FmResult<()> {
+ self.tree.select_root();
+ (self.selected_index, self.content) = self.tree.into_navigable_content(colors);
+ Ok(())
+ }
+
+ /// Unselect every child node.
+ pub fn unselect_children(&mut self) {
+ self.tree.unselect_children()
+ }
+
+ /// Select the next sibling if any.
+ pub fn select_next_sibling(&mut self, colors: &Colors) -> FmResult<()> {
+ self.tree.select_next_sibling()?;
+ (self.selected_index, self.content) = self.tree.into_navigable_content(colors);
+ Ok(())
+ }
+
+ /// Select the previous sibling if any.
+ pub fn select_prev_sibling(&mut self, colors: &Colors) -> FmResult<()> {
+ self.tree.select_prev_sibling()?;
+ (self.selected_index, self.content) = self.tree.into_navigable_content(colors);
+ Ok(())
+ }
+
+ /// Select the first child, if any.
+ pub fn select_first_child(&mut self, colors: &Colors) -> FmResult<()> {
+ self.tree.select_first_child()?;
+ (self.selected_index, self.content) = self.tree.into_navigable_content(colors);
+ Ok(())
+ }
+
+ /// Select the parent of current node.
+ pub fn select_parent(&mut self, colors: &Colors) -> FmResult<()> {
+ self.tree.select_parent()?;
+ (self.selected_index, self.content) = self.tree.into_navigable_content(colors);
+ Ok(())
+ }
+
+ /// Select the last leaf of the tree (ie the last line.)
+ pub fn go_to_bottom_leaf(&mut self, colors: &Colors) -> FmResult<()> {
+ self.tree.go_to_bottom_leaf()?;
+ (self.selected_index, self.content) = self.tree.into_navigable_content(colors);
+ Ok(())
+ }
+
+ /// Make a preview of the tree.
+ pub fn make_preview(&mut self, colors: &Colors) {
+ (self.selected_index, self.content) = self.tree.into_navigable_content(colors);
+ }
+
+ /// Calculates the top, bottom and lenght of the view, depending on which element
+ /// is selected and the size of the window used to display.
+ pub fn calculate_tree_window(&self, height: usize) -> (usize, usize, usize) {
+ let length = self.content.len();
+ let mut top = if self.selected_index < height {
+ 0
+ } else {
+ self.selected_index
+ };
+ let mut bottom = if self.selected_index < height {
+ height
+ } else {
+ self.selected_index + height
+ };
+
+ let padding = std::cmp::max(10, height / 2);
+ if self.selected_index > height {
+ top -= padding;
+ bottom += padding;
+ }
+
+ (top, bottom, length)
+ }
}
/// Common trait for many preview methods which are just a bunch of lines with
@@ -563,13 +685,15 @@ macro_rules! impl_window {
};
}
+type ColoredPair = (String, ColoredString);
+
impl_window!(TextContent, String);
impl_window!(BinaryContent, Line);
impl_window!(PdfContent, String);
impl_window!(ZipContent, String);
impl_window!(ExifContent, String);
impl_window!(MediaContent, String);
-impl_window!(Directory, String);
+impl_window!(Directory, ColoredPair);
fn is_ext_zip(ext: &str) -> bool {
matches!(
@@ -607,35 +731,6 @@ fn is_ext_pdf(ext: &str) -> bool {
ext == "pdf"
}
-fn filename_as_string<P: AsRef<Path>>(p: P) -> String {
- p.as_ref()
- .file_name()
- .unwrap_or_default()
- .to_str()
- .unwrap_or_default()
- .to_owned()
-}
-
-fn tree_view<P: AsRef<Path>>(p: P, max_depth: usize) -> std::io::Result<Tree<String>> {
- Ok(std::fs::read_dir(&p)?.filter_map(|e| e.ok()).fold(
- Tree::new(filename_as_string(p.as_ref().canonicalize()?)),
- |mut root, entry| {
- if max_depth > 0 {
- if let Ok(dir) = entry.metadata() {
- if dir.is_dir() {
- if let Ok(tree) = tree_view(entry.path(), max_depth - 1) {
- root.push(tree);
- }
- } else {
- root.push(Tree::new(filename_as_string(entry.path())));
- }
- }
- }
- root
- },
- ))
-}
-
fn catch_unwind_silent<F: FnOnce() -> R + panic::UnwindSafe, R>(f: F) -> std::thread::Result<R> {
let prev_hook = panic::take_hook();
panic::set_hook(Box::new(|_| {}));