1use std::cell::RefCell;
27use std::rc::Rc;
28
29use crate::oxml::shape::Sp as OxmlSp;
30use crate::oxml::slidelayout::SldLayout as OxmlSldLayout;
31
32#[derive(Debug, Clone)]
37pub struct SlideLayoutRef {
38 #[allow(dead_code)]
40 pub(crate) idx: usize,
41 pub(crate) partname: String,
43 pub(crate) rid: String,
45 pub(crate) oxml: Rc<RefCell<OxmlSldLayout>>,
47 pub(crate) master_partname: String,
52}
53
54impl SlideLayoutRef {
55 pub fn partname(&self) -> &str {
57 &self.partname
58 }
59 pub fn rid(&self) -> &str {
61 &self.rid
62 }
63 pub fn name(&self) -> String {
65 self.oxml.borrow().name.clone()
66 }
67 pub fn set_name(&mut self, n: String) {
69 self.oxml.borrow_mut().name = n;
70 }
71 pub fn layout_type(&self) -> String {
73 self.oxml.borrow().type_.clone()
74 }
75 pub fn set_layout_type(&mut self, t: String) {
77 self.oxml.borrow_mut().type_ = t;
78 }
79
80 pub fn shapes(&self) -> Vec<OxmlSp> {
84 self.oxml.borrow().shapes.clone()
85 }
86
87 pub fn shapes_mut(&self) -> std::cell::RefMut<'_, Vec<OxmlSp>> {
89 std::cell::RefMut::map(self.oxml.borrow_mut(), |s| &mut s.shapes)
90 }
91
92 pub fn placeholders(&self) -> Vec<Placeholder> {
97 self.oxml
98 .borrow()
99 .shapes
100 .iter()
101 .filter(|s| s.is_placeholder)
102 .map(|s| Placeholder {
103 idx: s.ph_idx.unwrap_or(0),
104 ph_type: s.ph_type.clone().unwrap_or_else(|| "body".to_string()),
105 name: s.name.clone(),
106 })
107 .collect()
108 }
109
110 pub fn placeholder_indices(&self) -> Vec<usize> {
126 self.oxml
127 .borrow()
128 .shapes
129 .iter()
130 .enumerate()
131 .filter(|(_, s)| s.is_placeholder)
132 .map(|(i, _)| i)
133 .collect()
134 }
135
136 pub fn placeholder_index_by_ph_idx(&self, ph_idx: u32) -> Option<usize> {
141 self.oxml
142 .borrow()
143 .shapes
144 .iter()
145 .enumerate()
146 .find(|(_, s)| s.is_placeholder && s.ph_idx == Some(ph_idx))
147 .map(|(i, _)| i)
148 }
149}
150
151#[derive(Debug, Clone)]
156pub struct Placeholder {
157 pub idx: u32,
159 pub ph_type: String,
161 pub name: String,
163}
164
165impl Placeholder {
166 pub fn ph_type(&self) -> &str {
168 &self.ph_type
169 }
170 pub fn idx(&self) -> u32 {
172 self.idx
173 }
174 pub fn name(&self) -> &str {
176 &self.name
177 }
178}
179
180#[derive(Debug, Default, Clone)]
185pub struct SlideLayouts {
186 pub(crate) items: Vec<SlideLayoutRef>,
187}
188
189impl SlideLayouts {
190 pub fn new() -> Self {
192 SlideLayouts::default()
193 }
194 pub fn len(&self) -> usize {
196 self.items.len()
197 }
198 pub fn is_empty(&self) -> bool {
200 self.items.is_empty()
201 }
202 pub fn iter(&self) -> std::slice::Iter<'_, SlideLayoutRef> {
204 self.items.iter()
205 }
206 pub fn get(&self, idx: usize) -> Option<&SlideLayoutRef> {
208 self.items.get(idx)
209 }
210 pub fn get_mut(&mut self, idx: usize) -> Option<&mut SlideLayoutRef> {
212 self.items.get_mut(idx)
213 }
214
215 pub fn at(&self, idx: usize) -> Option<SlideLayout> {
219 self.items.get(idx).map(|r| SlideLayout {
220 name: r.oxml.borrow().name.clone(),
221 r#type: r.oxml.borrow().type_.clone(),
222 })
223 }
224
225 pub fn push(&mut self, layout: SlideLayoutRef) {
229 self.items.push(layout);
230 }
231
232 pub fn remove(&mut self, idx: usize) -> Option<SlideLayoutRef> {
245 if idx < self.items.len() {
246 Some(self.items.remove(idx))
247 } else {
248 None
249 }
250 }
251
252 pub fn index_of(&self, rid: &str) -> Option<usize> {
257 self.items.iter().position(|l| l.rid == rid)
258 }
259
260 pub fn get_by_name(&self, name: &str) -> Option<&SlideLayoutRef> {
265 self.items.iter().find(|l| l.oxml.borrow().name == name)
266 }
267
268 pub fn get_by_name_mut(&mut self, name: &str) -> Option<&mut SlideLayoutRef> {
270 self.items.iter_mut().find(|l| l.oxml.borrow().name == name)
271 }
272}
273
274#[derive(Debug, Clone)]
279pub struct SlideLayout {
280 pub name: String,
282 pub r#type: String,
285}
286
287#[cfg(test)]
288#[allow(clippy::field_reassign_with_default)]
289mod tests {
290 use super::*;
291 use std::cell::RefCell;
292 use std::rc::Rc;
293
294 fn make_layout(name: &str, rid: &str) -> SlideLayoutRef {
296 let oxml = OxmlSldLayout {
297 name: name.to_string(),
298 type_: "blank".to_string(),
299 shapes: Vec::new(),
300 };
301 SlideLayoutRef {
302 idx: 0,
303 partname: format!("/ppt/slideLayouts/{}.xml", name),
304 rid: rid.to_string(),
305 oxml: Rc::new(RefCell::new(oxml)),
306 master_partname: String::new(),
307 }
308 }
309
310 #[test]
312 fn layouts_remove_by_index() {
313 let mut layouts = SlideLayouts::new();
314 layouts.push(make_layout("Blank", "rId1"));
315 layouts.push(make_layout("Title", "rId2"));
316 layouts.push(make_layout("Content", "rId3"));
317 assert_eq!(layouts.len(), 3);
318
319 let removed = layouts.remove(1);
320 assert!(removed.is_some());
321 assert_eq!(removed.unwrap().rid(), "rId2");
322 assert_eq!(layouts.len(), 2);
323 assert_eq!(layouts.get(1).unwrap().rid(), "rId3");
324
325 assert!(layouts.remove(99).is_none());
327 }
328
329 #[test]
331 fn layouts_index_of_by_rid() {
332 let mut layouts = SlideLayouts::new();
333 layouts.push(make_layout("Blank", "rId1"));
334 layouts.push(make_layout("Title", "rId2"));
335
336 assert_eq!(layouts.index_of("rId1"), Some(0));
337 assert_eq!(layouts.index_of("rId2"), Some(1));
338 assert_eq!(layouts.index_of("rId999"), None);
339 }
340
341 #[test]
343 fn layouts_get_by_name() {
344 let mut layouts = SlideLayouts::new();
345 layouts.push(make_layout("Blank", "rId1"));
346 layouts.push(make_layout("Title Slide", "rId2"));
347
348 let found = layouts.get_by_name("Title Slide");
349 assert!(found.is_some());
350 assert_eq!(found.unwrap().rid(), "rId2");
351
352 assert!(layouts.get_by_name("Nonexistent").is_none());
353 }
354
355 #[test]
357 fn layout_placeholder_indices() {
358 let mut sp1 = OxmlSp::default();
359 sp1.is_placeholder = true;
360 sp1.ph_idx = Some(0);
361 sp1.ph_type = Some("title".into());
362
363 let mut sp2 = OxmlSp::default();
364 sp2.is_placeholder = false; let mut sp3 = OxmlSp::default();
367 sp3.is_placeholder = true;
368 sp3.ph_idx = Some(1);
369 sp3.ph_type = Some("body".into());
370
371 let oxml = OxmlSldLayout {
372 name: "Test".into(),
373 type_: "obj".into(),
374 shapes: vec![sp1, sp2, sp3],
375 };
376 let layout = SlideLayoutRef {
377 idx: 0,
378 partname: "/ppt/slideLayouts/slideLayout1.xml".into(),
379 rid: "rId1".into(),
380 oxml: Rc::new(RefCell::new(oxml)),
381 master_partname: String::new(),
382 };
383
384 let indices = layout.placeholder_indices();
385 assert_eq!(indices, vec![0, 2]);
386
387 assert_eq!(layout.placeholder_index_by_ph_idx(0), Some(0));
389 assert_eq!(layout.placeholder_index_by_ph_idx(1), Some(2));
390 assert_eq!(layout.placeholder_index_by_ph_idx(99), None);
391 }
392}