1use crate::app::App;
2use git2::Repository;
3use std::io::Error;
4use std::path::PathBuf;
5use tui::widgets::ListState;
6
7pub struct FileDialog<'a> {
8 pub title: &'a str,
9 pub location: PathBuf,
10 pub selection: Option<PathBuf>,
11 pub dirs: Vec<(String, bool)>,
12 pub error_message: Option<String>,
13 pub color: bool,
14 pub state: ListState,
15 pub previous_app: Option<App>,
16}
17
18impl<'a> FileDialog<'a> {
19 pub fn new(title: &'a str, color: bool) -> Result<FileDialog<'a>, Error> {
20 Ok(FileDialog {
21 title,
22 location: std::env::current_dir()?,
23 selection: None,
24 dirs: vec![],
25 error_message: None,
26 color,
27 state: ListState::default(),
28 previous_app: None,
29 })
30 }
31
32 pub fn fwd(&mut self, steps: usize) {
33 let i = match self.state.selected() {
34 Some(i) => std::cmp::min(i.saturating_add(steps), self.dirs.len() - 1),
35 None => 0,
36 };
37 self.state.select(Some(i));
38 }
39
40 pub fn bwd(&mut self, steps: usize) {
41 let i = match self.state.selected() {
42 Some(i) => i.saturating_sub(steps),
43 None => 0,
44 };
45 self.state.select(Some(i));
46 }
47
48 pub fn on_up(&mut self, is_shift: bool) {
49 let step = if is_shift { 10 } else { 1 };
50 self.bwd(step)
51 }
52
53 pub fn on_down(&mut self, is_shift: bool) {
54 let step = if is_shift { 10 } else { 1 };
55 self.fwd(step)
56 }
57
58 pub fn on_left(&mut self) -> Result<(), String> {
59 if let Some(par) = self.location.parent() {
60 let temp_path = self.location.clone();
61 let prev = self.location.clone();
62 let path = PathBuf::from(par);
63 self.location = path.clone();
64 match self.selection_changed(Some(prev)) {
65 Ok(_) => {}
66 Err(err) => {
67 self.location = temp_path;
68 self.error_message = Some(format!(
69 "Problem entering directory {}\n{}",
70 path.display(),
71 err
72 ));
73 }
74 };
75 }
76 Ok(())
77 }
78
79 pub fn on_right(&mut self) -> Result<(), String> {
80 if let Some(sel) = self.state.selected() {
81 if sel == 0 {
82 return self.on_left();
83 }
84 let temp_path = self.location.clone();
85 let file = &self.dirs[sel];
86 let mut path = PathBuf::from(&self.location);
87 path.push(&file.0);
88 self.location = path.clone();
89 match self.selection_changed(None) {
90 Ok(_) => {}
91 Err(err) => {
92 self.location = temp_path;
93 self.error_message = Some(format!(
94 "Problem entering directory {}\n{}",
95 path.display(),
96 err
97 ));
98 }
99 };
100 }
101 Ok(())
102 }
103
104 pub fn on_enter(&mut self) {
105 if let Some(sel) = self.state.selected() {
106 let file = &self.dirs[sel];
107 let mut path = PathBuf::from(&self.location);
108 path.push(&file.0);
109 self.selection = Some(path);
110 }
111 }
112
113 pub fn selection_changed(&mut self, prev_location: Option<PathBuf>) -> Result<(), String> {
114 self.dirs = std::fs::read_dir(&self.location)
115 .map_err(|err| err.to_string())?
116 .filter_map(|path| match path {
117 Ok(path) => {
118 if path.path().is_dir() {
119 let is_repo = Repository::open(path.path()).is_ok();
120 path.path()
121 .components()
122 .last()
123 .and_then(|c| c.as_os_str().to_str().map(|s| (s.to_string(), is_repo)))
124 } else {
125 None
126 }
127 }
128 Err(_) => None,
129 })
130 .collect();
131 self.dirs.insert(0, ("..".to_string(), false));
132
133 if self.dirs.is_empty() {
134 self.state.select(None);
135 } else if let Some(prev) = prev_location {
136 if let Some(prev_index) = prev
137 .components()
138 .last()
139 .and_then(|comp| comp.as_os_str().to_str())
140 .and_then(|dir| self.dirs.iter().position(|d| d.0 == dir))
141 {
142 self.state.select(Some(prev_index));
143 } else {
144 self.state.select(Some(0));
145 }
146 } else {
147 self.state.select(Some(0));
148 }
149 Ok(())
150 }
151
152 pub fn set_error(&mut self, msg: String) {
153 self.error_message = Some(msg);
154 }
155 pub fn clear_error(&mut self) {
156 self.error_message = None;
157 }
158}