Add default application selector for browsers, email, and file managers
This commit is contained in:
@@ -332,6 +332,28 @@ pub fn undo_all_modifications(db: &Database, appimage_id: i64) -> Result<(), Str
|
||||
.status();
|
||||
}
|
||||
}
|
||||
"default_app" => {
|
||||
if let Some(ref prev) = m.previous_value {
|
||||
match m.file_path.as_str() {
|
||||
"web-browser" => {
|
||||
let _ = Command::new("xdg-settings")
|
||||
.args(["set", "default-web-browser", prev])
|
||||
.status();
|
||||
}
|
||||
"email-client" => {
|
||||
let _ = Command::new("xdg-settings")
|
||||
.args(["set", "default-url-scheme-handler", "mailto", prev])
|
||||
.status();
|
||||
}
|
||||
"file-manager" => {
|
||||
let _ = Command::new("xdg-mime")
|
||||
.args(["default", prev, "inode/directory"])
|
||||
.status();
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
}
|
||||
"system_desktop" | "system_icon" | "system_binary" => {
|
||||
let _ = Command::new("pkexec")
|
||||
.args(["rm", "-f", &m.file_path])
|
||||
@@ -395,6 +417,124 @@ pub fn set_mime_default(
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// A system-level default application type that an AppImage can serve as.
|
||||
#[derive(Debug, Clone, PartialEq)]
|
||||
pub enum DefaultAppType {
|
||||
WebBrowser,
|
||||
EmailClient,
|
||||
FileManager,
|
||||
}
|
||||
|
||||
impl DefaultAppType {
|
||||
pub fn label(&self) -> &'static str {
|
||||
match self {
|
||||
Self::WebBrowser => "Web browser",
|
||||
Self::EmailClient => "Email client",
|
||||
Self::FileManager => "File manager",
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Detect which system-default roles an AppImage can fill based on its categories.
|
||||
pub fn detect_default_capabilities(categories: &str) -> Vec<DefaultAppType> {
|
||||
let mut caps = Vec::new();
|
||||
if categories.contains("WebBrowser") {
|
||||
caps.push(DefaultAppType::WebBrowser);
|
||||
}
|
||||
if categories.contains("Email") {
|
||||
caps.push(DefaultAppType::EmailClient);
|
||||
}
|
||||
if categories.contains("FileManager") {
|
||||
caps.push(DefaultAppType::FileManager);
|
||||
}
|
||||
caps
|
||||
}
|
||||
|
||||
/// Set an AppImage as a system-default application for the given role.
|
||||
/// Stores the previous default in system_modifications for reversal.
|
||||
pub fn set_default_app(
|
||||
db: &Database,
|
||||
appimage_id: i64,
|
||||
app_id: &str,
|
||||
cap: &DefaultAppType,
|
||||
) -> Result<(), String> {
|
||||
let desktop_filename = format!("driftwood-{}.desktop", app_id);
|
||||
|
||||
match cap {
|
||||
DefaultAppType::WebBrowser => {
|
||||
let prev = Command::new("xdg-settings")
|
||||
.args(["get", "default-web-browser"])
|
||||
.output()
|
||||
.ok()
|
||||
.and_then(|o| {
|
||||
if o.status.success() {
|
||||
let s = String::from_utf8_lossy(&o.stdout).trim().to_string();
|
||||
if s.is_empty() { None } else { Some(s) }
|
||||
} else {
|
||||
None
|
||||
}
|
||||
});
|
||||
|
||||
let status = Command::new("xdg-settings")
|
||||
.args(["set", "default-web-browser", &desktop_filename])
|
||||
.status()
|
||||
.map_err(|e| format!("xdg-settings failed: {}", e))?;
|
||||
if !status.success() {
|
||||
return Err("xdg-settings returned non-zero".to_string());
|
||||
}
|
||||
db.register_modification(appimage_id, "default_app", "web-browser", prev.as_deref()).ok();
|
||||
}
|
||||
DefaultAppType::EmailClient => {
|
||||
let prev = Command::new("xdg-settings")
|
||||
.args(["get", "default-url-scheme-handler", "mailto"])
|
||||
.output()
|
||||
.ok()
|
||||
.and_then(|o| {
|
||||
if o.status.success() {
|
||||
let s = String::from_utf8_lossy(&o.stdout).trim().to_string();
|
||||
if s.is_empty() { None } else { Some(s) }
|
||||
} else {
|
||||
None
|
||||
}
|
||||
});
|
||||
|
||||
let status = Command::new("xdg-settings")
|
||||
.args(["set", "default-url-scheme-handler", "mailto", &desktop_filename])
|
||||
.status()
|
||||
.map_err(|e| format!("xdg-settings failed: {}", e))?;
|
||||
if !status.success() {
|
||||
return Err("xdg-settings returned non-zero".to_string());
|
||||
}
|
||||
db.register_modification(appimage_id, "default_app", "email-client", prev.as_deref()).ok();
|
||||
}
|
||||
DefaultAppType::FileManager => {
|
||||
let prev = Command::new("xdg-mime")
|
||||
.args(["query", "default", "inode/directory"])
|
||||
.output()
|
||||
.ok()
|
||||
.and_then(|o| {
|
||||
if o.status.success() {
|
||||
let s = String::from_utf8_lossy(&o.stdout).trim().to_string();
|
||||
if s.is_empty() { None } else { Some(s) }
|
||||
} else {
|
||||
None
|
||||
}
|
||||
});
|
||||
|
||||
let status = Command::new("xdg-mime")
|
||||
.args(["default", &desktop_filename, "inode/directory"])
|
||||
.status()
|
||||
.map_err(|e| format!("xdg-mime failed: {}", e))?;
|
||||
if !status.success() {
|
||||
return Err("xdg-mime returned non-zero".to_string());
|
||||
}
|
||||
db.register_modification(appimage_id, "default_app", "file-manager", prev.as_deref()).ok();
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn update_desktop_database() {
|
||||
let apps_dir = applications_dir();
|
||||
Command::new("update-desktop-database")
|
||||
|
||||
@@ -1123,6 +1123,62 @@ fn build_system_tab(record: &AppImageRecord, db: &Rc<Database>, toast_overlay: &
|
||||
}
|
||||
}
|
||||
|
||||
// Default Application group
|
||||
if let Some(ref cats) = record.categories {
|
||||
let caps = integrator::detect_default_capabilities(cats);
|
||||
if !caps.is_empty() && record.integrated {
|
||||
let default_group = adw::PreferencesGroup::builder()
|
||||
.title("Default Application")
|
||||
.description(
|
||||
"Set this app as your system default for tasks it can handle."
|
||||
)
|
||||
.build();
|
||||
|
||||
let app_id = integrator::make_app_id(
|
||||
record.app_name.as_deref().unwrap_or(&record.filename),
|
||||
);
|
||||
|
||||
for cap in &caps {
|
||||
let row = adw::ActionRow::builder()
|
||||
.title(cap.label())
|
||||
.build();
|
||||
|
||||
let set_btn = gtk::Button::builder()
|
||||
.label("Set Default")
|
||||
.valign(gtk::Align::Center)
|
||||
.build();
|
||||
set_btn.add_css_class("flat");
|
||||
|
||||
let db_def = db.clone();
|
||||
let record_id = record.id;
|
||||
let app_id_clone = app_id.clone();
|
||||
let cap_clone = cap.clone();
|
||||
let toast_def = toast_overlay.clone();
|
||||
set_btn.connect_clicked(move |btn| {
|
||||
match integrator::set_default_app(
|
||||
&db_def, record_id, &app_id_clone, &cap_clone,
|
||||
) {
|
||||
Ok(()) => {
|
||||
toast_def.add_toast(adw::Toast::new(
|
||||
&format!("Set as default {}", cap_clone.label().to_lowercase()),
|
||||
));
|
||||
btn.set_sensitive(false);
|
||||
btn.set_label("Default");
|
||||
}
|
||||
Err(e) => {
|
||||
log::error!("Failed to set default app: {}", e);
|
||||
toast_def.add_toast(adw::Toast::new("Failed to set default"));
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
row.add_suffix(&set_btn);
|
||||
default_group.add(&row);
|
||||
}
|
||||
inner.append(&default_group);
|
||||
}
|
||||
}
|
||||
|
||||
// Runtime Compatibility group
|
||||
let compat_group = adw::PreferencesGroup::builder()
|
||||
.title("Compatibility")
|
||||
|
||||
Reference in New Issue
Block a user