diff options
Diffstat (limited to 'src/term_manager.rs')
-rw-r--r-- | src/term_manager.rs | 126 |
1 files changed, 76 insertions, 50 deletions
diff --git a/src/term_manager.rs b/src/term_manager.rs index 89bb937..498f610 100644 --- a/src/term_manager.rs +++ b/src/term_manager.rs @@ -2,6 +2,7 @@ use std::cmp::min; use std::path::PathBuf; use std::sync::Arc; +use anyhow::{Context, Result}; use log::info; use tuikit::attr::*; use tuikit::event::Event; @@ -17,8 +18,8 @@ use crate::constant_strings_paths::{ }; use crate::content_window::ContentWindow; use crate::fileinfo::{fileinfo_attr, shorten_path, FileInfo}; -use crate::fm_error::{FmError, FmResult}; use crate::mode::{InputSimple, MarkAction, Mode, Navigate, NeedConfirmation}; +use crate::mount_help::MountHelper; use crate::preview::{Preview, TextKind, Window}; use crate::selectable_content::SelectableContent; use crate::status::Status; @@ -76,7 +77,7 @@ impl EventReader { /// Returns the events as they're received. Wait indefinitely for a new one. /// We should spend most of the application life here, doing nothing :) - pub fn poll_event(&self) -> FmResult<Event> { + pub fn poll_event(&self) -> Result<Event> { Ok(self.term.poll_event()?) } } @@ -143,7 +144,7 @@ impl<'a> WinMain<'a> { } } - fn preview_as_second_pane(&self, canvas: &mut dyn Canvas) -> FmResult<()> { + fn preview_as_second_pane(&self, canvas: &mut dyn Canvas) -> Result<()> { let tab = &self.status.tabs[0]; let (_, height) = canvas.size()?; self.preview(tab, &tab.preview.window_for_second_pane(height), canvas)?; @@ -157,11 +158,11 @@ impl<'a> WinMain<'a> { /// When a confirmation is needed we ask the user to input `'y'` or /// something else. /// Returns the result of the number of printed chars. - fn first_line(&self, tab: &Tab, disk_space: &str, canvas: &mut dyn Canvas) -> FmResult<()> { + fn first_line(&self, tab: &Tab, disk_space: &str, canvas: &mut dyn Canvas) -> Result<()> { draw_colored_strings(0, 0, self.create_first_row(tab, disk_space)?, canvas) } - fn second_line(&self, status: &Status, tab: &Tab, canvas: &mut dyn Canvas) -> FmResult<usize> { + fn second_line(&self, status: &Status, tab: &Tab, canvas: &mut dyn Canvas) -> Result<usize> { match tab.mode { Mode::Normal => { if !status.display_full { @@ -182,7 +183,7 @@ impl<'a> WinMain<'a> { } } - fn second_line_detailed(&self, file: &FileInfo, canvas: &mut dyn Canvas) -> FmResult<usize> { + fn second_line_detailed(&self, file: &FileInfo, canvas: &mut dyn Canvas) -> Result<usize> { let owner_size = file.owner.len(); let group_size = file.group.len(); let mut attr = fileinfo_attr(file, self.colors); @@ -190,7 +191,7 @@ impl<'a> WinMain<'a> { Ok(canvas.print_with_attr(1, 0, &file.format(owner_size, group_size)?, attr)?) } - fn second_line_simple(&self, status: &Status, canvas: &mut dyn Canvas) -> FmResult<usize> { + fn second_line_simple(&self, status: &Status, canvas: &mut dyn Canvas) -> Result<usize> { Ok(canvas.print_with_attr( 1, 0, @@ -199,7 +200,7 @@ impl<'a> WinMain<'a> { )?) } - fn normal_first_row(&self, disk_space: &str) -> FmResult<Vec<String>> { + fn normal_first_row(&self, disk_space: &str) -> Result<Vec<String>> { Ok(vec![ format!("{} ", shorten_path(&self.tab.path_content.path, None)?), format!("{} files ", self.tab.path_content.true_len()), @@ -211,8 +212,10 @@ impl<'a> WinMain<'a> { } fn help_first_row() -> Vec<String> { + let version = std::env!("CARGO_PKG_VERSION"); vec![ HELP_FIRST_SENTENCE.to_owned(), + format!("Version: {version} "), HELP_SECOND_SENTENCE.to_owned(), ] } @@ -240,7 +243,7 @@ impl<'a> WinMain<'a> { } } - fn create_first_row(&self, tab: &Tab, disk_space: &str) -> FmResult<Vec<String>> { + fn create_first_row(&self, tab: &Tab, disk_space: &str) -> Result<Vec<String>> { let first_row = match tab.mode { Mode::Normal | Mode::Tree => self.normal_first_row(disk_space)?, Mode::Preview => match &tab.preview { @@ -266,7 +269,7 @@ impl<'a> WinMain<'a> { /// We reverse the attributes of the selected one, underline the flagged files. /// When we display a simpler version, the second line is used to display the /// metadata of the selected file. - fn files(&self, status: &Status, tab: &Tab, canvas: &mut dyn Canvas) -> FmResult<()> { + fn files(&self, status: &Status, tab: &Tab, canvas: &mut dyn Canvas) -> Result<()> { let len = tab.path_content.content.len(); let group_size: usize; let owner_size: usize; @@ -302,7 +305,7 @@ impl<'a> WinMain<'a> { Ok(()) } - fn tree(&self, status: &Status, tab: &Tab, canvas: &mut dyn Canvas) -> FmResult<()> { + fn tree(&self, status: &Status, tab: &Tab, canvas: &mut dyn Canvas) -> Result<()> { let line_number_width = 3; let (_, height) = canvas.size()?; @@ -324,7 +327,7 @@ impl<'a> WinMain<'a> { row_position_in_canvas: usize, line_number_to_print: usize, canvas: &mut dyn Canvas, - ) -> FmResult<usize> { + ) -> Result<usize> { Ok(canvas.print_with_attr( row_position_in_canvas, 0, @@ -340,7 +343,7 @@ impl<'a> WinMain<'a> { /// else the content is supposed to be text and shown as such. /// It may fail to recognize some usual extensions, notably `.toml`. /// It may fail to recognize small files (< 1024 bytes). - fn preview(&self, tab: &Tab, window: &ContentWindow, canvas: &mut dyn Canvas) -> FmResult<()> { + fn preview(&self, tab: &Tab, window: &ContentWindow, canvas: &mut dyn Canvas) -> Result<()> { let length = tab.preview.len(); let line_number_width = length.to_string().len(); match &tab.preview { @@ -391,6 +394,15 @@ impl<'a> WinMain<'a> { )?; } } + Preview::ColoredText(colored_text) => { + for (i, line) in colored_text.window(window.top, window.bottom, length) { + let row = calc_line_row(i, window); + let mut col = 3; + for (chr, attr) in skim::AnsiString::parse(line).iter() { + col += canvas.print_with_attr(row, col, &chr.to_string(), attr)?; + } + } + } Preview::Archive(text) => { impl_preview!(text, tab, length, canvas, line_number_width, window) } @@ -406,6 +418,9 @@ impl<'a> WinMain<'a> { Preview::Diff(text) => { impl_preview!(text, tab, length, canvas, line_number_width, window) } + Preview::Iso(text) => { + impl_preview!(text, tab, length, canvas, line_number_width, window) + } Preview::Empty => (), } @@ -432,6 +447,7 @@ impl<'a> Draw for WinSecondary<'a> { } Mode::Navigate(Navigate::Marks(_)) => self.marks(self.status, canvas), Mode::Navigate(Navigate::ShellMenu) => self.shell_menu(self.status, canvas), + Mode::Navigate(Navigate::CliInfo) => self.cli_info(self.status, canvas), Mode::NeedConfirmation(confirmed_mode) => { self.confirmation(self.status, self.tab, confirmed_mode, canvas) } @@ -457,11 +473,11 @@ impl<'a> WinSecondary<'a> { } } - fn first_line(&self, tab: &Tab, canvas: &mut dyn Canvas) -> FmResult<()> { + fn first_line(&self, tab: &Tab, canvas: &mut dyn Canvas) -> Result<()> { draw_colored_strings(0, 0, self.create_first_row(tab)?, canvas) } - fn create_first_row(&self, tab: &Tab) -> FmResult<Vec<String>> { + fn create_first_row(&self, tab: &Tab) -> Result<Vec<String>> { let first_row = match tab.mode { Mode::NeedConfirmation(confirmed_action) => { vec![format!("{confirmed_action}"), " (y/n)".to_owned()] @@ -472,7 +488,7 @@ impl<'a> WinSecondary<'a> { Mode::Navigate(Navigate::Marks(MarkAction::New)) => { vec!["Save mark...".to_owned()] } - Mode::InputSimple(InputSimple::Password(password_kind, _encrypted_action)) => { + Mode::InputSimple(InputSimple::Password(password_kind, _encrypted_action, _)) => { vec![format!("{password_kind}"), tab.input.password()] } Mode::InputCompleted(mode) => { @@ -482,10 +498,7 @@ impl<'a> WinSecondary<'a> { completion_strings.push(completion.to_owned()) } if let InputCompleted::Exec = mode { - let selected_path = &tab - .selected() - .ok_or_else(|| FmError::custom("create_first_row", "can't parse path"))? - .path; + let selected_path = &tab.selected().context("can't parse path")?.path; let selected_path = format!(" {}", selected_path.display()); completion_strings.push(selected_path); @@ -504,7 +517,7 @@ impl<'a> WinSecondary<'a> { /// Display the possible completion items. The currently selected one is /// reversed. - fn completion(&self, tab: &Tab, canvas: &mut dyn Canvas) -> FmResult<()> { + fn completion(&self, tab: &Tab, canvas: &mut dyn Canvas) -> Result<()> { canvas.set_cursor(0, tab.input.cursor_index + Self::EDIT_BOX_OFFSET)?; for (row, candidate) in tab.completion.proposals.iter().enumerate() { let mut attr = Attr::default(); @@ -516,7 +529,7 @@ impl<'a> WinSecondary<'a> { Ok(()) } /// Display a cursor in the top row, at a correct column. - fn cursor(&self, tab: &Tab, canvas: &mut dyn Canvas) -> FmResult<()> { + fn cursor(&self, tab: &Tab, canvas: &mut dyn Canvas) -> Result<()> { match tab.mode { Mode::Normal | Mode::Tree @@ -529,7 +542,7 @@ impl<'a> WinSecondary<'a> { canvas.show_cursor(true)?; canvas.set_cursor(0, Self::SORT_CURSOR_OFFSET)?; } - Mode::InputSimple(InputSimple::Password(_, _)) => { + Mode::InputSimple(InputSimple::Password(_, _, _)) => { canvas.show_cursor(true)?; canvas.set_cursor(0, Self::PASSWORD_CURSOR_OFFSET + tab.input.cursor_index)?; } @@ -550,7 +563,7 @@ impl<'a> WinSecondary<'a> { &self, canvas: &mut dyn Canvas, selectable: &impl SelectableContent<PathBuf>, - ) -> FmResult<()> { + ) -> Result<()> { canvas.print(0, 0, "Go to...")?; for (row, path, attr) in enumerated_colored_iter!(selectable) { let mut attr = *attr; @@ -560,8 +573,7 @@ impl<'a> WinSecondary<'a> { let _ = canvas.print_with_attr( row + ContentWindow::WINDOW_MARGIN_TOP, 4, - path.to_str() - .ok_or_else(|| FmError::custom("display", "Unreadable filename"))?, + path.to_str().context("Unreadable filename")?, attr, ); } @@ -572,7 +584,7 @@ impl<'a> WinSecondary<'a> { &self, canvas: &mut dyn Canvas, selectable: &impl SelectableContent<String>, - ) -> FmResult<()> { + ) -> Result<()> { canvas.print(0, 0, "Action...")?; for (row, text, attr) in enumerated_colored_iter!(selectable) { let mut attr = *attr; @@ -588,7 +600,7 @@ impl<'a> WinSecondary<'a> { &self, canvas: &mut dyn Canvas, selectable: &impl SelectableContent<TrashInfo>, - ) -> FmResult<()> { + ) -> Result<()> { canvas.print( 1, 0, @@ -613,7 +625,7 @@ impl<'a> WinSecondary<'a> { &self, canvas: &mut dyn Canvas, selectable: &impl SelectableContent<CompressionMethod>, - ) -> FmResult<()> { + ) -> Result<()> { canvas.print_with_attr( 1, 0, @@ -636,7 +648,7 @@ impl<'a> WinSecondary<'a> { Ok(()) } - fn marks(&self, status: &Status, canvas: &mut dyn Canvas) -> FmResult<()> { + fn marks(&self, status: &Status, canvas: &mut dyn Canvas) -> Result<()> { canvas.print_with_attr(2, 1, "mark path", Self::ATTR_YELLOW)?; for ((row, line), attr) in std::iter::zip( @@ -653,7 +665,7 @@ impl<'a> WinSecondary<'a> { Ok(()) } - fn shell_menu(&self, status: &Status, canvas: &mut dyn Canvas) -> FmResult<()> { + fn shell_menu(&self, status: &Status, canvas: &mut dyn Canvas) -> Result<()> { canvas.print_with_attr(2, 1, "pick a command", Self::ATTR_YELLOW)?; let tab = status.selected_non_mut(); @@ -672,12 +684,26 @@ impl<'a> WinSecondary<'a> { Ok(()) } - fn encrypted_devices( - &self, - status: &Status, - tab: &Tab, - canvas: &mut dyn Canvas, - ) -> FmResult<()> { + fn cli_info(&self, status: &Status, canvas: &mut dyn Canvas) -> Result<()> { + canvas.print_with_attr(2, 1, "pick a command", Self::ATTR_YELLOW)?; + + let tab = status.selected_non_mut(); + for ((row, command), attr) in std::iter::zip( + status.cli_info.content.iter().enumerate(), + MENU_COLORS.iter().cycle(), + ) { + let mut attr = *attr; + if row == status.cli_info.index() { + attr.effect |= Effect::REVERSE; + } + let row = calc_line_row(row, &tab.window) + 2; + + canvas.print_with_attr(row, 3, command, attr)?; + } + Ok(()) + } + + fn encrypted_devices(&self, status: &Status, tab: &Tab, canvas: &mut dyn Canvas) -> Result<()> { canvas.print_with_attr(2, 3, "m: mount -- u: unmount", Self::ATTR_YELLOW)?; for (i, device) in status.encrypted_devices.content.iter().enumerate() { let row = calc_line_row(i, &tab.window) + 2; @@ -708,7 +734,7 @@ impl<'a> WinSecondary<'a> { tab: &Tab, confirmed_mode: NeedConfirmation, canvas: &mut dyn Canvas, - ) -> FmResult<()> { + ) -> Result<()> { info!("confirmed action: {:?}", confirmed_mode); match confirmed_mode { NeedConfirmation::EmptyTrash => { @@ -726,8 +752,7 @@ impl<'a> WinSecondary<'a> { canvas.print_with_attr( row + ContentWindow::WINDOW_MARGIN_TOP + 2, 4, - path.to_str() - .ok_or_else(|| FmError::custom("display", "Unreadable filename"))?, + path.to_str().context("Unreadable filename")?, Attr::default(), )?; } @@ -774,11 +799,11 @@ impl Display { /// Used to force a display of the cursor before leaving the application. /// Most of the times we don't need a cursor and it's hidden. We have to /// do it unless the shell won't display a cursor anymore. - pub fn show_cursor(&self) -> FmResult<()> { + pub fn show_cursor(&self) -> Result<()> { Ok(self.term.show_cursor(true)?) } - fn hide_cursor(&self) -> FmResult<()> { + fn hide_cursor(&self) -> Result<()> { self.term.set_cursor(0, 0)?; Ok(self.term.show_cursor(false)?) } @@ -800,7 +825,7 @@ impl Display { /// The preview in preview mode. /// Displays one pane or two panes, depending of the width and current /// status of the application. - pub fn display_all(&mut self, status: &Status, colors: &Colors) -> FmResult<()> { + pub fn display_all(&mut self, status: &Status, colors: &Colors) -> Result<()> { self.hide_cursor()?; self.term.clear()?; @@ -815,14 +840,15 @@ impl Display { Ok(self.term.present()?) } - pub fn force_clear(&mut self) -> FmResult<()> { + /// Hide the curose, clear the terminal and present. + pub fn force_clear(&mut self) -> Result<()> { self.hide_cursor()?; self.term.clear()?; self.term.present()?; Ok(()) } - fn size_for_second_window(&self, tab: &Tab) -> FmResult<usize> { + fn size_for_second_window(&self, tab: &Tab) -> Result<usize> { if tab.need_second_window() { Ok(self.height()? / 2) } else { @@ -836,7 +862,7 @@ impl Display { win_secondary: &'a WinSecondary, border: Attr, size: usize, - ) -> FmResult<VSplit<'a>> { + ) -> Result<VSplit<'a>> { Ok(VSplit::default() .split( Win::new(win_main) @@ -868,7 +894,7 @@ impl Display { disk_space_tab_0: &str, disk_space_tab_1: &str, colors: &Colors, - ) -> FmResult<()> { + ) -> Result<()> { let (width, _) = self.term.term_size()?; let win_main_left = WinMain::new(status, 0, disk_space_tab_0, colors, 0, false); let win_main_right = WinMain::new(status, 1, disk_space_tab_1, colors, width / 2, true); @@ -898,7 +924,7 @@ impl Display { status: &Status, disk_space_tab_0: &str, colors: &Colors, - ) -> FmResult<()> { + ) -> Result<()> { let win_main_left = WinMain::new(status, 0, disk_space_tab_0, colors, 0, false); let win_second_left = WinSecondary::new(status, 0); let percent_left = self.size_for_second_window(&status.tabs[0])?; @@ -912,7 +938,7 @@ impl Display { } /// Reads and returns the `tuikit::term::Term` height. - pub fn height(&self) -> FmResult<usize> { + pub fn height(&self) -> Result<usize> { let (_, height) = self.term.term_size()?; Ok(height) } @@ -934,7 +960,7 @@ fn draw_colored_strings( offset: usize, strings: Vec<String>, canvas: &mut dyn Canvas, -) -> FmResult<()> { +) -> Result<()> { let mut col = 0; for (text, attr) in std::iter::zip(strings.iter(), FIRST_LINE_COLORS.iter().cycle()) { col += canvas.print_with_attr(row, offset + col, text, *attr)?; |