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:
@@ -104,7 +104,10 @@ pub fn integrate(record: &AppImageRecord) -> Result<IntegrationResult, Integrati
|
||||
|
||||
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]
|
||||
Type=Application
|
||||
Name={name}
|
||||
@@ -128,6 +131,13 @@ X-AppImage-Integrated-Date={date}
|
||||
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)?;
|
||||
|
||||
// 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(())
|
||||
}
|
||||
|
||||
/// 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() {
|
||||
let apps_dir = applications_dir();
|
||||
Command::new("update-desktop-database")
|
||||
|
||||
@@ -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
|
||||
let compat_group = adw::PreferencesGroup::builder()
|
||||
.title("Compatibility")
|
||||
|
||||
Reference in New Issue
Block a user