Add file manager integration install/uninstall logic
Implements actual extension file creation for Nautilus (Python extension), Nemo (.nemo_action files), Thunar (custom actions XML), and Dolphin (KDE service menu .desktop). Each extension creates a "Process with Pixstrip" submenu with all presets listed. Toggle switches in welcome wizard and settings now call install/uninstall.
This commit is contained in:
430
pixstrip-core/src/fm_integration.rs
Normal file
430
pixstrip-core/src/fm_integration.rs
Normal file
@@ -0,0 +1,430 @@
|
|||||||
|
use std::path::{Path, PathBuf};
|
||||||
|
|
||||||
|
use crate::error::Result;
|
||||||
|
use crate::preset::Preset;
|
||||||
|
use crate::storage::PresetStore;
|
||||||
|
|
||||||
|
/// Supported file managers for right-click integration.
|
||||||
|
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||||
|
pub enum FileManager {
|
||||||
|
Nautilus,
|
||||||
|
Nemo,
|
||||||
|
Thunar,
|
||||||
|
Dolphin,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl FileManager {
|
||||||
|
/// Human-readable name.
|
||||||
|
pub fn name(&self) -> &'static str {
|
||||||
|
match self {
|
||||||
|
Self::Nautilus => "Nautilus",
|
||||||
|
Self::Nemo => "Nemo",
|
||||||
|
Self::Thunar => "Thunar",
|
||||||
|
Self::Dolphin => "Dolphin",
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Desktop file ID used to detect if the FM is installed.
|
||||||
|
pub fn desktop_id(&self) -> &'static str {
|
||||||
|
match self {
|
||||||
|
Self::Nautilus => "org.gnome.Nautilus",
|
||||||
|
Self::Nemo => "org.nemo.Nemo",
|
||||||
|
Self::Thunar => "thunar",
|
||||||
|
Self::Dolphin => "org.kde.dolphin",
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Install the integration extension for this file manager.
|
||||||
|
pub fn install(&self) -> Result<()> {
|
||||||
|
match self {
|
||||||
|
Self::Nautilus => install_nautilus(),
|
||||||
|
Self::Nemo => install_nemo(),
|
||||||
|
Self::Thunar => install_thunar(),
|
||||||
|
Self::Dolphin => install_dolphin(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Remove the integration extension for this file manager.
|
||||||
|
pub fn uninstall(&self) -> Result<()> {
|
||||||
|
match self {
|
||||||
|
Self::Nautilus => uninstall_nautilus(),
|
||||||
|
Self::Nemo => uninstall_nemo(),
|
||||||
|
Self::Thunar => uninstall_thunar(),
|
||||||
|
Self::Dolphin => uninstall_dolphin(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Check if the integration is currently installed.
|
||||||
|
pub fn is_installed(&self) -> bool {
|
||||||
|
match self {
|
||||||
|
Self::Nautilus => nautilus_extension_path().exists(),
|
||||||
|
Self::Nemo => nemo_action_path().exists(),
|
||||||
|
Self::Thunar => thunar_action_path().exists(),
|
||||||
|
Self::Dolphin => dolphin_service_path().exists(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Regenerate the extension files for all installed file managers.
|
||||||
|
/// Call this after presets change so the submenu stays up to date.
|
||||||
|
pub fn regenerate_all() -> Result<()> {
|
||||||
|
for fm in &[FileManager::Nautilus, FileManager::Nemo, FileManager::Thunar, FileManager::Dolphin] {
|
||||||
|
if fm.is_installed() {
|
||||||
|
fm.install()?;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn pixstrip_bin() -> String {
|
||||||
|
// Try to find the pixstrip binary path
|
||||||
|
if let Ok(exe) = std::env::current_exe() {
|
||||||
|
// If running from the GTK app, find the CLI sibling
|
||||||
|
let dir = exe.parent().unwrap_or(Path::new("/usr/bin"));
|
||||||
|
let cli_path = dir.join("pixstrip");
|
||||||
|
if cli_path.exists() {
|
||||||
|
return cli_path.display().to_string();
|
||||||
|
}
|
||||||
|
let gtk_path = dir.join("pixstrip-gtk");
|
||||||
|
if gtk_path.exists() {
|
||||||
|
return gtk_path.display().to_string();
|
||||||
|
}
|
||||||
|
return exe.display().to_string();
|
||||||
|
}
|
||||||
|
"pixstrip-gtk".into()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_preset_names() -> Vec<String> {
|
||||||
|
let mut names: Vec<String> = Preset::all_builtins()
|
||||||
|
.into_iter()
|
||||||
|
.map(|p| p.name)
|
||||||
|
.collect();
|
||||||
|
|
||||||
|
let store = PresetStore::new();
|
||||||
|
if let Ok(user_presets) = store.list() {
|
||||||
|
for p in user_presets {
|
||||||
|
if p.is_custom && !names.contains(&p.name) {
|
||||||
|
names.push(p.name);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
names
|
||||||
|
}
|
||||||
|
|
||||||
|
// --- Nautilus (Python extension) ---
|
||||||
|
|
||||||
|
fn nautilus_extension_dir() -> PathBuf {
|
||||||
|
let data = std::env::var("XDG_DATA_HOME")
|
||||||
|
.unwrap_or_else(|_| {
|
||||||
|
let home = std::env::var("HOME").unwrap_or_else(|_| "~".into());
|
||||||
|
format!("{}/.local/share", home)
|
||||||
|
});
|
||||||
|
PathBuf::from(data).join("nautilus-python").join("extensions")
|
||||||
|
}
|
||||||
|
|
||||||
|
fn nautilus_extension_path() -> PathBuf {
|
||||||
|
nautilus_extension_dir().join("pixstrip-integration.py")
|
||||||
|
}
|
||||||
|
|
||||||
|
fn install_nautilus() -> Result<()> {
|
||||||
|
let dir = nautilus_extension_dir();
|
||||||
|
std::fs::create_dir_all(&dir)?;
|
||||||
|
|
||||||
|
let bin = pixstrip_bin();
|
||||||
|
let presets = get_preset_names();
|
||||||
|
|
||||||
|
let mut preset_items = String::new();
|
||||||
|
for name in &presets {
|
||||||
|
preset_items.push_str(&format!(
|
||||||
|
" item = Nautilus.MenuItem(\n\
|
||||||
|
\x20 name='Pixstrip::Preset::{}',\n\
|
||||||
|
\x20 label='{}',\n\
|
||||||
|
\x20 )\n\
|
||||||
|
\x20 item.connect('activate', self._on_preset, '{}', files)\n\
|
||||||
|
\x20 submenu.append_item(item)\n\n",
|
||||||
|
name.replace(' ', "_"),
|
||||||
|
name,
|
||||||
|
name,
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
let script = format!(
|
||||||
|
r#"import subprocess
|
||||||
|
from gi.repository import Nautilus, GObject
|
||||||
|
|
||||||
|
class PixstripExtension(GObject.GObject, Nautilus.MenuProvider):
|
||||||
|
def get_file_items(self, files):
|
||||||
|
if not files:
|
||||||
|
return []
|
||||||
|
|
||||||
|
# Only show for image files
|
||||||
|
image_mimes = ['image/jpeg', 'image/png', 'image/webp', 'image/gif',
|
||||||
|
'image/tiff', 'image/avif', 'image/bmp']
|
||||||
|
valid = [f for f in files if f.get_mime_type() in image_mimes or f.is_directory()]
|
||||||
|
if not valid:
|
||||||
|
return []
|
||||||
|
|
||||||
|
top = Nautilus.MenuItem(
|
||||||
|
name='Pixstrip::Menu',
|
||||||
|
label='Process with Pixstrip',
|
||||||
|
icon='applications-graphics-symbolic',
|
||||||
|
)
|
||||||
|
|
||||||
|
submenu = Nautilus.Menu()
|
||||||
|
top.set_submenu(submenu)
|
||||||
|
|
||||||
|
# Open in Pixstrip (wizard)
|
||||||
|
open_item = Nautilus.MenuItem(
|
||||||
|
name='Pixstrip::Open',
|
||||||
|
label='Open in Pixstrip...',
|
||||||
|
)
|
||||||
|
open_item.connect('activate', self._on_open, files)
|
||||||
|
submenu.append_item(open_item)
|
||||||
|
|
||||||
|
# Separator via disabled item
|
||||||
|
sep = Nautilus.MenuItem(
|
||||||
|
name='Pixstrip::Sep',
|
||||||
|
label='---',
|
||||||
|
sensitive=False,
|
||||||
|
)
|
||||||
|
submenu.append_item(sep)
|
||||||
|
|
||||||
|
# Preset items
|
||||||
|
{preset_items}
|
||||||
|
return [top]
|
||||||
|
|
||||||
|
def _on_open(self, menu, files):
|
||||||
|
paths = [f.get_location().get_path() for f in files if f.get_location()]
|
||||||
|
subprocess.Popen(['{bin}', '--files'] + paths)
|
||||||
|
|
||||||
|
def _on_preset(self, menu, preset_name, files):
|
||||||
|
paths = [f.get_location().get_path() for f in files if f.get_location()]
|
||||||
|
subprocess.Popen(['{bin}', '--preset', preset_name, '--files'] + paths)
|
||||||
|
"#,
|
||||||
|
preset_items = preset_items,
|
||||||
|
bin = bin,
|
||||||
|
);
|
||||||
|
|
||||||
|
std::fs::write(nautilus_extension_path(), script)?;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn uninstall_nautilus() -> Result<()> {
|
||||||
|
let path = nautilus_extension_path();
|
||||||
|
if path.exists() {
|
||||||
|
std::fs::remove_file(path)?;
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
// --- Nemo (.nemo_action files) ---
|
||||||
|
|
||||||
|
fn nemo_action_dir() -> PathBuf {
|
||||||
|
let data = std::env::var("XDG_DATA_HOME")
|
||||||
|
.unwrap_or_else(|_| {
|
||||||
|
let home = std::env::var("HOME").unwrap_or_else(|_| "~".into());
|
||||||
|
format!("{}/.local/share", home)
|
||||||
|
});
|
||||||
|
PathBuf::from(data).join("nemo").join("actions")
|
||||||
|
}
|
||||||
|
|
||||||
|
fn nemo_action_path() -> PathBuf {
|
||||||
|
nemo_action_dir().join("pixstrip-open.nemo_action")
|
||||||
|
}
|
||||||
|
|
||||||
|
fn install_nemo() -> Result<()> {
|
||||||
|
let dir = nemo_action_dir();
|
||||||
|
std::fs::create_dir_all(&dir)?;
|
||||||
|
|
||||||
|
let bin = pixstrip_bin();
|
||||||
|
|
||||||
|
// Main "Open in Pixstrip" action
|
||||||
|
let open_action = format!(
|
||||||
|
"[Nemo Action]\n\
|
||||||
|
Name=Open in Pixstrip...\n\
|
||||||
|
Comment=Process images with Pixstrip\n\
|
||||||
|
Exec={bin} --files %F\n\
|
||||||
|
Icon-Name=applications-graphics-symbolic\n\
|
||||||
|
Selection=Any\n\
|
||||||
|
Extensions=jpg;jpeg;png;webp;gif;tiff;tif;avif;bmp;\n\
|
||||||
|
Mimetypes=image/*;\n",
|
||||||
|
bin = bin,
|
||||||
|
);
|
||||||
|
std::fs::write(nemo_action_path(), open_action)?;
|
||||||
|
|
||||||
|
// Per-preset actions
|
||||||
|
let presets = get_preset_names();
|
||||||
|
for name in &presets {
|
||||||
|
let safe_name = name.replace(' ', "-").to_lowercase();
|
||||||
|
let action_path = dir.join(format!("pixstrip-preset-{}.nemo_action", safe_name));
|
||||||
|
let action = format!(
|
||||||
|
"[Nemo Action]\n\
|
||||||
|
Name=Pixstrip: {name}\n\
|
||||||
|
Comment=Process with {name} preset\n\
|
||||||
|
Exec={bin} --preset \"{name}\" --files %F\n\
|
||||||
|
Icon-Name=applications-graphics-symbolic\n\
|
||||||
|
Selection=Any\n\
|
||||||
|
Extensions=jpg;jpeg;png;webp;gif;tiff;tif;avif;bmp;\n\
|
||||||
|
Mimetypes=image/*;\n",
|
||||||
|
name = name,
|
||||||
|
bin = bin,
|
||||||
|
);
|
||||||
|
std::fs::write(action_path, action)?;
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn uninstall_nemo() -> Result<()> {
|
||||||
|
let dir = nemo_action_dir();
|
||||||
|
// Remove main action
|
||||||
|
let main_path = nemo_action_path();
|
||||||
|
if main_path.exists() {
|
||||||
|
std::fs::remove_file(main_path)?;
|
||||||
|
}
|
||||||
|
// Remove preset actions
|
||||||
|
if dir.exists() {
|
||||||
|
for entry in std::fs::read_dir(&dir)?.flatten() {
|
||||||
|
let name = entry.file_name();
|
||||||
|
let name_str = name.to_string_lossy();
|
||||||
|
if name_str.starts_with("pixstrip-preset-") && name_str.ends_with(".nemo_action") {
|
||||||
|
let _ = std::fs::remove_file(entry.path());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
// --- Thunar (Custom Actions via uca.xml) ---
|
||||||
|
|
||||||
|
fn thunar_action_dir() -> PathBuf {
|
||||||
|
let config = std::env::var("XDG_CONFIG_HOME")
|
||||||
|
.unwrap_or_else(|_| {
|
||||||
|
let home = std::env::var("HOME").unwrap_or_else(|_| "~".into());
|
||||||
|
format!("{}/.config", home)
|
||||||
|
});
|
||||||
|
PathBuf::from(config).join("Thunar")
|
||||||
|
}
|
||||||
|
|
||||||
|
fn thunar_action_path() -> PathBuf {
|
||||||
|
thunar_action_dir().join("pixstrip-actions.xml")
|
||||||
|
}
|
||||||
|
|
||||||
|
fn install_thunar() -> Result<()> {
|
||||||
|
let dir = thunar_action_dir();
|
||||||
|
std::fs::create_dir_all(&dir)?;
|
||||||
|
|
||||||
|
let bin = pixstrip_bin();
|
||||||
|
let presets = get_preset_names();
|
||||||
|
|
||||||
|
let mut actions = String::from("<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<actions>\n");
|
||||||
|
|
||||||
|
// Open in Pixstrip
|
||||||
|
actions.push_str(&format!(
|
||||||
|
" <action>\n\
|
||||||
|
\x20 <icon>applications-graphics-symbolic</icon>\n\
|
||||||
|
\x20 <name>Open in Pixstrip...</name>\n\
|
||||||
|
\x20 <command>{bin} --files %F</command>\n\
|
||||||
|
\x20 <description>Process images with Pixstrip</description>\n\
|
||||||
|
\x20 <patterns>*.jpg;*.jpeg;*.png;*.webp;*.gif;*.tiff;*.tif;*.avif;*.bmp</patterns>\n\
|
||||||
|
\x20 <image-files/>\n\
|
||||||
|
\x20 <directories/>\n\
|
||||||
|
</action>\n",
|
||||||
|
bin = bin,
|
||||||
|
));
|
||||||
|
|
||||||
|
for name in &presets {
|
||||||
|
actions.push_str(&format!(
|
||||||
|
" <action>\n\
|
||||||
|
\x20 <icon>applications-graphics-symbolic</icon>\n\
|
||||||
|
\x20 <name>Pixstrip: {name}</name>\n\
|
||||||
|
\x20 <command>{bin} --preset \"{name}\" --files %F</command>\n\
|
||||||
|
\x20 <description>Process with {name} preset</description>\n\
|
||||||
|
\x20 <patterns>*.jpg;*.jpeg;*.png;*.webp;*.gif;*.tiff;*.tif;*.avif;*.bmp</patterns>\n\
|
||||||
|
\x20 <image-files/>\n\
|
||||||
|
\x20 <directories/>\n\
|
||||||
|
</action>\n",
|
||||||
|
name = name,
|
||||||
|
bin = bin,
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
actions.push_str("</actions>\n");
|
||||||
|
std::fs::write(thunar_action_path(), actions)?;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn uninstall_thunar() -> Result<()> {
|
||||||
|
let path = thunar_action_path();
|
||||||
|
if path.exists() {
|
||||||
|
std::fs::remove_file(path)?;
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
// --- Dolphin (KDE Service Menu) ---
|
||||||
|
|
||||||
|
fn dolphin_service_dir() -> PathBuf {
|
||||||
|
let data = std::env::var("XDG_DATA_HOME")
|
||||||
|
.unwrap_or_else(|_| {
|
||||||
|
let home = std::env::var("HOME").unwrap_or_else(|_| "~".into());
|
||||||
|
format!("{}/.local/share", home)
|
||||||
|
});
|
||||||
|
PathBuf::from(data).join("kio").join("servicemenus")
|
||||||
|
}
|
||||||
|
|
||||||
|
fn dolphin_service_path() -> PathBuf {
|
||||||
|
dolphin_service_dir().join("pixstrip.desktop")
|
||||||
|
}
|
||||||
|
|
||||||
|
fn install_dolphin() -> Result<()> {
|
||||||
|
let dir = dolphin_service_dir();
|
||||||
|
std::fs::create_dir_all(&dir)?;
|
||||||
|
|
||||||
|
let bin = pixstrip_bin();
|
||||||
|
let presets = get_preset_names();
|
||||||
|
|
||||||
|
let mut desktop = format!(
|
||||||
|
"[Desktop Entry]\n\
|
||||||
|
Type=Service\n\
|
||||||
|
X-KDE-ServiceTypes=KonqPopupMenu/Plugin\n\
|
||||||
|
MimeType=image/jpeg;image/png;image/webp;image/gif;image/tiff;image/avif;image/bmp;inode/directory;\n\
|
||||||
|
Actions=Open;{preset_actions}\n\
|
||||||
|
X-KDE-Submenu=Process with Pixstrip\n\
|
||||||
|
Icon=applications-graphics-symbolic\n\n\
|
||||||
|
[Desktop Action Open]\n\
|
||||||
|
Name=Open in Pixstrip...\n\
|
||||||
|
Icon=applications-graphics-symbolic\n\
|
||||||
|
Exec={bin} --files %F\n\n",
|
||||||
|
bin = bin,
|
||||||
|
preset_actions = presets
|
||||||
|
.iter()
|
||||||
|
.enumerate()
|
||||||
|
.map(|(i, _)| format!("Preset{}", i))
|
||||||
|
.collect::<Vec<_>>()
|
||||||
|
.join(";"),
|
||||||
|
);
|
||||||
|
|
||||||
|
for (i, name) in presets.iter().enumerate() {
|
||||||
|
desktop.push_str(&format!(
|
||||||
|
"[Desktop Action Preset{i}]\n\
|
||||||
|
Name={name}\n\
|
||||||
|
Icon=applications-graphics-symbolic\n\
|
||||||
|
Exec={bin} --preset \"{name}\" --files %F\n\n",
|
||||||
|
i = i,
|
||||||
|
name = name,
|
||||||
|
bin = bin,
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
std::fs::write(dolphin_service_path(), desktop)?;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn uninstall_dolphin() -> Result<()> {
|
||||||
|
let path = dolphin_service_path();
|
||||||
|
if path.exists() {
|
||||||
|
std::fs::remove_file(path)?;
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
@@ -3,6 +3,7 @@ pub mod discovery;
|
|||||||
pub mod encoder;
|
pub mod encoder;
|
||||||
pub mod error;
|
pub mod error;
|
||||||
pub mod executor;
|
pub mod executor;
|
||||||
|
pub mod fm_integration;
|
||||||
pub mod loader;
|
pub mod loader;
|
||||||
pub mod operations;
|
pub mod operations;
|
||||||
pub mod pipeline;
|
pub mod pipeline;
|
||||||
|
|||||||
@@ -85,15 +85,16 @@ pub fn build_settings_dialog() -> adw::PreferencesDialog {
|
|||||||
.description("Add 'Process with Pixstrip' to your file manager's right-click menu")
|
.description("Add 'Process with Pixstrip' to your file manager's right-click menu")
|
||||||
.build();
|
.build();
|
||||||
|
|
||||||
|
use pixstrip_core::fm_integration::FileManager;
|
||||||
let file_managers = [
|
let file_managers = [
|
||||||
("Nautilus", "org.gnome.Nautilus"),
|
(FileManager::Nautilus, "org.gnome.Nautilus"),
|
||||||
("Nemo", "org.nemo.Nemo"),
|
(FileManager::Nemo, "org.nemo.Nemo"),
|
||||||
("Thunar", "thunar"),
|
(FileManager::Thunar, "thunar"),
|
||||||
("Dolphin", "org.kde.dolphin"),
|
(FileManager::Dolphin, "org.kde.dolphin"),
|
||||||
];
|
];
|
||||||
|
|
||||||
let mut found_fm = false;
|
let mut found_fm = false;
|
||||||
for (name, desktop_id) in &file_managers {
|
for (fm, desktop_id) in &file_managers {
|
||||||
let is_installed = gtk::gio::AppInfo::all()
|
let is_installed = gtk::gio::AppInfo::all()
|
||||||
.iter()
|
.iter()
|
||||||
.any(|info| {
|
.any(|info| {
|
||||||
@@ -104,11 +105,22 @@ pub fn build_settings_dialog() -> adw::PreferencesDialog {
|
|||||||
|
|
||||||
if is_installed {
|
if is_installed {
|
||||||
found_fm = true;
|
found_fm = true;
|
||||||
|
let already_installed = fm.is_installed();
|
||||||
let row = adw::SwitchRow::builder()
|
let row = adw::SwitchRow::builder()
|
||||||
.title(*name)
|
.title(fm.name())
|
||||||
.subtitle(format!("Add right-click menu to {}", name))
|
.subtitle(format!("Add right-click menu to {}", fm.name()))
|
||||||
.active(false)
|
.active(already_installed)
|
||||||
.build();
|
.build();
|
||||||
|
|
||||||
|
let fm_copy = *fm;
|
||||||
|
row.connect_active_notify(move |row| {
|
||||||
|
if row.is_active() {
|
||||||
|
let _ = fm_copy.install();
|
||||||
|
} else {
|
||||||
|
let _ = fm_copy.uninstall();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
fm_group.add(&row);
|
fm_group.add(&row);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -197,16 +197,16 @@ fn build_file_manager_page(nav_view: &adw::NavigationView) -> adw::NavigationPag
|
|||||||
.build();
|
.build();
|
||||||
|
|
||||||
// Detect installed file managers
|
// Detect installed file managers
|
||||||
|
use pixstrip_core::fm_integration::FileManager;
|
||||||
let file_managers = [
|
let file_managers = [
|
||||||
("Nautilus", "org.gnome.Nautilus", "system-file-manager-symbolic"),
|
(FileManager::Nautilus, "org.gnome.Nautilus", "system-file-manager-symbolic"),
|
||||||
("Nemo", "org.nemo.Nemo", "system-file-manager-symbolic"),
|
(FileManager::Nemo, "org.nemo.Nemo", "system-file-manager-symbolic"),
|
||||||
("Thunar", "thunar", "system-file-manager-symbolic"),
|
(FileManager::Thunar, "thunar", "system-file-manager-symbolic"),
|
||||||
("Dolphin", "org.kde.dolphin", "system-file-manager-symbolic"),
|
(FileManager::Dolphin, "org.kde.dolphin", "system-file-manager-symbolic"),
|
||||||
];
|
];
|
||||||
|
|
||||||
let mut found_any = false;
|
let mut found_any = false;
|
||||||
for (name, desktop_id, icon) in &file_managers {
|
for (fm, desktop_id, icon) in &file_managers {
|
||||||
// Check if the file manager is installed by looking for its .desktop file
|
|
||||||
let is_installed = gtk::gio::AppInfo::all()
|
let is_installed = gtk::gio::AppInfo::all()
|
||||||
.iter()
|
.iter()
|
||||||
.any(|info| {
|
.any(|info| {
|
||||||
@@ -217,12 +217,23 @@ fn build_file_manager_page(nav_view: &adw::NavigationView) -> adw::NavigationPag
|
|||||||
|
|
||||||
if is_installed {
|
if is_installed {
|
||||||
found_any = true;
|
found_any = true;
|
||||||
|
let already_installed = fm.is_installed();
|
||||||
let row = adw::SwitchRow::builder()
|
let row = adw::SwitchRow::builder()
|
||||||
.title(*name)
|
.title(fm.name())
|
||||||
.subtitle(format!("Add right-click menu to {}", name))
|
.subtitle(format!("Add right-click menu to {}", fm.name()))
|
||||||
.active(false)
|
.active(already_installed)
|
||||||
.build();
|
.build();
|
||||||
row.add_prefix(>k::Image::from_icon_name(icon));
|
row.add_prefix(>k::Image::from_icon_name(icon));
|
||||||
|
|
||||||
|
let fm_copy = *fm;
|
||||||
|
row.connect_active_notify(move |row| {
|
||||||
|
if row.is_active() {
|
||||||
|
let _ = fm_copy.install();
|
||||||
|
} else {
|
||||||
|
let _ = fm_copy.uninstall();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
group.add(&row);
|
group.add(&row);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user