use super::*;
use crate::opener::{Launch, Opener};
pub struct OpenWith {
pub path: PathBuf,
pub openers: Vec<Opener>,
pub index: usize,
}
impl AppState {
pub fn open_with_menu(&mut self, path: PathBuf, is_dir: bool) {
if is_dir && self.panel_archive(self.is_left_active).is_some() {
return;
}
let associations = crate::options::load_associations();
let openers = crate::opener::openers(&path, is_dir, &associations);
self.dialog = Some(Dialog::OpenWith(Box::new(OpenWith { path, openers, index: 0 })));
}
pub fn move_open_with_cursor(&mut self, step: isize) {
if let Some(Dialog::OpenWith(menu)) = &mut self.dialog {
menu.index = menu.index.saturating_add_signed(step).min(menu.openers.len().saturating_sub(1));
}
}
pub fn edit_associations(&mut self) {
self.dialog = None;
let Some(file) = crate::options::associations_file() else {
self.display_error("No config directory to keep associations in".to_string());
return;
};
if self.options.editor.trim().is_empty() {
self.request_open(file, true);
} else {
self.external_edit = Some(file);
}
}
pub fn open_with_chosen(&mut self) {
let Some(Dialog::OpenWith(menu)) = self.dialog.take() else {
return;
};
let Some(opener) = menu.openers.get(menu.index).cloned() else {
return;
};
if self.panel_archive(self.is_left_active).is_some() {
self.extract_then(&menu.path, |dest| AfterJob::OpenWith(dest, Box::new(opener)));
return;
}
self.launch(&opener, &menu.path);
}
pub fn open_outside(&mut self, path: &Path, is_dir: bool) {
let associations = if is_dir { Vec::new() } else { crate::options::load_associations() };
match crate::opener::configured(path, &associations).into_iter().next() {
Some(opener) => self.launch(&opener, path),
None => {
if let Err(e) = crate::input::open_with_default(path) {
self.display_error(format!("Cannot open file: {e}"));
}
}
}
}
pub fn launch(&mut self, opener: &Opener, path: &Path) {
let dir = path.parent().map(Path::to_path_buf).unwrap_or_default();
let mut process = match &opener.launch {
Launch::Default => {
if let Err(e) = crate::input::open_with_default(path) {
self.display_error(format!("Cannot open file: {e}"));
}
return;
}
Launch::Choose => choose_program(path),
Launch::Shell(command) => {
let (file, files) = uses_entries(command);
let command = if file || files { command.clone() } else { format!("{command} {{file}}") };
let values = MenuValues {
file: Some(path.to_path_buf()),
files: vec![path.to_path_buf()],
dir: dir.clone(),
other: self.panel_real_dir(!self.is_left_active),
};
shell_command(&command, &values)
}
Launch::Desktop { words, name, icon, entry } => {
let mut args = crate::opener::exec_args(words, path, name, icon.as_deref(), entry).into_iter();
let mut process = std::process::Command::new(args.next().unwrap_or_default());
process.args(args).current_dir(&dir);
process
}
};
if opener.terminal {
self.user_command = Some(UserCommand {
label: opener.label.clone(),
process,
pause: false,
});
return;
}
process.stdin(Stdio::null()).stdout(Stdio::null()).stderr(Stdio::null());
crate::input::detach(&mut process);
match process.spawn() {
Ok(mut child) => {
std::thread::spawn(move || child.wait());
}
Err(e) => self.display_error(format!("Cannot start {}: {e}", opener.label)),
}
}
}
fn choose_program(path: &Path) -> std::process::Command {
let mut process = std::process::Command::new("rundll32.exe");
process.arg("shell32.dll,OpenAs_RunDLL").arg(path);
process
}