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}
48
49impl SlideLayoutRef {
50 pub fn partname(&self) -> &str {
52 &self.partname
53 }
54 pub fn rid(&self) -> &str {
56 &self.rid
57 }
58 pub fn name(&self) -> String {
60 self.oxml.borrow().name.clone()
61 }
62 pub fn set_name(&mut self, n: String) {
64 self.oxml.borrow_mut().name = n;
65 }
66 pub fn layout_type(&self) -> String {
68 self.oxml.borrow().type_.clone()
69 }
70 pub fn set_layout_type(&mut self, t: String) {
72 self.oxml.borrow_mut().type_ = t;
73 }
74
75 pub fn shapes(&self) -> Vec<OxmlSp> {
79 self.oxml.borrow().shapes.clone()
80 }
81
82 pub fn shapes_mut(&self) -> std::cell::RefMut<'_, Vec<OxmlSp>> {
84 std::cell::RefMut::map(self.oxml.borrow_mut(), |s| &mut s.shapes)
85 }
86
87 pub fn placeholders(&self) -> Vec<Placeholder> {
92 self.oxml
93 .borrow()
94 .shapes
95 .iter()
96 .filter(|s| s.is_placeholder)
97 .map(|s| Placeholder {
98 idx: s.ph_idx.unwrap_or(0),
99 ph_type: s.ph_type.clone().unwrap_or_else(|| "body".to_string()),
100 name: s.name.clone(),
101 })
102 .collect()
103 }
104
105 pub fn placeholder_indices(&self) -> Vec<usize> {
121 self.oxml
122 .borrow()
123 .shapes
124 .iter()
125 .enumerate()
126 .filter(|(_, s)| s.is_placeholder)
127 .map(|(i, _)| i)
128 .collect()
129 }
130
131 pub fn placeholder_index_by_ph_idx(&self, ph_idx: u32) -> Option<usize> {
136 self.oxml
137 .borrow()
138 .shapes
139 .iter()
140 .enumerate()
141 .find(|(_, s)| s.is_placeholder && s.ph_idx == Some(ph_idx))
142 .map(|(i, _)| i)
143 }
144}
145
146#[derive(Debug, Clone)]
151pub struct Placeholder {
152 pub idx: u32,
154 pub ph_type: String,
156 pub name: String,
158}
159
160impl Placeholder {
161 pub fn ph_type(&self) -> &str {
163 &self.ph_type
164 }
165 pub fn idx(&self) -> u32 {
167 self.idx
168 }
169 pub fn name(&self) -> &str {
171 &self.name
172 }
173}
174
175#[derive(Debug, Default, Clone)]
180pub struct SlideLayouts {
181 pub(crate) items: Vec<SlideLayoutRef>,
182}
183
184impl SlideLayouts {
185 pub fn new() -> Self {
187 SlideLayouts::default()
188 }
189 pub fn len(&self) -> usize {
191 self.items.len()
192 }
193 pub fn is_empty(&self) -> bool {
195 self.items.is_empty()
196 }
197 pub fn iter(&self) -> std::slice::Iter<'_, SlideLayoutRef> {
199 self.items.iter()
200 }
201 pub fn get(&self, idx: usize) -> Option<&SlideLayoutRef> {
203 self.items.get(idx)
204 }
205 pub fn get_mut(&mut self, idx: usize) -> Option<&mut SlideLayoutRef> {
207 self.items.get_mut(idx)
208 }
209
210 pub fn at(&self, idx: usize) -> Option<SlideLayout> {
214 self.items.get(idx).map(|r| SlideLayout {
215 name: r.oxml.borrow().name.clone(),
216 r#type: r.oxml.borrow().type_.clone(),
217 })
218 }
219
220 pub fn push(&mut self, layout: SlideLayoutRef) {
224 self.items.push(layout);
225 }
226
227 pub fn remove(&mut self, idx: usize) -> Option<SlideLayoutRef> {
240 if idx < self.items.len() {
241 Some(self.items.remove(idx))
242 } else {
243 None
244 }
245 }
246
247 pub fn index_of(&self, rid: &str) -> Option<usize> {
252 self.items.iter().position(|l| l.rid == rid)
253 }
254
255 pub fn get_by_name(&self, name: &str) -> Option<&SlideLayoutRef> {
260 self.items.iter().find(|l| l.oxml.borrow().name == name)
261 }
262
263 pub fn get_by_name_mut(&mut self, name: &str) -> Option<&mut SlideLayoutRef> {
265 self.items.iter_mut().find(|l| l.oxml.borrow().name == name)
266 }
267}
268
269#[derive(Debug, Clone)]
274pub struct SlideLayout {
275 pub name: String,
277 pub r#type: String,
280}
281
282#[cfg(test)]
283#[allow(clippy::field_reassign_with_default)]
284mod tests {
285 use super::*;
286 use std::cell::RefCell;
287 use std::rc::Rc;
288
289 fn make_layout(name: &str, rid: &str) -> SlideLayoutRef {
291 let oxml = OxmlSldLayout {
292 name: name.to_string(),
293 type_: "blank".to_string(),
294 shapes: Vec::new(),
295 };
296 SlideLayoutRef {
297 idx: 0,
298 partname: format!("/ppt/slideLayouts/{}.xml", name),
299 rid: rid.to_string(),
300 oxml: Rc::new(RefCell::new(oxml)),
301 }
302 }
303
304 #[test]
306 fn layouts_remove_by_index() {
307 let mut layouts = SlideLayouts::new();
308 layouts.push(make_layout("Blank", "rId1"));
309 layouts.push(make_layout("Title", "rId2"));
310 layouts.push(make_layout("Content", "rId3"));
311 assert_eq!(layouts.len(), 3);
312
313 let removed = layouts.remove(1);
314 assert!(removed.is_some());
315 assert_eq!(removed.unwrap().rid(), "rId2");
316 assert_eq!(layouts.len(), 2);
317 assert_eq!(layouts.get(1).unwrap().rid(), "rId3");
318
319 assert!(layouts.remove(99).is_none());
321 }
322
323 #[test]
325 fn layouts_index_of_by_rid() {
326 let mut layouts = SlideLayouts::new();
327 layouts.push(make_layout("Blank", "rId1"));
328 layouts.push(make_layout("Title", "rId2"));
329
330 assert_eq!(layouts.index_of("rId1"), Some(0));
331 assert_eq!(layouts.index_of("rId2"), Some(1));
332 assert_eq!(layouts.index_of("rId999"), None);
333 }
334
335 #[test]
337 fn layouts_get_by_name() {
338 let mut layouts = SlideLayouts::new();
339 layouts.push(make_layout("Blank", "rId1"));
340 layouts.push(make_layout("Title Slide", "rId2"));
341
342 let found = layouts.get_by_name("Title Slide");
343 assert!(found.is_some());
344 assert_eq!(found.unwrap().rid(), "rId2");
345
346 assert!(layouts.get_by_name("Nonexistent").is_none());
347 }
348
349 #[test]
351 fn layout_placeholder_indices() {
352 let mut sp1 = OxmlSp::default();
353 sp1.is_placeholder = true;
354 sp1.ph_idx = Some(0);
355 sp1.ph_type = Some("title".into());
356
357 let mut sp2 = OxmlSp::default();
358 sp2.is_placeholder = false; let mut sp3 = OxmlSp::default();
361 sp3.is_placeholder = true;
362 sp3.ph_idx = Some(1);
363 sp3.ph_type = Some("body".into());
364
365 let oxml = OxmlSldLayout {
366 name: "Test".into(),
367 type_: "obj".into(),
368 shapes: vec![sp1, sp2, sp3],
369 };
370 let layout = SlideLayoutRef {
371 idx: 0,
372 partname: "/ppt/slideLayouts/slideLayout1.xml".into(),
373 rid: "rId1".into(),
374 oxml: Rc::new(RefCell::new(oxml)),
375 };
376
377 let indices = layout.placeholder_indices();
378 assert_eq!(indices, vec![0, 2]);
379
380 assert_eq!(layout.placeholder_index_by_ph_idx(0), Some(0));
382 assert_eq!(layout.placeholder_index_by_ph_idx(1), Some(2));
383 assert_eq!(layout.placeholder_index_by_ph_idx(99), None);
384 }
385}