fix(clipboard): preserve pasted content

Reset cross-document ownership, include non-wire and locked objects, and rebuild image caches for embedded OLE content.

Refs #934
This commit is contained in:
Hakan Seven committed 2026-08-29 22:40:59 +03:00
1 parent 4fc8446df7
commit 217f20d8bc
5 files changed
+71 -26

No files matched your search

-3
View File
@@ -72,7 +72,6 @@ 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;
@@ -108,7 +107,6 @@ 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;
@@ -135,7 +133,6 @@ 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]);
+1 -11
View File
@@ -10,17 +10,7 @@ impl OpenCADStudio {
// ── Selection utilities ───────────────────────────────────────
"SELECTALL" => {
use crate::scene::Scene;
let handles: Vec<acadrust::Handle> = self.tabs[i]
.scene
.entity_wires()
.iter()
.filter_map(|w| Scene::handle_from_wire_name(&w.name))
.collect();
let count = handles.len();
for h in handles {
self.tabs[i].scene.select_entity(h, false);
}
let count = self.tabs[i].scene.select_all_visible();
self.command_line
.push_output(crate::tf!("SELECTALL: {} object(s) selected.", count).as_ref());
self.refresh_properties();
+6 -8
View File
@@ -2123,16 +2123,14 @@ impl Scene {
.collect();
}
/// Tessellate all `Solid3D` entities in the current document into
/// GPU-ready `MeshModel`s and store them in `self.meshes`.
///
/// Called after loading a document or after undo/redo so that every
/// `Solid3D` entity is represented in the mesh cache.
/// ImageModel for an edited image-bearing entity (RasterImage, or a PDF
/// UNDERLAY resolved through its definition object). `None` for others.
fn image_seed_for(&self, entity: &acadrust::entities::EntityType) -> Option<ImageModel> {
/// ImageModel for an image-bearing entity. `None` when it cannot decode.
pub(super) fn image_seed_for(
&self,
entity: &acadrust::entities::EntityType,
) -> Option<ImageModel> {
match entity {
EntityType::RasterImage(img) => ImageModel::from_raster_image(img),
EntityType::Ole2Frame(ole) => ImageModel::from_ole2frame(ole),
EntityType::Underlay(u) => match self.document.objects.get(&u.definition_handle) {
Some(acadrust::objects::ObjectType::UnderlayDefinition(def)) => {
ImageModel::from_underlay(u, def)
+31 -4
View File
@@ -245,6 +245,24 @@ impl Scene {
self.hatches.insert(h, model);
}
}
let image_bearing = self.document.get_entity(h).is_some_and(|entity| {
matches!(
entity,
EntityType::RasterImage(_)
| EntityType::Ole2Frame(_)
| EntityType::Underlay(_)
)
});
if image_bearing {
let model = self
.document
.get_entity(h)
.and_then(|entity| self.image_seed_for(entity));
self.images.remove(&h);
if let Some(model) = model {
self.images.insert(h, model);
}
}
}
if preserve_text_orientation {
for (h, o) in text_orient_backup {
@@ -458,12 +476,21 @@ impl Scene {
}
}
/// Add a freshly-cloned entity, allocating a new handle for it *and* every
/// inline sub-entity so the copy never shares a handle with its source.
/// Use this (not `add_entity`) whenever inserting a duplicate. (#129)
/// Add a clone to the active space with fresh top-level and inline handles.
/// Source owner handles are document-local and must not cross drawings.
/// (#129, #934)
pub fn add_entity_clone(&mut self, mut entity: EntityType) -> Handle {
Self::reset_clone_subhandles(&mut self.document, &mut entity);
entity.common_mut().handle = Handle::NULL;
let common = entity.common_mut();
common.handle = Handle::NULL;
common.owner_handle = Handle::NULL;
common.entity_mode = Some(
if self.current_layout != "Model" && self.active_viewport.is_none() {
1
} else {
2
},
);
self.add_entity(entity)
}
+33
View File
@@ -69,6 +69,39 @@ impl Scene {
self.bump_selection_set();
}
pub fn select_all_visible(&mut self) -> usize {
let block = self.interaction_block_handle();
let frozen: Option<HashSet<Handle>> = self
.interaction_viewport_frozen_layers()
.map(|layers| layers.iter().copied().collect());
let annotation_scale = self.displayed_annotation_scale_handle();
let all_visible = self.annotation_all_visible();
let handles = self
.document
.block_records
.iter()
.find(|record| record.handle == block)
.map(|record| record.entity_handles.clone())
.unwrap_or_default();
let selected = handles
.into_iter()
.filter(|handle| self.passes_selection_filter(*handle))
.filter(|handle| {
self.document.get_entity(*handle).is_some_and(|entity| {
self.resident_entity_visible(
entity,
block,
frozen.as_ref(),
annotation_scale,
all_visible,
)
})
})
.collect();
self.replace_selection(selected);
self.selected.len()
}
pub(crate) fn selection_fingerprint(&mut self) -> u64 {
if self.selection_fingerprint_dirty {
let mut fingerprint = self.selected.len() as u64;