include/oox/drawingml/shape.hxx                        |    6 
 oox/source/drawingml/diagram/diagramlayoutatoms.cxx    |  107 ++++++++++++-----
 oox/source/drawingml/diagram/diagramlayoutatoms.hxx    |    1 
 oox/source/drawingml/diagram/layoutatomvisitorbase.hxx |    4 
 oox/source/drawingml/diagram/layoutatomvisitors.cxx    |   22 ++-
 oox/source/drawingml/diagram/layoutatomvisitors.hxx    |    4 
 sd/qa/unit/data/pptx/smartart-org-chart2.pptx          |binary
 sd/qa/unit/import-tests-smartart.cxx                   |   60 +++++++++
 8 files changed, 168 insertions(+), 36 deletions(-)

New commits:
commit 53af44593672cd456b32d4aba56220f10dad16ae
Author:     Grzegorz Araminowicz <grzegorz.araminow...@collabora.com>
AuthorDate: Sun Jul 7 14:12:05 2019 +0200
Commit:     Grzegorz Araminowicz <grzegorz.araminow...@collabora.com>
CommitDate: Thu Jul 11 10:59:16 2019 +0200

    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 <vmik...@collabora.com>
    Reviewed-on: https://gerrit.libreoffice.org/75396
    Tested-by: Jenkins CollaboraOffice <jenkinscollaboraoff...@gmail.com>
    Reviewed-by: Grzegorz Araminowicz <grzegorz.araminow...@collabora.com>

diff --git a/include/oox/drawingml/shape.hxx b/include/oox/drawingml/shape.hxx
index 73fb85ad8dc9..4396a17a69f9 100644
--- a/include/oox/drawingml/shape.hxx
+++ b/include/oox/drawingml/shape.hxx
@@ -223,6 +223,9 @@ public:
 
     double getAspectRatio() const { return mfAspectRatio; }
 
+    void setVerticalShapesCount(sal_Int32 nVerticalShapesCount) { 
mnVerticalShapesCount = nVerticalShapesCount; }
+    sal_Int32 getVerticalShapesCount() const { return mnVerticalShapesCount; }
+
     /// Changes reference semantics to value semantics for fill properties.
     void cloneFillProperties();
 
@@ -357,6 +360,9 @@ private:
 
     /// Aspect ratio for an in-diagram shape.
     double mfAspectRatio = 0;
+
+    /// Number of child shapes to be layouted vertically inside org chart 
in-diagram shape.
+    sal_Int32 mnVerticalShapesCount = 0;
 };
 
 } }
diff --git a/oox/source/drawingml/diagram/diagramlayoutatoms.cxx 
b/oox/source/drawingml/diagram/diagramlayoutatoms.cxx
index 9c83d95fea5d..93d12f23db8d 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<Constraint>& rConstraints )
 {
@@ -621,6 +655,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.
@@ -630,31 +667,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)
             {
@@ -665,18 +691,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);
@@ -690,13 +729,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 c742f604a409..e6089a96f4d3 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<Constraint>& 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<Constraint> maConstraints;
-    enum {LAYOUT_NODE, CONSTRAINT, ALGORITHM} meLookFor;
 };
 
 } }
diff --git a/sd/qa/unit/data/pptx/smartart-org-chart2.pptx 
b/sd/qa/unit/data/pptx/smartart-org-chart2.pptx
new file mode 100644
index 000000000000..5e2be2167976
Binary files /dev/null and b/sd/qa/unit/data/pptx/smartart-org-chart2.pptx 
differ
diff --git a/sd/qa/unit/import-tests-smartart.cxx 
b/sd/qa/unit/import-tests-smartart.cxx
index e10d407ee359..8e5632801b45 100644
--- a/sd/qa/unit/import-tests-smartart.cxx
+++ b/sd/qa/unit/import-tests-smartart.cxx
@@ -36,6 +36,28 @@ uno::Reference<drawing::XShape> getChildShape(const 
uno::Reference<drawing::XSha
 
     return xRet;
 }
+
+uno::Reference<drawing::XShape> findChildShapeByText(const 
uno::Reference<drawing::XShape>& xShape,
+                                                     const OUString& sText)
+{
+    uno::Reference<text::XText> xText(xShape, uno::UNO_QUERY);
+    if (xText.is() && xText->getString() == sText)
+        return xShape;
+
+    uno::Reference<container::XIndexAccess> xGroup(xShape, uno::UNO_QUERY);
+    if (!xGroup.is())
+        return uno::Reference<drawing::XShape>();
+
+    for (sal_Int32 i = 0; i < xGroup->getCount(); i++)
+    {
+        uno::Reference<drawing::XShape> xChildShape(xGroup->getByIndex(i), 
uno::UNO_QUERY);
+        uno::Reference<drawing::XShape> xReturnShape = 
findChildShapeByText(xChildShape, sText);
+        if (xReturnShape.is())
+            return xReturnShape;
+    }
+
+    return uno::Reference<drawing::XShape>();
+}
 }
 
 class SdImportTestSmartArt : public SdModelTestBase
@@ -78,6 +100,7 @@ public:
     void testBulletList();
     void testRecursion();
     void testDataFollow();
+    void testOrgChart2();
 
     CPPUNIT_TEST_SUITE(SdImportTestSmartArt);
 
@@ -118,6 +141,7 @@ public:
     CPPUNIT_TEST(testBulletList);
     CPPUNIT_TEST(testRecursion);
     CPPUNIT_TEST(testDataFollow);
+    CPPUNIT_TEST(testOrgChart2);
 
     CPPUNIT_TEST_SUITE_END();
 };
@@ -1196,6 +1220,42 @@ void SdImportTestSmartArt::testDataFollow()
     xDocShRef->DoClose();
 }
 
+void SdImportTestSmartArt::testOrgChart2()
+{
+    sd::DrawDocShellRef xDocShRef = 
loadURL(m_directories.getURLFromSrc("/sd/qa/unit/data/pptx/smartart-org-chart2.pptx"),
 PPTX);
+    uno::Reference<drawing::XShape> xGroup(getShapeFromPage(0, 0, xDocShRef), 
uno::UNO_QUERY);
+
+    uno::Reference<drawing::XShape> xShapeC1 = findChildShapeByText(xGroup, 
"C1");
+    uno::Reference<drawing::XShape> xShapeC2 = findChildShapeByText(xGroup, 
"C2");
+    uno::Reference<drawing::XShape> xShapeC3 = findChildShapeByText(xGroup, 
"C3");
+    uno::Reference<drawing::XShape> xShapeC4 = findChildShapeByText(xGroup, 
"C4");
+    uno::Reference<drawing::XShape> xShapeD1 = findChildShapeByText(xGroup, 
"D1");
+    uno::Reference<drawing::XShape> xShapeD2 = findChildShapeByText(xGroup, 
"D2");
+
+    CPPUNIT_ASSERT(xShapeC1.is());
+    CPPUNIT_ASSERT(xShapeC2.is());
+    CPPUNIT_ASSERT(xShapeC3.is());
+    CPPUNIT_ASSERT(xShapeC4.is());
+    CPPUNIT_ASSERT(xShapeD1.is());
+    CPPUNIT_ASSERT(xShapeD2.is());
+
+    CPPUNIT_ASSERT_EQUAL(xShapeC1->getPosition().Y, xShapeC2->getPosition().Y);
+    CPPUNIT_ASSERT_GREATEREQUAL(xShapeC1->getPosition().X + 
xShapeC1->getSize().Width, xShapeC2->getPosition().X);
+
+    CPPUNIT_ASSERT_EQUAL(xShapeC3->getPosition().X, xShapeC4->getPosition().X);
+    CPPUNIT_ASSERT_GREATEREQUAL(xShapeC3->getPosition().Y + 
xShapeC3->getSize().Height, xShapeC4->getPosition().Y);
+
+    CPPUNIT_ASSERT_EQUAL(xShapeD1->getPosition().X, xShapeD2->getPosition().X);
+    CPPUNIT_ASSERT_GREATEREQUAL(xShapeD1->getPosition().Y + 
xShapeD1->getSize().Height, xShapeD2->getPosition().Y);
+
+    CPPUNIT_ASSERT_GREATEREQUAL(xShapeC2->getPosition().X, 
xShapeD1->getPosition().X);
+    CPPUNIT_ASSERT_GREATEREQUAL(xShapeC2->getPosition().Y + 
xShapeC2->getSize().Height, xShapeD1->getPosition().Y);
+
+    CPPUNIT_ASSERT_GREATEREQUAL(xShapeD1->getPosition().X + 
xShapeD1->getSize().Width, xShapeC4->getPosition().X);
+
+    xDocShRef->DoClose();
+}
+
 CPPUNIT_TEST_SUITE_REGISTRATION(SdImportTestSmartArt);
 
 CPPUNIT_PLUGIN_IMPLEMENT();
_______________________________________________
Libreoffice-commits mailing list
libreoffice-comm...@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/libreoffice-commits

Reply via email to