From 4a96a5d862ed46bbcb64d34b32720b5b1b3d7ca8 Mon Sep 17 00:00:00 2001 From: Grzegorz Araminowicz Date: Sun, 7 Jul 2019 14:12:05 +0200 Subject: SmartArt: improve organization chart layout layout shapes in two steps: * first calculate vertical child shapes count for every shape (taking into accout hierBranch alg variable) * then actual layout using that count to calculate size for subtrees Change-Id: I2e5ca34ed3383aa9502c52511cc1fb2bee215572 Reviewed-on: https://gerrit.libreoffice.org/75195 Tested-by: Jenkins Reviewed-by: Miklos Vajna --- .../drawingml/diagram/diagramlayoutatoms.cxx | 107 +++++++++++++++------ .../drawingml/diagram/diagramlayoutatoms.hxx | 1 + .../drawingml/diagram/layoutatomvisitorbase.hxx | 4 +- .../drawingml/diagram/layoutatomvisitors.cxx | 22 ++++- .../drawingml/diagram/layoutatomvisitors.hxx | 4 +- 5 files changed, 102 insertions(+), 36 deletions(-) (limited to 'oox/source/drawingml/diagram') diff --git a/oox/source/drawingml/diagram/diagramlayoutatoms.cxx b/oox/source/drawingml/diagram/diagramlayoutatoms.cxx index f3c3f52dce04..7f77880b28e8 100644 --- a/oox/source/drawingml/diagram/diagramlayoutatoms.cxx +++ b/oox/source/drawingml/diagram/diagramlayoutatoms.cxx @@ -418,6 +418,40 @@ sal_Int32 AlgAtom::getConnectorType() return oox::XML_rightArrow; } +sal_Int32 AlgAtom::getVerticalShapesCount(const ShapePtr& rShape) +{ + if (rShape->getChildren().empty()) + return (rShape->getSubType() != XML_conn) ? 1 : 0; + + sal_Int32 nDir = XML_fromL; + if (mnType == XML_hierRoot) + nDir = XML_fromT; + else if (maMap.count(XML_linDir)) + nDir = maMap.find(XML_linDir)->second; + + const sal_Int32 nSecDir = maMap.count(XML_secLinDir) ? maMap.find(XML_secLinDir)->second : 0; + + sal_Int32 nCount = 0; + if (nDir == XML_fromT || nDir == XML_fromB) + { + for (ShapePtr& pChild : rShape->getChildren()) + nCount += pChild->getVerticalShapesCount(); + } + else if ((nDir == XML_fromL || nDir == XML_fromR) && nSecDir == XML_fromT) + { + for (ShapePtr& pChild : rShape->getChildren()) + nCount += pChild->getVerticalShapesCount(); + nCount = (nCount + 1) / 2; + } + else + { + for (ShapePtr& pChild : rShape->getChildren()) + nCount = std::max(nCount, pChild->getVerticalShapesCount()); + } + + return nCount; +} + void AlgAtom::layoutShape( const ShapePtr& rShape, const std::vector& rConstraints ) { @@ -660,6 +694,9 @@ void AlgAtom::layoutShape( const ShapePtr& rShape, case XML_hierChild: case XML_hierRoot: { + if (rShape->getChildren().empty() || rShape->getSize().Width == 0 || rShape->getSize().Height == 0) + break; + // hierRoot is the manager -> employees vertical linear path, // hierChild is the first employee -> last employee horizontal // linear path. @@ -669,31 +706,20 @@ void AlgAtom::layoutShape( const ShapePtr& rShape, else if (maMap.count(XML_linDir)) nDir = maMap.find(XML_linDir)->second; - if (rShape->getChildren().empty() || rShape->getSize().Width == 0 - || rShape->getSize().Height == 0) - break; + const sal_Int32 nSecDir = maMap.count(XML_secLinDir) ? maMap.find(XML_secLinDir)->second : 0; sal_Int32 nCount = rShape->getChildren().size(); if (mnType == XML_hierChild) { - // Connectors should not influence the size of non-connect - // shapes. + // Connectors should not influence the size of non-connect shapes. nCount = std::count_if( rShape->getChildren().begin(), rShape->getChildren().end(), [](const ShapePtr& pShape) { return pShape->getSubType() != XML_conn; }); } - // A manager node's height should be independent from if it has - // assistants and employees, compensate for that. - bool bTop = mnType == XML_hierRoot && rShape->getInternalName() == "hierRoot1"; - - // Add spacing, so connectors have a chance to be visible. - double fSpace = (nCount > 1 || bTop) ? 0.3 : 0; - - double fHeightScale = 1.0; - if (mnType == XML_hierRoot && nCount < 3 && bTop) - fHeightScale = fHeightScale * nCount / 3; + const double fSpaceWidth = 0.1; + const double fSpaceHeight = 0.3; if (mnType == XML_hierRoot && nCount == 3) { @@ -704,18 +730,31 @@ void AlgAtom::layoutShape( const ShapePtr& rShape, std::swap(rChildren[1], rChildren[2]); } + sal_Int32 nHorizontalShapesCount = 1; + if (nSecDir == XML_fromT) + nHorizontalShapesCount = 2; + else if (nDir == XML_fromL || nDir == XML_fromR) + nHorizontalShapesCount = nCount; + awt::Size aChildSize = rShape->getSize(); - if (nDir == XML_fromT) - { - aChildSize.Height /= (nCount + nCount * fSpace); - } - else - aChildSize.Width /= nCount; - aChildSize.Height *= fHeightScale; + aChildSize.Height /= (rShape->getVerticalShapesCount() + (rShape->getVerticalShapesCount() - 1) * fSpaceHeight); + aChildSize.Width /= (nHorizontalShapesCount + (nHorizontalShapesCount - 1) * fSpaceWidth); + awt::Size aConnectorSize = aChildSize; aConnectorSize.Width = 1; awt::Point aChildPos(0, 0); + + // indent children to show they are descendants, not siblings + if (mnType == XML_hierChild && nHorizontalShapesCount == 1) + { + const double fChildIndent = 0.1; + aChildPos.X = aChildSize.Width * fChildIndent; + aChildSize.Width *= (1 - 2 * fChildIndent); + } + + sal_Int32 nIdx = 0; + sal_Int32 nRowHeight = 0; for (auto& pChild : rShape->getChildren()) { pChild->setPosition(aChildPos); @@ -729,13 +768,27 @@ void AlgAtom::layoutShape( const ShapePtr& rShape, continue; } - pChild->setSize(aChildSize); - pChild->setChildSize(aChildSize); + awt::Size aCurrSize = aChildSize; + aCurrSize.Height *= pChild->getVerticalShapesCount() + (pChild->getVerticalShapesCount() - 1) * fSpaceHeight; - if (nDir == XML_fromT) - aChildPos.Y += aChildSize.Height + aChildSize.Height * fSpace; + pChild->setSize(aCurrSize); + pChild->setChildSize(aCurrSize); + + if (nDir == XML_fromT || nDir == XML_fromB) + aChildPos.Y += aCurrSize.Height + aChildSize.Height * fSpaceHeight; else - aChildPos.X += aChildSize.Width; + aChildPos.X += aCurrSize.Width + aCurrSize.Width * fSpaceWidth; + + nRowHeight = std::max(nRowHeight, aCurrSize.Height); + + if (nSecDir == XML_fromT && nIdx % 2 == 1) + { + aChildPos.X = 0; + aChildPos.Y += nRowHeight + aChildSize.Height * fSpaceHeight; + nRowHeight = 0; + } + + nIdx++; } break; diff --git a/oox/source/drawingml/diagram/diagramlayoutatoms.hxx b/oox/source/drawingml/diagram/diagramlayoutatoms.hxx index d77a98135c02..91028971473e 100644 --- a/oox/source/drawingml/diagram/diagramlayoutatoms.hxx +++ b/oox/source/drawingml/diagram/diagramlayoutatoms.hxx @@ -160,6 +160,7 @@ public: { mnType = nToken; } void addParam( sal_Int32 nType, sal_Int32 nVal ) { maMap[nType]=nVal; } + sal_Int32 getVerticalShapesCount(const ShapePtr& rShape); void layoutShape( const ShapePtr& rShape, const std::vector& rConstraints ); diff --git a/oox/source/drawingml/diagram/layoutatomvisitorbase.hxx b/oox/source/drawingml/diagram/layoutatomvisitorbase.hxx index 7007cf283070..ff12f82e2f96 100644 --- a/oox/source/drawingml/diagram/layoutatomvisitorbase.hxx +++ b/oox/source/drawingml/diagram/layoutatomvisitorbase.hxx @@ -50,7 +50,8 @@ public: mpCurrentNode(pRootPoint), mnCurrIdx(0), mnCurrStep(0), - mnCurrCnt(0) + mnCurrCnt(0), + meLookFor(LAYOUT_NODE) {} void defaultVisit(LayoutAtom const& rAtom); @@ -67,6 +68,7 @@ protected: sal_Int32 mnCurrIdx; sal_Int32 mnCurrStep; sal_Int32 mnCurrCnt; + enum {LAYOUT_NODE, CONSTRAINT, ALGORITHM} meLookFor; }; class ShallowPresNameVisitor : public LayoutAtomVisitorBase diff --git a/oox/source/drawingml/diagram/layoutatomvisitors.cxx b/oox/source/drawingml/diagram/layoutatomvisitors.cxx index 31b853664577..4bfadc3affe8 100644 --- a/oox/source/drawingml/diagram/layoutatomvisitors.cxx +++ b/oox/source/drawingml/diagram/layoutatomvisitors.cxx @@ -37,11 +37,18 @@ void ShapeCreationVisitor::visit(ConstraintAtom& /*rAtom*/) void ShapeCreationVisitor::visit(AlgAtom& rAtom) { - mpParentShape->setAspectRatio(rAtom.getAspectRatio()); + if (meLookFor == ALGORITHM) + { + mpParentShape->setAspectRatio(rAtom.getAspectRatio()); + mpParentShape->setVerticalShapesCount(rAtom.getVerticalShapesCount(mpParentShape)); + } } void ShapeCreationVisitor::visit(LayoutNode& rAtom) { + if (meLookFor != LAYOUT_NODE) + return; + // stop processing if it's not a child of previous LayoutNode const DiagramData::PointsNameMap::const_iterator aDataNode = mrDgm.getData()->getPointsPresNameMap().find(rAtom.getName()); @@ -108,17 +115,22 @@ void ShapeCreationVisitor::visit(LayoutNode& rAtom) mpParentShape=pCurrParent; // process children + meLookFor = LAYOUT_NODE; defaultVisit(rAtom); - // restore parent - mpParentShape=pPreviousParent; - mpCurrentNode = pPreviousNode; - // remove unneeded empty group shapes pCurrParent->getChildren().erase( std::remove_if(pCurrParent->getChildren().begin(), pCurrParent->getChildren().end(), [] (const ShapePtr & aChild) { return aChild->getServiceName() == "com.sun.star.drawing.GroupShape" && aChild->getChildren().empty(); }), pCurrParent->getChildren().end()); + + meLookFor = ALGORITHM; + defaultVisit(rAtom); + meLookFor = LAYOUT_NODE; + + // restore parent + mpParentShape=pPreviousParent; + mpCurrentNode = pPreviousNode; } void ShapeCreationVisitor::visit(ShapeAtom& /*rAtom*/) diff --git a/oox/source/drawingml/diagram/layoutatomvisitors.hxx b/oox/source/drawingml/diagram/layoutatomvisitors.hxx index 2950fb01a17c..656f61d79e6a 100644 --- a/oox/source/drawingml/diagram/layoutatomvisitors.hxx +++ b/oox/source/drawingml/diagram/layoutatomvisitors.hxx @@ -74,8 +74,7 @@ class ShapeLayoutingVisitor : public LayoutAtomVisitorBase { public: ShapeLayoutingVisitor(const Diagram& rDgm, const dgm::Point* pRootPoint) : - LayoutAtomVisitorBase(rDgm, pRootPoint), - meLookFor(LAYOUT_NODE) + LayoutAtomVisitorBase(rDgm, pRootPoint) {} using LayoutAtomVisitorBase::visit; @@ -86,7 +85,6 @@ public: private: std::vector maConstraints; - enum {LAYOUT_NODE, CONSTRAINT, ALGORITHM} meLookFor; }; } } -- cgit