diff --git a/src/app.rs b/src/app.rs index 71d5d540..0d3a9787 100644 --- a/src/app.rs +++ b/src/app.rs @@ -1685,7 +1685,7 @@ impl Application for App { } } for path in paths { - if let Some(mut command) = terminal.command(None) { + if let Some(mut command) = terminal.command(Vec::new()) { command.current_dir(&path); match spawn_detached(&mut command) { Ok(()) => {} @@ -1705,7 +1705,7 @@ impl Application for App { } } Message::OpenWith(path, app) => { - if let Some(mut command) = app.command(Some(path.clone())) { + if let Some(mut command) = app.command(vec![Some(path.clone())]) { match spawn_detached(&mut command) { Ok(()) => { let _ = recently_used_xbel::update_recently_used( @@ -2127,7 +2127,7 @@ impl Application for App { Ok(entry) => { match entry.section("Desktop Entry").attr("Exec") { Some(exec) => { - match mime_app::exec_to_command(exec, None) { + match mime_app::exec_to_command(exec, Vec::new()) { Some(mut command) => { match spawn_detached(&mut command) { Ok(()) => { @@ -2173,6 +2173,51 @@ impl Application for App { } } } + tab::Command::OpenMultipleFiles(items) => { + let mut groups: HashMap< + String, + Vec<(mime_app::MimeApp, Option)>, + > = HashMap::new(); + + for item in items { + item.open_with + .into_iter() + .filter(|mime| mime.is_default) + .for_each(|app| { + groups + .entry(app.id.clone()) + .or_default() + .push((app, item.path_opt.clone())); + }) + } + + for (_id, values) in groups { + let mut paths = Vec::new(); + let mut mime_app: Option = None; + + for (app, path_opt) in values { + paths.push(path_opt); + mime_app = Some(app); + } + + if let Some(app) = mime_app { + if let Some(mut command) = app.command(paths) { + match spawn_detached(&mut command) { + Ok(()) => {} + Err(err) => { + log::warn!( + "failed to open with {:?}: {}", + app.id, + err + ) + } + } + } else { + log::warn!("failed to get command for {:?}", app.id); + } + } + } + } tab::Command::OpenInNewTab(path) => { commands.push(self.open_tab(Location::Path(path.clone()), false, None)); } diff --git a/src/mime_app.rs b/src/mime_app.rs index 268152fb..4f82bb32 100644 --- a/src/mime_app.rs +++ b/src/mime_app.rs @@ -10,16 +10,27 @@ use std::{ cmp::Ordering, collections::HashMap, env, path::PathBuf, process, sync::Mutex, time::Instant, }; -pub fn exec_to_command(exec: &str, path_opt: Option) -> Option { +pub fn exec_to_command(exec: &str, path_opt: Vec>) -> Option { let args_vec: Vec = shlex::split(exec)?; let mut args = args_vec.iter(); let mut command = process::Command::new(args.next()?); for arg in args { if arg.starts_with('%') { match arg.as_str() { - "%f" | "%F" | "%u" | "%U" => { - if let Some(path) = &path_opt { - command.arg(path); + "%f" | "%u" => { + if !path_opt.is_empty() { + if let Some(Some(path)) = &path_opt.first() { + command.arg(path); + } + } + } + "%F" | "%U" => { + if !path_opt.is_empty() { + path_opt.iter().for_each(|path| { + if let Some(path) = &path { + command.arg(path); + } + }) } } _ => { @@ -46,7 +57,7 @@ pub struct MimeApp { impl MimeApp { //TODO: move to libcosmic, support multiple files - pub fn command(&self, path_opt: Option) -> Option { + pub fn command(&self, path_opt: Vec>) -> Option { exec_to_command(self.exec.as_deref()?, path_opt) } } diff --git a/src/tab.rs b/src/tab.rs index e4a0463f..ce3259ad 100644 --- a/src/tab.rs +++ b/src/tab.rs @@ -764,6 +764,7 @@ pub enum Command { LocationProperties(usize), MoveToTrash(Vec), OpenFile(PathBuf), + OpenMultipleFiles(Vec), OpenInNewTab(PathBuf), OpenInNewWindow(PathBuf), } @@ -1950,21 +1951,35 @@ impl Tab { } } Message::Open => { + let mut many_items = Vec::new(); + if let Some(ref mut items) = self.items_opt { + let selections = items.iter().filter(|item| item.selected).count(); + for item in items.iter() { + let has_default_app = item.open_with.iter().any(|x| x.is_default); + if item.selected { if let Some(path) = &item.path_opt { if path.is_dir() { //TODO: allow opening multiple tabs? cd = Some(Location::Path(path.clone())); - } else { + } else if selections == 1 || !has_default_app { commands.push(Command::OpenFile(path.clone())); } } else { //TODO: open properties? } + + if selections > 1 && has_default_app { + many_items.push(item.clone()); + } } } + + if !many_items.is_empty() { + commands.push(Command::OpenMultipleFiles(many_items)); + } } } Message::RightClick(click_i_opt) => {