feat(io): FILEASSOC command to toggle file association on/off (#205)

`FILEASSOC 1` (re)registers the app as a .dwg/.dxf/.bak handler; `FILEASSOC 0`
unregisters it (removes the .desktop + CAD mime package on Linux, the per-user
registry keys on Windows); bare `FILEASSOC` reports the current state. The
choice persists (file_assoc_enabled) and gates the every-launch
re-registration, so turning it off stays off across restarts.

Co-Authored-By: Claude Opus 4.8 <[email protected]>
This commit is contained in:
Hakan SevenandClaude Opus 4.8 committed 2026-06-29 12:58:23 +03:00
1 parent 962268e16e
commit 9ea800f049
5 files changed
+113 -4

No files matched your search

+33
View File
@@ -1468,6 +1468,39 @@ impl OpenCADStudio {
_ => self.command_line.push_error("Requires 0 or 1"),
}
}
"FILEASSOC" => {
let v = if self.file_assoc_enabled { 1 } else { 0 };
self.command_line.push_output(&format!("FILEASSOC = {v}"));
}
cmd if cmd.starts_with("FILEASSOC ") => {
match cmd.trim_start_matches("FILEASSOC").trim() {
"1" => {
self.file_assoc_enabled = true;
self.persist_settings_if_changed();
match crate::io::file_association::register_as_handler() {
Ok(()) => self.command_line.push_output(
"FILEASSOC set to 1 — registered as a .dwg/.dxf/.bak handler",
),
Err(e) => self
.command_line
.push_error(&format!("FILEASSOC: registration failed: {e}")),
}
}
"0" => {
self.file_assoc_enabled = false;
self.persist_settings_if_changed();
match crate::io::file_association::unregister_handler() {
Ok(()) => self
.command_line
.push_output("FILEASSOC set to 0 — unregistered as a file handler"),
Err(e) => self
.command_line
.push_error(&format!("FILEASSOC: unregister failed: {e}")),
}
}
_ => self.command_line.push_error("Requires 0 or 1"),
}
}
"PDSIZE" => {
use crate::command::ValuePromptCommand;
let c = ValuePromptCommand::new(
+13 -2
View File
@@ -250,6 +250,9 @@ pub(super) struct OpenCADStudio {
/// When true (default), saving over an existing file first writes a `.bak`
/// copy of it for recovery (#205). Toggle with the ISAVEBAK command.
pub backup_on_save: bool,
/// When true (default), the app registers itself as a .dwg/.dxf/.bak file
/// handler on each launch. Toggle with the FILEASSOC command.
pub file_assoc_enabled: bool,
/// Persisted default viewport background, restored from settings and applied
/// to every drawing tab (new and opened) so a chosen background survives
/// restarts (#188). `None` = the built-in dark-grey / off-white defaults.
@@ -1866,6 +1869,7 @@ impl OpenCADStudio {
dyn_input: true,
texteditmode: false,
backup_on_save: true,
file_assoc_enabled: true,
default_bg_color: None,
default_paper_bg_color: None,
awaiting_vports: false,
@@ -2151,8 +2155,15 @@ impl OpenCADStudio {
// portable .exe / AppImage that no installer has registered. Detached so
// it never delays startup.
std::thread::spawn(|| {
if let Err(e) = crate::io::file_association::register_as_handler() {
eprintln!("file association: handler registration failed: {e}");
// Honour the FILEASSOC toggle: skip (re)registration when the user
// has turned file association off.
let enabled = settings::UserSettings::load()
.map(|s| s.file_assoc_enabled)
.unwrap_or(true);
if enabled {
if let Err(e) = crate::io::file_association::register_as_handler() {
eprintln!("file association: handler registration failed: {e}");
}
}
});
let state = Self::new();
+7 -1
View File
@@ -106,6 +106,9 @@ pub struct UserSettings {
/// When true, saving over an existing file first copies it to a sibling
/// `<name>.bak` so a faulty or accidental save can be recovered (#205).
pub backup_on_save: bool,
/// When true (default), the app (re)registers itself as a .dwg/.dxf/.bak
/// handler on every launch. Toggle with the FILEASSOC command.
pub file_assoc_enabled: bool,
/// Persisted viewport background colours (0–255 RGB); `None` = app default
/// (dark grey model / off-white paper). Applied to every drawing tab on
/// launch and to tabs opened later, so a chosen background survives restarts
@@ -137,6 +140,7 @@ impl Default for UserSettings {
plugin_repos: Vec::new(),
texteditmode: false,
backup_on_save: true,
file_assoc_enabled: true,
bg_color: None,
paper_bg_color: None,
}
@@ -182,6 +186,7 @@ impl UserSettings {
}
}
"backup_on_save" => s.backup_on_save = val == "1",
"file_assoc_enabled" => s.file_assoc_enabled = val == "1",
"disabled_plugins" => {
s.disabled_plugins = val
.split(',')
@@ -223,7 +228,7 @@ impl UserSettings {
.collect::<Vec<_>>()
.join(",");
let body = format!(
"dyn={}\northo={}\npolar={}\npolar_increment_deg={}\nosnap={}\notrack={}\ndefault_assoc_prompted={}\nsnap_modes={}\ndisabled_plugins={}\nplugin_repos={}\ntexteditmode={}\nbackup_on_save={}\nbg_color={}\npaper_bg_color={}\n",
"dyn={}\northo={}\npolar={}\npolar_increment_deg={}\nosnap={}\notrack={}\ndefault_assoc_prompted={}\nsnap_modes={}\ndisabled_plugins={}\nplugin_repos={}\ntexteditmode={}\nbackup_on_save={}\nfile_assoc_enabled={}\nbg_color={}\npaper_bg_color={}\n",
b(self.dyn_input),
b(self.ortho),
b(self.polar),
@@ -236,6 +241,7 @@ impl UserSettings {
self.plugin_repos.join(","),
self.texteditmode,
b(self.backup_on_save),
b(self.file_assoc_enabled),
rgb_to_str(self.bg_color),
rgb_to_str(self.paper_bg_color),
);
+2
View File
@@ -41,6 +41,7 @@ impl OpenCADStudio {
plugin_repos: self.plugin_repos.clone(),
texteditmode: self.texteditmode,
backup_on_save: self.backup_on_save,
file_assoc_enabled: self.file_assoc_enabled,
bg_color: self.default_bg_color.map(f4_to_u3),
paper_bg_color: self.default_paper_bg_color.map(f4_to_u3),
}
@@ -60,6 +61,7 @@ impl OpenCADStudio {
self.plugin_repos = s.plugin_repos.clone();
self.texteditmode = s.texteditmode;
self.backup_on_save = s.backup_on_save;
self.file_assoc_enabled = s.file_assoc_enabled;
self.default_bg_color = s.bg_color.map(u3_to_f4);
self.default_paper_bg_color = s.paper_bg_color.map(u3_to_f4);
// Push the restored background onto every drawing tab that exists now