diff options
author | Miklos Vajna <vmiklos@collabora.com> | 2021-01-20 10:49:30 +0100 |
---|---|---|
committer | Miklos Vajna <vmiklos@collabora.com> | 2021-01-20 18:17:29 +0100 |
commit | 9e8cb640ed16a392d628c0ec513f6b4f5b947bfd (patch) | |
tree | 8ba0a3ea3eec3da16de96d99937bb30112269dc4 | |
parent | e2fcbac50549ca96b092d64bd14a37cee6b12e0a (diff) |
oox smartart: extract composite algo from AlgAtom::layoutShape()
AlgAtom::layoutShape() is more or less the single function where all
layouting happens for all algoritms. Extract the composite algorithm
part from it to a separate CompositeAlg::layoutShapeChildren() before
that function grows too large. This also allows making it clear that
some local functions are only meant to be used with the composite
algorithm.
Change-Id: Iccfd5ea0dcca2f531dae14cbed3e900d4ffd9cc8
Reviewed-on: https://gerrit.libreoffice.org/c/core/+/109668
Reviewed-by: Miklos Vajna <vmiklos@collabora.com>
Tested-by: Jenkins
-rw-r--r-- | oox/source/drawingml/diagram/diagramlayoutatoms.cxx | 513 | ||||
-rw-r--r-- | oox/source/drawingml/diagram/diagramlayoutatoms.hxx | 31 |
2 files changed, 285 insertions, 259 deletions
diff --git a/oox/source/drawingml/diagram/diagramlayoutatoms.cxx b/oox/source/drawingml/diagram/diagramlayoutatoms.cxx index 562c20d05e7f..66df25d9ef4a 100644 --- a/oox/source/drawingml/diagram/diagramlayoutatoms.cxx +++ b/oox/source/drawingml/diagram/diagramlayoutatoms.cxx @@ -108,7 +108,7 @@ bool containsDataNodeType(const oox::drawingml::ShapePtr& pShape, sal_Int32 nTyp } namespace oox::drawingml { -void SnakeAlg::layoutShapeChildren(const AlgAtom::ParamMap& rMap, const ShapePtr& rShape, +void SnakeAlg::layoutShapeChildren(const AlgAtom& rAlg, const ShapePtr& rShape, const std::vector<Constraint>& rConstraints) { if (rShape->getChildren().empty() || rShape->getSize().Width == 0 @@ -199,6 +199,7 @@ void SnakeAlg::layoutShapeChildren(const AlgAtom::ParamMap& rMap, const ShapePtr bool bSpaceFromConstraints = fSpaceFromConstraint != 1.0; + const AlgAtom::ParamMap& rMap = rAlg.getMap(); const sal_Int32 nDir = rMap.count(XML_grDir) ? rMap.find(XML_grDir)->second : XML_tL; sal_Int32 nIncX = 1; sal_Int32 nIncY = 1; @@ -477,6 +478,257 @@ void PyraAlg::layoutShapeChildren(const ShapePtr& rShape) } } +bool CompositeAlg::inferFromLayoutProperty(const LayoutProperty& rMap, sal_Int32 nRefType, + sal_Int32& rValue) +{ + switch (nRefType) + { + case XML_r: + { + auto it = rMap.find(XML_l); + if (it == rMap.end()) + { + return false; + } + sal_Int32 nLeft = it->second; + it = rMap.find(XML_w); + if (it == rMap.end()) + { + return false; + } + rValue = nLeft + it->second; + return true; + } + default: + break; + } + + return false; +} + +void CompositeAlg::applyConstraintToLayout(const Constraint& rConstraint, + LayoutPropertyMap& rProperties) +{ + // TODO handle the case when we have ptType="...", not forName="...". + if (rConstraint.msForName.isEmpty()) + { + return; + } + + const LayoutPropertyMap::const_iterator aRef = rProperties.find(rConstraint.msRefForName); + if (aRef == rProperties.end()) + return; + + const LayoutProperty::const_iterator aRefType = aRef->second.find(rConstraint.mnRefType); + sal_Int32 nInferredValue = 0; + if (aRefType != aRef->second.end()) + { + // Reference is found directly. + rProperties[rConstraint.msForName][rConstraint.mnType] + = aRefType->second * rConstraint.mfFactor; + } + else if (inferFromLayoutProperty(aRef->second, rConstraint.mnRefType, nInferredValue)) + { + // Reference can be inferred. + rProperties[rConstraint.msForName][rConstraint.mnType] + = nInferredValue * rConstraint.mfFactor; + } + else + { + // Reference not found, assume a fixed value. + // Values are never in EMU, while oox::drawingml::Shape position and size are always in + // EMU. + double fUnitFactor = 0; + if (isFontUnit(rConstraint.mnRefType)) + // Points -> EMU. + fUnitFactor = EMU_PER_PT; + else + // Millimeters -> EMU. + fUnitFactor = EMU_PER_HMM * 100; + rProperties[rConstraint.msForName][rConstraint.mnType] = rConstraint.mfValue * fUnitFactor; + } +} + +void CompositeAlg::layoutShapeChildren(AlgAtom& rAlg, const ShapePtr& rShape, + const std::vector<Constraint>& rConstraints) +{ + LayoutPropertyMap aProperties; + LayoutProperty& rParent = aProperties[""]; + + sal_Int32 nParentXOffset = 0; + + // Track min/max vertical positions, so we can center everything at the end, if needed. + sal_Int32 nVertMin = std::numeric_limits<sal_Int32>::max(); + sal_Int32 nVertMax = 0; + + if (rAlg.getAspectRatio() != 1.0) + { + rParent[XML_w] = rShape->getSize().Width; + rParent[XML_h] = rShape->getSize().Height; + rParent[XML_l] = 0; + rParent[XML_t] = 0; + rParent[XML_r] = rShape->getSize().Width; + rParent[XML_b] = rShape->getSize().Height; + } + else + { + // Shrink width to be only as large as height. + rParent[XML_w] = std::min(rShape->getSize().Width, rShape->getSize().Height); + rParent[XML_h] = rShape->getSize().Height; + if (rParent[XML_w] < rShape->getSize().Width) + nParentXOffset = (rShape->getSize().Width - rParent[XML_w]) / 2; + rParent[XML_l] = nParentXOffset; + rParent[XML_t] = 0; + rParent[XML_r] = rShape->getSize().Width - rParent[XML_l]; + rParent[XML_b] = rShape->getSize().Height; + } + + for (const auto& rConstr : rConstraints) + { + // Apply direct constraints for all layout nodes. + applyConstraintToLayout(rConstr, aProperties); + } + + for (auto& aCurrShape : rShape->getChildren()) + { + // Apply constraints from the current layout node for this child shape. + // Previous child shapes may have changed aProperties. + for (const auto& rConstr : rConstraints) + { + if (rConstr.msForName != aCurrShape->getInternalName()) + { + continue; + } + + applyConstraintToLayout(rConstr, aProperties); + } + + // Apply constraints from the child layout node for this child shape. + // This builds on top of the own parent state + the state of previous shapes in the + // same composite algorithm. + const LayoutNode& rLayoutNode = rAlg.getLayoutNode(); + for (const auto& pDirectChild : rLayoutNode.getChildren()) + { + auto pLayoutNode = dynamic_cast<LayoutNode*>(pDirectChild.get()); + if (!pLayoutNode) + { + continue; + } + + if (pLayoutNode->getName() != aCurrShape->getInternalName()) + { + continue; + } + + for (const auto& pChild : pLayoutNode->getChildren()) + { + auto pConstraintAtom = dynamic_cast<ConstraintAtom*>(pChild.get()); + if (!pConstraintAtom) + { + continue; + } + + const Constraint& rConstraint = pConstraintAtom->getConstraint(); + if (!rConstraint.msForName.isEmpty()) + { + continue; + } + + if (!rConstraint.msRefForName.isEmpty()) + { + continue; + } + + // Either an absolute value or a factor of a property. + if (rConstraint.mfValue == 0.0 && rConstraint.mnRefType == XML_none) + { + continue; + } + + Constraint aConstraint(rConstraint); + aConstraint.msForName = pLayoutNode->getName(); + aConstraint.msRefForName = pLayoutNode->getName(); + + applyConstraintToLayout(aConstraint, aProperties); + } + } + + awt::Size aSize = rShape->getSize(); + awt::Point aPos(0, 0); + + const LayoutPropertyMap::const_iterator aPropIt + = aProperties.find(aCurrShape->getInternalName()); + if (aPropIt != aProperties.end()) + { + const LayoutProperty& rProp = aPropIt->second; + LayoutProperty::const_iterator it, it2; + + if ((it = rProp.find(XML_w)) != rProp.end()) + aSize.Width = std::min(it->second, rShape->getSize().Width); + if ((it = rProp.find(XML_h)) != rProp.end()) + aSize.Height = std::min(it->second, rShape->getSize().Height); + + if ((it = rProp.find(XML_l)) != rProp.end()) + aPos.X = it->second; + else if ((it = rProp.find(XML_ctrX)) != rProp.end()) + aPos.X = it->second - aSize.Width / 2; + else if ((it = rProp.find(XML_r)) != rProp.end()) + aPos.X = it->second - aSize.Width; + + if ((it = rProp.find(XML_t)) != rProp.end()) + aPos.Y = it->second; + else if ((it = rProp.find(XML_ctrY)) != rProp.end()) + aPos.Y = it->second - aSize.Height / 2; + else if ((it = rProp.find(XML_b)) != rProp.end()) + aPos.Y = it->second - aSize.Height; + + if ((it = rProp.find(XML_l)) != rProp.end() && (it2 = rProp.find(XML_r)) != rProp.end()) + aSize.Width = it2->second - it->second; + if ((it = rProp.find(XML_t)) != rProp.end() && (it2 = rProp.find(XML_b)) != rProp.end()) + aSize.Height = it2->second - it->second; + + aPos.X += nParentXOffset; + aSize.Width = std::min(aSize.Width, rShape->getSize().Width - aPos.X); + aSize.Height = std::min(aSize.Height, rShape->getSize().Height - aPos.Y); + } + else + SAL_WARN("oox.drawingml", "composite layout properties not found for shape " + << aCurrShape->getInternalName()); + + aCurrShape->setSize(aSize); + aCurrShape->setChildSize(aSize); + aCurrShape->setPosition(aPos); + + nVertMin = std::min(aPos.Y, nVertMin); + nVertMax = std::max(aPos.Y + aSize.Height, nVertMax); + + NamedShapePairs& rDiagramFontHeights + = rAlg.getLayoutNode().getDiagram().getShape()->getDiagramFontHeights(); + auto it = rDiagramFontHeights.find(aCurrShape->getInternalName()); + if (it != rDiagramFontHeights.end()) + { + // Internal name matches: put drawingml::Shape to the relevant group, for + // synchronized font height handling. + it->second.insert({ aCurrShape, {} }); + } + } + + // See if all vertical space is used or we have to center the content. + if (nVertMin >= 0 && nVertMin <= nVertMax && nVertMax <= rParent[XML_h]) + { + sal_Int32 nDiff = rParent[XML_h] - (nVertMax - nVertMin); + if (nDiff > 0) + { + for (auto& aCurrShape : rShape->getChildren()) + { + awt::Point aPosition = aCurrShape->getPosition(); + aPosition.Y += nDiff / 2; + aCurrShape->setPosition(aPosition); + } + } + } +} + IteratorAttr::IteratorAttr( ) : mnCnt( -1 ) , mbHideLastTrans( true ) @@ -820,87 +1072,6 @@ sal_Int32 AlgAtom::getVerticalShapesCount(const ShapePtr& rShape) namespace { -/** - * Decides if a certain reference type (e.g. "right") can be inferred from the available properties - * in rMap (e.g. left and width). Returns true if rValue is written to. - */ -bool InferFromLayoutProperty(const LayoutProperty& rMap, sal_Int32 nRefType, sal_Int32& rValue) -{ - switch (nRefType) - { - case XML_r: - { - auto it = rMap.find(XML_l); - if (it == rMap.end()) - { - return false; - } - sal_Int32 nLeft = it->second; - it = rMap.find(XML_w); - if (it == rMap.end()) - { - return false; - } - rValue = nLeft + it->second; - return true; - } - default: - break; - } - - return false; -} - -/** - * Apply rConstraint to the rProperties shared layout state. - * - * Note that the order in which constraints are applied matters, given that constraints can refer to - * each other, and in case A depends on B and A is applied before B, the effect of A won't be - * updated when B is applied. - */ -void ApplyConstraintToLayout(const Constraint& rConstraint, LayoutPropertyMap& rProperties) -{ - // TODO handle the case when we have ptType="...", not forName="...". - if (rConstraint.msForName.isEmpty()) - { - return; - } - - const LayoutPropertyMap::const_iterator aRef = rProperties.find(rConstraint.msRefForName); - if (aRef == rProperties.end()) - return; - - const LayoutProperty::const_iterator aRefType = aRef->second.find(rConstraint.mnRefType); - sal_Int32 nInferredValue = 0; - if (aRefType != aRef->second.end()) - { - // Reference is found directly. - rProperties[rConstraint.msForName][rConstraint.mnType] - = aRefType->second * rConstraint.mfFactor; - } - else if (InferFromLayoutProperty(aRef->second, rConstraint.mnRefType, nInferredValue)) - { - // Reference can be inferred. - rProperties[rConstraint.msForName][rConstraint.mnType] - = nInferredValue * rConstraint.mfFactor; - } - else - { - // Reference not found, assume a fixed value. - // Values are never in EMU, while oox::drawingml::Shape position and size are always in - // EMU. - double fUnitFactor = 0; - if (isFontUnit(rConstraint.mnRefType)) - // Points -> EMU. - fUnitFactor = EMU_PER_PT; - else - // Millimeters -> EMU. - fUnitFactor = EMU_PER_HMM * 100; - rProperties[rConstraint.msForName][rConstraint.mnType] - = rConstraint.mfValue * fUnitFactor; - } -} - /// Does the first data node of this shape have customized text properties? bool HasCustomText(const ShapePtr& rShape, LayoutNode& rLayoutNode) { @@ -969,181 +1140,7 @@ void AlgAtom::layoutShape(const ShapePtr& rShape, const std::vector<Constraint>& { case XML_composite: { - // layout shapes using basic constraints - - LayoutPropertyMap aProperties; - LayoutProperty& rParent = aProperties[""]; - - sal_Int32 nParentXOffset = 0; - - // Track min/max vertical positions, so we can center everything at the end, if needed. - sal_Int32 nVertMin = std::numeric_limits<sal_Int32>::max(); - sal_Int32 nVertMax = 0; - - if (mfAspectRatio != 1.0) - { - rParent[XML_w] = rShape->getSize().Width; - rParent[XML_h] = rShape->getSize().Height; - rParent[XML_l] = 0; - rParent[XML_t] = 0; - rParent[XML_r] = rShape->getSize().Width; - rParent[XML_b] = rShape->getSize().Height; - } - else - { - // Shrink width to be only as large as height. - rParent[XML_w] = std::min(rShape->getSize().Width, rShape->getSize().Height); - rParent[XML_h] = rShape->getSize().Height; - if (rParent[XML_w] < rShape->getSize().Width) - nParentXOffset = (rShape->getSize().Width - rParent[XML_w]) / 2; - rParent[XML_l] = nParentXOffset; - rParent[XML_t] = 0; - rParent[XML_r] = rShape->getSize().Width - rParent[XML_l]; - rParent[XML_b] = rShape->getSize().Height; - } - - for (const auto & rConstr : rConstraints) - { - // Apply direct constraints for all layout nodes. - ApplyConstraintToLayout(rConstr, aProperties); - } - - for (auto& aCurrShape : rShape->getChildren()) - { - // Apply constraints from the current layout node for this child shape. - // Previous child shapes may have changed aProperties. - for (const auto& rConstr : rConstraints) - { - if (rConstr.msForName != aCurrShape->getInternalName()) - { - continue; - } - - ApplyConstraintToLayout(rConstr, aProperties); - } - - // Apply constraints from the child layout node for this child shape. - // This builds on top of the own parent state + the state of previous shapes in the - // same composite algorithm. - const LayoutNode& rLayoutNode = getLayoutNode(); - for (const auto& pDirectChild : rLayoutNode.getChildren()) - { - auto pLayoutNode = dynamic_cast<LayoutNode*>(pDirectChild.get()); - if (!pLayoutNode) - { - continue; - } - - if (pLayoutNode->getName() != aCurrShape->getInternalName()) - { - continue; - } - - for (const auto& pChild : pLayoutNode->getChildren()) - { - auto pConstraintAtom = dynamic_cast<ConstraintAtom*>(pChild.get()); - if (!pConstraintAtom) - { - continue; - } - - const Constraint& rConstraint = pConstraintAtom->getConstraint(); - if (!rConstraint.msForName.isEmpty()) - { - continue; - } - - if (!rConstraint.msRefForName.isEmpty()) - { - continue; - } - - // Either an absolute value or a factor of a property. - if (rConstraint.mfValue == 0.0 && rConstraint.mnRefType == XML_none) - { - continue; - } - - Constraint aConstraint(rConstraint); - aConstraint.msForName = pLayoutNode->getName(); - aConstraint.msRefForName = pLayoutNode->getName(); - - ApplyConstraintToLayout(aConstraint, aProperties); - } - } - - awt::Size aSize = rShape->getSize(); - awt::Point aPos(0, 0); - - const LayoutPropertyMap::const_iterator aPropIt = aProperties.find(aCurrShape->getInternalName()); - if (aPropIt != aProperties.end()) - { - const LayoutProperty& rProp = aPropIt->second; - LayoutProperty::const_iterator it, it2; - - if ( (it = rProp.find(XML_w)) != rProp.end() ) - aSize.Width = std::min(it->second, rShape->getSize().Width); - if ( (it = rProp.find(XML_h)) != rProp.end() ) - aSize.Height = std::min(it->second, rShape->getSize().Height); - - if ( (it = rProp.find(XML_l)) != rProp.end() ) - aPos.X = it->second; - else if ( (it = rProp.find(XML_ctrX)) != rProp.end() ) - aPos.X = it->second - aSize.Width/2; - else if ((it = rProp.find(XML_r)) != rProp.end()) - aPos.X = it->second - aSize.Width; - - if ( (it = rProp.find(XML_t)) != rProp.end()) - aPos.Y = it->second; - else if ( (it = rProp.find(XML_ctrY)) != rProp.end() ) - aPos.Y = it->second - aSize.Height/2; - else if ((it = rProp.find(XML_b)) != rProp.end()) - aPos.Y = it->second - aSize.Height; - - if ( (it = rProp.find(XML_l)) != rProp.end() && (it2 = rProp.find(XML_r)) != rProp.end() ) - aSize.Width = it2->second - it->second; - if ( (it = rProp.find(XML_t)) != rProp.end() && (it2 = rProp.find(XML_b)) != rProp.end() ) - aSize.Height = it2->second - it->second; - - aPos.X += nParentXOffset; - aSize.Width = std::min(aSize.Width, rShape->getSize().Width - aPos.X); - aSize.Height = std::min(aSize.Height, rShape->getSize().Height - aPos.Y); - } - else - SAL_WARN("oox.drawingml", "composite layout properties not found for shape " << aCurrShape->getInternalName()); - - aCurrShape->setSize(aSize); - aCurrShape->setChildSize(aSize); - aCurrShape->setPosition(aPos); - - nVertMin = std::min(aPos.Y, nVertMin); - nVertMax = std::max(aPos.Y + aSize.Height, nVertMax); - - NamedShapePairs& rDiagramFontHeights - = getLayoutNode().getDiagram().getShape()->getDiagramFontHeights(); - auto it = rDiagramFontHeights.find(aCurrShape->getInternalName()); - if (it != rDiagramFontHeights.end()) - { - // Internal name matches: put drawingml::Shape to the relevant group, for - // synchronized font height handling. - it->second.insert({ aCurrShape, {} }); - } - } - - // See if all vertical space is used or we have to center the content. - if (nVertMin >= 0 && nVertMin <= nVertMax && nVertMax <= rParent[XML_h]) - { - sal_Int32 nDiff = rParent[XML_h] - (nVertMax - nVertMin); - if (nDiff > 0) - { - for (auto& aCurrShape : rShape->getChildren()) - { - awt::Point aPosition = aCurrShape->getPosition(); - aPosition.Y += nDiff / 2; - aCurrShape->setPosition(aPosition); - } - } - } + CompositeAlg::layoutShapeChildren(*this, rShape, rConstraints); break; } @@ -1679,7 +1676,7 @@ void AlgAtom::layoutShape(const ShapePtr& rShape, const std::vector<Constraint>& case XML_snake: { - SnakeAlg::layoutShapeChildren(maMap, rShape, rConstraints); + SnakeAlg::layoutShapeChildren(*this, rShape, rConstraints); break; } diff --git a/oox/source/drawingml/diagram/diagramlayoutatoms.hxx b/oox/source/drawingml/diagram/diagramlayoutatoms.hxx index 149be2c97338..7645c9affc99 100644 --- a/oox/source/drawingml/diagram/diagramlayoutatoms.hxx +++ b/oox/source/drawingml/diagram/diagramlayoutatoms.hxx @@ -178,6 +178,7 @@ public: void setType( sal_Int32 nToken ) { mnType = nToken; } + const ParamMap& getMap() const { return maMap; } void addParam( sal_Int32 nType, sal_Int32 nVal ) { maMap[nType]=nVal; } sal_Int32 getVerticalShapesCount(const ShapePtr& rShape); @@ -205,7 +206,7 @@ typedef std::shared_ptr< AlgAtom > AlgAtomPtr; class SnakeAlg { public: - static void layoutShapeChildren(const AlgAtom::ParamMap& rMap, const ShapePtr& rShape, + static void layoutShapeChildren(const AlgAtom& rAlg, const ShapePtr& rShape, const std::vector<Constraint>& rConstraints); }; @@ -219,6 +220,34 @@ public: static void layoutShapeChildren(const ShapePtr& rShape); }; +/** + * Specifies the size and position for all child layout nodes. + */ +class CompositeAlg +{ +public: + static void layoutShapeChildren(AlgAtom& rAlg, const ShapePtr& rShape, + const std::vector<Constraint>& rConstraints); + +private: + /** + * Apply rConstraint to the rProperties shared layout state. + * + * Note that the order in which constraints are applied matters, given that constraints can refer to + * each other, and in case A depends on B and A is applied before B, the effect of A won't be + * updated when B is applied. + */ + static void applyConstraintToLayout(const Constraint& rConstraint, + LayoutPropertyMap& rProperties); + + /** + * Decides if a certain reference type (e.g. "right") can be inferred from the available properties + * in rMap (e.g. left and width). Returns true if rValue is written to. + */ + static bool inferFromLayoutProperty(const LayoutProperty& rMap, sal_Int32 nRefType, + sal_Int32& rValue); +}; + class ForEachAtom : public LayoutAtom { |