use crate::oxml::shape::{Graphic as OxmlGraphic, GraphicFrame as OxmlFrame, SmartArtRef};
use crate::shape::base::Shape;
use crate::units::Emu;
#[derive(Clone, Debug)]
pub struct SmartArtShape {
pub(crate) frame: OxmlFrame,
}
impl SmartArtShape {
pub fn new() -> Self {
let smart = SmartArtRef::from_rids("", "", "", "");
let frame = OxmlFrame {
graphic: OxmlGraphic::SmartArt(smart),
..Default::default()
};
SmartArtShape { frame }
}
pub fn from_rids(dm_rid: &str, lo_rid: &str, qs_rid: &str, cs_rid: &str) -> Self {
let smart = SmartArtRef::from_rids(dm_rid, lo_rid, qs_rid, cs_rid);
let frame = OxmlFrame {
graphic: OxmlGraphic::SmartArt(smart),
..Default::default()
};
SmartArtShape { frame }
}
pub fn from_frame(frame: OxmlFrame) -> Self {
SmartArtShape { frame }
}
pub fn smart_art(&self) -> Option<&SmartArtRef> {
match &self.frame.graphic {
OxmlGraphic::SmartArt(s) => Some(s),
_ => None,
}
}
pub fn smart_art_mut(&mut self) -> Option<&mut SmartArtRef> {
match &mut self.frame.graphic {
OxmlGraphic::SmartArt(s) => Some(s),
_ => None,
}
}
pub fn dm_rid(&self) -> &str {
self.smart_art()
.and_then(|s| s.dm_rid.as_deref())
.unwrap_or("")
}
pub fn lo_rid(&self) -> &str {
self.smart_art()
.and_then(|s| s.lo_rid.as_deref())
.unwrap_or("")
}
pub fn qs_rid(&self) -> &str {
self.smart_art()
.and_then(|s| s.qs_rid.as_deref())
.unwrap_or("")
}
pub fn cs_rid(&self) -> &str {
self.smart_art()
.and_then(|s| s.cs_rid.as_deref())
.unwrap_or("")
}
pub fn set_dm_rid(&mut self, rid: impl Into<String>) {
if let Some(s) = self.smart_art_mut() {
s.dm_rid = Some(rid.into());
rebuild_raw_xml(s);
}
}
pub fn set_lo_rid(&mut self, rid: impl Into<String>) {
if let Some(s) = self.smart_art_mut() {
s.lo_rid = Some(rid.into());
rebuild_raw_xml(s);
}
}
pub fn set_qs_rid(&mut self, rid: impl Into<String>) {
if let Some(s) = self.smart_art_mut() {
s.qs_rid = Some(rid.into());
rebuild_raw_xml(s);
}
}
pub fn set_cs_rid(&mut self, rid: impl Into<String>) {
if let Some(s) = self.smart_art_mut() {
s.cs_rid = Some(rid.into());
rebuild_raw_xml(s);
}
}
pub fn set_all_rids(
&mut self,
dm_rid: impl Into<String>,
lo_rid: impl Into<String>,
qs_rid: impl Into<String>,
cs_rid: impl Into<String>,
) {
if let Some(s) = self.smart_art_mut() {
s.dm_rid = Some(dm_rid.into());
s.lo_rid = Some(lo_rid.into());
s.qs_rid = Some(qs_rid.into());
s.cs_rid = Some(cs_rid.into());
rebuild_raw_xml(s);
}
}
pub fn set_placeholder(&mut self, ph_idx: u32, ph_type: Option<&str>) {
self.frame.is_placeholder = true;
self.frame.ph_idx = Some(ph_idx);
self.frame.ph_type = ph_type.map(|s| s.to_string());
}
pub fn clear_placeholder(&mut self) {
self.frame.is_placeholder = false;
self.frame.ph_idx = None;
self.frame.ph_type = None;
}
pub fn is_placeholder(&self) -> bool {
self.frame.is_placeholder
}
pub fn ph_idx(&self) -> Option<u32> {
self.frame.ph_idx
}
pub fn ph_type(&self) -> Option<&str> {
self.frame.ph_type.as_deref()
}
}
impl Default for SmartArtShape {
fn default() -> Self {
Self::new()
}
}
fn rebuild_raw_xml(s: &mut SmartArtRef) {
let dm = s.dm_rid.as_deref().unwrap_or("");
let lo = s.lo_rid.as_deref().unwrap_or("");
let qs = s.qs_rid.as_deref().unwrap_or("");
let cs = s.cs_rid.as_deref().unwrap_or("");
s.raw_xml = SmartArtRef::from_rids(dm, lo, qs, cs).raw_xml;
}
impl Shape for SmartArtShape {
fn id(&self) -> u32 {
self.frame.id
}
fn set_id(&mut self, id: u32) {
self.frame.id = id;
}
fn name(&self) -> &str {
&self.frame.name
}
fn set_name(&mut self, name: String) {
self.frame.name = name;
}
fn shape_type(&self) -> &'static str {
"smart_art"
}
fn left(&self) -> Emu {
self.frame.properties.xfrm.off_x.unwrap_or_default()
}
fn set_left(&mut self, emu: Emu) {
self.frame.properties.xfrm.off_x = Some(emu);
}
fn top(&self) -> Emu {
self.frame.properties.xfrm.off_y.unwrap_or_default()
}
fn set_top(&mut self, emu: Emu) {
self.frame.properties.xfrm.off_y = Some(emu);
}
fn width(&self) -> Emu {
self.frame.properties.xfrm.ext_cx.unwrap_or_default()
}
fn set_width(&mut self, emu: Emu) {
self.frame.properties.xfrm.ext_cx = Some(emu);
}
fn height(&self) -> Emu {
self.frame.properties.xfrm.ext_cy.unwrap_or_default()
}
fn set_height(&mut self, emu: Emu) {
self.frame.properties.xfrm.ext_cy = Some(emu);
}
fn rotation(&self) -> f64 {
0.0
}
fn set_rotation(&mut self, _deg: f64) {}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn new_smartart_shape_basics() {
let s = SmartArtShape::new();
assert_eq!(s.dm_rid(), "");
assert_eq!(s.lo_rid(), "");
assert_eq!(s.qs_rid(), "");
assert_eq!(s.cs_rid(), "");
assert!(s.smart_art().unwrap().raw_xml.contains("dgm:relIds"));
}
#[test]
fn from_rids_works() {
let s = SmartArtShape::from_rids("rId1", "rId2", "rId3", "rId4");
assert_eq!(s.dm_rid(), "rId1");
assert_eq!(s.lo_rid(), "rId2");
assert_eq!(s.qs_rid(), "rId3");
assert_eq!(s.cs_rid(), "rId4");
let xml = &s.smart_art().unwrap().raw_xml;
assert!(xml.contains("r:dm=\"rId1\""));
assert!(xml.contains("r:lo=\"rId2\""));
assert!(xml.contains("r:qs=\"rId3\""));
assert!(xml.contains("r:cs=\"rId4\""));
}
#[test]
fn set_single_rid_rebuilds_raw_xml() {
let mut s = SmartArtShape::from_rids("rId1", "rId2", "rId3", "rId4");
s.set_dm_rid("rIdDmNew");
assert_eq!(s.dm_rid(), "rIdDmNew");
assert_eq!(s.lo_rid(), "rId2");
assert_eq!(s.qs_rid(), "rId3");
assert_eq!(s.cs_rid(), "rId4");
let xml = &s.smart_art().unwrap().raw_xml;
assert!(xml.contains("r:dm=\"rIdDmNew\""));
assert!(xml.contains("r:lo=\"rId2\""));
}
#[test]
fn set_all_rids_works() {
let mut s = SmartArtShape::new();
s.set_all_rids("a", "b", "c", "d");
assert_eq!(s.dm_rid(), "a");
assert_eq!(s.lo_rid(), "b");
assert_eq!(s.qs_rid(), "c");
assert_eq!(s.cs_rid(), "d");
let xml = &s.smart_art().unwrap().raw_xml;
assert!(xml.contains("r:dm=\"a\""));
assert!(xml.contains("r:cs=\"d\""));
}
#[test]
fn set_placeholder_works() {
let mut s = SmartArtShape::new();
assert!(!s.is_placeholder());
s.set_placeholder(0, Some("obj"));
assert!(s.is_placeholder());
assert_eq!(s.ph_idx(), Some(0));
assert_eq!(s.ph_type(), Some("obj"));
s.clear_placeholder();
assert!(!s.is_placeholder());
assert_eq!(s.ph_idx(), None);
}
#[test]
fn shape_trait_geometry() {
let mut s = SmartArtShape::new();
s.set_left(crate::Emu(100));
s.set_top(crate::Emu(200));
s.set_width(crate::Emu(300));
s.set_height(crate::Emu(400));
assert_eq!(s.left(), crate::Emu(100));
assert_eq!(s.top(), crate::Emu(200));
assert_eq!(s.width(), crate::Emu(300));
assert_eq!(s.height(), crate::Emu(400));
s.set_rotation(45.0);
assert_eq!(s.rotation(), 0.0);
}
}