Add file type association manager with MIME type support

Generated .desktop files now include MimeType and StartupWMClass when
available. Detail view system tab shows MIME types with per-type
"Set Default" buttons that use xdg-mime and track previous defaults
in system_modifications for reversal.
This commit is contained in:
lashman
2026-02-28 00:00:42 +02:00
parent 45b45c0724
commit 01d453d329
2 changed files with 110 additions and 1 deletions

View File

@@ -104,7 +104,10 @@ pub fn integrate(record: &AppImageRecord) -> Result<IntegrationResult, Integrati
let icon_id = format!("driftwood-{}", app_id); let icon_id = format!("driftwood-{}", app_id);
let desktop_content = format!("\ let mime_types = record.mime_types.as_deref().unwrap_or("");
let wm_class = record.startup_wm_class.as_deref().unwrap_or("");
let mut desktop_content = format!("\
[Desktop Entry] [Desktop Entry]
Type=Application Type=Application
Name={name} Name={name}
@@ -128,6 +131,13 @@ X-AppImage-Integrated-Date={date}
date = now, date = now,
); );
if !mime_types.is_empty() {
desktop_content.push_str(&format!("MimeType={}\n", mime_types));
}
if !wm_class.is_empty() {
desktop_content.push_str(&format!("StartupWMClass={}\n", wm_class));
}
fs::write(&desktop_path, &desktop_content)?; fs::write(&desktop_path, &desktop_content)?;
// Install icon if we have a cached one // Install icon if we have a cached one
@@ -340,6 +350,51 @@ pub fn undo_all_modifications(db: &Database, appimage_id: i64) -> Result<(), Str
Ok(()) Ok(())
} }
/// Set an AppImage as the default handler for a MIME type.
/// Stores the previous default in system_modifications for reversal.
pub fn set_mime_default(
db: &Database,
appimage_id: i64,
app_id: &str,
mime_type: &str,
) -> Result<(), String> {
let desktop_filename = format!("driftwood-{}.desktop", app_id);
// Query current default
let prev = Command::new("xdg-mime")
.args(["query", "default", mime_type])
.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
}
});
// Set new default
let status = Command::new("xdg-mime")
.args(["default", &desktop_filename, mime_type])
.status()
.map_err(|e| format!("xdg-mime failed: {}", e))?;
if !status.success() {
return Err("xdg-mime returned non-zero".to_string());
}
// Track the modification
db.register_modification(
appimage_id,
"mime_default",
mime_type,
prev.as_deref(),
).ok();
Ok(())
}
fn update_desktop_database() { fn update_desktop_database() {
let apps_dir = applications_dir(); let apps_dir = applications_dir();
Command::new("update-desktop-database") Command::new("update-desktop-database")

View File

@@ -1085,6 +1085,60 @@ fn build_system_tab(record: &AppImageRecord, db: &Rc<Database>, toast_overlay: &
} }
} }
// File type associations group
if let Some(ref mime_str) = record.mime_types {
let types: Vec<&str> = mime_str.split(';').filter(|s| !s.is_empty()).collect();
if !types.is_empty() {
let mime_group = adw::PreferencesGroup::builder()
.title("File Type Associations")
.description("MIME types this app can handle. Set as default to open these files with this app.")
.build();
let app_id = integrator::make_app_id(
record.app_name.as_deref().unwrap_or(&record.filename),
);
for mime_type in &types {
let row = adw::ActionRow::builder()
.title(*mime_type)
.build();
let set_btn = gtk::Button::builder()
.label("Set Default")
.valign(gtk::Align::Center)
.build();
set_btn.add_css_class("flat");
let db_mime = db.clone();
let record_id = record.id;
let app_id_clone = app_id.clone();
let mime = mime_type.to_string();
let toast_mime = toast_overlay.clone();
set_btn.connect_clicked(move |btn| {
match integrator::set_mime_default(
&db_mime, record_id, &app_id_clone, &mime,
) {
Ok(()) => {
toast_mime.add_toast(adw::Toast::new(
&format!("Set as default for {}", mime),
));
btn.set_sensitive(false);
btn.set_label("Default");
}
Err(e) => {
log::error!("Failed to set MIME default: {}", e);
toast_mime.add_toast(adw::Toast::new("Failed to set default"));
}
}
});
row.add_suffix(&set_btn);
mime_group.add(&row);
}
inner.append(&mime_group);
}
}
// Runtime Compatibility group // Runtime Compatibility group
let compat_group = adw::PreferencesGroup::builder() let compat_group = adw::PreferencesGroup::builder()
.title("Compatibility") .title("Compatibility")