diff --git a/src/app/update/mod.rs b/src/app/update/mod.rs index be58a2101..f9d6dded3 100644 --- a/src/app/update/mod.rs +++ b/src/app/update/mod.rs @@ -12050,7 +12050,9 @@ impl OpenCADStudio { Message::DimStyleDialogApply => { let i = self.active_tab; self.apply_dimstyle_bufs(i); - self.style_stage_commit(); + if !self.selected_dimstyle_is_read_only() { + self.style_stage_commit(); + } Task::none() } Message::DimStyleDialogSelect(name) => { @@ -12073,6 +12075,9 @@ impl OpenCADStudio { Message::DimStyleDialogCopy => self.style_copy(crate::app::StyleKind::Dim), Message::DimStyleDialogSetCurrent => { // Staged: persists on Apply. + if self.selected_dimstyle_is_read_only() { + return Task::none(); + } let i = self.active_tab; self.tabs[i].scene.document.header.current_dimstyle_name = @@ -12093,6 +12098,9 @@ impl OpenCADStudio { Task::none() } Message::DsToggle(field) => { + if self.selected_dimstyle_is_read_only() { + return Task::none(); + } let separate_arrows = field == crate::app::DsField::Dimsah; self.apply_ds_toggle(field); if separate_arrows && self.ds_dimsah { @@ -12114,6 +12122,9 @@ impl OpenCADStudio { Task::none() } Message::DsToleranceMode(mode) => { + if self.selected_dimstyle_is_read_only() { + return Task::none(); + } self.ds_dimlim = mode == "limits"; self.ds_dimtol = matches!(mode.as_str(), "symmetrical" | "deviation"); if mode == "symmetrical" { @@ -12128,6 +12139,9 @@ impl OpenCADStudio { Task::none() } Message::DsZeroBase(field, base) => { + if self.selected_dimstyle_is_read_only() { + return Task::none(); + } let current = match &field { crate::app::DsField::Dimzin => &self.ds_dimzin, crate::app::DsField::Dimaltz => &self.ds_dimaltz, @@ -12142,6 +12156,9 @@ impl OpenCADStudio { Task::none() } Message::DsZeroFlag(field, bit) => { + if self.selected_dimstyle_is_read_only() { + return Task::none(); + } let current = match &field { crate::app::DsField::Dimzin => &self.ds_dimzin, crate::app::DsField::Dimaltz => &self.ds_dimaltz, @@ -12156,6 +12173,9 @@ impl OpenCADStudio { Task::none() } Message::DsCenterMarkMode(mode) => { + if self.selected_dimstyle_is_read_only() { + return Task::none(); + } let size = self.ds_dimcen.trim().parse::().unwrap_or(0.09).abs(); self.ds_dimcen = match mode.as_str() { "mark" => size.max(f64::EPSILON).to_string(), @@ -12165,6 +12185,9 @@ impl OpenCADStudio { Task::none() } Message::DsColorMore(field) => { + if self.selected_dimstyle_is_read_only() { + return Task::none(); + } self.ds_color_open = if self.ds_color_open.as_ref() == Some(&field) { None } else { diff --git a/src/app/update/style.rs b/src/app/update/style.rs index 2ff2d6b09..470f7f25f 100644 --- a/src/app/update/style.rs +++ b/src/app/update/style.rs @@ -21,6 +21,18 @@ use iced::time::Instant; use iced::{mouse, Point, Task}; impl OpenCADStudio { + pub(in crate::app) fn selected_dimstyle_is_read_only(&self) -> bool { + let Some(style) = self.tabs[self.active_tab] + .scene + .document + .dim_styles + .get(&self.dimstyle_selected) + else { + return true; + }; + style.xref_dependent || style.name.contains('|') + } + pub(in crate::app) fn mlstyle_mut( &mut self, tab: usize, @@ -364,6 +376,17 @@ impl OpenCADStudio { pub(in crate::app) fn apply_dimstyle_bufs(&mut self, tab: usize) { let doc = &mut self.tabs[tab].scene.document; + // Merged xref dimstyles are identified by their `xref|style` name. + // The codec's dependency bit is cleared during merge, so use the + // stable imported-name marker here as well as the DWG/DXF flag. + let selected_name = self.dimstyle_selected.clone(); + let Some(selected) = doc.dim_styles.get(&selected_name) else { + return; + }; + if selected.xref_dependent || selected.name.contains('|') { + return; + } + let text_style_handle = doc .text_styles .get(&self.ds_dimtxsty) @@ -499,6 +522,9 @@ impl OpenCADStudio { /// Update a single string buffer field. pub(in crate::app) fn apply_ds_edit(&mut self, field: crate::app::DsField, val: String) { + if self.selected_dimstyle_is_read_only() { + return; + } use crate::app::DsField::*; match field { Dimdle => self.ds_dimdle = val, @@ -570,6 +596,9 @@ impl OpenCADStudio { /// Toggle a boolean buffer field. pub(in crate::app) fn apply_ds_toggle(&mut self, field: crate::app::DsField) { + if self.selected_dimstyle_is_read_only() { + return; + } use crate::app::DsField::*; match field { Dimsd1 => self.ds_dimsd1 = !self.ds_dimsd1, @@ -1260,6 +1289,9 @@ impl OpenCADStudio { } pub(super) fn on_ds_set_handle(&mut self, field: &'static str, value: String) -> Task { + if self.selected_dimstyle_is_read_only() { + return Task::none(); + } let i = self.active_tab; let name = self.dimstyle_selected.clone(); let is_lt = matches!( @@ -1358,4 +1390,84 @@ mod ds_set_handle_tests { "picking the first arrowhead must not touch the second" ); } + + #[test] + fn xref_dimstyle_is_read_only_and_rejects_direct_edits() { + let mut app = app_with_arrow_blocks(); + let i = app.active_tab; + let name = app.dimstyle_selected.clone(); + let before = app.tabs[i] + .scene + .document + .dim_styles + .get(&name) + .unwrap() + .dimdle; + app.tabs[i] + .scene + .document + .dim_styles + .get_mut(&name) + .unwrap() + .xref_dependent = true; + assert!(app.selected_dimstyle_is_read_only()); + + app.ds_dimdle = "123".to_string(); + app.apply_dimstyle_bufs(i); + let _ = app.on_ds_set_handle("dimblk1", "ARROW_A".to_string()); + + assert_eq!( + app.tabs[i] + .scene + .document + .dim_styles + .get(&name) + .unwrap() + .dimdle, + before + ); + assert_eq!( + app.tabs[i] + .scene + .document + .dim_styles + .get(&name) + .unwrap() + .dimblk1, + codec::types::Handle::NULL + ); + } + + #[test] + fn merged_xref_dimstyle_name_is_read_only() { + let mut app = app_with_arrow_blocks(); + let i = app.active_tab; + let old_name = app.dimstyle_selected.clone(); + let mut style = app.tabs[i] + .scene + .document + .dim_styles + .get(&old_name) + .unwrap() + .clone(); + let before = style.dimdle; + app.tabs[i].scene.document.dim_styles.remove(&old_name); + style.name = "ARCH|Imported".to_string(); + app.tabs[i].scene.document.dim_styles.add_or_replace(style); + app.dimstyle_selected = "ARCH|Imported".to_string(); + + assert!(app.selected_dimstyle_is_read_only()); + app.ds_dimdle = "456".to_string(); + app.apply_dimstyle_bufs(i); + assert_ne!( + app.tabs[i] + .scene + .document + .dim_styles + .get("ARCH|Imported") + .unwrap() + .dimdle, + before + ); + } } diff --git a/src/app/view/modal.rs b/src/app/view/modal.rs index fb137baf7..4baeb3cbd 100644 --- a/src/app/view/modal.rs +++ b/src/app/view/modal.rs @@ -1477,7 +1477,8 @@ impl OpenCADStudio { } }; let ds_sel = doc.dim_styles.get(&self.dimstyle_selected); - let read_only = false; + let read_only = + ds_sel.is_none_or(|style| style.xref_dependent || style.name.contains('|')); let in_use = doc.entities().any(|entity| { matches!(entity, codec::EntityType::Dimension(dimension) if dimension.base().style_name.eq_ignore_ascii_case(&self.dimstyle_selected))