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