1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
use std::ffi::OsString;
use anyhow::anyhow;
use gtk::{FileChooserAction, FileChooserExt, FileFilter, NativeDialogExt, ResponseType, Window};
use crate::dialog::{FileDialogOptions, FileDialogType, FileSpec};
use crate::Error;
fn file_filter(fs: &FileSpec) -> FileFilter {
let ret = FileFilter::new();
ret.set_name(Some(fs.name));
for ext in fs.extensions {
ret.add_pattern(&format!("*.{}", ext));
}
ret
}
pub(crate) fn get_file_dialog_path(
window: &Window,
ty: FileDialogType,
options: FileDialogOptions,
) -> Result<OsString, Error> {
let (title, action) = match (ty, options.select_directories) {
(FileDialogType::Open, false) => ("Open File", FileChooserAction::Open),
(FileDialogType::Open, true) => ("Open Folder", FileChooserAction::SelectFolder),
(FileDialogType::Save, _) => ("Save File", FileChooserAction::Save),
};
let title = options.title.as_deref().unwrap_or(title);
let mut dialog = gtk::FileChooserNativeBuilder::new()
.transient_for(window)
.title(title);
if let Some(button_text) = &options.button_text {
dialog = dialog.accept_label(button_text);
}
let dialog = dialog.build();
dialog.set_action(action);
dialog.set_show_hidden(options.show_hidden);
if action != FileChooserAction::Save {
dialog.set_select_multiple(options.multi_selection);
}
if action != FileChooserAction::SelectFolder {
let mut found_default_filter = false;
if let Some(file_types) = &options.allowed_types {
for f in file_types {
let filter = file_filter(f);
dialog.add_filter(&filter);
if let Some(default) = &options.default_type {
if default == f {
dialog.set_filter(&filter);
found_default_filter = true;
}
}
}
}
if let Some(dt) = &options.default_type {
if !found_default_filter {
tracing::warn!("The default type {:?} is not present in allowed types.", dt);
}
}
}
if let Some(default_name) = &options.default_name {
dialog.set_current_name(default_name);
}
let result = dialog.run();
let result = match result {
ResponseType::Accept => match dialog.get_filename() {
Some(path) => Ok(path.into_os_string()),
None => Err(anyhow!("No path received for filename")),
},
ResponseType::Cancel => Err(anyhow!("Dialog was deleted")),
_ => {
tracing::warn!("Unhandled dialog result: {:?}", result);
Err(anyhow!("Unhandled dialog result"))
}
};
dialog.destroy();
Ok(result?)
}