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