REFEDIT: Edit Reference dialog and unique names for xref objects

REFEDIT opens a dialog after the pick: the nesting chain as chips with a
preview of the chosen definition, all objects or picking them in the
drawing, attribute definitions (offered only when the definition has
them), locking objects outside the working set and unique names for an
external reference's named objects. The settings are remembered.

With unique names an xref's dependent layers, linetypes, text styles and
blocks are checked out as $N$NAME records of their own; without, under
the plain name, sharing a host record of that name. Save writes them back
under the reference's names, and the records made for the session are
removed when it closes. -REFEDIT uses the remembered settings.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
This commit is contained in:
ramox81andClaude Opus 5.5 committed 2026-10-14 10:39:28 +03:00
1 parent 592289f4fd
commit 92a3a8ee4d
33 files changed
+1061 -22

No files matched your search

+115 -3
View File
@@ -75,6 +75,79 @@ pub struct RefEditSession {
pub handle_watermark: u64,
/// The referenced file when the definition is an xref.
pub xref_path: Option<String>,
/// Objects outside the working set can't be selected while it is open.
pub lock: bool,
/// The external reference's named objects the working set uses under
/// host names, and the host records made for the session.
pub names: RefEditNames,
}
/// A kind of named object the working set's objects refer to.
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum SymbolKind {
Layer,
Linetype,
TextStyle,
Block,
}
/// How an external reference's dependent names (`XREF|NAME`) are used in
/// the host while its objects are checked out: as `$0$NAME` records of
/// their own (unique names), or under the plain name, sharing a host record
/// of that name when there is one.
#[derive(Debug, Clone, Default)]
pub struct RefEditNames {
/// (kind, name used in the host, the reference's dependent name).
pub renamed: Vec<(SymbolKind, String, String)>,
/// Records added to the host for the session, removed when it closes.
pub created: Vec<(SymbolKind, String)>,
}
impl RefEditNames {
/// The reference's own name for a host name used in the session.
pub fn original(&self, kind: SymbolKind, name: &str) -> Option<&str> {
self.renamed
.iter()
.find(|(k, used, _)| *k == kind && used.eq_ignore_ascii_case(name))
.map(|(_, _, original)| original.as_str())
}
}
/// Rewrites every named-object reference of an entity through `map`
/// (layer, linetype, text style, block).
pub fn rename_symbols(entity: &mut EntityType, map: &dyn Fn(SymbolKind, &str) -> Option<String>) {
let common = entity.common_mut();
if let Some(name) = map(SymbolKind::Layer, &common.layer) {
common.layer = name;
common.layer_handle = None;
}
if let Some(name) = map(SymbolKind::Linetype, &common.linetype) {
common.linetype = name;
common.linetype_handle = None;
}
match entity {
EntityType::Insert(insert) => {
if let Some(name) = map(SymbolKind::Block, &insert.block_name) {
insert.block_name = name;
}
}
EntityType::Text(text) => {
if let Some(name) = map(SymbolKind::TextStyle, &text.style) {
text.style = name;
}
}
EntityType::MText(text) => {
if let Some(name) = map(SymbolKind::TextStyle, &text.style) {
text.style = name;
}
}
EntityType::AttributeDefinition(attdef) => {
if let Some(name) = map(SymbolKind::TextStyle, &attdef.text_style) {
attdef.text_style = name;
}
}
_ => {}
}
}
impl RefEditSession {
@@ -315,10 +388,23 @@ pub struct RefEditOptionsCommand {
level: usize,
step: OptionStep,
nested: Vec<Handle>,
/// The session settings (lock objects outside the working set, unique
/// names for an external reference's named objects).
lock: bool,
unique: bool,
/// Set when the Edit Reference dialog already chose the attribute
/// option: the nested pick then opens the session directly.
attributes: Option<bool>,
}
impl RefEditOptionsCommand {
pub fn new(top: Handle, chain: Vec<ChainLevel>, hits: Vec<LevelHits>) -> Self {
pub fn new(
top: Handle,
chain: Vec<ChainLevel>,
hits: Vec<LevelHits>,
lock: bool,
unique: bool,
) -> Self {
let level = chain.len().saturating_sub(1);
Self {
top,
@@ -327,9 +413,30 @@ impl RefEditOptionsCommand {
level,
step: OptionStep::Nesting,
nested: Vec::new(),
lock,
unique,
attributes: None,
}
}
/// `Select nested objects:` for the dialog's "pick in the drawing"
/// working set at `level`.
pub fn nested_pick(
top: Handle,
chain: Vec<ChainLevel>,
hits: Vec<LevelHits>,
level: usize,
attributes: bool,
lock: bool,
unique: bool,
) -> Self {
let mut command = Self::new(top, chain, hits, lock, unique);
command.level = level.min(command.chain.len().saturating_sub(1));
command.step = OptionStep::Nested;
command.attributes = Some(attributes);
command
}
fn begin(&self, attributes: bool) -> CmdResult {
let path: Vec<String> = self.chain[1..=self.level]
.iter()
@@ -338,11 +445,13 @@ impl RefEditOptionsCommand {
let nested: Vec<String> = self.nested.iter().map(|h| h.value().to_string()).collect();
CmdResult::Relaunch(
format!(
"REFEDIT_BEGIN:{}:{}:{}:{}",
"REFEDIT_BEGIN:{}:{}:{}:{}:{}:{}",
self.top.value(),
path.join(","),
attributes as u8,
nested.join(",")
nested.join(","),
self.lock as u8,
self.unique as u8
),
vec![],
)
@@ -450,6 +559,9 @@ impl CadCommand for RefEditOptionsCommand {
if n == 0 {
return CmdResult::CancelWithMessage(t!("0 items selected").into_owned());
}
if let Some(attributes) = self.attributes {
return self.begin(attributes);
}
self.step = OptionStep::Attributes;
CmdResult::ReportMeasurement(if n == 1 {
t!("1 item selected").into_owned()