forked from 1b-agent/opencadstudio-upstream
fix(layers): protect locked entities
Keep locked-layer objects selectable for inspection while suppressing grips, property edits, command mutations, and stale editor writes.
This commit is contained in:
1 parent
ca331fff48
commit
0be9c41773
25 files changed
+524
-118
No files matched your search
+49
-21
@@ -71,6 +71,7 @@ impl OpenCADStudio {
|
||||
.selected_entities()
|
||||
.into_iter()
|
||||
.map(|(h, _)| h)
|
||||
.filter(|handle| !self.tabs[i].scene.is_layer_locked(*handle))
|
||||
.collect();
|
||||
if handles.is_empty() {
|
||||
use crate::modules::draw::select::SelectObjectsCommand;
|
||||
@@ -106,6 +107,7 @@ impl OpenCADStudio {
|
||||
.selected_entities()
|
||||
.into_iter()
|
||||
.map(|(h, _)| h)
|
||||
.filter(|handle| !self.tabs[i].scene.is_layer_locked(*handle))
|
||||
.collect();
|
||||
if handles.is_empty() {
|
||||
use crate::modules::draw::select::SelectObjectsCommand;
|
||||
@@ -132,6 +134,7 @@ impl OpenCADStudio {
|
||||
.selected_entities()
|
||||
.into_iter()
|
||||
.map(|(h, _)| h)
|
||||
.filter(|handle| !self.tabs[i].scene.is_layer_locked(*handle))
|
||||
.collect();
|
||||
if coords.len() == 3 && !handles.is_empty() {
|
||||
let base = glam::DVec3::new(coords[0], coords[1], coords[2]);
|
||||
@@ -160,6 +163,7 @@ impl OpenCADStudio {
|
||||
.selected_entities()
|
||||
.into_iter()
|
||||
.map(|(h, _)| h)
|
||||
.filter(|handle| !self.tabs[i].scene.is_layer_locked(*handle))
|
||||
.collect();
|
||||
if handles.is_empty() {
|
||||
use crate::modules::draw::select::SelectObjectsCommand;
|
||||
@@ -393,32 +397,55 @@ impl OpenCADStudio {
|
||||
.collect()
|
||||
})
|
||||
.unwrap_or_default();
|
||||
let inserts: Vec<_> = self.tabs[i]
|
||||
.scene
|
||||
.document
|
||||
.entities()
|
||||
.filter_map(|entity| match entity {
|
||||
acadrust::EntityType::Insert(insert)
|
||||
if insert.block_name.eq_ignore_ascii_case(&block)
|
||||
&& !self.tabs[i]
|
||||
.scene
|
||||
.is_layer_locked(insert.common.handle) =>
|
||||
{
|
||||
Some(insert.common.handle)
|
||||
}
|
||||
_ => None,
|
||||
})
|
||||
.collect();
|
||||
if inserts.is_empty() {
|
||||
self.command_line.push_error(
|
||||
crate::t!("ATTSYNC: no editable block references.").as_ref(),
|
||||
);
|
||||
return Some(Task::none());
|
||||
}
|
||||
self.push_undo_snapshot(i, "ATTSYNC");
|
||||
let mut synced = 0usize;
|
||||
let mut changes = Vec::new();
|
||||
for e in self.tabs[i].scene.document.entities_mut() {
|
||||
if let acadrust::EntityType::Insert(ins) = e {
|
||||
if ins.block_name.eq_ignore_ascii_case(&block) {
|
||||
ins.attributes.retain(|a| {
|
||||
attdefs.iter().any(|(t, _)| t.eq_ignore_ascii_case(&a.tag))
|
||||
});
|
||||
for (tag, default) in &attdefs {
|
||||
if !ins
|
||||
.attributes
|
||||
.iter()
|
||||
.any(|a| a.tag.eq_ignore_ascii_case(tag))
|
||||
{
|
||||
ins.attributes
|
||||
.push(acadrust::entities::AttributeEntity::new(
|
||||
tag.clone(),
|
||||
default.clone(),
|
||||
));
|
||||
}
|
||||
for handle in inserts {
|
||||
let Some(acadrust::EntityType::Insert(ins)) =
|
||||
self.tabs[i].scene.document.get_entity_mut(handle)
|
||||
else {
|
||||
continue;
|
||||
};
|
||||
ins.attributes.retain(|a| {
|
||||
attdefs.iter().any(|(t, _)| t.eq_ignore_ascii_case(&a.tag))
|
||||
});
|
||||
for (tag, default) in &attdefs {
|
||||
if !ins
|
||||
.attributes
|
||||
.iter()
|
||||
.any(|a| a.tag.eq_ignore_ascii_case(tag))
|
||||
{
|
||||
ins.attributes
|
||||
.push(acadrust::entities::AttributeEntity::new(
|
||||
tag.clone(),
|
||||
default.clone(),
|
||||
));
|
||||
}
|
||||
synced += 1;
|
||||
changes.push((ins.common.handle, crate::scene::ChangeKind::Modified));
|
||||
}
|
||||
}
|
||||
synced += 1;
|
||||
changes.push((ins.common.handle, crate::scene::ChangeKind::Modified));
|
||||
}
|
||||
self.tabs[i].scene.bump_entities(&changes);
|
||||
self.tabs[i].dirty = true;
|
||||
@@ -626,6 +653,7 @@ impl OpenCADStudio {
|
||||
.iter()
|
||||
.filter(|(_, e)| matches!(e, acadrust::EntityType::Insert(_)))
|
||||
.map(|(h, _)| *h)
|
||||
.filter(|handle| !self.tabs[i].scene.is_layer_locked(*handle))
|
||||
.collect();
|
||||
if inserts.is_empty() {
|
||||
self.command_line
|
||||
|
||||
+13
-2
@@ -86,6 +86,7 @@ impl OpenCADStudio {
|
||||
.selected_entities()
|
||||
.iter()
|
||||
.map(|(h, _)| *h)
|
||||
.filter(|handle| !self.tabs[i].scene.is_layer_locked(*handle))
|
||||
.collect();
|
||||
if handles.is_empty() {
|
||||
self.command_line
|
||||
@@ -208,6 +209,7 @@ impl OpenCADStudio {
|
||||
.selected_entities()
|
||||
.iter()
|
||||
.map(|(h, _)| *h)
|
||||
.filter(|handle| !self.tabs[i].scene.is_layer_locked(*handle))
|
||||
.collect();
|
||||
if handles.is_empty() {
|
||||
self.command_line
|
||||
@@ -297,6 +299,7 @@ impl OpenCADStudio {
|
||||
)
|
||||
})
|
||||
.map(|(h, _)| *h)
|
||||
.filter(|handle| !self.tabs[i].scene.is_layer_locked(*handle))
|
||||
.collect();
|
||||
if handles.is_empty() {
|
||||
self.command_line
|
||||
@@ -374,6 +377,7 @@ impl OpenCADStudio {
|
||||
.iter()
|
||||
.filter(|(_, e)| matches!(e, acadrust::EntityType::Text(_)))
|
||||
.map(|(h, _)| *h)
|
||||
.filter(|handle| !self.tabs[i].scene.is_layer_locked(*handle))
|
||||
.collect();
|
||||
if handles.is_empty() {
|
||||
self.command_line
|
||||
@@ -469,7 +473,9 @@ impl OpenCADStudio {
|
||||
.selected_entities()
|
||||
.iter()
|
||||
.filter_map(|(h, e)| match e {
|
||||
acadrust::EntityType::Text(t) => {
|
||||
acadrust::EntityType::Text(t)
|
||||
if !self.tabs[i].scene.is_layer_locked(*h) =>
|
||||
{
|
||||
Some((*h, t.insertion_point.x, t.insertion_point.y))
|
||||
}
|
||||
_ => None,
|
||||
@@ -1127,7 +1133,12 @@ impl OpenCADStudio {
|
||||
|
||||
"EXPLODE" => {
|
||||
use crate::modules::draw::modify::explode::explode_entity;
|
||||
let entities: Vec<_> = self.tabs[i].scene.selected_entities().into_iter().collect();
|
||||
let entities: Vec<_> = self.tabs[i]
|
||||
.scene
|
||||
.selected_entities()
|
||||
.into_iter()
|
||||
.filter(|(handle, _)| !self.tabs[i].scene.is_layer_locked(*handle))
|
||||
.collect();
|
||||
if entities.is_empty() {
|
||||
use crate::modules::draw::select::SelectObjectsCommand;
|
||||
let cmd = SelectObjectsCommand::new("EXPLODE");
|
||||
|
||||
@@ -30,6 +30,7 @@ impl OpenCADStudio {
|
||||
.selected_entities()
|
||||
.iter()
|
||||
.map(|(h, _)| *h)
|
||||
.filter(|handle| !self.tabs[i].scene.is_layer_locked(*handle))
|
||||
.collect();
|
||||
let mut found = false;
|
||||
for sh in &selected_handles {
|
||||
@@ -389,8 +390,20 @@ impl OpenCADStudio {
|
||||
"SET" => {
|
||||
let app = parts.get(1).copied().unwrap_or("OpenCADStudio");
|
||||
let val = parts.get(2).copied().unwrap_or("");
|
||||
let editable: Vec<_> = selected_handles
|
||||
.iter()
|
||||
.copied()
|
||||
.filter(|handle| !self.tabs[i].scene.is_layer_locked(*handle))
|
||||
.collect();
|
||||
if editable.is_empty() {
|
||||
self.command_line.push_error(
|
||||
crate::t!("XDATA: selected entities are on locked layers.")
|
||||
.as_ref(),
|
||||
);
|
||||
return Some(Task::none());
|
||||
}
|
||||
self.push_undo_snapshot(i, "XDATA SET");
|
||||
for sh in &selected_handles {
|
||||
for sh in &editable {
|
||||
if let Some(entity) =
|
||||
self.tabs[i].scene.document.get_entity_mut(*sh)
|
||||
{
|
||||
@@ -402,13 +415,25 @@ impl OpenCADStudio {
|
||||
self.tabs[i].dirty = true;
|
||||
self.command_line.push_output(crate::tf!(
|
||||
"XDATA: set [{app}] = \"{val}\" on {} entity/entities.",
|
||||
selected_handles.len()
|
||||
editable.len()
|
||||
).as_ref());
|
||||
}
|
||||
"CLEAR" => {
|
||||
let app_filter = parts.get(1).copied();
|
||||
let editable: Vec<_> = selected_handles
|
||||
.iter()
|
||||
.copied()
|
||||
.filter(|handle| !self.tabs[i].scene.is_layer_locked(*handle))
|
||||
.collect();
|
||||
if editable.is_empty() {
|
||||
self.command_line.push_error(
|
||||
crate::t!("XDATA: selected entities are on locked layers.")
|
||||
.as_ref(),
|
||||
);
|
||||
return Some(Task::none());
|
||||
}
|
||||
self.push_undo_snapshot(i, "XDATA CLEAR");
|
||||
for sh in &selected_handles {
|
||||
for sh in &editable {
|
||||
if let Some(entity) =
|
||||
self.tabs[i].scene.document.get_entity_mut(*sh)
|
||||
{
|
||||
@@ -600,6 +625,7 @@ impl OpenCADStudio {
|
||||
.selected_entities()
|
||||
.iter()
|
||||
.map(|(h, _)| *h)
|
||||
.filter(|handle| !self.tabs[i].scene.is_layer_locked(*handle))
|
||||
.collect();
|
||||
if handles.is_empty() {
|
||||
self.command_line
|
||||
@@ -706,6 +732,7 @@ impl OpenCADStudio {
|
||||
.selected_entities()
|
||||
.iter()
|
||||
.map(|(h, _)| *h)
|
||||
.filter(|handle| !self.tabs[i].scene.is_layer_locked(*handle))
|
||||
.collect();
|
||||
if handles.is_empty() {
|
||||
self.command_line
|
||||
@@ -765,8 +792,13 @@ impl OpenCADStudio {
|
||||
self.command_line.push_info(crate::t!("Usage: HYPERLINK <url> (select objects first)").as_ref());
|
||||
return Some(Task::none());
|
||||
}
|
||||
let handles: Vec<acadrust::Handle> =
|
||||
self.tabs[i].scene.selected_entities().iter().map(|(h, _)| *h).collect();
|
||||
let handles: Vec<acadrust::Handle> = self.tabs[i]
|
||||
.scene
|
||||
.selected_entities()
|
||||
.iter()
|
||||
.map(|(h, _)| *h)
|
||||
.filter(|handle| !self.tabs[i].scene.is_layer_locked(*handle))
|
||||
.collect();
|
||||
if handles.is_empty() {
|
||||
self.command_line.push_error(crate::t!("HYPERLINK: select objects first.").as_ref());
|
||||
return Some(Task::none());
|
||||
@@ -816,6 +848,7 @@ impl OpenCADStudio {
|
||||
.selected_entities()
|
||||
.iter()
|
||||
.map(|(h, _)| *h)
|
||||
.filter(|handle| !self.tabs[i].scene.is_layer_locked(*handle))
|
||||
.collect();
|
||||
if handles.is_empty() {
|
||||
self.command_line
|
||||
@@ -938,6 +971,7 @@ impl OpenCADStudio {
|
||||
.selected_entities()
|
||||
.iter()
|
||||
.map(|(handle, _)| *handle)
|
||||
.filter(|handle| !self.tabs[i].scene.is_layer_locked(*handle))
|
||||
.collect();
|
||||
if handles.is_empty() {
|
||||
self.command_line
|
||||
|
||||
@@ -145,6 +145,9 @@ impl OpenCADStudio {
|
||||
let mut changed = 0usize;
|
||||
self.push_undo_snapshot(i, "ATTEDIT");
|
||||
for sh in &selected_handles {
|
||||
if self.tabs[i].scene.is_layer_locked(*sh) {
|
||||
continue;
|
||||
}
|
||||
if let Some(acadrust::EntityType::Insert(ins)) = self.tabs[i]
|
||||
.scene
|
||||
.document
|
||||
@@ -195,10 +198,22 @@ impl OpenCADStudio {
|
||||
let sub = cmd.split_whitespace().nth(1).unwrap_or("").to_uppercase();
|
||||
match sub.as_str() {
|
||||
"ON" | "OFF" | "NORMAL" => {
|
||||
let handles: Vec<_> = self.tabs[i]
|
||||
.scene
|
||||
.document
|
||||
.entities()
|
||||
.filter_map(|entity| {
|
||||
matches!(entity, acadrust::EntityType::AttributeDefinition(_))
|
||||
.then_some(entity.common().handle)
|
||||
})
|
||||
.filter(|handle| !self.tabs[i].scene.is_layer_locked(*handle))
|
||||
.collect();
|
||||
self.push_undo_snapshot(i, "ATTDISP");
|
||||
let mut count = 0usize;
|
||||
for entity in self.tabs[i].scene.document.entities_mut() {
|
||||
if let acadrust::EntityType::AttributeDefinition(ad) = entity {
|
||||
for handle in handles {
|
||||
if let Some(acadrust::EntityType::AttributeDefinition(ad)) =
|
||||
self.tabs[i].scene.document.get_entity_mut(handle)
|
||||
{
|
||||
match sub.as_str() {
|
||||
"ON" => {
|
||||
ad.flags.invisible = false;
|
||||
@@ -208,8 +223,6 @@ impl OpenCADStudio {
|
||||
ad.flags.invisible = true;
|
||||
count += 1;
|
||||
}
|
||||
"NORMAL" => { /* leave existing flags — they are already the "normal" state */
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
@@ -490,6 +503,7 @@ impl OpenCADStudio {
|
||||
.selected_entities()
|
||||
.into_iter()
|
||||
.map(|(h, _)| h)
|
||||
.filter(|handle| !self.tabs[i].scene.is_layer_locked(*handle))
|
||||
.collect();
|
||||
if handles.is_empty() {
|
||||
use crate::modules::draw::select::SelectObjectsCommand;
|
||||
@@ -730,6 +744,7 @@ impl OpenCADStudio {
|
||||
.selected_entities()
|
||||
.into_iter()
|
||||
.map(|(h, _)| h)
|
||||
.filter(|handle| !self.tabs[i].scene.is_layer_locked(*handle))
|
||||
.collect();
|
||||
if handles.is_empty() {
|
||||
use crate::modules::draw::select::SelectObjectsCommand;
|
||||
|
||||
@@ -16,8 +16,6 @@ impl OpenCADStudio {
|
||||
.entity_wires()
|
||||
.iter()
|
||||
.filter_map(|w| Scene::handle_from_wire_name(&w.name))
|
||||
// Objects on a locked layer aren't selectable.
|
||||
.filter(|&h| !self.tabs[i].scene.is_layer_locked(h))
|
||||
.collect();
|
||||
let count = handles.len();
|
||||
for h in handles {
|
||||
@@ -362,6 +360,9 @@ impl OpenCADStudio {
|
||||
|
||||
let handle_u64: u64 = cmd["BEDIT_BEGIN:".len()..].parse().unwrap_or(0);
|
||||
let insert_handle = Handle::new(handle_u64);
|
||||
if self.reject_locked_edit(i, insert_handle) {
|
||||
return Some(Task::none());
|
||||
}
|
||||
|
||||
let insert = match self.tabs[i].scene.document.get_entity(insert_handle) {
|
||||
Some(acadrust::EntityType::Insert(ins)) => ins.clone(),
|
||||
@@ -637,6 +638,9 @@ impl OpenCADStudio {
|
||||
|
||||
let handle_u64: u64 = cmd["REFEDIT_BEGIN:".len()..].parse().unwrap_or(0);
|
||||
let insert_handle = Handle::new(handle_u64);
|
||||
if self.reject_locked_edit(i, insert_handle) {
|
||||
return Some(Task::none());
|
||||
}
|
||||
|
||||
// Get INSERT entity.
|
||||
let insert = match self.tabs[i].scene.document.get_entity(insert_handle) {
|
||||
@@ -995,9 +999,13 @@ impl OpenCADStudio {
|
||||
.document
|
||||
.entities()
|
||||
.map(|e| e.common().handle)
|
||||
.filter(|handle| !self.tabs[i].scene.is_layer_locked(*handle))
|
||||
.collect()
|
||||
} else {
|
||||
sel.into_iter().map(|(h, _)| h).collect()
|
||||
sel.into_iter()
|
||||
.map(|(h, _)| h)
|
||||
.filter(|handle| !self.tabs[i].scene.is_layer_locked(*handle))
|
||||
.collect()
|
||||
}
|
||||
};
|
||||
if handles.is_empty() {
|
||||
@@ -1165,7 +1173,10 @@ impl OpenCADStudio {
|
||||
handles.clone()
|
||||
} else {
|
||||
handles.iter().copied().take(1).collect()
|
||||
};
|
||||
}
|
||||
.into_iter()
|
||||
.filter(|handle| !self.tabs[i].scene.is_layer_locked(*handle))
|
||||
.collect();
|
||||
if targets.is_empty() {
|
||||
self.command_line
|
||||
.push_output(crate::tf!("FIND: \"{}\" not found.", search).as_ref());
|
||||
|
||||
@@ -106,6 +106,7 @@ impl OpenCADStudio {
|
||||
}
|
||||
self.push_undo_snapshot(i, "LAYER LOCK");
|
||||
self.tabs[i].dirty = true;
|
||||
self.refresh_properties();
|
||||
self.command_line.push_output(crate::t!("LAYER: layers locked.").as_ref());
|
||||
}
|
||||
"UNLOCK" | "UL" => {
|
||||
@@ -116,6 +117,7 @@ impl OpenCADStudio {
|
||||
}
|
||||
self.push_undo_snapshot(i, "LAYER UNLOCK");
|
||||
self.tabs[i].dirty = true;
|
||||
self.refresh_properties();
|
||||
self.command_line.push_output(crate::t!("LAYER: layers unlocked.").as_ref());
|
||||
}
|
||||
"COLOR" | "C" => {
|
||||
|
||||
@@ -399,6 +399,7 @@ impl OpenCADStudio {
|
||||
self.tabs[i].dirty = true;
|
||||
self.commit_layer_undo(i, undo);
|
||||
self.refresh_layer_panel();
|
||||
self.refresh_properties();
|
||||
self.command_line.push_info(crate::t!("Layer(s) locked.").as_ref());
|
||||
}
|
||||
}
|
||||
@@ -508,6 +509,7 @@ impl OpenCADStudio {
|
||||
self.tabs[i].dirty = true;
|
||||
self.commit_layer_undo(i, undo);
|
||||
self.refresh_layer_panel();
|
||||
self.refresh_properties();
|
||||
self.command_line.push_info(crate::t!("Layer(s) unlocked.").as_ref());
|
||||
}
|
||||
}
|
||||
@@ -686,6 +688,7 @@ impl OpenCADStudio {
|
||||
.selected_entities()
|
||||
.into_iter()
|
||||
.map(|(h, _)| h)
|
||||
.filter(|handle| !self.tabs[i].scene.is_layer_locked(*handle))
|
||||
.collect();
|
||||
if handles.is_empty() {
|
||||
use crate::modules::draw::groups::ungroup::UngroupCommand;
|
||||
|
||||
@@ -607,6 +607,7 @@ impl OpenCADStudio {
|
||||
.selected_entities()
|
||||
.into_iter()
|
||||
.map(|(h, _)| h)
|
||||
.filter(|handle| !self.tabs[i].scene.is_layer_locked(*handle))
|
||||
.collect();
|
||||
if handles.is_empty() {
|
||||
self.command_line
|
||||
@@ -708,6 +709,7 @@ impl OpenCADStudio {
|
||||
.selected_entities()
|
||||
.into_iter()
|
||||
.map(|(h, _)| h)
|
||||
.filter(|handle| !self.tabs[i].scene.is_layer_locked(*handle))
|
||||
.collect();
|
||||
if handles.is_empty() {
|
||||
self.command_line
|
||||
@@ -756,7 +758,8 @@ impl OpenCADStudio {
|
||||
.document
|
||||
.entities()
|
||||
.filter(|e| {
|
||||
selected.is_empty() || selected.contains(&e.common().handle.value())
|
||||
(selected.is_empty() || selected.contains(&e.common().handle.value()))
|
||||
&& !self.tabs[i].scene.is_layer_locked(e.common().handle)
|
||||
})
|
||||
.map(|e| {
|
||||
let key = crate::entities::names::dxf_name(e).to_string();
|
||||
@@ -2284,6 +2287,7 @@ impl OpenCADStudio {
|
||||
.selected_entities()
|
||||
.iter()
|
||||
.map(|(h, _)| *h)
|
||||
.filter(|handle| !self.tabs[i].scene.is_layer_locked(*handle))
|
||||
.collect();
|
||||
if selected_handles.is_empty() {
|
||||
self.command_line
|
||||
|
||||
@@ -652,6 +652,16 @@ impl OpenCADStudio {
|
||||
}
|
||||
})
|
||||
.collect();
|
||||
if to_erase
|
||||
.iter()
|
||||
.any(|handle| self.tabs[i].scene.is_layer_locked(*handle))
|
||||
{
|
||||
self.command_line.push_error(
|
||||
crate::t!("VPORTS: unlock existing viewport layers first.")
|
||||
.as_ref(),
|
||||
);
|
||||
return Some(Task::none());
|
||||
}
|
||||
self.push_undo_snapshot(i, "VPORTS");
|
||||
self.tabs[i].scene.erase_entities(&to_erase);
|
||||
// Create new viewports.
|
||||
@@ -814,6 +824,7 @@ impl OpenCADStudio {
|
||||
.selected_entities()
|
||||
.iter()
|
||||
.map(|(h, _)| *h)
|
||||
.filter(|handle| !self.tabs[i].scene.is_layer_locked(*handle))
|
||||
.collect();
|
||||
if selected.is_empty() {
|
||||
self.command_line
|
||||
@@ -1004,6 +1015,7 @@ impl OpenCADStudio {
|
||||
.iter()
|
||||
.filter(|(_, e)| matches!(e, acadrust::EntityType::Viewport(_)))
|
||||
.map(|(h, _)| *h)
|
||||
.filter(|handle| !self.tabs[i].scene.is_layer_locked(*handle))
|
||||
.collect();
|
||||
if vps.len() < 2 {
|
||||
self.command_line.push_error(
|
||||
|
||||
Reference in new issue
Block a user