summaryrefslogtreecommitdiffstats
path: root/src/actioner.rs
diff options
context:
space:
mode:
authorqkzk <qu3nt1n@gmail.com>2022-12-06 20:26:56 +0100
committerqkzk <qu3nt1n@gmail.com>2022-12-06 20:26:56 +0100
commitfc211f60836223c72b91b1bbd557b049e66a3f4d (patch)
tree174d3e6fd56ae61825fd7d41a7e4112867fd756a /src/actioner.rs
parentbf851ff019f144700a7aa824865016c644cce9f2 (diff)
move event & exec to actioner
Diffstat (limited to 'src/actioner.rs')
-rw-r--r--src/actioner.rs974
1 files changed, 874 insertions, 100 deletions
diff --git a/src/actioner.rs b/src/actioner.rs
index 39fd5f1..9bc6eef 100644
--- a/src/actioner.rs
+++ b/src/actioner.rs
@@ -1,11 +1,32 @@
+use std::path::PathBuf;
+
use tuikit::prelude::{Event, Key, MouseButton};
-use crate::fm_error::FmResult;
+use crate::bulkrename::Bulkrename;
+use crate::compress::decompress;
+use crate::copy_move::CopyMove;
+use crate::fileinfo::{FileKind, PathContent, SortBy};
+use crate::fm_error::{FmError, FmResult};
use crate::keybindings::Keybindings;
+use crate::last_edition::LastEdition;
use crate::mode::{MarkAction, Mode};
+use crate::preview::Preview;
use crate::status::Status;
+use crate::tab::Tab;
use crate::term_manager::MIN_WIDTH_FOR_DUAL_PANE;
+use std::borrow::Borrow;
+use std::cmp::min;
+use std::fs;
+use std::path;
+
+use copypasta::{ClipboardContext, ClipboardProvider};
+use log::info;
+
+use crate::content_window::ContentWindow;
+use crate::filter::FilterKind;
+use crate::opener::execute_in_child;
+
/// Struct which mutates `tabs.selected()..
/// Holds a mapping which can't be static since it's read from a config file.
/// All keys are mapped to relevent events on tabs.selected().
@@ -23,55 +44,55 @@ impl Actioner {
/// Reaction to received events.
pub fn read_event(&self, status: &mut Status, ev: Event) -> FmResult<()> {
match ev {
- Event::Key(Key::ESC) => self.escape(status),
- Event::Key(Key::Up) => self.up(status),
- Event::Key(Key::Down) => self.down(status),
- Event::Key(Key::Left) => self.left(status),
- Event::Key(Key::Right) => self.right(status),
- Event::Key(Key::Backspace) => self.backspace(status),
- Event::Key(Key::Ctrl('d')) => self.delete(status),
- Event::Key(Key::Ctrl('q')) => self.escape(status),
+ Event::Key(Key::ESC) => Self::escape(status),
+ Event::Key(Key::Up) => Self::up(status),
+ Event::Key(Key::Down) => Self::down(status),
+ Event::Key(Key::Left) => Self::left(status),
+ Event::Key(Key::Right) => Self::right(status),
+ Event::Key(Key::Backspace) => Self::backspace(status),
+ Event::Key(Key::Ctrl('d')) => Self::delete(status),
+ Event::Key(Key::Ctrl('q')) => Self::escape(status),
Event::Key(Key::Char(c)) => self.char(status, c),
- Event::Key(Key::Home) => self.home(status),
- Event::Key(Key::End) => self.end(status),
- Event::Key(Key::PageDown) => self.page_down(status),
- Event::Key(Key::PageUp) => self.page_up(status),
- Event::Key(Key::Enter) => self.enter(status),
- Event::Key(Key::Tab) => self.tab(status),
- Event::Key(Key::BackTab) => self.backtab(status),
- Event::Key(Key::WheelUp(_, _, _)) => self.up(status),
- Event::Key(Key::WheelDown(_, _, _)) => self.down(status),
+ Event::Key(Key::Home) => Self::home(status),
+ Event::Key(Key::End) => Self::end(status),
+ Event::Key(Key::PageDown) => Self::page_down(status),
+ Event::Key(Key::PageUp) => Self::page_up(status),
+ Event::Key(Key::Enter) => Self::enter(status),
+ Event::Key(Key::Tab) => Self::tab(status),
+ Event::Key(Key::BackTab) => Self::backtab(status),
+ Event::Key(Key::WheelUp(_, _, _)) => Self::up(status),
+ Event::Key(Key::WheelDown(_, _, _)) => Self::down(status),
Event::Key(Key::SingleClick(MouseButton::Left, row, _)) => {
- self.left_click(status, row);
+ Self::left_click(status, row);
Ok(())
}
Event::Key(Key::SingleClick(MouseButton::Right, row, _)) => {
- self.right_click(status, row);
+ Self::right_click(status, row);
Ok(())
}
- Event::Key(Key::Ctrl('f')) => self.ctrl_f(status),
- Event::Key(Key::Ctrl('c')) => self.ctrl_c(status),
- Event::Key(Key::Ctrl('p')) => self.ctrl_p(status),
- Event::Key(Key::Ctrl('r')) => self.refresh_selected_view(status),
- Event::Key(Key::Ctrl('x')) => self.ctrl_x(status),
- Event::User(_) => self.refresh_selected_view(status),
- Event::Resize { width, height } => self.resize(status, width, height),
+ Event::Key(Key::Ctrl('f')) => Self::ctrl_f(status),
+ Event::Key(Key::Ctrl('c')) => Self::ctrl_c(status),
+ Event::Key(Key::Ctrl('p')) => Self::ctrl_p(status),
+ Event::Key(Key::Ctrl('r')) => Self::refresh_selected_view(status),
+ Event::Key(Key::Ctrl('x')) => Self::ctrl_x(status),
+ Event::User(_) => Self::refresh_selected_view(status),
+ Event::Resize { width, height } => Self::resize(status, width, height),
_ => Ok(()),
}
}
/// Leaving a mode reset the window
- fn escape(&self, status: &mut Status) -> FmResult<()> {
- status.selected().event_normal()
+ fn escape(status: &mut Status) -> FmResult<()> {
+ Self::event_normal(status.selected())
}
/// Move one line up
- fn up(&self, status: &mut Status) -> FmResult<()> {
+ fn up(status: &mut Status) -> FmResult<()> {
match status.selected().mode {
- Mode::Normal | Mode::Preview | Mode::Help => status.selected().event_up_one_row(),
- Mode::Jump => status.event_jumplist_prev(),
- Mode::History => status.selected().event_history_prev(),
- Mode::Shortcut => status.selected().event_shortcut_prev(),
+ Mode::Normal | Mode::Preview | Mode::Help => Self::event_up_one_row(status.selected()),
+ Mode::Jump => Self::event_jumplist_prev(status),
+ Mode::History => Self::event_history_prev(status.selected()),
+ Mode::Shortcut => Self::event_shortcut_prev(status.selected()),
Mode::Goto | Mode::Exec | Mode::Search => {
status.selected().completion.prev();
}
@@ -81,12 +102,14 @@ impl Actioner {
}
/// Move one line down
- fn down(&self, status: &mut Status) -> FmResult<()> {
+ fn down(status: &mut Status) -> FmResult<()> {
match status.selected().mode {
- Mode::Normal | Mode::Preview | Mode::Help => status.selected().event_down_one_row(),
- Mode::Jump => status.event_jumplist_next(),
- Mode::History => status.selected().event_history_next(),
- Mode::Shortcut => status.selected().event_shortcut_next(),
+ Mode::Normal | Mode::Preview | Mode::Help => {
+ Self::event_down_one_row(status.selected())
+ }
+ Mode::Jump => Self::event_jumplist_next(status),
+ Mode::History => Self::event_history_next(status.selected()),
+ Mode::Shortcut => Self::event_shortcut_next(status.selected()),
Mode::Goto | Mode::Exec | Mode::Search => {
status.selected().completion.next();
}
@@ -96,9 +119,9 @@ impl Actioner {
}
/// Move left in a string, move to parent in normal mode
- fn left(&self, status: &mut Status) -> FmResult<()> {
+ fn left(status: &mut Status) -> FmResult<()> {
match status.selected().mode {
- Mode::Normal => status.selected().event_move_to_parent(),
+ Mode::Normal => Self::event_move_to_parent(status.selected()),
Mode::Rename
| Mode::Chmod
| Mode::Newdir
@@ -108,7 +131,7 @@ impl Actioner {
| Mode::Goto
| Mode::RegexMatch
| Mode::Filter => {
- status.selected().event_move_cursor_left();
+ Self::event_move_cursor_left(status.selected());
Ok(())
}
@@ -117,9 +140,9 @@ impl Actioner {
}
/// Move right in a string, move to children in normal mode.
- fn right(&self, status: &mut Status) -> FmResult<()> {
+ fn right(status: &mut Status) -> FmResult<()> {
match status.selected().mode {
- Mode::Normal => status.selected().exec_file(),
+ Mode::Normal => Self::exec_file(status.selected()),
Mode::Rename
| Mode::Chmod
| Mode::Newdir
@@ -129,7 +152,7 @@ impl Actioner {
| Mode::Goto
| Mode::RegexMatch
| Mode::Filter => {
- status.selected().event_move_cursor_right();
+ Self::event_move_cursor_right(status.selected());
Ok(())
}
_ => Ok(()),
@@ -137,7 +160,7 @@ impl Actioner {
}
/// Deletes a char in input string
- fn backspace(&self, status: &mut Status) -> FmResult<()> {
+ fn backspace(status: &mut Status) -> FmResult<()> {
match status.selected().mode {
Mode::Rename
| Mode::Newdir
@@ -148,7 +171,7 @@ impl Actioner {
| Mode::Goto
| Mode::RegexMatch
| Mode::Filter => {
- status.selected().event_delete_char_left();
+ Self::event_delete_char_left(status.selected());
Ok(())
}
Mode::Normal => Ok(()),
@@ -158,7 +181,7 @@ impl Actioner {
/// Deletes chars right of cursor in input string.
/// Remove current tab in normal mode.
- fn delete(&self, status: &mut Status) -> FmResult<()> {
+ fn delete(status: &mut Status) -> FmResult<()> {
match status.selected().mode {
Mode::Rename
| Mode::Newdir
@@ -169,7 +192,7 @@ impl Actioner {
| Mode::Goto
| Mode::RegexMatch
| Mode::Filter => {
- status.selected().event_delete_chars_right();
+ Self::event_delete_chars_right(status.selected());
Ok(())
}
_ => Ok(()),
@@ -177,59 +200,59 @@ impl Actioner {
}
/// Move to top or beggining of line.
- fn home(&self, status: &mut Status) -> FmResult<()> {
+ fn home(status: &mut Status) -> FmResult<()> {
match status.selected().mode {
- Mode::Normal | Mode::Preview | Mode::Help => status.selected().event_go_top(),
- _ => status.selected().event_cursor_home(),
+ Mode::Normal | Mode::Preview | Mode::Help => Self::event_go_top(status.selected()),
+ _ => Self::event_cursor_home(status.selected()),
};
Ok(())
}
/// Move to end or end of line.
- fn end(&self, status: &mut Status) -> FmResult<()> {
+ fn end(status: &mut Status) -> FmResult<()> {
match status.selected().mode {
- Mode::Normal | Mode::Preview | Mode::Help => status.selected().event_go_bottom(),
- _ => status.selected().event_cursor_end(),
+ Mode::Normal | Mode::Preview | Mode::Help => Self::event_go_bottom(status.selected()),
+ _ => Self::event_cursor_end(status.selected()),
};
Ok(())
}
/// Move down 10 rows
- fn page_down(&self, status: &mut Status) -> FmResult<()> {
+ fn page_down(status: &mut Status) -> FmResult<()> {
match status.selected().mode {
- Mode::Normal | Mode::Preview | Mode::Help => status.selected().event_page_down(),
+ Mode::Normal | Mode::Preview | Mode::Help => Self::event_page_down(status.selected()),
_ => (),
};
Ok(())
}
/// Move up 10 rows
- fn page_up(&self, status: &mut Status) -> FmResult<()> {
+ fn page_up(status: &mut Status) -> FmResult<()> {
match status.selected().mode {
- Mode::Normal | Mode::Preview | Mode::Help => status.selected().event_page_up(),
+ Mode::Normal | Mode::Preview | Mode::Help => Self::event_page_up(status.selected()),
_ => (),
};
Ok(())
}
/// Execute a command
- fn enter(&self, status: &mut Status) -> FmResult<()> {
+ fn enter(status: &mut Status) -> FmResult<()> {
match status.selected().mode {
- Mode::Rename => status.selected().exec_rename()?,
- Mode::Newfile => status.selected().exec_newfile()?,
- Mode::Newdir => status.selected().exec_newdir()?,
- Mode::Chmod => status.exec_chmod()?,
- Mode::Exec => status.selected().exec_exec()?,
- Mode::Search => status.selected().exec_search(),
- Mode::Goto => status.selected().exec_goto()?,
- Mode::RegexMatch => status.exec_regex()?,
- Mode::Jump => status.exec_jump()?,
- Mode::History => status.selected().exec_history()?,
- Mode::Shortcut => status.selected().exec_shortcut()?,
- Mode::Filter => status.selected().exec_filter()?,
- Mode::Normal => status.selected().exec_file()?,
+ Mode::Rename => Self::exec_rename(status.selected())?,
+ Mode::Newfile => Self::exec_newfile(status.selected())?,
+ Mode::Newdir => Self::exec_newdir(status.selected())?,
+ Mode::Chmod => Self::exec_chmod(status)?,
+ Mode::Exec => Self::exec_exec(status.selected())?,
+ Mode::Search => Self::exec_search(status.selected()),
+ Mode::Goto => Self::exec_goto(status.selected())?,
+ Mode::RegexMatch => Self::exec_regex(status)?,
+ Mode::Jump => Self::exec_jump(status)?,
+ Mode::History => Self::exec_history(status.selected())?,
+ Mode::Shortcut => Self::exec_shortcut(status.selected())?,
+ Mode::Filter => Self::exec_filter(status.selected())?,
+ Mode::Normal => Self::exec_file(status.selected())?,
Mode::NeedConfirmation | Mode::Help | Mode::Sort | Mode::Preview | Mode::Marks(_) => (),
- }
+ };
status.selected().input.reset();
status.selected().mode = Mode::Normal;
@@ -237,25 +260,25 @@ impl Actioner {
}
/// Select this file
- fn left_click(&self, status: &mut Status, row: u16) {
+ fn left_click(status: &mut Status, row: u16) {
if let Mode::Normal = status.selected().mode {
- status.selected().event_select_row(row)
+ Self::event_select_row(status.selected(), row)
}
}
/// Open a directory or a file
- fn right_click(&self, status: &mut Status, row: u16) {
+ fn right_click(status: &mut Status, row: u16) {
if let Mode::Normal = status.selected().mode {
- let _ = status.selected().event_right_click(row);
+ let _ = Self::event_right_click(status.selected(), row);
}
}
/// Select next completion and insert it
/// Select next tab
- fn tab(&self, status: &mut Status) -> FmResult<()> {
+ fn tab(status: &mut Status) -> FmResult<()> {
match status.selected().mode {
Mode::Goto | Mode::Exec | Mode::Search => {
- status.selected().event_replace_input_with_completion()
+ Self::event_replace_input_with_completion(status.selected())
}
Mode::Normal => status.next(),
_ => (),
@@ -264,38 +287,38 @@ impl Actioner {
}
/// Select previous tab
- fn backtab(&self, status: &mut Status) -> FmResult<()> {
+ fn backtab(status: &mut Status) -> FmResult<()> {
if let Mode::Normal = status.selected().mode {
status.prev()
}
Ok(())
}
- fn ctrl_f(&self, status: &mut Status) -> FmResult<()> {
+ fn ctrl_f(status: &mut Status) -> FmResult<()> {
status.create_tabs_from_skim()?;
Ok(())
}
- fn ctrl_c(&self, status: &mut Status) -> FmResult<()> {
+ fn ctrl_c(status: &mut Status) -> FmResult<()> {
if let Mode::Normal = status.selected_non_mut().mode {
- return status.selected_non_mut().event_filename_to_clipboard();
+ return Self::event_filename_to_clipboard(status.selected());
}
Ok(())
}
- fn ctrl_p(&self, status: &mut Status) -> FmResult<()> {
+ fn ctrl_p(status: &mut Status) -> FmResult<()> {
if let Mode::Normal = status.selected_non_mut().mode {
- return status.selected_non_mut().event_filepath_to_clipboard();
+ return Self::event_filepath_to_clipboard(status.selected());
}
Ok(())
}
- fn refresh_selected_view(&self, status: &mut Status) -> FmResult<()> {
+ fn refresh_selected_view(status: &mut Status) -> FmResult<()> {
status.selected().refresh_view()
}
- fn ctrl_x(&self, status: &mut Status) -> FmResult<()> {
- status.selected().event_decompress()
+ fn ctrl_x(status: &mut Status) -> FmResult<()> {
+ Self::event_decompress(status.selected())
}
/// Match read key to a relevent event, depending on keybindings.
@@ -303,41 +326,41 @@ impl Actioner {
fn char(&self, status: &mut Status, c: char) -> FmResult<()> {
match status.selected().mode {
Mode::Newfile | Mode::Newdir | Mode::Chmod | Mode::Rename | Mode::Filter => {
- status.selected().event_text_insertion(c);
+ Self::event_text_insertion(status.selected(), c);
Ok(())
}
Mode::RegexMatch => {
- status.selected().event_text_insertion(c);
+ Self::event_text_insertion(status.selected(), c);
status.select_from_regex()?;
Ok(())
}
Mode::Goto | Mode::Exec | Mode::Search => {
- status.selected().event_text_insert_and_complete(c)
+ Self::event_text_insert_and_complete(status.selected(), c)
}
Mode::Normal => match self.binds.get(&c) {
Some(event_char) => event_char.match_char(status),
None => Ok(()),
},
- Mode::Help | Mode::Preview | Mode::Shortcut => status.selected().event_normal(),
+ Mode::Help | Mode::Preview | Mode::Shortcut => Self::event_normal(status.selected()),
Mode::Jump => Ok(()),
Mode::History => Ok(()),
Mode::NeedConfirmation => {
if c == 'y' {
- let _ = status.exec_last_edition();
+ let _ = Self::exec_last_edition(status);
}
- status.selected().event_leave_need_confirmation();
+ Self::event_leave_need_confirmation(status.selected());
Ok(())
}
- Mode::Marks(MarkAction::Jump) => status.exec_marks_jump(c),
- Mode::Marks(MarkAction::New) => status.exec_marks_new(c),
+ Mode::Marks(MarkAction::Jump) => Self::exec_marks_jump(status, c),
+ Mode::Marks(MarkAction::New) => Self::exec_marks_new(status, c),
Mode::Sort => {
- status.selected().event_leave_sort(c);
+ Self::event_leave_sort(status.selected(), c);
Ok(())
}
}
}
- fn resize(&self, status: &mut Status, width: usize, height: usize) -> FmResult<()> {
+ fn resize(status: &mut Status, width: usize, height: usize) -> FmResult<()> {
if width < MIN_WIDTH_FOR_DUAL_PANE {
status.select_tab(0)?;
status.set_dual_pane(false);
@@ -345,7 +368,758 @@ impl Actioner {
status.set_dual_pane(true);
}
status.selected().set_height(height);
- self.refresh_selected_view(status)?;
+ Self::refresh_selected_view(status)?;
+ Ok(())
+ }
+
+ pub fn event_clear_flags(status: &mut Status) -> FmResult<()> {
+ status.flagged.clear();
+ Ok(())
+ }
+
+ pub fn event_flag_all(status: &mut Status) -> FmResult<()> {
+ status.tabs[status.index]
+ .path_content
+ .files
+ .iter()
+ .for_each(|file| {
+ status.flagged.insert(file.path.clone());
+ });
+ status.reset_statuses()
+ }
+
+ pub fn event_reverse_flags(status: &mut Status) -> FmResult<()> {
+ // for file in self.selected().path_content.files.iter() {
+ // self.toggle_flag_on_path(file.path.clone())
+ // }
+
+ status.tabs[status.index]
+ .path_content
+ .files
+ .iter()
+ .for_each(|file| {
+ if status.flagged.contains(&file.path.clone()) {
+ status.flagged.remove(&file.path.clone());
+ } else {
+ status.flagged.insert(file.path.clone());
+ }
+ });
+ status.reset_statuses()
+ }
+ pub fn event_toggle_flag(status: &mut Status) -> FmResult<()> {
+ let file = status.tabs[status.index]
+ .path_content
+ .selected_file()
+ .ok_or_else(|| FmError::new("No selected file"))?;
+ status.toggle_flag_on_path(file.path.clone());
+ Self::event_down_one_row(status.selected());
+ Ok(())
+ }
+
+ pub fn event_jumplist_next(status: &mut Status) {
+ if status.jump_index < status.flagged.len() {
+ status.jump_index += 1;
+ }
+ }
+
+ pub fn event_jumplist_prev(status: &mut Status) {
+ if status.jump_index > 0 {
+ status.jump_index -= 1;
+ }
+ }
+
+ pub fn event_chmod(status: &mut Status) -> FmResult<()> {
+ if status.selected().path_content.files.is_empty() {
+ return Ok(());
+ }
+ status.selected().mode = Mode::Chmod;
+ if status.flagged.is_empty() {
+ status.flagged.insert(
+ status.tabs[status.index]
+ .path_content
+ .selected_file()
+ .unwrap()
+ .path
+ .clone(),
+ );
+ };
+ status.reset_statuses()
+ }
+
+ pub fn event_jump(status: &mut Status) {
+ if !status.flagged.is_empty() {
+ status.jump_index = 0;
+ status.selected().mode = Mode::Jump
+ }
+ }
+
+ pub fn event_marks_new(status: &mut Status) {
+ status.selected().mode = Mode::Marks(MarkAction::New)
+ }
+
+ pub fn event_marks_jump(status: &mut Status) {
+ status.selected().mode = Mode::Marks(MarkAction::Jump)
+ }
+
+ pub fn exec_marks_new(status: &mut Status, c: char) -> FmResult<()> {
+ let path = status.selected().path_content.path.clone();
+ status.marks.new_mark(c, path)?;
+ Self::event_normal(status.selected())
+ }
+
+ pub fn exec_marks_jump(status: &mut Status, c: char) -> FmResult<()> {
+ if let Some(path) = status.marks.get(c) {
+ let path = path.to_owned();
+ status.selected().history.push(&path);
+ status.selected().path_content = PathContent::new(path, status.selected().show_hidden)?;
+ };
+ Self::event_normal(status.selected())
+ }
+
+ /// Creates a symlink of every flagged file to the current directory.
+ pub fn event_symlink(status: &mut Status) -> FmResult<()> {
+ for oldpath in status.flagged.iter() {
+ let newpath = status.tabs[status.index].path_content.path.clone().join(
+ oldpath
+ .as_path()
+ .file_name()
+ .ok_or_else(|| FmError::new("File not found"))?,
+ );
+ std::os::unix::fs::symlink(oldpath, newpath)?;
+ }
+ status.clear_flags_and_reset_view()
+ }
+
+ pub fn event_bulkrename(status: &mut Status) -> FmResult<()> {
+ Bulkrename::new(status.filtered_flagged_files())?
+ .rename(&status.selected_non_mut().opener)?;
+ status.selected().refresh_view()
+ }
+
+ fn exec_copy_paste(status: &mut Status) -> FmResult<()> {
+ status.cut_or_copy_flagged_files(CopyMove::Copy)
+ }
+
+ fn exec_cut_paste(status: &mut Status) -> FmResult<()> {
+ status.cut_or_copy_flagged_files(CopyMove::Move)
+ }
+
+ fn exec_delete_files(status: &mut Status) -> FmResult<()> {
+ for pathbuf in status.flagged.iter() {
+ if pathbuf.is_dir() {
+ std::fs::remove_dir_all(pathbuf)?;
+ } else {
+ std::fs::remove_file(pathbuf)?;
+ }
+ }
+ status.clear_flags_and_reset_view()
+ }
+
+ pub fn exec_chmod(status: &mut Status) -> FmResult<()> {
+ if status.selected().input.string.is_empty() {
+ return Ok(());
+ }
+ let permissions: u32 =
+ u32::from_str_radix(&status.selected().input.string, 8).unwrap_or(0_u32);
+ if permissions <= Status::MAX_PERMISSIONS {
+ for path in status.flagged.iter() {
+ Status::set_permissions(path.clone(), permissions)?
+ }
+ status.flagged.clear()
+ }
+ status.selected().refresh_view()?;
+ status.reset_statuses()
+ }
+ pub fn _exec_last_edition(status: &mut Status) -> FmResult<()> {
+ match status.selected().last_edition {
+ LastEdition::Delete => Self::exec_delete_files(status),
+ LastEdition::CutPaste => Self::exec_cut_paste(status),
+ LastEdition::CopyPaste => Self::exec_copy_paste(status),
+ LastEdition::Nothing => Ok(()),
+ }
+ }
+
+ pub fn exec_jump(status: &mut Status) -> FmResult<()> {
+ status.selected().input.string.clear();
+ let jump_list: Vec<&PathBuf> = status.flagged.iter().collect();
+ let jump_target = jump_list[status.jump_index].clone();
+ let target_dir = match jump_target.parent() {
+ Some(parent) => parent.to_path_buf(),
+ None => jump_target.clone(),
+ };
+ status.selected().history.push(&target_dir);
+ status.selected().path_content =
+ PathContent::new(target_dir, status.selected().show_hidden)?;
+ if let Some(index) = status.find_jump_target(&jump_target) {
+ status.selected().line_index = index;
+ } else {
+ status.selected().line_index = 0;
+ }
+
+ let s_index = status.tabs[status.index].line_index;
+ status.tabs[status.index].path_content.select_index(s_index);
+ let len = status.tabs[status.index].path_content.files.len();
+ status.selected().window.reset(len);
+ status.selected().window.scroll_to(s_index);
+ Ok(())
+ }
+
+ pub fn exec_last_edition(status: &mut Status) -> FmResult<()> {
+ Self::_exec_last_edition(status)?;
+ status.selected().mode = Mode::Normal;
+ status.selected().last_edition = LastEdition::Nothing;
+ Ok(())
+ }
+
+ pub fn exec_regex(status: &mut Status) -> Result<(), regex::Error> {
+ status.select_from_regex()?;
+ status.selected().input.reset();
+ Ok(())
+ }
+
+ pub fn event_normal(tab: &mut Tab) -> FmResult<()> {
+ tab.input.reset();
+ tab.completion.reset();
+ tab.path_content.reset_files()?;
+ tab.window.reset(tab.path_content.files.len());
+ tab.mode = Mode::Normal;
+ tab.preview = Preview::empty();
+ Ok(())
+ }
+
+ pub fn event_up_one_row(tab: &mut Tab) {
+ match tab.mode {
+ Mode::Normal => {
+ tab.path_content.select_prev();
+ if tab.line_index > 0 {
+ tab.line_index -= 1;
+ }
+ }
+ Mode::Preview | Mode::Help => tab.line_index = tab.window.top,
+ _ => (),
+ }
+ tab.window.scroll_up_one(tab.line_index);
+ }
+
+ pub fn event_down_one_row(tab: &mut Tab) {
+ match tab.mode {
+ Mode::Normal => {
+ tab.path_content.select_next();
+ let max_line = tab.path_content.files.len();
+ if max_line >= ContentWindow::WINDOW_MARGIN_TOP
+ && tab.line_index < max_line - ContentWindow::WINDOW_MARGIN_TOP
+ {
+ tab.line_index += 1;
+ }
+ }
+ Mode::Preview | Mode::Help => tab.line_index = tab.window.bottom,
+ _ => (),
+ }
+ tab.window.scroll_down_one(tab.line_index);
+ }
+
+ pub fn event_go_top(tab: &mut Tab) {
+ if let Mode::Normal = tab.mode {
+ tab.path_content.select_index(0);
+ }
+ tab.line_index = 0;
+ tab.window.scroll_to(0);
+ }
+
+ pub fn event_page_up(tab: &mut Tab) {
+ let scroll_up: usize = if let Mode::Normal = tab.mode {
+ 10
+ } else {
+ tab.height
+ };
+ let up_index = if tab.line_index > scroll_up {
+ tab.line_index - scroll_up
+ } else {
+ 0
+ };
+ if let Mode::Normal = tab.mode {
+ tab.path_content.select_index(up_index);
+ }
+ tab.line_index = up_index;
+ tab.window.scroll_to(up_index);
+ }
+
+ pub fn event_go_bottom(tab: &mut Tab) {
+ let last_index: usize;
+ if let Mode::Normal = tab.mode {
+ last_index = tab.path_content.files.len() - 1;
+ tab.path_content.select_index(last_index);
+ } else {
+ last_index = tab.preview.len() - 1;
+ }
+ tab.line_index = last_index;
+ tab.window.scroll_to(last_index);
+ }
+
+ pub fn event_cursor_home(tab: &mut Tab) {
+ tab.input.cursor_start()
+ }
+
+ pub fn event_cursor_end(tab: &mut Tab) {
+ tab.input.cursor_end()
+ }
+
+ pub fn event_page_down(tab: &mut Tab) {
+ let down_index: usize;
+ if let Mode::Normal = tab.mode {
+ down_index = min(tab.path_content.files.len() - 1, tab.line_index + 10);
+ tab.path_content.select_index(down_index);
+ } else {
+ down_index = min(tab.preview.len() - 1, tab.line_index + 30)
+ }
+ tab.line_index = down_index;
+ tab.window.scroll_to(down_index);
+ }
+
+ pub fn event_select_row(tab: &mut Tab, row: u16) {
+ tab.line_index = (row - 2).into();
+ tab.path_content.select_index(tab.line_index);
+ tab.window.scroll_to(tab.line_index)
+ }
+
+ pub fn event_shortcut_next(tab: &mut Tab) {
+ tab.shortcut.next()
+ }
+
+ pub fn event_shortcut_prev(tab: &mut Tab) {
+ tab.shortcut.prev()
+ }
+
+ pub fn event_history_next(tab: &mut Tab) {
+ tab.history.next()
+ }
+
+ pub fn event_history_prev(tab: &mut Tab) {
+ tab.history.prev()
+ }
+
+ pub fn event_move_to_parent(tab: &mut Tab) -> FmResult<()> {
+ let parent = tab.path_content.path.parent();
+ let path = path::PathBuf::from(
+ parent.ok_or_else(|| FmError::new("Root directory has no parent"))?,
+ );
+ tab.history.push(&path);
+ tab.path_content = PathContent::new(path, tab.show_hidden)?;
+ tab.window.reset(tab.path_content.files.len());
+ tab.line_index = 0;
+ tab.input.cursor_start();
+ Ok(())
+ }
+
+ pub fn event_move_cursor_left(tab: &mut Tab) {
+ tab.input.cursor_left()
+ }
+
+ pub fn exec_file(tab: &mut Tab) -> FmResult<()> {
+ if tab.path_content.is_empty() {
+ return Ok(());
+ }
+ if tab.path_content.is_selected_dir()? {
+ tab.go_to_child()
+ } else {
+ Self::event_open_file(tab)
+ }
+ }
+
+ pub fn event_move_cursor_right(tab: &mut Tab) {
+ tab.input.cursor_right()
+ }
+
+ pub fn event_delete_char_left(tab: &mut Tab) {
+ tab.input.delete_char_left()
+ }
+
+ pub fn event_delete_chars_right(tab: &mut Tab) {
+ tab.input.delete_chars_right()
+ }
+
+ pub fn event_text_insert_and_complete(tab: &mut Tab, c: char) -> FmResult<()> {
+ Self::event_text_insertion(tab, c);
+ tab.fill_completion()
+ }
+
+ pub fn event_copy_paste(tab: &mut Tab) {
+ tab.mode = Mode::NeedConfirmation;
+ tab.last_edition = LastEdition::CopyPaste;
+ }
+
+ pub fn event_cur_paste(tab: &mut Tab) {
+ tab.mode = Mode::NeedConfirmation;
+ tab.last_edition = LastEdition::CutPaste;
+ }
+
+ pub fn event_new_dir(tab: &mut Tab) {
+ tab.mode = Mode::Newdir
+ }
+
+ pub fn event_new_file(tab: &mut Tab) {
+ tab.mode = Mode::Newfile
+ }
+
+ pub fn event_exec(tab: &mut Tab) {
+ tab.mode = Mode::Exec
+ }
+
+ pub fn event_preview(tab: &mut Tab) -> FmResult<()> {
+ if tab.path_content.files.is_empty() {
+ return Err(FmError::new("No file to preview"));
+ }
+ if let Some(file) = tab.path_content.selected_file() {
+ if let FileKind::NormalFile = file.file_kind {
+ tab.mode = Mode::Preview;
+ tab.preview = Preview::new(&tab.path_content)?;
+ tab.window.reset(tab.preview.len());
+ }
+ }
+ Ok(())
+ }
+
+ pub fn event_delete_file(tab: &mut Tab) {
+ tab.mode = Mode::NeedConfirmation;
+ tab.last_edition = LastEdition::Delete;
+ }
+
+ pub fn event_help(tab: &mut Tab) {
+ tab.mode = Mode::Help;
+ tab.preview = Preview::help(tab.help.clone());
+ tab.window.reset(tab.preview.len())
+