1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
3 * This file is part of the LibreOffice project.
5 * This Source Code Form is subject to the terms of the Mozilla Public
6 * License, v. 2.0. If a copy of the MPL was not distributed with this
7 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
9 * This file incorporates work covered by the following license notice:
11 * Licensed to the Apache Software Foundation (ASF) under one or more
12 * contributor license agreements. See the NOTICE file distributed
13 * with this work for additional information regarding copyright
14 * ownership. The ASF licenses this file to you under the Apache
15 * License, Version 2.0 (the "License"); you may not use this file
16 * except in compliance with the License. You may obtain a copy of
17 * the License at http://www.apache.org/licenses/LICENSE-2.0 .
20 #include "diagramlayoutatoms.hxx"
24 #include <basegfx/numeric/ftools.hxx>
26 #include "oox/helper/attributelist.hxx"
27 #include "drawingml/fillproperties.hxx"
28 #include "drawingml/lineproperties.hxx"
29 #include "drawingml/textbody.hxx"
30 #include "drawingml/textparagraph.hxx"
31 #include "drawingml/textrun.hxx"
32 #include "drawingml/customshapeproperties.hxx"
33 #include "layoutnodecontext.hxx"
35 using namespace ::com::sun::star
;
36 using namespace ::com::sun::star::uno
;
37 using namespace ::com::sun::star::xml::sax
;
38 using namespace ::oox::core
;
40 namespace oox
{ namespace drawingml
{
42 IteratorAttr::IteratorAttr( )
45 , mbHideLastTrans( false )
52 void IteratorAttr::loadFromXAttr( const Reference
< XFastAttributeList
>& xAttr
)
54 AttributeList
attr( xAttr
);
55 mnAxis
= xAttr
->getOptionalValueToken( XML_axis
, 0 );
56 mnCnt
= attr
.getInteger( XML_cnt
, -1 );
57 mbHideLastTrans
= attr
.getBool( XML_hideLastTrans
, false );
58 mnPtType
= xAttr
->getOptionalValueToken( XML_ptType
, 0 );
59 mnSt
= attr
.getInteger( XML_st
, 0 );
60 mnStep
= attr
.getInteger( XML_step
, 1 );
63 ConditionAttr::ConditionAttr()
71 void ConditionAttr::loadFromXAttr( const Reference
< XFastAttributeList
>& xAttr
)
73 mnFunc
= xAttr
->getOptionalValueToken( XML_func
, 0 );
74 // mnArg will be -1 for "none" or any other unknown value
75 mnArg
= LayoutNodeContext::tagToVarIdx( xAttr
->getOptionalValueToken( XML_arg
, XML_none
) );
76 mnOp
= xAttr
->getOptionalValueToken( XML_op
, 0 );
77 msVal
= xAttr
->getOptionalValue( XML_val
);
80 void LayoutAtom::dump(int level
)
82 SAL_INFO("oox.drawingml", "level = " << level
<< " - " << msName
<< " of type " << typeid(*this).name() );
83 const std::vector
<LayoutAtomPtr
>& rChildren
=getChildren();
84 std::for_each( rChildren
.begin(), rChildren
.end(),
85 [level
] (LayoutAtomPtr
const& pAtom
) { pAtom
->dump(level
+ 1); } );
88 ForEachAtom::ForEachAtom(const Reference
< XFastAttributeList
>& xAttributes
)
90 maIter
.loadFromXAttr(xAttributes
);
93 void ForEachAtom::accept( LayoutAtomVisitor
& rVisitor
)
95 rVisitor
.visit(*this);
98 void ChooseAtom::accept( LayoutAtomVisitor
& rVisitor
)
100 rVisitor
.visit(*this);
103 ConditionAtom::ConditionAtom(const Reference
< XFastAttributeList
>& xAttributes
) :
106 maIter
.loadFromXAttr( xAttributes
);
107 maCond
.loadFromXAttr( xAttributes
);
110 const std::vector
<LayoutAtomPtr
>& ConditionAtom::getChildren() const
112 bool bDecisionVar
=true;
114 if( maCond
.mnFunc
== XML_var
&& maCond
.mnArg
== XML_dir
&& maCond
.mnOp
== XML_equ
&& maCond
.msVal
!= "norm" )
120 return mpElseChildNodes
;
123 void ConditionAtom::accept( LayoutAtomVisitor
& rVisitor
)
125 rVisitor
.visit(*this);
128 void ConditionAtom::addChild( const LayoutAtomPtr
& pNode
)
131 mpElseChildNodes
.push_back( pNode
);
133 mpChildNodes
.push_back( pNode
);
136 void ConstraintAtom::accept( LayoutAtomVisitor
& rVisitor
)
138 rVisitor
.visit(*this);
141 void AlgAtom::accept( LayoutAtomVisitor
& rVisitor
)
143 rVisitor
.visit(*this);
146 void AlgAtom::layoutShape( const ShapePtr
& rShape
,
147 const OUString
& rName
) const
153 if( rShape
->getChildren().empty() )
155 rShape
->setSize(awt::Size(50,50));
159 // just put stuff below each other
160 const sal_Int32 nIncX
=0;
161 const sal_Int32 nIncY
=1;
163 std::vector
<ShapePtr
>::const_iterator aCurrShape
=rShape
->getChildren().begin();
164 const std::vector
<ShapePtr
>::const_iterator aLastShape
=rShape
->getChildren().end();
166 // find biggest shape
168 while( aCurrShape
!= aLastShape
)
170 const awt::Size
& sz
=(*aCurrShape
)->getSize();
172 aMaxSize
.Width
= std::max(
175 aMaxSize
.Height
= std::max(
182 aCurrShape
=rShape
->getChildren().begin();
183 const awt::Point aStartPos
=(*aCurrShape
)->getPosition();
184 awt::Point aCurrPos
=aStartPos
;
185 awt::Size aTotalSize
;
186 aTotalSize
.Width
= aMaxSize
.Width
;
187 while( aCurrShape
!= aLastShape
)
189 const awt::Size
& sz
=(*aCurrShape
)->getSize();
190 (*aCurrShape
)->setPosition(aCurrPos
);
191 (*aCurrShape
)->setSize(
192 awt::Size(aMaxSize
.Width
,
195 aTotalSize
.Height
= std::max(
197 aCurrPos
.Y
+ sz
.Height
);
199 aCurrPos
.X
+= nIncX
*sz
.Width
;
200 aCurrPos
.Y
+= nIncY
*sz
.Height
;
205 rShape
->setSize(aTotalSize
);
214 if( rShape
->getChildren().empty() )
216 rShape
->setSize(awt::Size(50,50));
220 const sal_Int32 nStartAngle
=maMap
.count(XML_stAng
) ? maMap
.find(XML_stAng
)->second
: 0;
221 const sal_Int32 nSpanAngle
=maMap
.count(XML_spanAng
) ? maMap
.find(XML_spanAng
)->second
: 360;
223 std::vector
<ShapePtr
>::const_iterator aCurrShape
=rShape
->getChildren().begin();
224 const std::vector
<ShapePtr
>::const_iterator aLastShape
=rShape
->getChildren().end();
225 const sal_Int32 nShapes
=aLastShape
-aCurrShape
;
227 // find biggest shape
229 while( aCurrShape
!= aLastShape
)
231 const awt::Size
& sz
=(*aCurrShape
)->getSize();
233 aMaxSize
.Width
= std::max(
236 aMaxSize
.Height
= std::max(
244 const sal_Int32 nMaxDim
=std::max(aMaxSize
.Width
,aMaxSize
.Height
);
245 awt::Size aTotalSize
;
246 aCurrShape
=rShape
->getChildren().begin();
247 for( sal_Int32 i
=0; i
<nShapes
; ++i
, ++aCurrShape
)
249 const awt::Size
& sz
=(*aCurrShape
)->getSize();
251 const double r
=nShapes
*nMaxDim
/F_2PI
* 360.0/nSpanAngle
;
252 const awt::Point
aCurrPos(
253 r
+ r
*sin( (double(i
)*nSpanAngle
/nShapes
+ nStartAngle
)*F_PI180
),
254 r
- r
*cos( (double(i
)*nSpanAngle
/nShapes
+ nStartAngle
)*F_PI180
) );
255 (*aCurrShape
)->setPosition(aCurrPos
);
257 aTotalSize
.Width
= std::max(
259 aCurrPos
.X
+ sz
.Width
);
260 aTotalSize
.Height
= std::max(
262 aCurrPos
.Y
+ sz
.Height
);
265 rShape
->setSize(aTotalSize
);
275 if( rShape
->getChildren().empty() )
277 rShape
->setSize(awt::Size(50,50));
281 const sal_Int32 nDir
=maMap
.count(XML_linDir
) ? maMap
.find(XML_linDir
)->second
: XML_fromL
;
282 const sal_Int32 nIncX
=nDir
==XML_fromL
? 1 : (nDir
==XML_fromR
? -1 : 0);
283 const sal_Int32 nIncY
=nDir
==XML_fromT
? 1 : (nDir
==XML_fromB
? -1 : 0);
285 std::vector
<ShapePtr
>::const_iterator aCurrShape
=rShape
->getChildren().begin();
286 const std::vector
<ShapePtr
>::const_iterator aLastShape
=rShape
->getChildren().end();
287 const awt::Point aStartPos
=(*aCurrShape
)->getPosition();
288 awt::Point aCurrPos
=aStartPos
;
289 awt::Size aTotalSize
;
290 while( aCurrShape
!= aLastShape
)
292 const awt::Size
& sz
=(*aCurrShape
)->getSize();
293 (*aCurrShape
)->setPosition(aCurrPos
);
295 aTotalSize
.Width
= std::max(
297 aCurrPos
.X
+ sz
.Width
);
298 aTotalSize
.Height
= std::max(
300 aCurrPos
.Y
+ sz
.Height
);
302 // HACK: the spacing is arbitrary
303 aCurrPos
.X
+= nIncX
*(sz
.Width
+5);
304 aCurrPos
.Y
+= nIncY
*(sz
.Height
+5);
309 rShape
->setSize(aTotalSize
);
318 // HACK. Handled one level higher. Or rather, planned to
323 TextBodyPtr pTextBody
=rShape
->getTextBody();
325 pTextBody
->getParagraphs().empty() ||
326 pTextBody
->getParagraphs().front()->getRuns().empty() )
328 rShape
->setSize(awt::Size(5,5));
332 // HACK - count chars & paragraphs to come up with *some*
333 // notion of necessary size
334 const sal_Int32 nHackyFontHeight
=50;
335 const sal_Int32 nHackyFontWidth
=20;
336 awt::Size aTotalSize
;
337 for( size_t nPara
=0; nPara
<pTextBody
->getParagraphs().size(); ++nPara
)
339 aTotalSize
.Height
+= nHackyFontHeight
;
341 sal_Int32 nLocalWidth
=0;
342 for( size_t nRun
=0; nRun
<pTextBody
->getParagraphs().at(nPara
)->getRuns().size(); ++nRun
)
344 pTextBody
->getParagraphs().at(nPara
)->getRuns().at(nRun
)->getText().getLength()
347 aTotalSize
.Width
= std::max(
352 rShape
->setSize(aTotalSize
);
362 "Layouting shape " << rName
<< ": (" << rShape
->getPosition().X
<< ","
363 << rShape
->getPosition().Y
<< "," << rShape
->getSize().Width
<< ","
364 << rShape
->getSize().Height
<< ")");
367 void LayoutNode::accept( LayoutAtomVisitor
& rVisitor
)
369 rVisitor
.visit(*this);
372 bool LayoutNode::setupShape( const ShapePtr
& rShape
, const Diagram
& rDgm
, sal_uInt32 nIdx
) const
374 // find the data node to grab text from
375 DiagramData::PointsNameMap::const_iterator aDataNode
=rDgm
.getData()->getPointsPresNameMap().find(msName
);
376 if( aDataNode
!= rDgm
.getData()->getPointsPresNameMap().end() &&
377 aDataNode
->second
.size() > nIdx
)
381 "Filling content from " << nIdx
<< "th layout node named \""
382 << msName
<< "\", modelId \""
383 << aDataNode
->second
.at(nIdx
)->msModelId
<< "\"");
385 // got the presentation node - now, need the actual data node:
386 const DiagramData::StringMap::const_iterator aNodeName
=rDgm
.getData()->getPresOfNameMap().find(
387 aDataNode
->second
.at(nIdx
)->msModelId
);
388 if( aNodeName
!= rDgm
.getData()->getPresOfNameMap().end() )
390 DiagramData::StringMap::value_type::second_type::const_iterator aVecIter
=aNodeName
->second
.begin();
391 const DiagramData::StringMap::value_type::second_type::const_iterator aVecEnd
=aNodeName
->second
.end();
392 while( aVecIter
!= aVecEnd
)
394 DiagramData::PointNameMap
& rMap
= rDgm
.getData()->getPointNameMap();
395 DiagramData::PointNameMap::const_iterator aDataNode2
= rMap
.find(aVecIter
->first
);
396 if (aDataNode2
== rMap
.end())
403 if( aVecIter
->second
== 0 )
405 // grab shape attr from topmost element(s)
406 rShape
->getShapeProperties() = aDataNode2
->second
->mpShape
->getShapeProperties();
407 rShape
->getLineProperties() = aDataNode2
->second
->mpShape
->getLineProperties();
408 rShape
->getFillProperties() = aDataNode2
->second
->mpShape
->getFillProperties();
409 rShape
->getCustomShapeProperties() = aDataNode2
->second
->mpShape
->getCustomShapeProperties();
410 rShape
->setMasterTextListStyle( aDataNode2
->second
->mpShape
->getMasterTextListStyle() );
414 "Custom shape with preset type "
415 << (rShape
->getCustomShapeProperties()
416 ->getShapePresetType())
417 << " added for layout node named \"" << msName
421 // append text with right outline level
422 if( aDataNode2
->second
->mpShape
->getTextBody() &&
423 !aDataNode2
->second
->mpShape
->getTextBody()->getParagraphs().empty() &&
424 !aDataNode2
->second
->mpShape
->getTextBody()->getParagraphs().front()->getRuns().empty() )
426 TextBodyPtr pTextBody
=rShape
->getTextBody();
429 pTextBody
.reset( new TextBody() );
431 // also copy text attrs
432 pTextBody
->getTextListStyle() =
433 aDataNode2
->second
->mpShape
->getTextBody()->getTextListStyle();
434 pTextBody
->getTextProperties() =
435 aDataNode2
->second
->mpShape
->getTextBody()->getTextProperties();
437 rShape
->setTextBody(pTextBody
);
440 TextParagraph
& rPara
=pTextBody
->addParagraph();
441 if( aVecIter
->second
!= -1 )
442 rPara
.getProperties().setLevel(aVecIter
->second
);
445 aDataNode2
->second
->mpShape
->getTextBody()->getParagraphs().front()->getRuns().front());
446 rPara
.getProperties().apply(
447 aDataNode2
->second
->mpShape
->getTextBody()->getParagraphs().front()->getProperties());
457 "ShapeCreationVisitor::visit: no data node name found while"
458 " processing shape type "
459 << rShape
->getCustomShapeProperties()->getShapePresetType()
460 << " for layout node named \"" << msName
<< "\"");
463 // TODO(Q1): apply styling & coloring - taking
464 // layout node's styleLbl for both style & color
465 // now, but docs are a bit unclear on this
466 if( !msStyleLabel
.isEmpty() )
468 const DiagramQStyleMap::const_iterator aStyle
=rDgm
.getStyles().find(msStyleLabel
);
469 if( aStyle
!= rDgm
.getStyles().end() )
471 rShape
->getShapeStyleRefs()[XML_fillRef
] = aStyle
->second
.maFillStyle
;
472 rShape
->getShapeStyleRefs()[XML_lnRef
] = aStyle
->second
.maLineStyle
;
473 rShape
->getShapeStyleRefs()[XML_effectRef
] = aStyle
->second
.maEffectStyle
;
474 rShape
->getShapeStyleRefs()[XML_fontRef
] = aStyle
->second
.maTextStyle
;
475 Color aColor
=aStyle
->second
.maTextStyle
.maPhClr
;
478 const DiagramColorMap::const_iterator aColor
=rDgm
.getColors().find(msStyleLabel
);
479 if( aColor
!= rDgm
.getColors().end() )
481 const DiagramColor
& rColor
=aColor
->second
;
482 if( rColor
.maFillColor
.isUsed() )
483 rShape
->getShapeStyleRefs()[XML_fillRef
].maPhClr
= rColor
.maFillColor
;
484 if( rColor
.maLineColor
.isUsed() )
485 rShape
->getShapeStyleRefs()[XML_lnRef
].maPhClr
= rColor
.maLineColor
;
486 if( rColor
.maEffectColor
.isUsed() )
487 rShape
->getShapeStyleRefs()[XML_effectRef
].maPhClr
= rColor
.maEffectColor
;
488 if( rColor
.maTextFillColor
.isUsed() )
489 rShape
->getShapeStyleRefs()[XML_fontRef
].maPhClr
= rColor
.maTextFillColor
;
493 // even if no data node found, successful anyway. it's
494 // contained at the layoutnode
501 "no text found while processing shape type "
502 << rShape
->getCustomShapeProperties()->getShapePresetType()
503 << " for layout node named \"" << msName
<< "\"");
511 class ShapeLayoutingVisitor
: public LayoutAtomVisitor
513 ShapePtr mpParentShape
;
516 virtual void visit(ConstraintAtom
& rAtom
) override
;
517 virtual void visit(AlgAtom
& rAtom
) override
;
518 virtual void visit(ForEachAtom
& rAtom
) override
;
519 virtual void visit(ConditionAtom
& rAtom
) override
;
520 virtual void visit(ChooseAtom
& rAtom
) override
;
521 virtual void visit(LayoutNode
& rAtom
) override
;
524 ShapeLayoutingVisitor(const ShapePtr
& rParentShape
,
525 const OUString
& rName
) :
526 mpParentShape(rParentShape
),
530 void defaultVisit(LayoutAtom
& rAtom
);
533 class ShallowPresNameVisitor
: public LayoutAtomVisitor
535 const Diagram
& mrDgm
;
538 void defaultVisit(LayoutAtom
& rAtom
);
539 virtual void visit(ConstraintAtom
& rAtom
) override
;
540 virtual void visit(AlgAtom
& rAtom
) override
;
541 virtual void visit(ForEachAtom
& rAtom
) override
;
542 virtual void visit(ConditionAtom
& rAtom
) override
;
543 virtual void visit(ChooseAtom
& rAtom
) override
;
544 virtual void visit(LayoutNode
& rAtom
) override
;
547 explicit ShallowPresNameVisitor(const Diagram
& rDgm
) :
552 size_t getCount() const
556 void ShapeCreationVisitor::defaultVisit(LayoutAtom
& rAtom
)
558 const std::vector
<LayoutAtomPtr
>& rChildren
=rAtom
.getChildren();
559 std::for_each( rChildren
.begin(), rChildren
.end(),
560 [this] (LayoutAtomPtr
const& pAtom
) { pAtom
->accept(*this); } );
563 void ShapeCreationVisitor::visit(ConstraintAtom
& /*rAtom*/)
565 // TODO: eval the constraints
568 void ShapeCreationVisitor::visit(AlgAtom
& rAtom
)
573 void ShapeCreationVisitor::visit(ForEachAtom
& rAtom
)
575 const std::vector
<LayoutAtomPtr
>& rChildren
=rAtom
.getChildren();
577 sal_Int32 nChildren
=1;
578 if( rAtom
.iterator().mnPtType
== XML_node
)
580 // cound child data nodes - check all child Atoms for "name"
581 // attribute that is contained in diagram's
582 // getPointsPresNameMap()
583 ShallowPresNameVisitor
aVisitor(mrDgm
);
584 std::for_each( rChildren
.begin(), rChildren
.end(),
585 [&] (LayoutAtomPtr
const& pAtom
) { pAtom
->accept(aVisitor
); } );
586 nChildren
= aVisitor
.getCount();
589 const sal_Int32 nCnt
= std::min(
591 rAtom
.iterator().mnCnt
==-1 ? nChildren
: rAtom
.iterator().mnCnt
);
593 const sal_Int32 nOldIdx
=mnCurrIdx
;
594 const sal_Int32 nStep
=rAtom
.iterator().mnStep
;
595 for( mnCurrIdx
=0; mnCurrIdx
<nCnt
&& nStep
>0; mnCurrIdx
+=nStep
)
597 // TODO there is likely some conditions
598 std::for_each( rChildren
.begin(), rChildren
.end(),
599 [this] (LayoutAtomPtr
const& pAtom
) { pAtom
->accept(*this); } );
606 void ShapeCreationVisitor::visit(ConditionAtom
& rAtom
)
611 void ShapeCreationVisitor::visit(ChooseAtom
& rAtom
)
616 void ShapeCreationVisitor::visit(LayoutNode
& rAtom
)
618 ShapePtr
pCurrParent(mpParentShape
);
619 ShapePtr
pCurrShape(rAtom
.getShape());
624 "processing shape type "
625 << (pCurrShape
->getCustomShapeProperties()
626 ->getShapePresetType()));
628 // TODO(F3): cloned shape shares all properties by reference,
629 // don't change them!
630 ShapePtr
pClonedShape(
631 new Shape( pCurrShape
));
633 if( rAtom
.setupShape(pClonedShape
, mrDgm
, mnCurrIdx
) )
635 pCurrParent
->addChild(pClonedShape
);
636 pCurrParent
= pClonedShape
;
641 SAL_WARN("oox.drawingml", "ShapeCreationVisitor::visit: no shape set while processing layoutnode named " << rAtom
.getName() );
644 // set new parent for children
645 ShapePtr
pPreviousParent(mpParentShape
);
646 mpParentShape
=pCurrParent
;
652 mpParentShape
=pPreviousParent
;
654 // layout shapes - now all child shapes are created
655 ShapeLayoutingVisitor
aLayoutingVisitor(pCurrParent
,
657 aLayoutingVisitor
.defaultVisit(rAtom
);
660 void ShapeLayoutingVisitor::defaultVisit(LayoutAtom
& rAtom
)
662 // visit all children, one of them needs to be the layout algorithm
663 const std::vector
<LayoutAtomPtr
>& rChildren
=rAtom
.getChildren();
664 std::for_each( rChildren
.begin(), rChildren
.end(),
665 [this] (LayoutAtomPtr
const& pAtom
) { pAtom
->accept(*this); } );
668 void ShapeLayoutingVisitor::visit(ConstraintAtom
& /*rAtom*/)
673 void ShapeLayoutingVisitor::visit(AlgAtom
& rAtom
)
675 rAtom
.layoutShape(mpParentShape
, maName
);
678 void ShapeLayoutingVisitor::visit(ForEachAtom
& /*rAtom*/)
683 void ShapeLayoutingVisitor::visit(ConditionAtom
& rAtom
)
688 void ShapeLayoutingVisitor::visit(ChooseAtom
& rAtom
)
693 void ShapeLayoutingVisitor::visit(LayoutNode
& /*rAtom*/)
695 // stop processing - only traverse Condition/Choose atoms
698 void ShallowPresNameVisitor::defaultVisit(LayoutAtom
& rAtom
)
700 // visit all children, at least one of them needs to have proper
702 const std::vector
<LayoutAtomPtr
>& rChildren
=rAtom
.getChildren();
703 std::for_each( rChildren
.begin(), rChildren
.end(),
704 [this] (LayoutAtomPtr
const& pAtom
) { pAtom
->accept(*this); } );
707 void ShallowPresNameVisitor::visit(ConstraintAtom
& /*rAtom*/)
712 void ShallowPresNameVisitor::visit(AlgAtom
& /*rAtom*/)
717 void ShallowPresNameVisitor::visit(ForEachAtom
& rAtom
)
722 void ShallowPresNameVisitor::visit(ConditionAtom
& rAtom
)
727 void ShallowPresNameVisitor::visit(ChooseAtom
& rAtom
)
732 void ShallowPresNameVisitor::visit(LayoutNode
& rAtom
)
734 DiagramData::PointsNameMap::const_iterator aDataNode
=
735 mrDgm
.getData()->getPointsPresNameMap().find(rAtom
.getName());
736 if( aDataNode
!= mrDgm
.getData()->getPointsPresNameMap().end() )
737 mnCnt
= std::max(mnCnt
,
738 aDataNode
->second
.size());
743 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */