1use std::borrow::Borrow;
2use std::path;
3
4use anyhow::{Context, Result};
5use clap::Parser;
6use indicatif::InMemoryTerm;
7
8use crate::app::{Direction, Focus, Status, Tab};
9use crate::common::{
10 content_to_clipboard, filename_to_clipboard, filepath_to_clipboard, get_clipboard,
11 open_in_current_neovim, set_clipboard, set_current_dir, tilde, CONFIG_PATH,
12};
13use crate::config::{Bindings, START_FOLDER};
14use crate::io::{read_log, Args, External};
15use crate::log_info;
16use crate::log_line;
17use crate::modes::{
18 help_string, lsblk_and_udisksctl_installed, nvim_inform_ipc, Content, ContentWindow,
19 Direction as FuzzyDirection, Display, FuzzyKind, Go, InputCompleted, InputSimple, LeaveMenu,
20 MarkAction, Menu, Navigate, NeedConfirmation, NvimIPCAction, Preview, PreviewBuilder,
21 ReEnterMenu, Search, Selectable, To,
22};
23
24pub struct EventAction {}
27
28impl EventAction {
29 pub fn quit(status: &mut Status) -> Result<()> {
33 if status.focus.is_file() {
34 status.internal_settings.quit();
35 } else {
36 status.reset_menu_mode()?;
37 }
38 Ok(())
39 }
40
41 pub fn refresh_view(status: &mut Status) -> Result<()> {
43 status.refresh_view()
44 }
45
46 pub fn refresh_if_needed(status: &mut Status) -> Result<()> {
48 status.menu.flagged.remove_non_existant();
49 status.tabs[0].refresh_if_needed()?;
50 status.tabs[1].refresh_if_needed()
51 }
52
53 pub fn paste(status: &mut Status, pasted: String) -> Result<()> {
59 log_info!("pasted: ###'{pasted}'###");
60 let pasted = pasted.trim();
61 let display_mode = status.current_tab().display_mode;
62 if status.focus.is_file() && (display_mode.is_tree() || display_mode.is_directory()) {
63 status.paste_pathes(pasted)
64 } else {
65 status.paste_input(pasted)
66 }
67 }
68
69 pub fn resize(status: &mut Status, width: u16, height: u16) -> Result<()> {
70 status.resize(width, height)
71 }
72
73 pub fn reset_mode(status: &mut Status) -> Result<()> {
76 if status.focus.is_file() && status.current_tab().display_mode.is_preview() {
77 status.leave_preview()?;
78 }
79 if matches!(status.current_tab().menu_mode, Menu::Nothing) {
80 status.current_tab_mut().reset_visual();
81 return Ok(());
82 };
83 status.leave_menu_mode()?;
84 status.menu.input.reset();
85 status.menu.completion.reset();
86 Ok(())
87 }
88
89 pub fn toggle_display_full(status: &mut Status) -> Result<()> {
92 status.session.toggle_metadata();
93 Ok(())
94 }
95
96 pub fn toggle_dualpane(status: &mut Status) -> Result<()> {
99 status.clear_preview_right();
100 status.session.toggle_dual();
101 status.select_left();
102 Ok(())
103 }
104
105 pub fn toggle_preview_second(status: &mut Status) -> Result<()> {
107 if !status.session.dual() {
108 Self::toggle_dualpane(status)?;
109 status.session.set_preview();
110 } else {
111 status.session.toggle_preview();
112 }
113 if status.session.preview() {
114 status.update_second_pane_for_preview()
115 } else {
116 status.set_menu_mode(1, Menu::Nothing)?;
117 status.tabs[1].display_mode = Display::Directory;
118 status.tabs[1].refresh_view()
119 }
120 }
121
122 pub fn tree(status: &mut Status) -> Result<()> {
125 if !status.focus.is_file() {
126 return Ok(());
127 }
128 status.current_tab_mut().toggle_tree_mode()?;
129 status.refresh_view()
130 }
131
132 pub fn tree_depth_decr(status: &mut Status) -> Result<()> {
134 if !status.focus.is_file() && status.current_tab().display_mode.is_tree() {
135 return Ok(());
136 }
137 let current_depth = status.current_tab().settings.tree_max_depth;
138 if current_depth > 1 {
139 status.current_tab_mut().settings.tree_max_depth -= 1;
140 let new_depth = status.current_tab().settings.tree_max_depth;
141 log_info!("Decrease tree depth current: {new_depth}");
142 log_line!("Decrease tree depth current: {new_depth}");
143 }
144 status.current_tab_mut().toggle_tree_mode()?;
145 status.current_tab_mut().toggle_tree_mode()?;
146 Ok(())
147 }
148
149 pub fn tree_depth_incr(status: &mut Status) -> Result<()> {
151 if !status.focus.is_file() && status.current_tab().display_mode.is_tree() {
152 return Ok(());
153 }
154 status.current_tab_mut().settings.tree_max_depth += 1;
155 let new_depth = status.current_tab().settings.tree_max_depth;
156 log_info!("Increase tree depth current: {new_depth}");
157 log_line!("Increase tree depth current: {new_depth}");
158
159 status.current_tab_mut().toggle_tree_mode()?;
160 status.current_tab_mut().toggle_tree_mode()?;
161 Ok(())
162 }
163
164 pub fn tree_fold(status: &mut Status) -> Result<()> {
167 if !status.focus.is_file() {
168 return Ok(());
169 }
170 let tab = status.current_tab_mut();
171 tab.tree.toggle_fold();
172 Ok(())
173 }
174
175 pub fn tree_unfold_all(status: &mut Status) -> Result<()> {
179 if !status.focus.is_file() {
180 return Ok(());
181 }
182 let tab = status.current_tab_mut();
183 tab.tree.unfold_all();
184 Ok(())
185 }
186
187 pub fn tree_fold_all(status: &mut Status) -> Result<()> {
191 if !status.focus.is_file() {
192 return Ok(());
193 }
194 let tab = status.current_tab_mut();
195 tab.tree.fold_all();
196 Ok(())
197 }
198
199 pub fn display_flagged(status: &mut Status) -> Result<()> {
202 if !status.focus.is_file() {
203 return Ok(());
204 }
205 let menu_mode = &status.current_tab().menu_mode;
206 if matches!(menu_mode, Menu::Navigate(Navigate::Flagged)) {
207 status.leave_menu_mode()?;
208 } else if matches!(menu_mode, Menu::Nothing) {
209 status.set_menu_mode(status.index, Menu::Navigate(Navigate::Flagged))?;
210 }
211 Ok(())
212 }
213
214 pub fn preview(status: &mut Status) -> Result<()> {
219 if !status.focus.is_file() {
220 return Ok(());
221 }
222 status.current_tab_mut().make_preview()
223 }
224
225 pub fn toggle_hidden(status: &mut Status) -> Result<()> {
227 if !status.focus.is_file() || status.current_tab().display_mode.is_preview() {
228 return Ok(());
229 }
230 status.current_tab_mut().toggle_hidden()
231 }
232
233 pub fn clear_flags(status: &mut Status) -> Result<()> {
235 if status.focus.is_file() {
236 status.menu.flagged.clear();
237 }
238 Ok(())
239 }
240
241 pub fn flag_all(status: &mut Status) -> Result<()> {
243 if status.focus.is_file() {
244 status.flag_all();
245 }
246 Ok(())
247 }
248
249 pub fn flagged_to_clipboard(status: &mut Status) -> Result<()> {
251 set_clipboard(status.menu.flagged.content_to_string());
252 Ok(())
253 }
254
255 pub fn flagged_from_clipboard(status: &mut Status) -> Result<()> {
258 let Some(files) = get_clipboard() else {
259 return Ok(());
260 };
261 if files.is_empty() {
262 return Ok(());
263 }
264 log_info!("clipboard read: {files}");
265 status.menu.flagged.replace_by_string(files);
266 Ok(())
267 }
268
269 pub fn reverse_flags(status: &mut Status) -> Result<()> {
272 if status.focus.is_file() {
273 status.reverse_flags();
274 }
275 Ok(())
276 }
277
278 pub fn toggle_flag(status: &mut Status) -> Result<()> {
280 if status.focus.is_file() {
281 status.toggle_flag_for_selected();
282 }
283 Ok(())
284 }
285
286 pub fn toggle_flag_children(status: &mut Status) -> Result<()> {
288 if status.focus.is_file() {
289 status.toggle_flag_for_children();
290 }
291 Ok(())
292 }
293
294 pub fn reenter_menu_from_picker(status: &mut Status, menu: Menu) -> Result<()> {
295 menu.reenter(status)?;
296 if !menu.is_input() {
297 return Ok(());
298 }
299 let Some(picked) = &status.menu.picker.selected() else {
300 return Ok(());
301 };
302 status.menu.input.replace(picked);
303 let Menu::InputCompleted(input_completed) = menu else {
304 return Ok(());
305 };
306 status.complete(input_completed)
307 }
308
309 pub fn rename(status: &mut Status) -> Result<()> {
314 if matches!(
315 status.current_tab().menu_mode,
316 Menu::InputSimple(InputSimple::Rename)
317 ) {
318 status.reset_menu_mode()?;
319 return Ok(());
320 };
321 let selected_path = status
322 .current_tab()
323 .selected_path()
324 .context("No selected file")?;
325 if selected_path == status.current_tab().directory.path {
326 return Ok(());
327 }
328 if let Some(parent) = status.current_tab().directory.path.parent() {
329 if selected_path.as_ref() == parent {
330 return Ok(());
331 }
332 }
333 let old_name = &selected_path.to_string_lossy();
334 status.set_menu_mode(status.index, Menu::InputSimple(InputSimple::Rename))?;
335 status.menu.input.replace(old_name);
336 Ok(())
337 }
338
339 pub fn copy_paste(status: &mut Status) -> Result<()> {
344 if matches!(
345 status.current_tab().menu_mode,
346 Menu::NeedConfirmation(NeedConfirmation::Copy)
347 ) {
348 status.reset_menu_mode()?;
349 } else {
350 Self::set_copy_paste(status, NeedConfirmation::Copy)?;
351 }
352 Ok(())
353 }
354
355 pub fn cut_paste(status: &mut Status) -> Result<()> {
360 if matches!(
361 status.current_tab().menu_mode,
362 Menu::NeedConfirmation(NeedConfirmation::Move)
363 ) {
364 status.reset_menu_mode()?;
365 } else {
366 Self::set_copy_paste(status, NeedConfirmation::Move)?;
367 }
368 Ok(())
369 }
370
371 fn set_copy_paste(status: &mut Status, copy_or_move: NeedConfirmation) -> Result<()> {
372 if status.menu.flagged.is_empty() {
373 return Ok(());
374 }
375 status.set_menu_mode(status.index, Menu::NeedConfirmation(copy_or_move))
376 }
377
378 pub fn symlink(status: &mut Status) -> Result<()> {
380 if !status.focus.is_file() {
381 return Ok(());
382 }
383 for original_file in status.menu.flagged.content.iter() {
384 let filename = original_file
385 .as_path()
386 .file_name()
387 .context("event symlink: File not found")?;
388 let link = status.current_tab().directory_of_selected()?.join(filename);
389 std::os::unix::fs::symlink(original_file, &link)?;
390 log_line!(
391 "Symlink {link} links to {original_file}",
392 original_file = original_file.display(),
393 link = link.display()
394 );
395 }
396 status.clear_flags_and_reset_view()
397 }
398
399 pub fn delete_file(status: &mut Status) -> Result<()> {
403 if status.menu.flagged.is_empty() {
404 Self::toggle_flag(status)?;
405 }
406 status.set_menu_mode(
407 status.index,
408 Menu::NeedConfirmation(NeedConfirmation::Delete),
409 )
410 }
411
412 pub fn chmod(status: &mut Status) -> Result<()> {
414 if matches!(
415 status.current_tab().menu_mode,
416 Menu::InputSimple(InputSimple::Chmod)
417 ) {
418 status.reset_menu_mode()?;
419 } else {
420 status.set_mode_chmod()?;
421 }
422 Ok(())
423 }
424
425 fn new_node(status: &mut Status, input_kind: InputSimple) -> Result<()> {
427 if !matches!(input_kind, InputSimple::Newdir | InputSimple::Newfile) {
428 return Ok(());
429 }
430 if matches!(
431 status.current_tab().menu_mode,
432 Menu::InputSimple(InputSimple::Newdir | InputSimple::Newfile)
433 ) {
434 status.reset_menu_mode()?;
435 return Ok(());
436 }
437 if matches!(
438 status.current_tab().display_mode,
439 Display::Directory | Display::Tree
440 ) {
441 status.set_menu_mode(status.index, Menu::InputSimple(input_kind))?;
442 }
443 Ok(())
444 }
445
446 pub fn new_dir(status: &mut Status) -> Result<()> {
448 Self::new_node(status, InputSimple::Newdir)
449 }
450
451 pub fn new_file(status: &mut Status) -> Result<()> {
453 Self::new_node(status, InputSimple::Newfile)
454 }
455
456 fn enter_file(status: &mut Status) -> Result<()> {
457 match status.current_tab_mut().display_mode {
458 Display::Directory => Self::normal_enter_file(status),
459 Display::Tree => Self::tree_enter_file(status),
460 Display::Fuzzy => status.fuzzy_select(),
461 Display::Preview => status.enter_from_preview(),
462 }
463 }
464
465 fn normal_enter_file(status: &mut Status) -> Result<()> {
467 let tab = status.current_tab_mut();
468 if tab.display_mode.is_tree() {
469 return EventAction::open_file(status);
470 };
471 if tab.directory.is_empty() {
472 return Ok(());
473 }
474 if tab.directory.is_selected_dir()? {
475 tab.go_to_selected_dir()?;
476 status.thumbnail_directory_video();
477 Ok(())
478 } else {
479 EventAction::open_file(status)
480 }
481 }
482
483 fn tree_enter_file(status: &mut Status) -> Result<()> {
485 let path = status
486 .current_tab()
487 .selected_path()
488 .context("No selected file")?;
489 if path.is_dir() {
490 status.current_tab_mut().tree_enter_dir(path)
491 } else {
492 EventAction::open_file(status)
493 }
494 }
495
496 pub fn open_file(status: &mut Status) -> Result<()> {
504 if !status.focus.is_file() {
505 return Ok(());
506 }
507 if status.menu.flagged.is_empty() {
508 status.open_selected_file()
509 } else {
510 status.open_flagged_files()
511 }
512 }
513
514 pub fn open_all(status: &mut Status) -> Result<()> {
515 status.open_flagged_files()
516 }
517
518 pub fn exec(status: &mut Status) -> Result<()> {
520 if matches!(
521 status.current_tab().menu_mode,
522 Menu::InputCompleted(InputCompleted::Exec)
523 ) {
524 status.reset_menu_mode()?;
525 return Ok(());
526 }
527 if status.menu.flagged.is_empty() {
528 status.menu.flagged.push(
529 status
530 .current_tab()
531 .selected_path()
532 .context("No selected file")?
533 .to_path_buf(),
534 );
535 }
536 status.set_menu_mode(status.index, Menu::InputCompleted(InputCompleted::Exec))
537 }
538
539 pub fn sort(status: &mut Status) -> Result<()> {
541 if matches!(
542 status.current_tab().menu_mode,
543 Menu::InputSimple(InputSimple::Sort)
544 ) {
545 status.reset_menu_mode()?;
546 }
547 status.set_height_for_menu_mode(status.index, Menu::Nothing)?;
548 status.tabs[status.index].menu_mode = Menu::Nothing;
549 let len = status.menu.len(Menu::Nothing);
550 let height = status.second_window_height()?;
551 status.menu.window = ContentWindow::new(len, height);
552 status.tabs[status.index].menu_mode = Menu::InputSimple(InputSimple::Sort);
553 status.set_focus_from_mode();
554 Ok(())
555 }
556
557 pub fn filter(status: &mut Status) -> Result<()> {
560 if matches!(
561 status.current_tab().menu_mode,
562 Menu::InputSimple(InputSimple::Filter)
563 ) {
564 status.reset_menu_mode()?;
565 } else if matches!(
566 status.current_tab().display_mode,
567 Display::Tree | Display::Directory
568 ) {
569 status.set_menu_mode(status.index, Menu::InputSimple(InputSimple::Filter))?;
570 }
571 Ok(())
572 }
573
574 pub fn bulk(status: &mut Status) -> Result<()> {
578 if !status.focus.is_file() {
579 return Ok(());
580 }
581 status.bulk_ask_filenames()?;
582 Ok(())
583 }
584
585 pub fn search(status: &mut Status) -> Result<()> {
588 if matches!(
589 status.current_tab().menu_mode,
590 Menu::InputCompleted(InputCompleted::Search)
591 ) {
592 status.reset_menu_mode()?;
593 }
594 let tab = status.current_tab_mut();
595 tab.search = Search::empty();
596 status.set_menu_mode(status.index, Menu::InputCompleted(InputCompleted::Search))
597 }
598
599 pub fn regex_match(status: &mut Status) -> Result<()> {
602 if matches!(
603 status.current_tab().menu_mode,
604 Menu::InputSimple(InputSimple::RegexMatch)
605 ) {
606 status.reset_menu_mode()?;
607 }
608 if matches!(
609 status.current_tab().display_mode,
610 Display::Tree | Display::Directory
611 ) {
612 status.set_menu_mode(status.index, Menu::InputSimple(InputSimple::RegexMatch))
613 } else {
614 Ok(())
615 }
616 }
617
618 pub fn help(status: &mut Status, binds: &Bindings) -> Result<()> {
621 if !status.focus.is_file() {
622 return Ok(());
623 }
624 let help = help_string(binds, &status.internal_settings.opener);
625 status.current_tab_mut().set_display_mode(Display::Preview);
626 status.current_tab_mut().preview = PreviewBuilder::help(&help);
627 let len = status.current_tab().preview.len();
628 status.current_tab_mut().window.reset(len);
629 Ok(())
630 }
631
632 pub fn log(status: &mut Status) -> Result<()> {
634 if !status.focus.is_file() {
635 return Ok(());
636 }
637 let Ok(log) = read_log() else {
638 return Ok(());
639 };
640 let tab = status.current_tab_mut();
641 tab.set_display_mode(Display::Preview);
642 tab.preview = PreviewBuilder::log(log);
643 tab.window.reset(tab.preview.len());
644 tab.preview_go_bottom();
645 Ok(())
646 }
647
648 pub fn cd(status: &mut Status) -> Result<()> {
650 if matches!(
651 status.current_tab().menu_mode,
652 Menu::InputCompleted(InputCompleted::Cd)
653 ) {
654 status.reset_menu_mode()?;
655 } else {
656 status.set_menu_mode(status.index, Menu::InputCompleted(InputCompleted::Cd))?;
657
658 status.tabs[status.index].save_origin_path();
659 status.menu.completion.reset();
660 }
661 Ok(())
662 }
663
664 pub fn shell(status: &mut Status) -> Result<()> {
668 if !status.focus.is_file() {
669 return Ok(());
670 }
671 set_current_dir(status.current_tab().current_directory_path())?;
672 status.internal_settings.disable_display();
673 External::open_shell_in_window()?;
674 status.internal_settings.enable_display();
675 Ok(())
676 }
677
678 pub fn shell_command(status: &mut Status) -> Result<()> {
681 if matches!(
682 status.current_tab().menu_mode,
683 Menu::InputSimple(InputSimple::ShellCommand)
684 ) {
685 status.reset_menu_mode()?;
686 }
687 status.set_menu_mode(status.index, Menu::InputSimple(InputSimple::ShellCommand))
688 }
689
690 pub fn tui_menu(status: &mut Status) -> Result<()> {
692 if matches!(
693 status.current_tab().menu_mode,
694 Menu::Navigate(Navigate::TuiApplication)
695 ) {
696 status.reset_menu_mode()?;
697 } else {
698 if status.menu.tui_applications.is_not_set() {
699 status.menu.tui_applications.setup();
700 }
701 status.set_menu_mode(status.index, Menu::Navigate(Navigate::TuiApplication))?;
702 }
703 Ok(())
704 }
705
706 pub fn cli_menu(status: &mut Status) -> Result<()> {
709 if matches!(
710 status.current_tab().menu_mode,
711 Menu::Navigate(Navigate::CliApplication)
712 ) {
713 status.reset_menu_mode()?;
714 } else {
715 if status.menu.cli_applications.is_empty() {
716 status.menu.cli_applications.setup();
717 }
718 status.set_menu_mode(status.index, Menu::Navigate(Navigate::CliApplication))?;
719 }
720 Ok(())
721 }
722
723 pub fn history(status: &mut Status) -> Result<()> {
726 if matches!(
727 status.current_tab().menu_mode,
728 Menu::Navigate(Navigate::History)
729 ) {
730 status.reset_menu_mode()?;
731 } else if matches!(
732 status.current_tab().display_mode,
733 Display::Directory | Display::Tree
734 ) {
735 status.set_menu_mode(status.index, Menu::Navigate(Navigate::History))?;
736 }
737 Ok(())
738 }
739
740 pub fn marks_new(status: &mut Status) -> Result<()> {
742 if matches!(
743 status.current_tab().menu_mode,
744 Menu::Navigate(Navigate::Marks(MarkAction::New))
745 ) {
746 status.reset_menu_mode()?;
747 } else {
748 if status.menu.marks.is_empty() {
749 status.menu.marks.setup();
750 }
751 status.set_menu_mode(
752 status.index,
753 Menu::Navigate(Navigate::Marks(MarkAction::New)),
754 )?;
755 }
756 Ok(())
757 }
758
759 pub fn marks_jump(status: &mut Status) -> Result<()> {
761 if matches!(
762 status.current_tab().menu_mode,
763 Menu::Navigate(Navigate::Marks(MarkAction::Jump))
764 ) {
765 status.reset_menu_mode()?;
766 } else {
767 if status.menu.marks.is_empty() {
768 status.menu.marks.setup();
769 }
770 if status.menu.marks.is_empty() {
771 return Ok(());
772 }
773 status.set_menu_mode(
774 status.index,
775 Menu::Navigate(Navigate::Marks(MarkAction::Jump)),
776 )?;
777 }
778 Ok(())
779 }
780
781 pub fn temp_marks_jump(status: &mut Status) -> Result<()> {
783 if matches!(
784 status.current_tab().menu_mode,
785 Menu::Navigate(Navigate::TempMarks(MarkAction::Jump))
786 ) {
787 status.reset_menu_mode()?;
788 } else {
789 status.set_menu_mode(
790 status.index,
791 Menu::Navigate(Navigate::TempMarks(MarkAction::Jump)),
792 )?;
793 }
794 Ok(())
795 }
796
797 pub fn temp_marks_new(status: &mut Status) -> Result<()> {
799 if matches!(
800 status.current_tab().menu_mode,
801 Menu::Navigate(Navigate::TempMarks(MarkAction::New))
802 ) {
803 status.reset_menu_mode()?;
804 } else {
805 status.set_menu_mode(
806 status.index,
807 Menu::Navigate(Navigate::TempMarks(MarkAction::New)),
808 )?;
809 }
810 Ok(())
811 }
812
813 pub fn shortcut(status: &mut Status) -> Result<()> {
817 if matches!(
818 status.current_tab().menu_mode,
819 Menu::Navigate(Navigate::Shortcut)
820 ) {
821 status.reset_menu_mode()?;
822 } else {
823 status.refresh_shortcuts();
824 set_current_dir(status.current_tab().directory_of_selected()?)?;
825 status.set_menu_mode(status.index, Menu::Navigate(Navigate::Shortcut))?;
826 }
827 Ok(())
828 }
829
830 pub fn nvim_filepicker(status: &mut Status) -> Result<()> {
836 if !status.focus.is_file() {
837 return Ok(());
838 }
839 status.update_nvim_listen_address();
840 if status.internal_settings.nvim_server.is_empty() {
841 return Ok(());
842 };
843 let nvim_server = &status.internal_settings.nvim_server;
844 if status.menu.flagged.is_empty() {
845 let Some(path) = status.current_tab().selected_path() else {
846 return Ok(());
847 };
848 open_in_current_neovim(&path, nvim_server);
849 } else {
850 for file_path in status.menu.flagged.content.iter() {
851 open_in_current_neovim(file_path, nvim_server)
852 }
853 }
854
855 Ok(())
856 }
857
858 pub fn set_nvim_server(status: &mut Status) -> Result<()> {
861 if matches!(
862 status.current_tab().menu_mode,
863 Menu::InputSimple(InputSimple::SetNvimAddr)
864 ) {
865 status.reset_menu_mode()?;
866 return Ok(());
867 };
868 status.set_menu_mode(status.index, Menu::InputSimple(InputSimple::SetNvimAddr))
869 }
870
871 pub fn back(status: &mut Status) -> Result<()> {
873 if !status.focus.is_file() {
874 return Ok(());
875 }
876 status.current_tab_mut().back()?;
877 status.update_second_pane_for_preview()
878 }
879
880 pub fn home(status: &mut Status) -> Result<()> {
882 if !status.focus.is_file() {
883 return Ok(());
884 }
885 let home_cow = tilde("~");
886 let home: &str = home_cow.borrow();
887 let home_path = path::Path::new(home);
888 status.current_tab_mut().cd(home_path)?;
889 status.update_second_pane_for_preview()
890 }
891
892 pub fn go_root(status: &mut Status) -> Result<()> {
893 if !status.focus.is_file() {
894 return Ok(());
895 }
896 let root_path = std::path::PathBuf::from("/");
897 status.current_tab_mut().cd(&root_path)?;
898 status.update_second_pane_for_preview()
899 }
900
901 pub fn go_start(status: &mut Status) -> Result<()> {
902 if !status.focus.is_file() {
903 return Ok(());
904 }
905 status
906 .current_tab_mut()
907 .cd(START_FOLDER.get().context("Start folder should be set")?)?;
908 status.update_second_pane_for_preview()
909 }
910
911 pub fn search_next(status: &mut Status) -> Result<()> {
912 if !status.focus.is_file() {
913 return Ok(());
914 }
915 match status.current_tab().display_mode {
916 Display::Tree => status.tabs[status.index]
917 .search
918 .tree(&mut status.tabs[status.index].tree),
919 Display::Directory => status.current_tab_mut().directory_search_next(),
920 Display::Preview | Display::Fuzzy => {
921 return Ok(());
922 }
923 }
924 status.refresh_status()?;
925 status.update_second_pane_for_preview()?;
926 Ok(())
927 }
928
929 pub fn move_up(status: &mut Status) -> Result<()> {
932 if status.focus.is_file() {
933 Self::move_display_up(status)?;
934 } else {
935 let tab = status.current_tab_mut();
936 match tab.menu_mode {
937 Menu::Nothing => Self::move_display_up(status)?,
938 Menu::Navigate(Navigate::History) => tab.history.prev(),
939 Menu::Navigate(navigate) => status.menu.prev(navigate),
940 Menu::InputCompleted(input_completed) => {
941 status.menu.completion_prev(input_completed);
942 if matches!(input_completed, InputCompleted::Search) {
943 status.follow_search()?;
944 }
945 }
946 Menu::NeedConfirmation(need_confirmation)
947 if need_confirmation.use_flagged_files() =>
948 {
949 status.menu.prev(Navigate::Flagged)
950 }
951 Menu::NeedConfirmation(NeedConfirmation::EmptyTrash) => {
952 status.menu.prev(Navigate::Trash)
953 }
954 Menu::NeedConfirmation(NeedConfirmation::BulkAction) => {
955 status.menu.bulk.prev();
956 status.menu.window.scroll_to(status.menu.bulk.index())
957 }
958 _ => (),
959 };
960 }
961 status.update_second_pane_for_preview()
962 }
963
964 pub fn next_thing(status: &mut Status) -> Result<()> {
968 if status.current_tab().display_mode.is_tree() && status.focus.is_file() {
969 status.current_tab_mut().tree_next_sibling();
970 } else {
971 status.input_history_prev()?;
972 }
973 Ok(())
974 }
975
976 pub fn previous_thing(status: &mut Status) -> Result<()> {
980 if status.current_tab().display_mode.is_tree() && status.focus.is_file() {
981 status.current_tab_mut().tree_prev_sibling();
982 } else {
983 status.input_history_next()?;
984 }
985 Ok(())
986 }
987
988 pub fn next_word(status: &mut Status) -> Result<()> {
989 if status.current_tab().menu_mode.is_input() && !status.focus.is_file() {
990 status.menu.input.next_word();
991 }
992 Ok(())
993 }
994
995 pub fn previous_word(status: &mut Status) -> Result<()> {
996 if status.current_tab().menu_mode.is_input() && !status.focus.is_file() {
997 status.menu.input.previous_word();
998 }
999 Ok(())
1000 }
1001
1002 fn move_display_up(status: &mut Status) -> Result<()> {
1003 let tab = status.current_tab_mut();
1004 match tab.display_mode {
1005 Display::Directory => {
1006 tab.normal_up_one_row();
1007 status.toggle_flag_visual();
1008 }
1009 Display::Preview if matches!(&tab.preview, Preview::Tree(_)) => {
1010 if let Preview::Tree(tree) = &mut tab.preview {
1011 tree.go(To::Prev);
1012 tab.window.scroll_up_one(tree.displayable().index());
1013 }
1014 }
1015 Display::Preview => tab.preview_page_up(),
1016 Display::Tree => {
1017 tab.tree_select_prev();
1018 status.toggle_flag_visual();
1019 }
1020 Display::Fuzzy => status.fuzzy_navigate(FuzzyDirection::Up)?,
1021 }
1022 Ok(())
1023 }
1024
1025 fn move_display_down(status: &mut Status) -> Result<()> {
1026 let tab = status.current_tab_mut();
1027 match tab.display_mode {
1028 Display::Directory => {
1029 tab.normal_down_one_row();
1030 status.toggle_flag_visual();
1031 }
1032 Display::Preview if matches!(&tab.preview, Preview::Tree(_)) => {
1033 if let Preview::Tree(tree) = &mut tab.preview {
1034 tree.go(To::Next);
1035 tab.window.scroll_down_one(tree.displayable().index());
1036 }
1037 }
1038 Display::Preview => tab.preview_page_down(),
1039 Display::Tree => {
1040 tab.tree_select_next();
1041 status.toggle_flag_visual()
1042 }
1043 Display::Fuzzy => status.fuzzy_navigate(FuzzyDirection::Down)?,
1044 }
1045 Ok(())
1046 }
1047 pub fn move_down(status: &mut Status) -> Result<()> {
1050 if status.focus.is_file() {
1051 Self::move_display_down(status)?
1052 } else {
1053 match status.current_tab_mut().menu_mode {
1054 Menu::Nothing => Self::move_display_down(status)?,
1055 Menu::Navigate(Navigate::History) => status.current_tab_mut().history.next(),
1056 Menu::Navigate(navigate) => status.menu.next(navigate),
1057 Menu::InputCompleted(input_completed) => {
1058 status.menu.completion_next(input_completed);
1059 if matches!(input_completed, InputCompleted::Search) {
1060 status.follow_search()?;
1061 }
1062 }
1063 Menu::NeedConfirmation(need_confirmation)
1064 if need_confirmation.use_flagged_files() =>
1065 {
1066 status.menu.next(Navigate::Flagged)
1067 }
1068 Menu::NeedConfirmation(NeedConfirmation::EmptyTrash) => {
1069 status.menu.next(Navigate::Trash)
1070 }
1071 Menu::NeedConfirmation(NeedConfirmation::BulkAction) => {
1072 status.menu.bulk.next();
1073 status.menu.window.scroll_to(status.menu.bulk.index())
1074 }
1075 _ => (),
1076 };
1077 }
1078 status.update_second_pane_for_preview()
1079 }
1080
1081 pub fn move_left(status: &mut Status) -> Result<()> {
1084 if status.focus.is_file() {
1085 Self::file_move_left(status.current_tab_mut())?;
1086 } else {
1087 let tab = status.current_tab_mut();
1088 match tab.menu_mode {
1089 Menu::InputSimple(_) | Menu::InputCompleted(_) => {
1090 status.menu.input.cursor_left();
1091 }
1092 Menu::Nothing => Self::file_move_left(tab)?,
1093 Menu::Navigate(Navigate::Cloud) => status.cloud_move_to_parent()?,
1094 _ => (),
1095 }
1096 }
1097 status.update_second_pane_for_preview()
1098 }
1099
1100 fn file_move_left(tab: &mut Tab) -> Result<()> {
1101 match tab.display_mode {
1102 Display::Directory => tab.move_to_parent()?,
1103 Display::Tree => tab.tree_select_parent()?,
1104 _ => (),
1105 };
1106 Ok(())
1107 }
1108
1109 pub fn move_right(status: &mut Status) -> Result<()> {
1112 if status.focus.is_file() {
1113 Self::enter_file(status)
1114 } else {
1115 let tab: &mut Tab = status.current_tab_mut();
1116 match tab.menu_mode {
1117 Menu::InputSimple(_) | Menu::InputCompleted(_) => {
1118 status.menu.input.cursor_right();
1119 Ok(())
1120 }
1121 Menu::Navigate(Navigate::Cloud) => status.cloud_enter_file_or_dir(),
1122 Menu::Nothing => Self::enter_file(status),
1123 _ => Ok(()),
1124 }
1125 }
1126 }
1127
1128 pub fn focus_follow_mouse(status: &mut Status, row: u16, col: u16) -> Result<()> {
1129 status.set_focus_from_pos(row, col)?;
1130 Ok(())
1131 }
1132
1133 fn click(status: &mut Status, binds: &Bindings, row: u16, col: u16) -> Result<()> {
1135 status.click(binds, row, col)
1136 }
1137
1138 pub fn left_click(status: &mut Status, binds: &Bindings, row: u16, col: u16) -> Result<()> {
1140 Self::click(status, binds, row, col)
1141 }
1142
1143 pub fn right_click(status: &mut Status, binds: &Bindings, row: u16, col: u16) -> Result<()> {
1145 Self::click(status, binds, row, col)?;
1146 Self::context(status)
1147 }
1148
1149 pub fn wheel_up(status: &mut Status, row: u16, col: u16) -> Result<()> {
1151 status.set_focus_from_pos(row, col)?;
1152 Self::move_up(status)
1153 }
1154
1155 pub fn wheel_down(status: &mut Status, row: u16, col: u16) -> Result<()> {
1157 status.set_focus_from_pos(row, col)?;
1158 Self::move_down(status)
1159 }
1160
1161 pub fn middle_click(status: &mut Status, binds: &Bindings, row: u16, col: u16) -> Result<()> {
1163 if Self::click(status, binds, row, col).is_ok() {
1164 if status.focus.is_file() {
1165 Self::enter_file(status)?;
1166 } else {
1167 LeaveMenu::leave_menu(status, binds)?;
1168 }
1169 };
1170 Ok(())
1171 }
1172
1173 pub fn backspace(status: &mut Status) -> Result<()> {
1175 if status.focus.is_file() {
1176 return Ok(());
1177 }
1178 match status.current_tab().menu_mode {
1179 Menu::Navigate(Navigate::Marks(_)) => {
1180 status.menu.marks.remove_selected()?;
1181 }
1182 Menu::Navigate(Navigate::TempMarks(_)) => {
1183 status.menu.temp_marks.erase_current_mark();
1184 }
1185 Menu::InputSimple(_) => {
1186 status.menu.input.delete_char_left();
1187 }
1188 Menu::InputCompleted(input_completed) => {
1189 status.menu.input.delete_char_left();
1190 status.complete(input_completed)?;
1191 if matches!(input_completed, InputCompleted::Search) {
1192 status.follow_search()?;
1193 }
1194 }
1195 _ => (),
1196 }
1197 Ok(())
1198 }
1199
1200 pub fn delete(status: &mut Status) -> Result<()> {
1204 if status.focus.is_file() {
1205 Self::delete_file(status)
1206 } else {
1207 match status.current_tab_mut().menu_mode {
1208 Menu::InputSimple(_) => {
1209 status.menu.input.delete_chars_right();
1210 Ok(())
1211 }
1212 Menu::InputCompleted(input_completed) => {
1213 status.menu.input.delete_chars_right();
1214 status.complete(input_completed)?;
1215 if matches!(input_completed, InputCompleted::Search) {
1216 status.follow_search()?;
1217 }
1218 Ok(())
1219 }
1220 _ => Ok(()),
1221 }
1222 }
1223 }
1224
1225 pub fn delete_line(status: &mut Status) -> Result<()> {
1226 if status.focus.is_file() {
1227 status.sync_tabs(Direction::RightToLeft)?;
1228 }
1229 match status.current_tab_mut().menu_mode {
1230 Menu::InputSimple(_) => {
1231 status.menu.input.delete_line();
1232 }
1233 Menu::InputCompleted(_) => {
1234 status.menu.input.delete_line();
1235 status.menu.completion_reset();
1236 }
1237 _ => (),
1238 }
1239 Ok(())
1240 }
1241
1242 pub fn delete_left(status: &mut Status) -> Result<()> {
1244 if status.focus.is_file() {
1245 return Ok(());
1246 }
1247 match status.current_tab_mut().menu_mode {
1248 Menu::InputSimple(_) => {
1249 status.menu.input.delete_left();
1250 }
1251 Menu::InputCompleted(_) => {
1252 status.menu.input.delete_left();
1253 status.menu.completion_reset();
1254 }
1255 _ => (),
1256 }
1257 Ok(())
1258 }
1259
1260 pub fn key_home(status: &mut Status) -> Result<()> {
1262 if status.focus.is_file() {
1263 let tab = status.current_tab_mut();
1264 match tab.display_mode {
1265 Display::Directory => tab.normal_go_top(),
1266 Display::Preview => tab.preview_go_top(),
1267 Display::Tree => tab.tree_go_to_root()?,
1268 Display::Fuzzy => status.fuzzy_start()?,
1269 };
1270 status.update_second_pane_for_preview()
1271 } else {
1272 match status.current_tab().menu_mode {
1273 Menu::InputSimple(_) | Menu::InputCompleted(_) => status.menu.input.cursor_start(),
1274 Menu::Navigate(navigate) => status.menu.set_index(0, navigate),
1275 _ => (),
1276 }
1277 Ok(())
1278 }
1279 }
1280
1281 pub fn end(status: &mut Status) -> Result<()> {
1283 if status.focus.is_file() {
1284 let tab = status.current_tab_mut();
1285 match tab.display_mode {
1286 Display::Directory => tab.normal_go_bottom(),
1287 Display::Preview => tab.preview_go_bottom(),
1288 Display::Tree => tab.tree_go_to_bottom_leaf(),
1289 Display::Fuzzy => status.fuzzy_end()?,
1290 };
1291 status.update_second_pane_for_preview()?;
1292 } else {
1293 match status.current_tab().menu_mode {
1294 Menu::InputSimple(_) | Menu::InputCompleted(_) => status.menu.input.cursor_end(),
1295 Menu::Navigate(navigate) => status.menu.select_last(navigate),
1296 _ => (),
1297 }
1298 }
1299 Ok(())
1300 }
1301
1302 pub fn page_up(status: &mut Status) -> Result<()> {
1304 if status.focus.is_file() {
1305 Self::file_page_up(status)?;
1306 } else {
1307 let tab = status.current_tab_mut();
1308 match tab.menu_mode {
1309 Menu::Nothing => Self::file_page_up(status)?,
1310 Menu::Navigate(navigate) => status.menu.page_up(navigate),
1311 Menu::InputCompleted(input_completed) => {
1312 for _ in 0..10 {
1313 status.menu.completion_prev(input_completed)
1314 }
1315 if matches!(input_completed, InputCompleted::Search) {
1316 status.follow_search()?;
1317 }
1318 }
1319 Menu::NeedConfirmation(need_confirmation)
1320 if need_confirmation.use_flagged_files() =>
1321 {
1322 for _ in 0..10 {
1323 status.menu.prev(Navigate::Flagged)
1324 }
1325 }
1326 Menu::NeedConfirmation(NeedConfirmation::EmptyTrash) => {
1327 for _ in 0..10 {
1328 status.menu.prev(Navigate::Trash)
1329 }
1330 }
1331 Menu::NeedConfirmation(NeedConfirmation::BulkAction) => {
1332 for _ in 0..10 {
1333 status.menu.bulk.prev()
1334 }
1335 status.menu.window.scroll_to(status.menu.bulk.index())
1336 }
1337 _ => (),
1338 };
1339 }
1340 Ok(())
1341 }
1342
1343 fn file_page_up(status: &mut Status) -> Result<()> {
1344 let tab = status.current_tab_mut();
1345 match tab.display_mode {
1346 Display::Directory => {
1347 tab.normal_page_up();
1348 status.update_second_pane_for_preview()?;
1349 }
1350 Display::Preview => tab.preview_page_up(),
1351 Display::Tree => {
1352 tab.tree_page_up();
1353 status.update_second_pane_for_preview()?;
1354 }
1355 Display::Fuzzy => status.fuzzy_navigate(FuzzyDirection::PageUp)?,
1356 };
1357 Ok(())
1358 }
1359
1360 pub fn page_down(status: &mut Status) -> Result<()> {
1362 if status.focus.is_file() {
1363 Self::file_page_down(status)?;
1364 } else {
1365 let tab = status.current_tab_mut();
1366 match tab.menu_mode {
1367 Menu::Nothing => Self::file_page_down(status)?,
1368 Menu::Navigate(navigate) => status.menu.page_down(navigate),
1369 Menu::InputCompleted(input_completed) => {
1370 for _ in 0..10 {
1371 status.menu.completion_next(input_completed)
1372 }
1373 if matches!(input_completed, InputCompleted::Search) {
1374 status.follow_search()?;
1375 }
1376 }
1377 Menu::NeedConfirmation(need_confirmation)
1378 if need_confirmation.use_flagged_files() =>
1379 {
1380 for _ in 0..10 {
1381 status.menu.next(Navigate::Flagged)
1382 }
1383 }
1384 Menu::NeedConfirmation(NeedConfirmation::EmptyTrash) => {
1385 for _ in 0..10 {
1386 status.menu.next(Navigate::Trash)
1387 }
1388 }
1389 Menu::NeedConfirmation(NeedConfirmation::BulkAction) => {
1390 for _ in 0..10 {
1391 status.menu.bulk.next()
1392 }
1393 status.menu.window.scroll_to(status.menu.bulk.index())
1394 }
1395 _ => (),
1396 };
1397 }
1398 Ok(())
1399 }
1400
1401 fn file_page_down(status: &mut Status) -> Result<()> {
1402 let tab = status.current_tab_mut();
1403 match tab.display_mode {
1404 Display::Directory => {
1405 tab.normal_page_down();
1406 status.update_second_pane_for_preview()?;
1407 }
1408 Display::Preview => tab.preview_page_down(),
1409 Display::Tree => {
1410 tab.tree_page_down();
1411 status.update_second_pane_for_preview()?;
1412 }
1413 Display::Fuzzy => status.fuzzy_navigate(FuzzyDirection::PageDown)?,
1414 };
1415 Ok(())
1416 }
1417
1418 pub fn enter(status: &mut Status, binds: &Bindings) -> Result<()> {
1424 if status.focus.is_file() {
1425 Self::enter_file(status)
1426 } else {
1427 LeaveMenu::leave_menu(status, binds)
1428 }
1429 }
1430
1431 pub fn tab(status: &mut Status) -> Result<()> {
1434 if status.focus.is_file() {
1435 status.next()
1436 } else if let Menu::InputCompleted(input_completed) = status.current_tab_mut().menu_mode {
1437 status.complete_tab(input_completed)?;
1438 }
1439 Ok(())
1440 }
1441
1442 pub fn fuzzyfind(status: &mut Status) -> Result<()> {
1444 status.force_clear();
1445 status.fuzzy_init(FuzzyKind::File);
1446 status.current_tab_mut().set_display_mode(Display::Fuzzy);
1447 status.fuzzy_find_files()?;
1448 status.update_second_pane_for_preview()
1449 }
1450
1451 pub fn fuzzyfind_line(status: &mut Status) -> Result<()> {
1453 status.force_clear();
1454 status.fuzzy_init(FuzzyKind::Line);
1455 status.current_tab_mut().set_display_mode(Display::Fuzzy);
1456 status.fuzzy_find_lines()
1457 }
1458
1459 pub fn fuzzyfind_help(status: &mut Status, binds: &Bindings) -> Result<()> {
1461 status.fuzzy_init(FuzzyKind::Action);
1462 status.current_tab_mut().set_display_mode(Display::Fuzzy);
1463 let help = help_string(binds, &status.internal_settings.opener);
1464 status.fuzzy_help(help)?;
1465 Ok(())
1466 }
1467
1468 pub fn copy_content(status: &Status) -> Result<()> {
1470 if !status.focus.is_file() {
1471 return Ok(());
1472 }
1473 match status.current_tab().display_mode {
1474 Display::Tree | Display::Directory => {
1475 let Some(path) = status.current_tab().selected_path() else {
1476 return Ok(());
1477 };
1478 content_to_clipboard(&path);
1479 }
1480 _ => return Ok(()),
1481 }
1482 Ok(())
1483 }
1484 pub fn copy_filename(status: &Status) -> Result<()> {
1486 if !status.focus.is_file() {
1487 return Ok(());
1488 }
1489 match status.current_tab().display_mode {
1490 Display::Tree | Display::Directory => {
1491 let Some(path) = status.current_tab().selected_path() else {
1492 return Ok(());
1493 };
1494 filename_to_clipboard(&path);
1495 }
1496 _ => return Ok(()),
1497 }
1498 Ok(())
1499 }
1500
1501 pub fn copy_filepath(status: &Status) -> Result<()> {
1503 if !status.focus.is_file() {
1504 return Ok(());
1505 }
1506 match status.current_tab().display_mode {
1507 Display::Tree | Display::Directory => {
1508 let Some(path) = status.current_tab().selected_path() else {
1509 return Ok(());
1510 };
1511 filepath_to_clipboard(&path);
1512 }
1513 _ => return Ok(()),
1514 }
1515 Ok(())
1516 }
1517
1518 pub fn trash_move_file(status: &mut Status) -> Result<()> {
1527 if !status.focus.is_file() {
1528 return Ok(());
1529 }
1530 if status.menu.flagged.is_empty() {
1531 Self::toggle_flag(status)?;
1532 }
1533
1534 status.menu.trash.update()?;
1535 let output_socket = Args::parse().output_socket;
1536 for flagged in status.menu.flagged.content.iter() {
1537 if status.menu.trash.trash(flagged).is_ok() {
1538 if let Some(output_socket) = &output_socket {
1539 nvim_inform_ipc(output_socket, NvimIPCAction::DELETE(flagged))?;
1540 }
1541 }
1542 }
1543 status.menu.flagged.clear();
1544 status.current_tab_mut().refresh_view()?;
1545 Ok(())
1546 }
1547
1548 pub fn trash_empty(status: &mut Status) -> Result<()> {
1551 if matches!(
1552 status.current_tab().menu_mode,
1553 Menu::NeedConfirmation(NeedConfirmation::EmptyTrash)
1554 ) {
1555 status.reset_menu_mode()?;
1556 } else {
1557 status.menu.trash.update()?;
1558 status.set_menu_mode(
1559 status.index,
1560 Menu::NeedConfirmation(NeedConfirmation::EmptyTrash),
1561 )?;
1562 }
1563 Ok(())
1564 }
1565
1566 pub fn trash_open(status: &mut Status) -> Result<()> {
1571 if matches!(
1572 status.current_tab().menu_mode,
1573 Menu::Navigate(Navigate::Trash)
1574 ) {
1575 status.reset_menu_mode()?;
1576 } else {
1577 status.menu.trash.update()?;
1578 status.set_menu_mode(status.index, Menu::Navigate(Navigate::Trash))?;
1579 }
1580 Ok(())
1581 }
1582
1583 pub fn trash_restore(status: &mut Status) -> Result<()> {
1584 LeaveMenu::trash(status)
1585 }
1586
1587 pub fn open_config(status: &mut Status) -> Result<()> {
1589 if !status.focus.is_file() {
1590 return Ok(());
1591 }
1592 match status.open_single_file(&path::PathBuf::from(tilde(CONFIG_PATH).to_string())) {
1593 Ok(_) => log_line!("Opened the config file {CONFIG_PATH}"),
1594 Err(e) => log_info!("Error opening {:?}: the config file {}", CONFIG_PATH, e),
1595 }
1596 Ok(())
1597 }
1598
1599 pub fn compress(status: &mut Status) -> Result<()> {
1601 if matches!(
1602 status.current_tab().menu_mode,
1603 Menu::Navigate(Navigate::Compress)
1604 ) {
1605 status.reset_menu_mode()?;
1606 } else {
1607 if status.menu.compression.is_empty() {
1608 status.menu.compression.setup();
1609 }
1610 status.set_menu_mode(status.index, Menu::Navigate(Navigate::Compress))?;
1611 }
1612 Ok(())
1613 }
1614
1615 pub fn context(status: &mut Status) -> Result<()> {
1617 if matches!(
1618 status.current_tab().display_mode,
1619 Display::Fuzzy | Display::Preview
1620 ) {
1621 return Ok(());
1622 }
1623 if matches!(
1624 status.current_tab().menu_mode,
1625 Menu::Navigate(Navigate::Context)
1626 ) {
1627 status.reset_menu_mode()?;
1628 } else {
1629 if status.menu.context.is_empty() {
1630 status.menu.context.setup();
1631 } else {
1632 status.menu.context.reset();
1633 }
1634 status.set_menu_mode(status.index, Menu::Navigate(Navigate::Context))?;
1635 }
1636 Ok(())
1637 }
1638
1639 pub fn action(status: &mut Status) -> Result<()> {
1642 if matches!(
1643 status.current_tab().menu_mode,
1644 Menu::InputCompleted(InputCompleted::Action)
1645 ) {
1646 status.reset_menu_mode()?;
1647 } else {
1648 status.set_menu_mode(status.index, Menu::InputCompleted(InputCompleted::Action))?;
1649 status.menu.completion.reset();
1650 }
1651 Ok(())
1652 }
1653
1654 pub fn remote_mount(status: &mut Status) -> Result<()> {
1657 if matches!(
1658 status.current_tab().menu_mode,
1659 Menu::InputSimple(InputSimple::Remote)
1660 ) {
1661 status.reset_menu_mode()?;
1662 }
1663 status.set_menu_mode(status.index, Menu::InputSimple(InputSimple::Remote))
1664 }
1665
1666 pub fn cloud_drive(status: &mut Status) -> Result<()> {
1667 status.cloud_open()
1668 }
1669
1670 pub fn cloud_enter_newdir_mode(status: &mut Status) -> Result<()> {
1671 status.cloud_enter_newdir_mode()
1672 }
1673
1674 pub fn cloud_enter_delete_mode(status: &mut Status) -> Result<()> {
1675 status.cloud_enter_delete_mode()
1676 }
1677 pub fn select_pane(status: &mut Status, col: u16) -> Result<()> {
1679 status.select_tab_from_col(col)
1680 }
1681
1682 pub fn focus_go_left(status: &mut Status) -> Result<()> {
1683 match status.focus {
1684 Focus::LeftMenu | Focus::LeftFile => (),
1685 Focus::RightFile => {
1686 status.index = 0;
1687 status.focus = Focus::LeftFile;
1688 }
1689 Focus::RightMenu => {
1690 status.index = 0;
1691 if matches!(status.tabs[0].menu_mode, Menu::Nothing) {
1692 status.focus = Focus::LeftFile;
1693 } else {
1694 status.focus = Focus::LeftMenu;
1695 }
1696 }
1697 }
1698 Ok(())
1699 }
1700
1701 pub fn focus_go_right(status: &mut Status) -> Result<()> {
1702 match status.focus {
1703 Focus::RightMenu | Focus::RightFile => (),
1704 Focus::LeftFile => {
1705 status.index = 1;
1706 status.focus = Focus::RightFile;
1707 }
1708 Focus::LeftMenu => {
1709 status.index = 1;
1710 if matches!(status.tabs[1].menu_mode, Menu::Nothing) {
1711 status.focus = Focus::RightFile;
1712 } else {
1713 status.focus = Focus::RightMenu;
1714 }
1715 }
1716 }
1717 Ok(())
1718 }
1719
1720 pub fn focus_go_down(status: &mut Status) -> Result<()> {
1721 match status.focus {
1722 Focus::RightMenu | Focus::LeftMenu => (),
1723 Focus::LeftFile => {
1724 if !matches!(status.tabs[0].menu_mode, Menu::Nothing) {
1725 status.focus = Focus::LeftMenu;
1726 }
1727 }
1728 Focus::RightFile => {
1729 if !matches!(status.tabs[1].menu_mode, Menu::Nothing) {
1730 status.focus = Focus::RightMenu;
1731 }
1732 }
1733 }
1734 Ok(())
1735 }
1736
1737 pub fn focus_go_up(status: &mut Status) -> Result<()> {
1738 match status.focus {
1739 Focus::LeftFile | Focus::RightFile => (),
1740 Focus::LeftMenu => status.focus = Focus::LeftFile,
1741 Focus::RightMenu => status.focus = Focus::RightFile,
1742 }
1743 Ok(())
1744 }
1745
1746 pub fn sync_ltr(status: &mut Status) -> Result<()> {
1747 if status.focus.is_file() {
1748 status.sync_tabs(Direction::LeftToRight)?;
1749 }
1750 Ok(())
1751 }
1752
1753 pub fn bulk_confirm(status: &mut Status) -> Result<()> {
1754 status.bulk_execute()
1755 }
1756
1757 pub fn file_copied(status: &mut Status) -> Result<()> {
1758 log_info!(
1759 "file copied - pool: {pool:?}",
1760 pool = status.internal_settings.copy_file_queue
1761 );
1762 status.internal_settings.copy_file_remove_head()?;
1763 if status.internal_settings.copy_file_queue.is_empty() {
1764 status.internal_settings.unset_copy_progress()
1765 } else {
1766 status.copy_next_file_in_queue()?;
1767 }
1768 Ok(())
1769 }
1770
1771 pub fn display_copy_progress(status: &mut Status, content: InMemoryTerm) -> Result<()> {
1772 status.internal_settings.store_copy_progress(content);
1773 Ok(())
1774 }
1775
1776 pub fn check_preview_fuzzy_tick(status: &mut Status) -> Result<()> {
1777 status.fuzzy_tick();
1778 status.check_preview()
1779 }
1780
1781 pub fn visual(status: &mut Status) -> Result<()> {
1782 status.current_tab_mut().toggle_visual();
1783 status.toggle_flag_visual();
1784
1785 Ok(())
1786 }
1787
1788 pub fn mount(status: &mut Status) -> Result<()> {
1790 if matches!(
1791 status.current_tab().menu_mode,
1792 Menu::Navigate(Navigate::Mount)
1793 ) {
1794 status.reset_menu_mode()?;
1795 } else if lsblk_and_udisksctl_installed() {
1796 status.menu.mount.update(status.internal_settings.disks())?;
1797 status.set_menu_mode(status.index, Menu::Navigate(Navigate::Mount))?;
1798 }
1799 Ok(())
1800 }
1801
1802 pub fn custom(status: &mut Status, input_string: &str) -> Result<()> {
1804 status.run_custom_command(input_string)
1805 }
1806
1807 pub fn parse_rpc(status: &mut Status, ipc_msg: String) -> Result<()> {
1809 status.parse_ipc(ipc_msg)
1810 }
1811}