diff --git a/src/core/integrator.rs b/src/core/integrator.rs index 311d929..6a22a9c 100644 --- a/src/core/integrator.rs +++ b/src/core/integrator.rs @@ -104,7 +104,10 @@ pub fn integrate(record: &AppImageRecord) -> Result 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") diff --git a/src/ui/detail_view.rs b/src/ui/detail_view.rs index 17b2514..3186c0f 100644 --- a/src/ui/detail_view.rs +++ b/src/ui/detail_view.rs @@ -1085,6 +1085,60 @@ fn build_system_tab(record: &AppImageRecord, db: &Rc, 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")