diff options
author | qkzk <qu3nt1n@gmail.com> | 2022-12-06 20:26:56 +0100 |
---|---|---|
committer | qkzk <qu3nt1n@gmail.com> | 2022-12-06 20:26:56 +0100 |
commit | fc211f60836223c72b91b1bbd557b049e66a3f4d (patch) | |
tree | 174d3e6fd56ae61825fd7d41a7e4112867fd756a /src/actioner.rs | |
parent | bf851ff019f144700a7aa824865016c644cce9f2 (diff) |
move event & exec to actioner
Diffstat (limited to 'src/actioner.rs')
-rw-r--r-- | src/actioner.rs | 974 |
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()) + |