1use blockworx_doc::{
11 document::Document,
12 geometry::GridVec,
13 id::{BlockId, PinId},
14};
15use blockworx_geom::Pos2;
16use blockworx_store::doc::DocumentNonce;
17
18use crate::grid::grid_point;
19use crate::{
20 edit::{
21 clipboard::{Clipboard, Paste, PasteTarget, PinPaste, Snapshot},
22 delete::Target,
23 geometry::Shape,
24 },
25 shape::ShapeId,
26 widget::drawing::Drawing,
27};
28
29const UNTARGETED_OFFSET: GridVec = GridVec::new(2, 2);
32
33pub fn is_object_clipboard(text: &str) -> bool {
36 Clipboard::from_json(text).is_some()
37}
38
39fn copy_target(id: ShapeId) -> Option<Target> {
42 Some(match id {
43 ShapeId::Rect(id) => Target::Block(id),
44 ShapeId::Port(id) => Target::Pin(id),
45 ShapeId::Text(id) => Target::Text(id),
46 ShapeId::Area(id) => Target::Area(id),
47 ShapeId::Image(id) => Target::Image(id),
48 ShapeId::Icon(_) => return None,
49 })
50}
51
52fn pasted_shape(shape: Shape) -> ShapeId {
54 match shape {
55 Shape::Block(id) => ShapeId::Rect(id),
56 Shape::Port(id) => ShapeId::Port(id),
57 Shape::Text(id) => ShapeId::Text(id),
58 Shape::Area(id) => ShapeId::Area(id),
59 Shape::Image(id) => ShapeId::Image(id),
60 Shape::Icon(id) => ShapeId::Icon(id),
61 }
62}
63
64impl Drawing<'_> {
65 pub fn copy_selection(&self, shapes: &[ShapeId]) -> Option<Clipboard> {
69 let targets: Vec<Target> = shapes.iter().filter_map(|&id| copy_target(id)).collect();
70 let clip = crate::edit::clipboard::copy(&self.indexed(), &targets);
71 (!clip.snapshot().is_empty()).then_some(clip)
72 }
73
74 pub fn copy_pins(&self, pins: &[PinId]) -> Option<Clipboard> {
77 let targets: Vec<Target> = pins.iter().copied().map(Target::Pin).collect();
78 let clip = crate::edit::clipboard::copy(&self.indexed(), &targets);
79 (!clip.snapshot().pins.is_empty()).then(|| clip.into_pins())
80 }
81
82 pub fn cut_selection(&mut self, shapes: &[ShapeId], from: DocumentNonce) -> Option<Clipboard> {
89 let targets: Vec<Target> = shapes.iter().filter_map(|&id| copy_target(id)).collect();
90 let mut clip = None;
91 self.author("cut_selection", |indexed, sink| {
92 clip = Some(crate::edit::clipboard::cut(indexed, &targets, from, sink));
93 });
94 clip.filter(|clip| !clip.snapshot().is_empty())
95 }
96
97 pub fn cut_pins(&mut self, pins: &[PinId], from: DocumentNonce) -> Option<Clipboard> {
99 let targets: Vec<Target> = pins.iter().copied().map(Target::Pin).collect();
100 let mut clip = None;
101 self.author("cut_pins", |indexed, sink| {
102 clip = Some(crate::edit::clipboard::cut(indexed, &targets, from, sink));
103 });
104 clip.filter(|clip| !clip.snapshot().pins.is_empty())
105 .map(Clipboard::into_pins)
106 }
107
108 pub fn paste_snapshot(
119 &mut self,
120 clip: &Clipboard,
121 into: DocumentNonce,
122 target_top_left: Option<Pos2>,
123 ) -> Vec<ShapeId> {
124 let snapshot = clip.snapshot();
125 let offset = match (target_top_left, snapshot.origin()) {
126 (Some(target), Some(origin)) => {
127 let target = grid_point(target);
128 GridVec::new(target.x - origin.x, target.y - origin.y)
129 }
130 _ => UNTARGETED_OFFSET,
131 };
132 let scope = self.current_scope();
133 let mut ids = self.ids();
134 let mut roots = Vec::new();
135 self.author("paste", |indexed, sink| {
136 roots = crate::edit::clipboard::paste(
137 indexed,
138 Paste {
139 clip,
140 into,
141 target: PasteTarget { scope, offset },
142 },
143 &mut ids,
144 sink,
145 );
146 });
147 roots.into_iter().map(pasted_shape).collect()
148 }
149
150 pub fn embed(&mut self, source: &Document, title: &str, at: Pos2) -> BlockId {
154 let scope = self.current_scope();
155 let mut ids = self.ids();
156 let mut embedded = BlockId::NULL;
157 self.author("embed", |indexed, sink| {
158 embedded = crate::edit::embed::embed(
159 indexed,
160 crate::edit::embed::Embed {
161 source,
162 title,
163 into: scope,
164 at,
165 },
166 &mut ids,
167 sink,
168 );
169 });
170 embedded
171 }
172
173 pub fn paste_pins(&mut self, snapshot: &Snapshot, owner: crate::path::Scope) -> Vec<PinId> {
176 let mut ids = self.ids();
177 let mut pasted = Vec::new();
178 self.author("paste_pins", |indexed, sink| {
179 pasted = crate::edit::clipboard::paste_pins(
180 indexed,
181 PinPaste {
182 pins: &snapshot.pins,
183 owner,
184 },
185 &mut ids,
186 sink,
187 );
188 });
189 pasted
190 }
191}