From 7f60a0c710381b2609d08b37393caae882f0dce0 Mon Sep 17 00:00:00 2001 From: Jeremy Soller Date: Thu, 3 Oct 2024 15:16:17 -0600 Subject: [PATCH] Support browsing for application in open with dialog --- i18n/en/cosmic_files.ftl | 1 + src/app.rs | 65 ++++++++++++++++++++++++++++++++++------ src/mime_app.rs | 7 +++-- 3 files changed, 61 insertions(+), 12 deletions(-) diff --git a/i18n/en/cosmic_files.ftl b/i18n/en/cosmic_files.ftl index 4fd4336..9e78ced 100644 --- a/i18n/en/cosmic_files.ftl +++ b/i18n/en/cosmic_files.ftl @@ -53,6 +53,7 @@ save-file = Save file ## Open With Dialog open-with-title = How do you want to open "{$name}"? +browse-store = Browse {$store} ## Rename Dialog rename-file = Rename file diff --git a/src/app.rs b/src/app.rs index ec88339..2b3323d 100644 --- a/src/app.rs +++ b/src/app.rs @@ -282,6 +282,7 @@ pub enum Message { OpenInNewTab(Option), OpenInNewWindow(Option), OpenItemLocation(Option), + OpenWithBrowse, OpenWithDialog(Option), OpenWithSelection(usize), Paste(Option), @@ -408,8 +409,10 @@ pub enum DialogPage { }, OpenWith { path: PathBuf, + mime: mime_guess::Mime, apps: Vec, selected: usize, + store_opt: Option, }, RenameItem { from: PathBuf, @@ -1451,9 +1454,10 @@ impl Application for App { path, apps, selected, + .. } => { if let Some(app) = apps.get(selected) { - if let Some(mut command) = app.command(Some(path.clone())) { + if let Some(mut command) = app.command(Some(path.clone().into())) { match spawn_detached(&mut command) { Ok(()) => { let _ = recently_used_xbel::update_recently_used( @@ -1837,6 +1841,38 @@ impl Application for App { }, )) } + Message::OpenWithBrowse => match self.dialog_pages.pop_front() { + Some(DialogPage::OpenWith { + mime, store_opt, .. + }) => { + if let Some(app) = store_opt { + let url = format!("mime:///{mime}"); + if let Some(mut command) = app.command(Some(url.clone().into())) { + match spawn_detached(&mut command) { + Ok(()) => {} + Err(err) => { + log::warn!( + "failed to open {:?} with {:?}: {}", + url, + app.id, + err + ) + } + } + } else { + log::warn!( + "failed to open {:?} with {:?}: failed to get command", + url, + app.id + ); + } + } + } + Some(dialog_page) => { + self.dialog_pages.push_front(dialog_page); + } + None => {} + }, Message::OpenWithDialog(entity_opt) => { let entity = entity_opt.unwrap_or_else(|| self.tab_model.active()); if let Some(tab) = self.tab_model.data::(entity) { @@ -1850,8 +1886,13 @@ impl Application for App { }; return self.update(Message::DialogPush(DialogPage::OpenWith { path: path.to_path_buf(), + mime: item.mime.clone(), apps: item.open_with.clone(), selected: 0, + store_opt: "x-scheme-handler/mime" + .parse::() + .ok() + .and_then(|mime| mime_app::mime_apps(&mime).first().cloned()), })); } } @@ -2801,10 +2842,7 @@ impl Application for App { }; let cosmic_theme::Spacing { - space_xxs, - space_s, - space_m, - .. + space_xxs, space_s, .. } = theme::active().cosmic().spacing; let dialog = match dialog_page { @@ -3098,6 +3136,8 @@ impl Application for App { path, apps, selected, + store_opt, + .. } => { let name = match path.file_name() { Some(file_name) => file_name.to_str(), @@ -3135,16 +3175,23 @@ impl Application for App { ); } - //TODO: Browse COSMIC Store link, does that imply auto updating the app list? - - widget::dialog(fl!("open-with-title", name = name)) + let mut dialog = widget::dialog(fl!("open-with-title", name = name)) .primary_action( widget::button::suggested(fl!("open")).on_press(Message::DialogComplete), ) .secondary_action( widget::button::standard(fl!("cancel")).on_press(Message::DialogCancel), ) - .control(column) + .control(column); + + if let Some(app) = store_opt { + dialog = dialog.control( + widget::button::link(fl!("browse-store", store = app.name.as_str())) + .on_press(Message::OpenWithBrowse), + ); + } + + dialog } DialogPage::RenameItem { from, diff --git a/src/mime_app.rs b/src/mime_app.rs index 45c74e3..9e11011 100644 --- a/src/mime_app.rs +++ b/src/mime_app.rs @@ -7,10 +7,11 @@ use cosmic::widget; pub use mime_guess::Mime; use once_cell::sync::Lazy; use std::{ - cmp::Ordering, collections::HashMap, env, path::PathBuf, process, sync::Mutex, time::Instant, + cmp::Ordering, collections::HashMap, env, ffi::OsString, 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: Option) -> Option { let args_vec: Vec = shlex::split(exec)?; let mut args = args_vec.iter(); let mut command = process::Command::new(args.next()?); @@ -46,7 +47,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: Option) -> Option { exec_to_command(self.exec.as_deref()?, path_opt) } }