diff --git a/src/app/command_driver/clipboard.rs b/src/app/command_driver/clipboard.rs index 1b841209f..64827cad8 100644 --- a/src/app/command_driver/clipboard.rs +++ b/src/app/command_driver/clipboard.rs @@ -301,10 +301,13 @@ impl OpenCADStudio { return; } let blocks = self.clipboard_deps.blocks.clone(); + let document = &mut self.tabs[i].scene.document; + merge_app_ids(document, &self.clipboard_deps); + let apps = eed_app_map(document, &self.clipboard_deps); // Single shared copy — see `merge_block_defs`. One batched geometry // rebuild instead of one per definition; same end state as the // per-definition bumps `Scene::define_block_raw` used to run. - if merge_block_defs(&mut self.tabs[i].scene.document, &blocks) > 0 { + if merge_block_defs(document, &blocks, &apps) > 0 { self.tabs[i].scene.bump_geometry(); } } @@ -515,6 +518,51 @@ pub(crate) fn merge_table_records(doc: &mut codec::CadDocument, deps: &crate::ap let _ = doc.dim_styles.add(r); } } + merge_app_ids(doc, deps); +} + +/// Register the copied XDATA's applications this drawing lacks. +pub(crate) fn merge_app_ids(doc: &mut codec::CadDocument, deps: &crate::app::ClipboardDeps) { + for rec in &deps.app_ids { + if !doc.app_ids.contains(&rec.name) { + let mut r = rec.clone(); + r.handle = doc.allocate_handle(); + let _ = doc.app_ids.add(r); + } + } +} + +/// Source APPID handle → this drawing's APPID of the same name, for the +/// copied XDATA. Run after [`merge_app_ids`]. +pub(crate) fn eed_app_map( + doc: &codec::CadDocument, + deps: &crate::app::ClipboardDeps, +) -> rustc_hash::FxHashMap { + deps.app_ids + .iter() + .filter_map(|a| Some((a.handle.value(), doc.app_ids.get(&a.name)?.handle.value()))) + .collect() +} + +/// Re-point a copied entity's verbatim EED at this drawing's APPIDs. A block +/// whose application the map does not know names no APPID here, and a DWG +/// carrying one is rejected outright by the reference program, so it goes. +pub(crate) fn remap_eed_apps(entity: &mut codec::EntityType, apps: &rustc_hash::FxHashMap) { + let remap = |xdata: &mut codec::xdata::ExtendedData| { + xdata.raw_dwg_eed.retain_mut(|(app, _)| match apps.get(app) { + Some(&target) => { + *app = target; + true + } + None => false, + }); + }; + remap(&mut entity.common_mut().extended_data); + if let codec::EntityType::Insert(insert) = entity { + for attribute in &mut insert.attributes { + remap(&mut attribute.common.extended_data); + } + } } /// Recreate block definitions the pasted INSERTs reference but the target @@ -524,11 +572,16 @@ pub(crate) fn merge_table_records(doc: &mut codec::CadDocument, deps: &crate::ap pub(crate) fn merge_block_defs( doc: &mut codec::CadDocument, blocks: &[crate::app::BlockDef], + apps: &rustc_hash::FxHashMap, ) -> usize { let mut created = Vec::new(); for def in blocks { let before = doc.block_records.len(); - define_block_raw_doc(doc, &def.name, def.base_point, &def.entities); + let mut entities = def.entities.clone(); + for entity in &mut entities { + remap_eed_apps(entity, apps); + } + define_block_raw_doc(doc, &def.name, def.base_point, &entities); if doc.block_records.len() > before { created.push(def); } @@ -605,12 +658,14 @@ pub(crate) fn paste_entities_kernel( // missing records with fresh handles from the target document. merge_table_records(doc, deps); // Shared block-definition merge (also used by `merge_clipboard_blocks`). - merge_block_defs(doc, &deps.blocks); + let apps = eed_app_map(doc, deps); + merge_block_defs(doc, &deps.blocks, &apps); let by_index: Vec = entities .iter() .cloned() .map(|mut entity| { + remap_eed_apps(&mut entity, &apps); let picture_placement = crate::scene::render_graph::dim_picture_placement(&entity); if let Some(t) = translate { crate::scene::view::dispatch::apply_transform(&mut entity, t); diff --git a/src/app/commands/refedit.rs b/src/app/commands/refedit.rs index 9011a16b7..72e94addb 100644 --- a/src/app/commands/refedit.rs +++ b/src/app/commands/refedit.rs @@ -920,9 +920,13 @@ impl OpenCADStudio { }) }) .collect(); - crate::app::command_driver::clipboard::merge_block_defs(document, &blocks); + use crate::app::command_driver::clipboard::{eed_app_map, merge_app_ids, remap_eed_apps}; + merge_app_ids(document, &deps); + let apps = eed_app_map(document, &deps); + crate::app::command_driver::clipboard::merge_block_defs(document, &blocks, &apps); for entity in &mut copies { rename_symbols(entity, &rename); + remap_eed_apps(entity, &apps); } (copies, names) } diff --git a/src/app/control/sheets.rs b/src/app/control/sheets.rs index 358b60d1e..3045149ac 100644 --- a/src/app/control/sheets.rs +++ b/src/app/control/sheets.rs @@ -296,13 +296,21 @@ impl OpenCADStudio { // Copy the referenced layer definitions out of the source before the // target borrow starts (the two tabs never borrow together). let mut missing_layers: Vec = Vec::new(); + // The APPIDs their XDATA names, so it is re-pointed at the target's. + let deps = crate::app::ClipboardDeps { + app_ids: crate::app::ClipboardDeps::capture( + &self.tabs[source_index].scene.document, + &cloned, + ) + .app_ids, + ..Default::default() + }; { let source_document = &self.tabs[source_index].scene.document; for entity in &cloned { let layer = entity.common().layer.clone(); if layer.is_empty() || layer == "0" - || source_document.layers.contains(&layer) || missing_layers.iter().any(|l| l.name == layer) { continue; @@ -317,7 +325,6 @@ impl OpenCADStudio { let mut created = Vec::new(); { let scene = &mut self.tabs[target_index].scene; - crate::io::linetypes::populate_document(&mut scene.document); for layer_record in &missing_layers { if !scene.document.layers.contains(&layer_record.name) { let mut layer_record = layer_record.clone(); @@ -325,7 +332,13 @@ impl OpenCADStudio { let _ = scene.document.layers.add(layer_record); } } - for entity in cloned { + use crate::app::command_driver::clipboard::{eed_app_map, merge_app_ids, remap_eed_apps}; + merge_app_ids(&mut scene.document, &deps); + let apps = eed_app_map(&scene.document, &deps); + for mut entity in cloned { + remap_eed_apps(&mut entity, &apps); + let linetype = entity.common().linetype.clone(); + crate::io::linetypes::ensure_catalog_linetype(&mut scene.document, &linetype); created.push(format!("{:X}", scene.add_entity_clone(entity).value())); } } diff --git a/src/app/mod.rs b/src/app/mod.rs index 4a6b1550b..c58598e43 100644 --- a/src/app/mod.rs +++ b/src/app/mod.rs @@ -1908,6 +1908,10 @@ pub struct ClipboardDeps { /// the in-drawing COPY path preserves it. Partly-selected groups are not /// captured (copying one member should not spawn a fragment). See #440. pub groups: Vec, + /// The APPID records the copied XDATA names. EED refers to its + /// application by APPID handle, so a paste into another drawing + /// re-points it at that drawing's own record of the same name. + pub app_ids: Vec, } /// A captured block definition: its base point and the entities it owns @@ -2065,6 +2069,33 @@ impl ClipboardDeps { }) .collect(); + // APPIDs named by the copied XDATA, verbatim EED (by handle) and + // structured records (by name) alike, block members and attributes + // included. + let mut app_handles = rustc_hash::FxHashSet::default(); + let mut app_names = rustc_hash::FxHashSet::default(); + let mut note_apps = |xdata: &codec::xdata::ExtendedData| { + app_handles.extend(xdata.raw_dwg_eed.iter().map(|(app, _)| *app)); + app_names.extend(xdata.records().iter().map(|r| r.application_name.clone())); + }; + for e in entities + .iter() + .chain(blocks.iter().flat_map(|d| d.entities.iter())) + { + note_apps(&e.common().extended_data); + if let EntityType::Insert(insert) = e { + for attribute in &insert.attributes { + note_apps(&attribute.common.extended_data); + } + } + } + let app_ids = doc + .app_ids + .iter() + .filter(|a| app_handles.contains(&a.handle.value()) || app_names.contains(&a.name)) + .cloned() + .collect(); + ClipboardDeps { layers: layers .iter() @@ -2086,6 +2117,7 @@ impl ClipboardDeps { dim_blocks, ext_objects, groups, + app_ids, } } diff --git a/src/app/update/blocks_palette.rs b/src/app/update/blocks_palette.rs index 9d8be52f6..0ad1312a8 100644 --- a/src/app/update/blocks_palette.rs +++ b/src/app/update/blocks_palette.rs @@ -424,17 +424,24 @@ impl OpenCADStudio { let i = self.active_tab; self.push_undo_snapshot(i, "INSERT"); self.merge_dependencies(i, &deps); + let apps = crate::app::command_driver::clipboard::eed_app_map( + &self.tabs[i].scene.document, + &deps, + ); let mut created = Vec::new(); for def in &deps.blocks { let scene = &mut self.tabs[i].scene; + let mut entities = def.entities.clone(); + for entity in &mut entities { + crate::app::command_driver::clipboard::remap_eed_apps(entity, &apps); + } if redefine && def.name.eq_ignore_ascii_case(name) { - let entities = def.entities.clone(); if let Err(err) = scene.redefine_block_raw(&def.name, def.base_point, entities) { self.command_line.push_error(&err); return Err(err); } } else if scene.document.block_records.get(&def.name).is_none() { - scene.define_block_raw(&def.name, def.base_point, def.entities.clone()); + scene.define_block_raw(&def.name, def.base_point, entities); created.push(def); } }