fm/app/
tab.rs

1use std::borrow::Borrow;
2use std::cmp::min;
3use std::iter::{Enumerate, Skip, Take};
4use std::path;
5use std::slice;
6use std::sync::Arc;
7
8use anyhow::{Context, Result};
9
10use crate::common::{
11    has_last_modification_happened_less_than, path_to_string, row_to_window_index, set_current_dir,
12};
13use crate::config::START_FOLDER;
14use crate::io::Args;
15use crate::log_info;
16use crate::modes::{
17    Content, ContentWindow, Directory, Display, FileInfo, FileKind, FilterKind, Go, History,
18    IndexToIndex, Menu, Preview, PreviewBuilder, Search, Selectable, SortKind, To, Tree,
19    TreeBuilder, Users,
20};
21
22/// Settings of a tab.
23/// Do we display hidden files ?
24/// What kind of filter is used ?
25/// What kind of sort is used ?
26/// Should the last image be cleared ?
27pub struct TabSettings {
28    /// read from command line
29    pub show_hidden: bool,
30    /// The filter use before displaying files
31    pub filter: FilterKind,
32    /// The kind of sort used to display the files.
33    pub sort_kind: SortKind,
34    /// should the last displayed image be erased ?
35    pub should_clear_image: bool,
36}
37
38impl TabSettings {
39    fn new(args: &Args) -> Self {
40        let filter = FilterKind::All;
41        let show_hidden = args.all;
42        let sort_kind = SortKind::default();
43        let should_clear_image = false;
44        Self {
45            show_hidden,
46            filter,
47            sort_kind,
48            should_clear_image,
49        }
50    }
51
52    fn toggle_hidden(&mut self) {
53        self.show_hidden = !self.show_hidden;
54    }
55
56    /// Apply the filter.
57    pub fn set_filter(&mut self, filter: FilterKind) {
58        self.filter = filter
59    }
60
61    pub fn reset_filter(&mut self) {
62        self.filter = FilterKind::All;
63    }
64
65    /// Update the kind of sort from a char typed by the user.
66    fn update_sort_from_char(&mut self, c: char) {
67        self.sort_kind.update_from_char(c)
68    }
69}
70
71/// Holds every thing about the current tab of the application.
72/// Most of the mutation is done externally.
73pub struct Tab {
74    /// Kind of display: `Preview, Normal, Tree`
75    pub display_mode: Display,
76
77    /// Files in current path
78    pub directory: Directory,
79    /// Tree representation of the same path
80    pub tree: Tree,
81    /// Lines of the previewed files.
82    /// Empty if not in preview mode.
83    pub preview: Preview,
84
85    /// The menu currently opened in this tab.
86    /// Most of the time is spent in `EditMode::Nothing`
87    pub menu_mode: Menu,
88
89    /// The indexes of displayed file
90    pub window: ContentWindow,
91    /// Height of the terminal window
92    pub height: usize,
93
94    /// Internal & display settings:
95    /// show hidden files ?
96    /// sort method
97    /// filter kind
98    pub settings: TabSettings,
99
100    /// Last searched string
101    pub search: Search,
102    // pub searched: Search,
103    /// Visited directories
104    pub history: History,
105    /// Users & groups
106    pub users: Users,
107    /// Saved path before entering "CD" mode.
108    /// Used if the cd is canceled
109    pub origin_path: Option<std::path::PathBuf>,
110    pub visual: bool,
111}
112
113impl Tab {
114    /// Creates a new tab from args and height.
115    ///
116    /// # Description
117    ///
118    /// It reads a path from args, which is defaulted to the starting path.
119    /// It explores the path and creates a content.
120    /// The path is then selected. If no path was provide from args, the current folder `.` is selected.
121    /// Every other attribute has its default value.
122    ///
123    /// # Errors
124    ///
125    /// it may fail if the path:
126    /// - doesn't exist
127    /// - can't be explored
128    /// - has no parent and isn't a directory (which can't happen)
129    pub fn new(args: &Args, height: usize, users: Users) -> Result<Self> {
130        let path = &START_FOLDER.get().context("Startfolder should be set")?;
131        let start_dir = Self::start_dir(path)?;
132        let settings = TabSettings::new(args);
133        let mut directory =
134            Directory::new(start_dir, &users, &settings.filter, settings.show_hidden)?;
135        let display_mode = Display::default();
136        let menu_mode = Menu::Nothing;
137        let mut window = ContentWindow::new(directory.content.len(), height);
138        let preview = Preview::Empty;
139        let history = History::default();
140        let search = Search::empty();
141        let index = directory.select_file(path);
142        let tree = Tree::default();
143        let origin_path = None;
144        let visual = false;
145
146        window.scroll_to(index);
147        Ok(Self {
148            display_mode,
149            menu_mode,
150            window,
151            directory,
152            height,
153            preview,
154            search,
155            history,
156            users,
157            tree,
158            settings,
159            origin_path,
160            visual,
161        })
162    }
163
164    fn start_dir(path: &path::Path) -> Result<&path::Path> {
165        if path.is_dir() {
166            Ok(path)
167        } else {
168            Ok(path.parent().context("Path has no parent")?)
169        }
170    }
171
172    /// Returns the directory owning the selected file.
173    /// In Tree mode, it's the current directory if the selected node is a directory,
174    /// its parent otherwise.
175    /// In normal mode it's the current working directory.
176    pub fn directory_of_selected(&self) -> Result<&path::Path> {
177        match self.display_mode {
178            Display::Tree => self.tree.directory_of_selected().context("No parent"),
179            _ => Ok(&self.directory.path),
180        }
181    }
182
183    /// Current path of this tab in directory display mode.
184    pub fn current_path(&self) -> &path::Path {
185        self.directory.path.borrow()
186    }
187
188    /// Fileinfo of the selected element.
189    pub fn current_file(&self) -> Result<FileInfo> {
190        match self.display_mode {
191            Display::Tree => {
192                FileInfo::new(self.tree.selected_node_or_parent()?.path(), &self.users)
193            }
194            _ => Ok(self
195                .directory
196                .selected()
197                .context("current_file: no selected file")?
198                .to_owned()),
199        }
200    }
201
202    /// Number of displayed element in this tab.
203    fn display_len(&self) -> usize {
204        match self.display_mode {
205            Display::Tree => self.tree.display_len(),
206            Display::Preview => self.preview.len(),
207            Display::Directory => self.directory.len(),
208            Display::Fuzzy => 0,
209        }
210    }
211
212    /// Path of the currently selected file.
213    pub fn current_file_string(&self) -> Result<String> {
214        Ok(path_to_string(&self.current_file()?.path))
215    }
216
217    /// Returns true if the current mode requires 2 windows.
218    /// Only Tree, Normal & Preview doesn't require 2 windows.
219    pub fn need_menu_window(&self) -> bool {
220        !matches!(self.menu_mode, Menu::Nothing)
221    }
222
223    /// Returns a string of the current directory path.
224    pub fn directory_str(&self) -> String {
225        path_to_string(&self.directory.path)
226    }
227
228    /// Refresh everything but the view
229    pub fn refresh_params(&mut self) {
230        if matches!(self.preview, Preview::Image(_)) {
231            self.settings.should_clear_image = true;
232        }
233        self.preview = PreviewBuilder::empty();
234        if self.display_mode.is_tree() {
235            self.remake_same_tree()
236        } else {
237            self.tree = Tree::default()
238        };
239    }
240
241    fn remake_same_tree(&mut self) {
242        let current_path = self.tree.selected_path().to_owned();
243        self.make_tree(None);
244        self.tree.go(To::Path(&current_path))
245    }
246
247    /// Refresh the current view.
248    /// displayed files is reset.
249    /// The first file is selected.
250    pub fn refresh_view(&mut self) -> Result<()> {
251        self.directory.reset_files(&self.settings, &self.users)?;
252        self.window.reset(self.display_len());
253        self.refresh_params();
254        Ok(())
255    }
256
257    /// Refresh the view if files were modified in current directory.
258    /// If a refresh occurs, tries to select the same file as before.
259    /// If it can't, the first file (`.`) is selected.
260    /// Does nothing in `DisplayMode::Preview`.
261    pub fn refresh_if_needed(&mut self) -> Result<()> {
262        if match self.display_mode {
263            Display::Preview => false,
264            Display::Directory => {
265                has_last_modification_happened_less_than(&self.directory.path, 10)?
266            }
267            Display::Tree => self.tree.has_modified_dirs(),
268            Display::Fuzzy => false,
269        } {
270            self.refresh_and_reselect_file()
271        } else {
272            Ok(())
273        }
274    }
275
276    /// Change the display mode.
277    pub fn set_display_mode(&mut self, new_display_mode: Display) {
278        self.reset_visual();
279        self.search.reset_paths();
280        self.reset_preview();
281        self.display_mode = new_display_mode
282    }
283
284    /// Makes a new tree of the current path.
285    pub fn make_tree(&mut self, sort_kind: Option<SortKind>) {
286        let sort_kind = sort_kind.unwrap_or_default();
287        self.settings.sort_kind = sort_kind;
288        let path = self.directory.path.clone();
289        let users = &self.users;
290        self.tree = TreeBuilder::new(path.clone(), users)
291            .with_hidden(self.settings.show_hidden)
292            .with_filter_kind(&self.settings.filter)
293            .with_sort_kind(sort_kind)
294            .build();
295    }
296
297    fn make_tree_for_parent(&mut self) -> Result<()> {
298        let Some(parent) = self.tree.root_path().parent() else {
299            return Ok(());
300        };
301        self.cd(parent.to_owned().as_ref())?;
302        self.make_tree(Some(self.settings.sort_kind));
303        Ok(())
304    }
305
306    /// Enter or leave display tree mode.
307    pub fn toggle_tree_mode(&mut self) -> Result<()> {
308        let current_file = self.current_file()?;
309        if self.display_mode.is_tree() {
310            {
311                self.tree = Tree::default();
312                self.refresh_view()
313            }?;
314            self.set_display_mode(Display::Directory);
315        } else {
316            self.make_tree(None);
317            self.window.reset(self.tree.displayable().lines().len());
318            self.set_display_mode(Display::Tree);
319        }
320        self.go_to_file(current_file.path);
321        Ok(())
322    }
323
324    /// Creates a new preview for the selected file.
325    /// If the selected file is a directory, it will create a tree.
326    /// Does nothing if directory is empty or in flagged or preview display mode.
327    pub fn make_preview(&mut self) -> Result<()> {
328        if self.directory.is_empty() {
329            return Ok(());
330        }
331        let Ok(file_info) = self.current_file() else {
332            return Ok(());
333        };
334        match file_info.file_kind {
335            FileKind::Directory => self.toggle_tree_mode()?,
336            _ => self.make_preview_unchecked(file_info),
337        }
338
339        Ok(())
340    }
341
342    /// Creates a preview and assign it.
343    /// Doesn't check if it's the correct action to do according to display.
344    fn make_preview_unchecked(&mut self, file_info: FileInfo) {
345        let preview = PreviewBuilder::new(&file_info.path)
346            .build()
347            .unwrap_or_default();
348        self.set_display_mode(Display::Preview);
349        self.window.reset(preview.len());
350        self.preview = preview;
351    }
352
353    /// Reset the preview to empty. Used to save some memory.
354    fn reset_preview(&mut self) {
355        log_info!(
356            "tab.reset_preview. prev = {prev}",
357            prev = self.preview.kind_display()
358        );
359        if matches!(self.preview, Preview::Image(_)) {
360            log_info!("Clear the image");
361            self.settings.should_clear_image = true;
362        }
363        if self.display_mode.is_preview() {
364            self.preview = PreviewBuilder::empty();
365        }
366    }
367
368    /// Refresh the folder, reselect the last selected file, move the window to it.
369    pub fn refresh_and_reselect_file(&mut self) -> Result<()> {
370        let selected_path = self.clone_selected_path()?;
371        self.refresh_view()?;
372        self.select_by_path(selected_path);
373        Ok(())
374    }
375
376    fn clone_selected_path(&self) -> Result<Arc<path::Path>> {
377        Ok(self
378            .current_file()
379            .context("refresh: no selected file")?
380            .path
381            .clone())
382    }
383
384    /// Select the given file from its path.
385    /// Action depends of the display mode.
386    /// For directory or tree, it selects the file and scroll to it.
387    /// when the file doesn't exists,
388    /// - in directory mode, the first file is selected;
389    /// - in tree mode, the root is selected.
390    ///
391    /// For preview or fuzzy, it does nothing
392    pub fn select_by_path(&mut self, selected_path: Arc<path::Path>) {
393        match self.display_mode {
394            Display::Directory => {
395                let index = self.directory.select_file(&selected_path);
396                self.scroll_to(index)
397            }
398            Display::Tree => {
399                self.tree.go(To::Path(&selected_path));
400                let index = self.tree.displayable().index();
401                self.scroll_to(index);
402            }
403            Display::Preview | Display::Fuzzy => (),
404        }
405    }
406
407    /// Reset the display mode and its view.
408    pub fn reset_display_mode_and_view(&mut self) -> Result<()> {
409        if self.display_mode.is_preview() {
410            self.set_display_mode(Display::Directory);
411        }
412        self.refresh_view()
413    }
414
415    pub fn set_filter(&mut self, filter: FilterKind) -> Result<()> {
416        self.settings.set_filter(filter);
417        self.directory.reset_files(&self.settings, &self.users)?;
418        if self.display_mode.is_tree() {
419            self.make_tree(None);
420        }
421        self.window.reset(self.directory.content.len());
422        Ok(())
423    }
424
425    /// Set the height of the window and itself.
426    pub fn set_height(&mut self, height: usize) {
427        self.window.set_height(height);
428        self.height = height;
429    }
430
431    /// Display or hide hidden files (filename starting with .).
432    pub fn toggle_hidden(&mut self) -> Result<()> {
433        self.settings.toggle_hidden();
434        self.directory.reset_files(&self.settings, &self.users)?;
435        self.window.reset(self.directory.content.len());
436        if self.display_mode.is_tree() {
437            self.make_tree(None)
438        }
439        Ok(())
440    }
441
442    /// Set the line index to `index` and scroll there.
443    pub fn scroll_to(&mut self, index: usize) {
444        self.window.scroll_to(index);
445    }
446
447    /// Sort the file with given criteria
448    /// Valid kind of sorts are :
449    /// by kind : directory first, files next, in alphanumeric order
450    /// by filename,
451    /// by date of modification,
452    /// by size,
453    /// by extension.
454    /// The first letter is used to identify the method.
455    /// If the user types an uppercase char, the sort is reverse.
456    pub fn sort(&mut self, c: char) -> Result<()> {
457        if self.directory.content.is_empty() {
458            return Ok(());
459        }
460        match self.display_mode {
461            Display::Directory => self.sort_directory(c)?,
462            Display::Tree => self.sort_tree(c),
463            _ => (),
464        }
465        Ok(())
466    }
467
468    fn sort_directory(&mut self, c: char) -> Result<()> {
469        let path = self.current_file()?.path;
470        self.settings.update_sort_from_char(c);
471        self.directory.sort(&self.settings.sort_kind);
472        self.normal_go_top();
473        self.directory.select_file(&path);
474        Ok(())
475    }
476
477    fn sort_tree(&mut self, c: char) {
478        self.settings.update_sort_from_char(c);
479        let selected_path = self.tree.selected_path().to_owned();
480        self.make_tree(Some(self.settings.sort_kind));
481        self.tree.go(To::Path(&selected_path));
482    }
483
484    pub fn set_sortkind_per_mode(&mut self) {
485        self.settings.sort_kind = match self.display_mode {
486            Display::Tree => SortKind::tree_default(),
487            _ => SortKind::default(),
488        };
489    }
490
491    pub fn cd_to_file(&mut self, path: &path::Path) -> Result<()> {
492        crate::log_info!("cd_to_file: {path}", path = path.display());
493        let parent = match path.parent() {
494            Some(parent) => parent,
495            None => std::path::Path::new("/"),
496        };
497        self.cd(parent)?;
498        self.go_to_file(path);
499        Ok(())
500    }
501
502    pub fn try_cd_to_file(&mut self, path_str: String) -> Result<bool> {
503        let path = path::Path::new(&path_str);
504        if path.exists() {
505            self.cd_to_file(path)?;
506            Ok(true)
507        } else {
508            Ok(false)
509        }
510    }
511
512    /// Set the pathcontent to a new path.
513    /// Reset the window.
514    /// Add the last path to the history of visited paths.
515    /// Does nothing in preview or flagged display mode.
516    pub fn cd(&mut self, path: &path::Path) -> Result<()> {
517        if self.display_mode.is_preview() {
518            return Ok(());
519        }
520        self.search.reset_paths();
521        match set_current_dir(path) {
522            Ok(()) => (),
523            Err(error) => {
524                log_info!("can't reach {path}. Error {error}", path = path.display());
525                return Ok(());
526            }
527        }
528        self.history.push(&self.current_file()?.path);
529        self.directory
530            .change_directory(path, &self.settings, &self.users)?;
531        if self.display_mode.is_tree() {
532            self.make_tree(Some(self.settings.sort_kind));
533            self.window.reset(self.tree.displayable().lines().len());
534        } else {
535            self.window.reset(self.directory.content.len());
536        }
537        Ok(())
538    }
539
540    pub fn back(&mut self) -> Result<()> {
541        if self.display_mode.is_preview() {
542            return Ok(());
543        }
544        if self.history.content.is_empty() {
545            return Ok(());
546        }
547        let Some(file) = self.history.content.pop() else {
548            return Ok(());
549        };
550        self.history.content.pop();
551        self.cd_to_file(&file)?;
552        Ok(())
553    }
554
555    /// Select a file in current view, either directory or tree mode.
556    pub fn go_to_file<P>(&mut self, file: P)
557    where
558        P: AsRef<path::Path>,
559    {
560        if self.display_mode.is_preview() {
561            self.tree.go(To::Path(file.as_ref()));
562        } else {
563            let index = self.directory.select_file(file.as_ref());
564            self.scroll_to(index);
565        }
566    }
567
568    /// Jump to the jump target.
569    /// Change the pathcontent and the tree if the jump target isn't in the
570    /// currently displayed files.
571    pub fn jump(&mut self, jump_target: path::PathBuf) -> Result<()> {
572        let target_dir = match jump_target.parent() {
573            Some(parent) => parent,
574            None => &jump_target,
575        };
576        match self.display_mode {
577            Display::Preview => return Ok(()),
578            Display::Directory => self.jump_directory(&jump_target, target_dir)?,
579            Display::Tree => self.jump_tree(&jump_target, target_dir)?,
580            Display::Fuzzy => return Ok(()),
581        }
582        Ok(())
583    }
584
585    fn jump_directory(&mut self, jump_target: &path::Path, target_dir: &path::Path) -> Result<()> {
586        if !self.directory.paths().contains(&jump_target) {
587            self.cd(target_dir)?
588        }
589        let index = self.directory.select_file(jump_target);
590        self.scroll_to(index);
591        Ok(())
592    }
593
594    fn jump_tree(&mut self, jump_target: &path::Path, target_dir: &path::Path) -> Result<()> {
595        if !self.tree.paths().contains(&target_dir) {
596            self.cd(target_dir)?;
597            self.make_tree(None);
598        }
599        self.tree.go(To::Path(jump_target));
600        Ok(())
601    }
602
603    /// Move back to a previously visited path.
604    /// It may fail if the user has no permission to visit the path
605    pub fn history_cd_to_last(&mut self) -> Result<()> {
606        let Some(file) = self.history.selected() else {
607            return Ok(());
608        };
609        let file = file.to_owned();
610        self.cd_to_file(&file)?;
611        self.history.drop_queue();
612        Ok(())
613    }
614
615    /// Move to the parent of current path
616    pub fn move_to_parent(&mut self) -> Result<()> {
617        let path = self.directory.path.clone();
618        let Some(parent) = path.parent() else {
619            return Ok(());
620        };
621        if self.history.is_this_the_last(parent) {
622            self.back()?;
623            return Ok(());
624        }
625        self.cd_to_file(&path)
626    }
627
628    /// Select the file at index and move the window to this file.
629    pub fn go_to_index(&mut self, index: usize) {
630        self.directory.select_index(index);
631        self.window.scroll_to(index);
632    }
633
634    /// Move to the currently selected directory.
635    /// Fail silently if the current directory is empty or if the selected
636    /// file isn't a directory.
637    pub fn go_to_selected_dir(&mut self) -> Result<()> {
638        self.cd(&self
639            .directory
640            .selected()
641            .context("Empty directory")?
642            .path
643            .clone())?;
644        Ok(())
645    }
646
647    /// Move down one row if possible.
648    pub fn normal_down_one_row(&mut self) {
649        self.directory.next();
650        self.window.scroll_down_one(self.directory.index)
651    }
652
653    /// Move up one row if possible.
654    pub fn normal_up_one_row(&mut self) {
655        self.directory.prev();
656        self.window.scroll_up_one(self.directory.index)
657    }
658
659    /// Move to the top of the current directory.
660    pub fn normal_go_top(&mut self) {
661        self.directory.select_index(0);
662        self.window.scroll_to(0)
663    }
664
665    /// Move to the bottom of current view.
666    pub fn normal_go_bottom(&mut self) {
667        let last_index = self.directory.content.len() - 1;
668        self.directory.select_index(last_index);
669        self.window.scroll_to(last_index)
670    }
671
672    /// Move 10 files up
673    pub fn normal_page_up(&mut self) {
674        let up_index = self.directory.index.saturating_sub(10);
675        self.directory.select_index(up_index);
676        self.window.scroll_to(up_index)
677    }
678
679    /// Move down 10 rows
680    pub fn normal_page_down(&mut self) {
681        let down_index = min(self.directory.content.len() - 1, self.directory.index + 10);
682        self.directory.select_index(down_index);
683        self.window.scroll_to(down_index);
684    }
685
686    /// Fold every child node in the tree.
687    /// Recursively explore the tree and fold every node. Reset the display.
688    pub fn tree_go_to_root(&mut self) -> Result<()> {
689        self.tree.go(To::Root);
690        self.window.scroll_to(0);
691        Ok(())
692    }
693
694    /// Select the parent of current node.
695    /// If we were at the root node, move to the parent and make a new tree.
696    pub fn tree_select_parent(&mut self) -> Result<()> {
697        if self.tree.is_on_root() {
698            self.make_tree_for_parent()?;
699        } else {
700            self.tree.go(To::Parent);
701        }
702        self.window.scroll_to(self.tree.displayable().index());
703        Ok(())
704    }
705
706    /// Move down 10 times in the tree
707    pub fn tree_page_down(&mut self) {
708        self.tree.page_down();
709        self.window.scroll_to(self.tree.displayable().index());
710    }
711
712    /// Move up 10 times in the tree
713    pub fn tree_page_up(&mut self) {
714        self.tree.page_up();
715        self.window.scroll_to(self.tree.displayable().index());
716    }
717
718    /// Select the next sibling.
719    pub fn tree_select_next(&mut self) {
720        self.tree.go(To::Next);
721        self.window.scroll_down_one(self.tree.displayable().index());
722    }
723
724    /// Select the previous siblging
725    pub fn tree_select_prev(&mut self) {
726        self.tree.go(To::Prev);
727        self.window.scroll_up_one(self.tree.displayable().index());
728    }
729
730    /// Go to the last leaf.
731    pub fn tree_go_to_bottom_leaf(&mut self) {
732        self.tree.go(To::Last);
733        self.window.scroll_to(self.tree.displayable().index());
734    }
735
736    /// Navigate to the next sibling of current file in tree mode.
737    pub fn tree_next_sibling(&mut self) {
738        self.tree.go(To::NextSibling);
739        self.window.scroll_to(self.tree.displayable().index());
740    }
741
742    /// Navigate to the previous sibling of current file in tree mode.
743    pub fn tree_prev_sibling(&mut self) {
744        self.tree.go(To::PreviousSibling);
745        self.window.scroll_to(self.tree.displayable().index());
746    }
747
748    pub fn tree_enter_dir(&mut self, path: std::sync::Arc<path::Path>) -> Result<()> {
749        self.cd(&path)?;
750        self.make_tree(None);
751        self.set_display_mode(Display::Tree);
752        Ok(())
753    }
754
755    /// Move the preview to the top
756    pub fn preview_go_top(&mut self) {
757        self.window.scroll_to(0)
758    }
759
760    /// Move the preview to the bottom
761    pub fn preview_go_bottom(&mut self) {
762        self.window.scroll_to(self.preview.len().saturating_sub(1))
763    }
764
765    fn preview_scroll(&self) -> usize {
766        if matches!(self.menu_mode, Menu::Nothing) {
767            2 * self.height / 3
768        } else {
769            self.height / 3
770        }
771    }
772
773    fn preview_binary_scroll(&self) -> usize {
774        if matches!(self.menu_mode, Menu::Nothing) {
775            self.height / 3
776        } else {
777            self.height / 6
778        }
779    }
780
781    /// Move 30 lines up or an image in Ueberzug.
782    pub fn preview_page_up(&mut self) {
783        match &mut self.preview {
784            Preview::Image(ref mut image) => image.up_one_row(),
785            Preview::Binary(_) => self.window.preview_page_up(self.preview_binary_scroll()),
786            _ => self.window.preview_page_up(self.preview_scroll()),
787        }
788    }
789
790    /// Move down 30 rows except for Ueberzug where it moves 1 image down
791    pub fn preview_page_down(&mut self) {
792        let len = self.preview.len();
793        match &mut self.preview {
794            Preview::Image(ref mut image) => image.down_one_row(),
795            Preview::Binary(_) => self
796                .window
797                .preview_page_down(self.preview_binary_scroll(), len),
798            _ => self.window.preview_page_down(self.preview_scroll(), len),
799        }
800    }
801
802    /// Select a clicked row in display directory
803    pub fn normal_select_row(&mut self, row: u16) {
804        let screen_index = row_to_window_index(row);
805        let index = screen_index + self.window.top;
806        self.directory.select_index(index);
807        self.window.scroll_to(index);
808    }
809
810    /// Select a clicked row in display tree
811    pub fn tree_select_row(&mut self, row: u16) -> Result<()> {
812        let screen_index = row_to_window_index(row);
813        let displayable = self.tree.displayable();
814        let index = screen_index + self.window.top;
815        let path = displayable
816            .lines()
817            .get(index)
818            .context("tree: no selected file")?
819            .path()
820            .to_owned();
821        self.tree.go(To::Path(&path));
822        Ok(())
823    }
824
825    pub fn completion_search_files(&mut self) -> Vec<String> {
826        match self.display_mode {
827            Display::Directory => self.search.matches_from(self.directory.content()),
828            Display::Tree => self.search.matches_from(self.tree.displayable().content()),
829            Display::Preview => vec![],
830            Display::Fuzzy => vec![],
831        }
832    }
833
834    pub fn directory_search_next(&mut self) {
835        if let Some(path) = self.search.select_next() {
836            self.go_to_file(path)
837        } else if let Some(path) = self
838            .search
839            .directory_search_next(self.directory.index_to_index())
840        {
841            self.go_to_file(path);
842        }
843    }
844
845    pub fn dir_enum_skip_take(&self) -> Take<Skip<Enumerate<slice::Iter<FileInfo>>>> {
846        let len = self.directory.content.len();
847        self.directory
848            .enumerate()
849            .skip(self.window.top)
850            .take(min(len, self.window.height))
851    }
852
853    pub fn cd_origin_path(&mut self) -> Result<()> {
854        if let Some(op) = &self.origin_path {
855            self.cd_to_file(&op.to_owned())?;
856        }
857        Ok(())
858    }
859
860    pub fn save_origin_path(&mut self) {
861        self.origin_path = Some(self.current_path().to_owned());
862    }
863
864    pub fn toggle_visual(&mut self) {
865        if matches!(self.display_mode, Display::Directory | Display::Tree) {
866            self.visual = !self.visual;
867        } else {
868            self.reset_visual();
869        }
870    }
871
872    pub fn reset_visual(&mut self) {
873        self.visual = false
874    }
875}