bump product version to 6.4.0.3
[LibreOffice.git] / sd / source / filter / eppt / pptx-animations.cxx
blobf533534f1abbd8ff423757d782845a6f7df2b9ed
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
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 <o3tl/any.hxx>
21 #include <oox/token/tokens.hxx>
22 #include "epptooxml.hxx"
23 #include <sax/fshelper.hxx>
24 #include <sal/log.hxx>
26 #include <com/sun/star/animations/AnimationAdditiveMode.hpp>
27 #include <com/sun/star/animations/AnimationCalcMode.hpp>
28 #include <com/sun/star/animations/AnimationFill.hpp>
29 #include <com/sun/star/animations/AnimationNodeType.hpp>
30 #include <com/sun/star/animations/AnimationRestart.hpp>
31 #include <com/sun/star/animations/AnimationTransformType.hpp>
32 #include <com/sun/star/animations/AnimationValueType.hpp>
33 #include <com/sun/star/animations/AnimationColorSpace.hpp>
34 #include <com/sun/star/animations/Event.hpp>
35 #include <com/sun/star/animations/EventTrigger.hpp>
36 #include <com/sun/star/animations/Timing.hpp>
37 #include <com/sun/star/animations/ValuePair.hpp>
38 #include <com/sun/star/animations/XAnimateMotion.hpp>
39 #include <com/sun/star/animations/XAnimateTransform.hpp>
40 #include <com/sun/star/animations/XAnimationNode.hpp>
41 #include <com/sun/star/animations/XAnimationNodeSupplier.hpp>
42 #include <com/sun/star/animations/XAnimateColor.hpp>
43 #include <com/sun/star/animations/XCommand.hpp>
44 #include <com/sun/star/animations/XAudio.hpp>
45 #include <com/sun/star/animations/XTransitionFilter.hpp>
46 #include <com/sun/star/animations/XIterateContainer.hpp>
47 #include <com/sun/star/container/XEnumerationAccess.hpp>
48 #include <com/sun/star/presentation/EffectCommands.hpp>
49 #include <com/sun/star/presentation/EffectNodeType.hpp>
50 #include <com/sun/star/presentation/EffectPresetClass.hpp>
51 #include <com/sun/star/presentation/ParagraphTarget.hpp>
52 #include <com/sun/star/presentation/TextAnimationType.hpp>
53 #include <com/sun/star/text/XSimpleText.hpp>
54 #include <com/sun/star/drawing/XShape.hpp>
55 #include <oox/export/utils.hxx>
56 #include <oox/ppt/pptfilterhelpers.hxx>
57 #include <basegfx/polygon/b2dpolypolygontools.hxx>
59 #include "pptexanimations.hxx"
60 #include "pptx-animations.hxx"
61 #include "../ppt/pptanimations.hxx"
63 using namespace ::com::sun::star::animations;
64 using namespace ::com::sun::star::container;
65 using namespace ::com::sun::star::presentation;
66 using namespace ::com::sun::star::uno;
67 using namespace ::ppt;
68 using namespace oox::drawingml;
69 using namespace oox::core;
70 using namespace oox;
72 using ::com::sun::star::beans::NamedValue;
73 using ::com::sun::star::drawing::XDrawPage;
74 using ::com::sun::star::drawing::XShape;
75 using ::com::sun::star::text::XSimpleText;
76 using ::sax_fastparser::FSHelperPtr;
78 namespace
80 void WriteAnimationProperty(const FSHelperPtr& pFS, const Any& rAny, sal_Int32 nToken = 0)
82 if (!rAny.hasValue())
83 return;
85 ValuePair aPair;
87 if (rAny >>= aPair)
89 double x, y;
90 if ((aPair.First >>= x) && (aPair.Second >>= y))
92 if (nToken == XML_by)
94 // MS needs ending values but we have offset values.
95 x += 1.0;
96 y += 1.0;
98 pFS->singleElementNS(XML_p, nToken, XML_x, OString::number(x * 100000), XML_y,
99 OString::number(y * 100000));
101 return;
104 sal_uInt32 nRgb;
105 double fDouble;
107 TypeClass aClass = rAny.getValueType().getTypeClass();
108 bool bWriteToken
109 = nToken
110 && (aClass == TypeClass_LONG || aClass == TypeClass_DOUBLE || aClass == TypeClass_STRING);
112 if (bWriteToken)
113 pFS->startElementNS(XML_p, nToken);
115 switch (rAny.getValueType().getTypeClass())
117 case TypeClass_LONG:
118 rAny >>= nRgb;
119 pFS->singleElementNS(XML_a, XML_srgbClr, XML_val, I32SHEX(nRgb));
120 break;
121 case TypeClass_DOUBLE:
122 rAny >>= fDouble;
123 pFS->singleElementNS(XML_p, XML_fltVal, XML_val, OString::number(fDouble));
124 break;
125 case TypeClass_STRING:
126 pFS->singleElementNS(XML_p, XML_strVal, XML_val,
127 (*o3tl::doAccess<OUString>(rAny)).toUtf8());
128 break;
129 default:
130 break;
133 if (bWriteToken)
134 pFS->endElementNS(XML_p, nToken);
137 void WriteAnimateColorColor(const FSHelperPtr& pFS, const Any& rAny, sal_Int32 nToken)
139 if (!rAny.hasValue())
140 return;
142 sal_Int32 nColor = 0;
143 if (rAny >>= nColor)
145 pFS->startElementNS(XML_p, nToken);
147 if (nToken == XML_by)
149 // CT_TLByRgbColorTransform
150 SAL_WARN("sd.eppt", "Export p:rgb in p:by of animClr isn't implemented yet.");
152 else
154 // CT_Color
155 pFS->singleElementNS(XML_a, XML_srgbClr, XML_val, I32SHEX(nColor));
158 pFS->endElementNS(XML_p, nToken);
161 Sequence<double> aHSL(3);
162 if (!(rAny >>= aHSL))
163 return;
165 pFS->startElementNS(XML_p, nToken);
167 if (nToken == XML_by)
169 // CT_TLByHslColorTransform
170 pFS->singleElementNS(XML_p, XML_hsl, XML_h, OString::number(aHSL[0] * 60000), // ST_Angel
171 XML_s, OString::number(aHSL[1] * 100000), XML_l,
172 OString::number(aHSL[2] * 100000));
174 else
176 // CT_Color
177 SAL_WARN("sd.eppt", "Export p:hsl in p:from or p:to of animClr isn't implemented yet.");
180 pFS->endElementNS(XML_p, nToken);
183 void WriteAnimateTo(const FSHelperPtr& pFS, const Any& rValue, const OUString& rAttributeName)
185 if (!rValue.hasValue())
186 return;
188 SAL_INFO("sd.eppt", "to attribute name: " << rAttributeName.toUtf8());
190 WriteAnimationProperty(pFS, AnimationExporter::convertAnimateValue(rValue, rAttributeName),
191 XML_to);
194 void WriteAnimateValues(const FSHelperPtr& pFS, const Reference<XAnimate>& rXAnimate)
196 const Sequence<double> aKeyTimes = rXAnimate->getKeyTimes();
197 if (!aKeyTimes.hasElements())
198 return;
199 const Sequence<Any> aValues = rXAnimate->getValues();
200 const OUString& sFormula = rXAnimate->getFormula();
201 const OUString& rAttributeName = rXAnimate->getAttributeName();
203 SAL_INFO("sd.eppt", "animate values, formula: " << sFormula.toUtf8());
205 pFS->startElementNS(XML_p, XML_tavLst);
207 for (int i = 0; i < aKeyTimes.getLength(); i++)
209 SAL_INFO("sd.eppt", "animate value " << i << ": " << aKeyTimes[i]);
210 if (aValues[i].hasValue())
212 pFS->startElementNS(XML_p, XML_tav, XML_fmla,
213 sFormula.isEmpty() ? nullptr : sFormula.toUtf8().getStr(), XML_tm,
214 OString::number(static_cast<sal_Int32>(aKeyTimes[i] * 100000.0)));
215 pFS->startElementNS(XML_p, XML_val);
216 ValuePair aPair;
217 if (aValues[i] >>= aPair)
219 WriteAnimationProperty(
220 pFS, AnimationExporter::convertAnimateValue(aPair.First, rAttributeName));
221 WriteAnimationProperty(
222 pFS, AnimationExporter::convertAnimateValue(aPair.Second, rAttributeName));
224 else
225 WriteAnimationProperty(
226 pFS, AnimationExporter::convertAnimateValue(aValues[i], rAttributeName));
228 pFS->endElementNS(XML_p, XML_val);
229 pFS->endElementNS(XML_p, XML_tav);
233 pFS->endElementNS(XML_p, XML_tavLst);
236 // Write condition list ( either prevCondlst or nextCondlst ) of Seq.
237 void WriteAnimationCondListForSeq(const FSHelperPtr& pFS, sal_Int32 nToken)
239 const char* pEvent = (nToken == XML_prevCondLst) ? "onPrev" : "onNext";
241 pFS->startElementNS(XML_p, nToken);
242 pFS->startElementNS(XML_p, XML_cond, XML_evt, pEvent);
243 pFS->startElementNS(XML_p, XML_tgtEl);
244 pFS->singleElementNS(XML_p, XML_sldTgt);
245 pFS->endElementNS(XML_p, XML_tgtEl);
246 pFS->endElementNS(XML_p, XML_cond);
247 pFS->endElementNS(XML_p, nToken);
250 const char* convertEventTrigger(sal_Int16 nTrigger)
252 const char* pEvent = nullptr;
253 switch (nTrigger)
255 case EventTrigger::ON_NEXT:
256 pEvent = "onNext";
257 break;
258 case EventTrigger::ON_PREV:
259 pEvent = "onPrev";
260 break;
261 case EventTrigger::BEGIN_EVENT:
262 pEvent = "begin";
263 break;
264 case EventTrigger::END_EVENT:
265 pEvent = "end";
266 break;
267 case EventTrigger::ON_BEGIN:
268 pEvent = "onBegin";
269 break;
270 case EventTrigger::ON_END:
271 pEvent = "onEnd";
272 break;
273 case EventTrigger::ON_CLICK:
274 pEvent = "onClick";
275 break;
276 case EventTrigger::ON_DBL_CLICK:
277 pEvent = "onDblClick";
278 break;
279 case EventTrigger::ON_STOP_AUDIO:
280 pEvent = "onStopAudio";
281 break;
282 case EventTrigger::ON_MOUSE_ENTER:
283 pEvent = "onMouseOver"; // not exact?
284 break;
285 case EventTrigger::ON_MOUSE_LEAVE:
286 pEvent = "onMouseOut";
287 break;
289 return pEvent;
292 void WriteAnimationAttributeName(const FSHelperPtr& pFS, const OUString& rAttributeName)
294 if (rAttributeName.isEmpty())
295 return;
297 pFS->startElementNS(XML_p, XML_attrNameLst);
299 SAL_INFO("sd.eppt", "write attribute name: " << rAttributeName.toUtf8());
301 if (rAttributeName == "X;Y")
303 pFS->startElementNS(XML_p, XML_attrName);
304 pFS->writeEscaped("ppt_x");
305 pFS->endElementNS(XML_p, XML_attrName);
307 pFS->startElementNS(XML_p, XML_attrName);
308 pFS->writeEscaped("ppt_y");
309 pFS->endElementNS(XML_p, XML_attrName);
311 else
313 const oox::ppt::ImplAttributeNameConversion* attrConv
314 = oox::ppt::getAttributeConversionList();
315 const char* pAttribute = nullptr;
317 while (attrConv->mpAPIName != nullptr)
319 if (rAttributeName.equalsAscii(attrConv->mpAPIName))
321 pAttribute = attrConv->mpMSName;
322 break;
324 attrConv++;
327 if (pAttribute)
329 pFS->startElementNS(XML_p, XML_attrName);
330 pFS->writeEscaped(pAttribute);
331 pFS->endElementNS(XML_p, XML_attrName);
333 else
335 SAL_WARN("sd.eppt", "unhandled animation attribute name: " << rAttributeName);
339 pFS->endElementNS(XML_p, XML_attrNameLst);
342 bool isValidTarget(const Any& rTarget)
344 Reference<XShape> xShape;
346 if ((rTarget >>= xShape) && xShape.is())
347 return true;
349 ParagraphTarget aParagraphTarget;
351 return (rTarget >>= aParagraphTarget) && aParagraphTarget.Shape.is();
354 /// extract ooxml node type from a XAnimationNode.
355 sal_Int32 extractNodeType(const Reference<XAnimationNode>& rXNode)
357 sal_Int16 nType = rXNode->getType();
358 sal_Int32 xmlNodeType = -1;
359 switch (nType)
361 case AnimationNodeType::ITERATE:
362 case AnimationNodeType::PAR:
363 xmlNodeType = XML_par;
364 break;
365 case AnimationNodeType::SEQ:
366 xmlNodeType = XML_seq;
367 break;
368 case AnimationNodeType::ANIMATE:
369 xmlNodeType = XML_anim;
370 break;
371 case AnimationNodeType::ANIMATEMOTION:
372 xmlNodeType = XML_animMotion;
373 break;
374 case AnimationNodeType::ANIMATETRANSFORM:
376 Reference<XAnimateTransform> xTransform(rXNode, UNO_QUERY);
377 if (xTransform.is())
379 if (xTransform->getTransformType() == AnimationTransformType::SCALE)
380 xmlNodeType = XML_animScale;
381 else if (xTransform->getTransformType() == AnimationTransformType::ROTATE)
382 xmlNodeType = XML_animRot;
384 break;
386 case AnimationNodeType::ANIMATECOLOR:
387 xmlNodeType = XML_animClr;
388 break;
389 case AnimationNodeType::SET:
390 xmlNodeType = XML_set;
391 break;
392 case AnimationNodeType::TRANSITIONFILTER:
393 xmlNodeType = XML_animEffect;
394 break;
395 case AnimationNodeType::COMMAND:
396 xmlNodeType = XML_cmd;
397 break;
398 case AnimationNodeType::AUDIO:
399 xmlNodeType = XML_audio;
400 break;
401 default:
402 SAL_WARN("sd.eppt", "unhandled animation node: " << nType);
403 break;
405 return xmlNodeType;
408 /// Convert AnimationRestart to ST_TLTimeNodeRestartType value.
409 const char* convertAnimationRestart(sal_Int16 nRestart)
411 const char* pRestart = nullptr;
412 switch (nRestart)
414 case AnimationRestart::ALWAYS:
415 pRestart = "always";
416 break;
417 case AnimationRestart::WHEN_NOT_ACTIVE:
418 pRestart = "whenNotActive";
419 break;
420 case AnimationRestart::NEVER:
421 pRestart = "never";
422 break;
424 return pRestart;
427 /// Convert EffectNodeType to ST_TLTimeNodeType
428 const char* convertEffectNodeType(sal_Int16 nType)
430 const char* pNodeType = nullptr;
431 switch (nType)
433 case EffectNodeType::TIMING_ROOT:
434 pNodeType = "tmRoot";
435 break;
436 case EffectNodeType::MAIN_SEQUENCE:
437 pNodeType = "mainSeq";
438 break;
439 case EffectNodeType::ON_CLICK:
440 pNodeType = "clickEffect";
441 break;
442 case EffectNodeType::AFTER_PREVIOUS:
443 pNodeType = "afterEffect";
444 break;
445 case EffectNodeType::WITH_PREVIOUS:
446 pNodeType = "withEffect";
447 break;
448 case EffectNodeType::INTERACTIVE_SEQUENCE:
449 pNodeType = "interactiveSeq";
450 break;
452 return pNodeType;
455 /// Convert EffectPresetClass to ST_TLTimeNodePresetClassType
456 const char* convertEffectPresetClass(sal_Int16 nPresetClass)
458 const char* pPresetClass = nullptr;
459 switch (nPresetClass)
461 case EffectPresetClass::ENTRANCE:
462 pPresetClass = "entr";
463 break;
464 case EffectPresetClass::EXIT:
465 pPresetClass = "exit";
466 break;
467 case EffectPresetClass::EMPHASIS:
468 pPresetClass = "emph";
469 break;
470 case EffectPresetClass::MOTIONPATH:
471 pPresetClass = "path";
472 break;
473 case EffectPresetClass::OLEACTION:
474 pPresetClass = "verb"; // ?
475 break;
476 case EffectPresetClass::MEDIACALL:
477 pPresetClass = "mediacall";
478 break;
480 return pPresetClass;
483 /// convert AnimationFill to ST_TLTimeNodeFillType.
484 const char* convertAnimationFill(sal_Int16 nFill)
486 const char* pFill = nullptr;
487 switch (nFill)
489 case AnimationFill::FREEZE:
490 pFill = "hold";
491 break;
492 case AnimationFill::HOLD:
493 pFill = "hold";
494 break;
495 case AnimationFill::REMOVE:
496 pFill = "remove";
497 break;
498 case AnimationFill::TRANSITION:
499 pFill = "transition";
500 break;
502 return pFill;
505 /// Convert TextAnimationType to ST_IterateType.
506 const char* convertTextAnimationType(sal_Int16 nType)
508 const char* sType = nullptr;
509 switch (nType)
511 case TextAnimationType::BY_PARAGRAPH:
512 sType = "el";
513 break;
514 case TextAnimationType::BY_LETTER:
515 sType = "lt";
516 break;
517 case TextAnimationType::BY_WORD:
518 default:
519 sType = "wd";
520 break;
522 return sType;
525 class NodeContext;
527 typedef std::unique_ptr<NodeContext> NodeContextPtr;
529 class NodeContext
531 const Reference<XAnimationNode> mxNode;
532 const bool mbMainSeqChild;
534 std::vector<NodeContextPtr> maChildNodes;
535 // if the node has valid target or contains at least one valid target.
536 bool mbValid;
538 // Attributes initialized from mxNode->getUserData().
539 sal_Int16 mnEffectNodeType;
540 sal_Int16 mnEffectPresetClass;
541 OUString msEffectPresetId;
542 OUString msEffectPresetSubType;
544 /// constructor helper for initializing user data.
545 void initUserData();
547 /// constructor helper to initialize maChildNodes.
548 /// return true if at least one childnode is valid.
549 bool initChildNodes();
551 /// constructor helper to initialize mbValid
552 void initValid(bool bHasValidChild, bool bIsIterateChild);
554 public:
555 NodeContext(const Reference<XAnimationNode>& xNode, bool bMainSeqChild, bool bIsIterateChild);
556 const Reference<XAnimationNode>& getNode() const { return mxNode; }
557 bool isMainSeqChild() const { return mbMainSeqChild; }
558 sal_Int16 getEffectNodeType() const { return mnEffectNodeType; }
559 sal_Int16 getEffectPresetClass() const { return mnEffectPresetClass; }
560 const OUString& getEffectPresetId() const { return msEffectPresetId; }
561 const OUString& getEffectPresetSubType() const { return msEffectPresetSubType; }
562 bool isValid() const { return mbValid; }
563 const std::vector<NodeContextPtr>& getChildNodes() const { return maChildNodes; };
564 Any getCondition(bool bBegin) const;
567 struct Cond
569 OString msDelay;
570 const char* mpEvent;
571 Reference<XShape> mxShape;
572 Reference<XAnimationNode> mxNode;
574 Cond(const Any& rAny, bool bIsMainSeqChild);
576 bool isValid() const { return msDelay.getLength() || mpEvent; }
577 const char* getDelay() const { return msDelay.getLength() ? msDelay.getStr() : nullptr; }
580 Cond::Cond(const Any& rAny, bool bIsMainSeqChild)
581 : mpEvent(nullptr)
583 bool bHasFDelay = false;
584 double fDelay = 0;
585 Timing eTiming;
586 Event aEvent;
588 if (rAny >>= eTiming)
590 if (eTiming == Timing_INDEFINITE)
591 msDelay = "indefinite";
593 else if (rAny >>= aEvent)
595 if (aEvent.Trigger == EventTrigger::ON_NEXT && bIsMainSeqChild)
596 msDelay = "indefinite";
597 else
599 mpEvent = convertEventTrigger(aEvent.Trigger);
600 if (!(aEvent.Source >>= mxShape))
601 aEvent.Source >>= mxNode;
603 if (aEvent.Offset >>= fDelay)
604 bHasFDelay = true;
607 else if (rAny >>= fDelay)
608 bHasFDelay = true;
610 if (bHasFDelay)
612 sal_Int32 nDelay = static_cast<sal_uInt32>(fDelay * 1000.0);
613 msDelay = OString::number(nDelay);
617 class PPTXAnimationExport
619 void WriteAnimationNode(const NodeContextPtr& pContext);
620 void WriteAnimationNodeAnimate(sal_Int32 nXmlNodeType);
621 void WriteAnimationNodeAnimateInside(bool bSimple, bool bWriteTo = true);
622 void WriteAnimationNodeSeq();
623 void WriteAnimationNodeEffect();
624 void WriteAnimationNodeCommand();
625 void WriteAnimationNodeAudio();
626 void WriteAnimationNodeCommonPropsStart();
627 void WriteAnimationTarget(const Any& rTarget);
628 void WriteAnimationCondList(const Any& rAny, sal_Int32 nToken);
629 void WriteAnimationCond(const Cond& rCond);
630 bool isMainSeqChild() const;
631 const Reference<XAnimationNode>& getCurrentNode() const;
633 PowerPointExport& mrPowerPointExport;
634 const FSHelperPtr& mpFS;
635 const NodeContext* mpContext;
637 std::map<Reference<XAnimationNode>, sal_Int32> maAnimationNodeIdMap;
638 sal_Int32 GetNextAnimationNodeId(const Reference<XAnimationNode>& rNode);
639 sal_Int32 GetAnimationNodeId(const Reference<XAnimationNode>& rNode);
641 public:
642 PPTXAnimationExport(PowerPointExport& rExport, const FSHelperPtr& pFS);
643 void WriteAnimations(const Reference<XDrawPage>& rXDrawPage);
647 namespace oox
649 namespace core
651 void WriteAnimations(const FSHelperPtr& pFS, const Reference<XDrawPage>& rXDrawPage,
652 PowerPointExport& rExport)
654 PPTXAnimationExport aAnimationExport(rExport, pFS);
655 aAnimationExport.WriteAnimations(rXDrawPage);
660 PPTXAnimationExport::PPTXAnimationExport(PowerPointExport& rExport, const FSHelperPtr& pFS)
661 : mrPowerPointExport(rExport)
662 , mpFS(pFS)
663 , mpContext(nullptr)
667 bool PPTXAnimationExport::isMainSeqChild() const
669 assert(mpContext);
670 return mpContext->isMainSeqChild();
673 const Reference<XAnimationNode>& PPTXAnimationExport::getCurrentNode() const
675 assert(mpContext);
676 return mpContext->getNode();
679 void PPTXAnimationExport::WriteAnimationTarget(const Any& rTarget)
681 sal_Int32 nParagraph = -1;
682 bool bParagraphTarget = false;
684 Reference<XShape> rXShape;
685 rTarget >>= rXShape;
687 if (!rXShape.is())
689 ParagraphTarget aParagraphTarget;
690 if (rTarget >>= aParagraphTarget)
691 rXShape = aParagraphTarget.Shape;
692 if (rXShape.is())
694 nParagraph = static_cast<sal_Int32>(aParagraphTarget.Paragraph);
695 Reference<XSimpleText> xText(rXShape, UNO_QUERY);
696 if (xText.is())
698 bParagraphTarget = true;
703 if (!rXShape.is())
704 return;
706 sal_Int32 nShapeID = mrPowerPointExport.GetShapeID(rXShape);
708 mpFS->startElementNS(XML_p, XML_tgtEl);
709 mpFS->startElementNS(XML_p, XML_spTgt, XML_spid, OString::number(nShapeID));
710 if (bParagraphTarget)
712 mpFS->startElementNS(XML_p, XML_txEl);
713 mpFS->singleElementNS(XML_p, XML_pRg, XML_st, OString::number(nParagraph), XML_end,
714 OString::number(nParagraph));
715 mpFS->endElementNS(XML_p, XML_txEl);
717 mpFS->endElementNS(XML_p, XML_spTgt);
718 mpFS->endElementNS(XML_p, XML_tgtEl);
721 void PPTXAnimationExport::WriteAnimationCondList(const Any& rAny, sal_Int32 nToken)
723 if (!rAny.hasValue())
724 return;
726 std::vector<Cond> aList;
728 bool bIsMainSeqChild = isMainSeqChild();
730 Sequence<Any> aCondSeq;
731 if (rAny >>= aCondSeq)
733 for (const auto& rCond : std::as_const(aCondSeq))
735 Cond aCond(rCond, bIsMainSeqChild);
736 if (aCond.isValid())
737 aList.push_back(aCond);
740 else
742 Cond aCond(rAny, bIsMainSeqChild);
743 if (aCond.isValid())
744 aList.push_back(aCond);
747 if (aList.size() > 0)
749 mpFS->startElementNS(XML_p, nToken);
751 for (const Cond& rCond : aList)
752 WriteAnimationCond(rCond);
754 mpFS->endElementNS(XML_p, nToken);
758 void PPTXAnimationExport::WriteAnimationCond(const Cond& rCond)
760 sal_Int32 nId = -1;
761 if (rCond.mpEvent)
763 if (rCond.mxShape.is())
765 mpFS->startElementNS(XML_p, XML_cond, XML_delay, rCond.getDelay(), XML_evt,
766 rCond.mpEvent);
767 WriteAnimationTarget(makeAny(rCond.mxShape));
768 mpFS->endElementNS(XML_p, XML_cond);
770 else if (rCond.mxNode.is() && (nId = GetAnimationNodeId(rCond.mxNode)) != -1)
772 mpFS->startElementNS(XML_p, XML_cond, XML_delay, rCond.getDelay(), XML_evt,
773 rCond.mpEvent);
774 mpFS->singleElementNS(XML_p, XML_tn, XML_val, OString::number(nId));
775 mpFS->endElementNS(XML_p, XML_cond);
777 else
779 mpFS->singleElementNS(XML_p, XML_cond, XML_delay, rCond.getDelay(), XML_evt,
780 rCond.mpEvent);
783 else
784 mpFS->singleElementNS(XML_p, XML_cond, XML_delay, rCond.getDelay());
787 void PPTXAnimationExport::WriteAnimationNodeAnimate(sal_Int32 nXmlNodeType)
789 const Reference<XAnimationNode>& rXNode = getCurrentNode();
790 Reference<XAnimate> rXAnimate(rXNode, UNO_QUERY);
791 if (!rXAnimate.is())
792 return;
794 const char* pCalcMode = nullptr;
795 const char* pValueType = nullptr;
796 bool bSimple = (nXmlNodeType != XML_anim);
797 bool bTo = true;
799 if (!bSimple)
801 switch (rXAnimate->getCalcMode())
803 case AnimationCalcMode::DISCRETE:
804 pCalcMode = "discrete";
805 break;
806 case AnimationCalcMode::LINEAR:
807 pCalcMode = "lin";
808 break;
811 switch (AnimationExporter::GetValueTypeForAttributeName(rXAnimate->getAttributeName()))
813 case AnimationValueType::STRING:
814 pValueType = "str";
815 break;
816 case AnimationValueType::NUMBER:
817 pValueType = "num";
818 break;
819 case AnimationValueType::COLOR:
820 pValueType = "clr";
821 break;
825 if (nXmlNodeType == XML_animMotion)
827 OUString aPath;
828 Reference<XAnimateMotion> xMotion(rXNode, UNO_QUERY);
829 if (xMotion.is())
831 xMotion->getPath() >>= aPath;
832 ::basegfx::B2DPolyPolygon aPolyPoly;
833 if (::basegfx::utils::importFromSvgD(aPolyPoly, aPath, true, nullptr))
834 aPath = ::basegfx::utils::exportToSvgD(aPolyPoly, false, false, true, true);
837 mpFS->startElementNS(XML_p, nXmlNodeType, XML_origin, "layout", XML_path, aPath.toUtf8());
839 else if (nXmlNodeType == XML_animRot)
841 // when const char* is nullptr, the attribute is completely omitted in the output
842 const char* pBy = nullptr;
843 const char* pFrom = nullptr;
844 const char* pTo = nullptr;
845 OString aBy, aFrom, aTo;
847 Reference<XAnimateTransform> xTransform(rXNode, UNO_QUERY);
848 if (xTransform.is())
850 double value;
851 if (xTransform->getBy() >>= value)
853 aBy = OString::number(static_cast<int>(value * PER_DEGREE));
854 pBy = aBy.getStr();
857 if (xTransform->getFrom() >>= value)
859 aFrom = OString::number(static_cast<int>(value * PER_DEGREE));
860 pFrom = aFrom.getStr();
863 if (xTransform->getTo() >>= value)
865 aTo = OString::number(static_cast<int>(value * PER_DEGREE));
866 pTo = aTo.getStr();
870 mpFS->startElementNS(XML_p, nXmlNodeType, XML_by, pBy, XML_from, pFrom, XML_to, pTo);
872 else if (nXmlNodeType == XML_animClr)
874 Reference<XAnimateColor> xColor(rXNode, UNO_QUERY);
875 const char* pColorSpace = "rgb";
876 const char* pDirection = nullptr;
877 if (xColor.is() && xColor->getColorInterpolation() == AnimationColorSpace::HSL)
879 // Note: from, to, by can still be specified in any supported format.
880 pColorSpace = "hsl";
881 pDirection = xColor->getDirection() ? "cw" : "ccw";
883 mpFS->startElementNS(XML_p, nXmlNodeType, XML_clrSpc, pColorSpace, XML_dir, pDirection,
884 XML_calcmode, pCalcMode, XML_valueType, pValueType);
886 else
888 OUString sFrom, sTo, sBy;
889 if (rXAnimate.is() && nXmlNodeType == XML_anim)
891 OUString sAttributeName = rXAnimate->getAttributeName();
892 Any aFrom
893 = AnimationExporter::convertAnimateValue(rXAnimate->getFrom(), sAttributeName);
894 aFrom >>= sFrom;
895 Any aTo = AnimationExporter::convertAnimateValue(rXAnimate->getTo(), sAttributeName);
896 aTo >>= sTo;
897 Any aBy = AnimationExporter::convertAnimateValue(rXAnimate->getBy(), sAttributeName);
898 aBy >>= sBy;
901 mpFS->startElementNS(XML_p, nXmlNodeType, XML_calcmode, pCalcMode, XML_valueType,
902 pValueType, XML_from,
903 sFrom.isEmpty() ? nullptr : sFrom.toUtf8().getStr(), XML_to,
904 sTo.isEmpty() ? nullptr : sTo.toUtf8().getStr(), XML_by,
905 sBy.isEmpty() ? nullptr : sBy.toUtf8().getStr());
906 bTo = sTo.isEmpty() && sFrom.isEmpty() && sBy.isEmpty();
909 WriteAnimationNodeAnimateInside(bSimple, bTo);
910 mpFS->endElementNS(XML_p, nXmlNodeType);
913 void PPTXAnimationExport::WriteAnimationNodeAnimateInside(bool bSimple, bool bWriteTo)
915 const Reference<XAnimationNode>& rXNode = getCurrentNode();
916 Reference<XAnimate> rXAnimate(rXNode, UNO_QUERY);
917 if (!rXAnimate.is())
918 return;
920 const char* pAdditive = nullptr;
922 if (!bSimple)
924 switch (rXAnimate->getAdditive())
926 case AnimationAdditiveMode::BASE:
927 pAdditive = "base";
928 break;
929 case AnimationAdditiveMode::SUM:
930 pAdditive = "sum";
931 break;
932 case AnimationAdditiveMode::REPLACE:
933 pAdditive = "repl";
934 break;
935 case AnimationAdditiveMode::MULTIPLY:
936 pAdditive = "mult";
937 break;
938 case AnimationAdditiveMode::NONE:
939 pAdditive = "none";
940 break;
944 mpFS->startElementNS(XML_p, XML_cBhvr, XML_additive, pAdditive);
945 WriteAnimationNodeCommonPropsStart();
947 Reference<XIterateContainer> xIterate(rXNode->getParent(), UNO_QUERY);
948 WriteAnimationTarget(xIterate.is() ? xIterate->getTarget() : rXAnimate->getTarget());
950 Reference<XAnimateTransform> xTransform(rXNode, UNO_QUERY);
952 // The attribute name of AnimateTransform is "Transform", we have to fix it.
953 OUString sNewAttr;
954 if (xTransform.is() && xTransform->getTransformType() == AnimationTransformType::ROTATE)
955 sNewAttr = "Rotate";
957 WriteAnimationAttributeName(mpFS, xTransform.is() ? sNewAttr : rXAnimate->getAttributeName());
959 mpFS->endElementNS(XML_p, XML_cBhvr);
960 WriteAnimateValues(mpFS, rXAnimate);
962 Reference<XAnimateColor> xColor(rXNode, UNO_QUERY);
964 if (xColor.is())
966 WriteAnimateColorColor(mpFS, xColor->getBy(), XML_by);
967 WriteAnimateColorColor(mpFS, xColor->getFrom(), XML_from);
968 WriteAnimateColorColor(mpFS, xColor->getTo(), XML_to);
970 else if (xTransform.is() && xTransform->getTransformType() == AnimationTransformType::SCALE)
972 WriteAnimationProperty(mpFS, rXAnimate->getBy(), XML_by);
973 WriteAnimationProperty(mpFS, rXAnimate->getFrom(), XML_from);
974 WriteAnimationProperty(mpFS, rXAnimate->getTo(), XML_to);
976 else if (bWriteTo)
977 WriteAnimateTo(mpFS, rXAnimate->getTo(), rXAnimate->getAttributeName());
980 void PPTXAnimationExport::WriteAnimationNodeCommonPropsStart()
982 const Reference<XAnimationNode>& rXNode = getCurrentNode();
983 const char* pDuration = nullptr;
984 const char* pRestart = nullptr;
985 const char* pNodeType = nullptr;
986 const char* pPresetClass = nullptr;
987 const char* pFill = nullptr;
988 double fDuration = 0;
989 Any aAny;
990 assert(mpContext);
992 aAny = rXNode->getDuration();
993 if (aAny.hasValue())
995 Timing eTiming;
997 if (aAny >>= eTiming)
999 if (eTiming == Timing_INDEFINITE)
1000 pDuration = "indefinite";
1002 else
1003 aAny >>= fDuration;
1006 pRestart = convertAnimationRestart(rXNode->getRestart());
1008 sal_Int16 nType = mpContext->getEffectNodeType();
1009 if (nType != -1)
1011 pNodeType = convertEffectNodeType(nType);
1012 if (nType == EffectNodeType::TIMING_ROOT)
1014 if (!pDuration)
1015 pDuration = "indefinite";
1016 if (!pRestart)
1017 pRestart = "never";
1019 else if (nType == EffectNodeType::MAIN_SEQUENCE)
1021 pDuration = "indefinite";
1025 sal_uInt32 nPresetClass = mpContext->getEffectPresetClass();
1026 if (nPresetClass != DFF_ANIM_PRESS_CLASS_USER_DEFINED)
1027 pPresetClass = convertEffectPresetClass(nPresetClass);
1029 sal_uInt32 nPresetId = 0;
1030 bool bPresetId = false;
1031 const OUString& rPresetId = mpContext->getEffectPresetId();
1032 if (rPresetId.getLength() > 0)
1034 nPresetId = AnimationExporter::GetPresetID(rPresetId, nPresetClass, bPresetId);
1035 bPresetId = true;
1038 sal_uInt32 nPresetSubType = 0;
1039 bool bPresetSubType = false;
1040 const OUString& sPresetSubType = mpContext->getEffectPresetSubType();
1041 if (sPresetSubType.getLength() > 0)
1043 nPresetSubType
1044 = AnimationExporter::TranslatePresetSubType(nPresetClass, nPresetId, sPresetSubType);
1045 bPresetSubType = true;
1048 if (nType != EffectNodeType::TIMING_ROOT && nType != EffectNodeType::MAIN_SEQUENCE)
1050 // it doesn't seem to work right on root and mainseq nodes
1051 sal_Int16 nFill = AnimationExporter::GetFillMode(rXNode, AnimationFill::AUTO);
1052 pFill = convertAnimationFill(nFill);
1055 bool bAutoReverse = rXNode->getAutoReverse();
1057 mpFS->startElementNS(
1058 XML_p, XML_cTn, XML_id, OString::number(GetNextAnimationNodeId(rXNode)), XML_dur,
1059 fDuration != 0 ? OString::number(static_cast<sal_Int32>(fDuration * 1000.0)).getStr()
1060 : pDuration,
1061 XML_autoRev, bAutoReverse ? "1" : nullptr, XML_restart, pRestart, XML_nodeType, pNodeType,
1062 XML_fill, pFill, XML_presetClass, pPresetClass, XML_presetID,
1063 bPresetId ? OString::number(nPresetId).getStr() : nullptr, XML_presetSubtype,
1064 bPresetSubType ? OString::number(nPresetSubType).getStr() : nullptr);
1066 WriteAnimationCondList(mpContext->getCondition(true), XML_stCondLst);
1067 WriteAnimationCondList(mpContext->getCondition(false), XML_endCondLst);
1069 if (rXNode->getType() == AnimationNodeType::ITERATE)
1071 Reference<XIterateContainer> xIterate(rXNode, UNO_QUERY);
1072 if (xIterate.is())
1074 const char* sType = convertTextAnimationType(xIterate->getIterateType());
1076 mpFS->startElementNS(XML_p, XML_iterate, XML_type, sType);
1077 mpFS->singleElementNS(XML_p, XML_tmAbs, XML_val,
1078 OString::number(xIterate->getIterateInterval() * 1000));
1079 mpFS->endElementNS(XML_p, XML_iterate);
1083 const std::vector<NodeContextPtr>& aChildNodes = mpContext->getChildNodes();
1084 if (!aChildNodes.empty())
1086 mpFS->startElementNS(XML_p, XML_childTnLst);
1087 for (const NodeContextPtr& pChildContext : aChildNodes)
1089 if (pChildContext->isValid())
1090 WriteAnimationNode(pChildContext);
1092 mpFS->endElementNS(XML_p, XML_childTnLst);
1094 mpFS->endElementNS(XML_p, XML_cTn);
1097 void PPTXAnimationExport::WriteAnimationNodeSeq()
1099 SAL_INFO("sd.eppt", "write animation node SEQ");
1101 mpFS->startElementNS(XML_p, XML_seq);
1103 WriteAnimationNodeCommonPropsStart();
1105 WriteAnimationCondListForSeq(mpFS, XML_prevCondLst);
1106 WriteAnimationCondListForSeq(mpFS, XML_nextCondLst);
1108 mpFS->endElementNS(XML_p, XML_seq);
1111 void PPTXAnimationExport::WriteAnimationNodeEffect()
1113 SAL_INFO("sd.eppt", "write animation node FILTER");
1114 Reference<XTransitionFilter> xFilter(getCurrentNode(), UNO_QUERY);
1115 if (xFilter.is())
1117 const char* pFilter = ::ppt::AnimationExporter::FindTransitionName(
1118 xFilter->getTransition(), xFilter->getSubtype(), xFilter->getDirection());
1119 const char* pMode = xFilter->getMode() ? "in" : "out";
1120 mpFS->startElementNS(XML_p, XML_animEffect, XML_filter, pFilter, XML_transition, pMode);
1122 WriteAnimationNodeAnimateInside(false);
1124 mpFS->endElementNS(XML_p, XML_animEffect);
1128 void PPTXAnimationExport::WriteAnimationNodeCommand()
1130 SAL_INFO("sd.eppt", "write animation node COMMAND");
1131 Reference<XCommand> xCommand(getCurrentNode(), UNO_QUERY);
1132 if (!xCommand.is())
1133 return;
1135 const char* pType = "call";
1136 const char* pCommand = nullptr;
1137 switch (xCommand->getCommand())
1139 case EffectCommands::VERB:
1140 pType = "verb";
1141 pCommand = "1"; /* FIXME hardcoded viewing */
1142 break;
1143 case EffectCommands::PLAY:
1144 pCommand = "play";
1145 break;
1146 case EffectCommands::TOGGLEPAUSE:
1147 pCommand = "togglePause";
1148 break;
1149 case EffectCommands::STOP:
1150 pCommand = "stop";
1151 break;
1152 default:
1153 SAL_WARN("sd.eppt", "unknown command: " << xCommand->getCommand());
1154 break;
1157 mpFS->startElementNS(XML_p, XML_cmd, XML_type, pType, XML_cmd, pCommand);
1159 WriteAnimationNodeAnimateInside(false);
1160 mpFS->startElementNS(XML_p, XML_cBhvr);
1161 WriteAnimationNodeCommonPropsStart();
1162 WriteAnimationTarget(xCommand->getTarget());
1163 mpFS->endElementNS(XML_p, XML_cBhvr);
1165 mpFS->endElementNS(XML_p, XML_cmd);
1168 void PPTXAnimationExport::WriteAnimationNodeAudio()
1170 SAL_INFO("sd.eppt", "write animation node audio");
1171 Reference<XAudio> xAudio(getCurrentNode(), UNO_QUERY);
1173 OUString sUrl;
1174 OUString sRelId;
1175 OUString sName;
1177 if (!(xAudio.is() && (xAudio->getSource() >>= sUrl) && !sUrl.isEmpty()
1178 && sUrl.endsWithIgnoreAsciiCase(".wav")))
1179 return;
1181 mrPowerPointExport.embedEffectAudio(mpFS, sUrl, sRelId, sName);
1183 mpFS->startElementNS(XML_p, XML_audio);
1184 mpFS->startElementNS(XML_p, XML_cMediaNode);
1186 mpFS->startElementNS(XML_p, XML_cTn);
1187 WriteAnimationCondList(mpContext->getCondition(true), XML_stCondLst);
1188 WriteAnimationCondList(mpContext->getCondition(false), XML_endCondLst);
1189 mpFS->endElementNS(XML_p, XML_cTn);
1191 mpFS->startElementNS(XML_p, XML_tgtEl);
1192 mpFS->singleElementNS(XML_p, XML_sndTgt, FSNS(XML_r, XML_embed),
1193 sRelId.isEmpty() ? nullptr : sRelId.toUtf8().getStr(), XML_name,
1194 sUrl.isEmpty() ? nullptr : sName.toUtf8().getStr());
1195 mpFS->endElementNS(XML_p, XML_tgtEl);
1197 mpFS->endElementNS(XML_p, XML_cMediaNode);
1198 mpFS->endElementNS(XML_p, XML_audio);
1201 void PPTXAnimationExport::WriteAnimationNode(const NodeContextPtr& pContext)
1203 const NodeContext* pSavedContext = mpContext;
1204 mpContext = pContext.get();
1206 const Reference<XAnimationNode>& rXNode = getCurrentNode();
1208 SAL_INFO("sd.eppt", "export node type: " << rXNode->getType());
1209 sal_Int32 xmlNodeType = extractNodeType(rXNode);
1211 switch (xmlNodeType)
1213 case XML_par:
1214 mpFS->startElementNS(XML_p, xmlNodeType);
1215 WriteAnimationNodeCommonPropsStart();
1216 mpFS->endElementNS(XML_p, xmlNodeType);
1217 break;
1218 case XML_seq:
1219 WriteAnimationNodeSeq();
1220 break;
1221 case XML_animScale:
1222 case XML_animRot:
1223 case XML_anim:
1224 case XML_animMotion:
1225 case XML_animClr:
1226 case XML_set:
1227 WriteAnimationNodeAnimate(xmlNodeType);
1228 break;
1229 case XML_animEffect:
1230 WriteAnimationNodeEffect();
1231 break;
1232 case XML_cmd:
1233 WriteAnimationNodeCommand();
1234 break;
1235 case XML_audio:
1236 WriteAnimationNodeAudio();
1237 break;
1238 default:
1239 SAL_WARN("sd.eppt", "export ooxml node type: " << xmlNodeType);
1240 break;
1243 mpContext = pSavedContext;
1246 void PPTXAnimationExport::WriteAnimations(const Reference<XDrawPage>& rXDrawPage)
1248 Reference<XAnimationNodeSupplier> xNodeSupplier(rXDrawPage, UNO_QUERY);
1249 if (!xNodeSupplier.is())
1250 return;
1252 const Reference<XAnimationNode> xNode(xNodeSupplier->getAnimationNode());
1253 if (!xNode.is())
1254 return;
1256 Reference<XEnumerationAccess> xEnumerationAccess(xNode, UNO_QUERY);
1257 if (!xEnumerationAccess.is())
1258 return;
1260 Reference<XEnumeration> xEnumeration = xEnumerationAccess->createEnumeration();
1261 if (!(xEnumeration.is() && xEnumeration->hasMoreElements()))
1262 return;
1264 auto pNodeContext = std::make_unique<NodeContext>(xNode, false, false);
1265 if (pNodeContext->isValid())
1267 mpFS->startElementNS(XML_p, XML_timing);
1268 mpFS->startElementNS(XML_p, XML_tnLst);
1270 WriteAnimationNode(pNodeContext);
1272 mpFS->endElementNS(XML_p, XML_tnLst);
1273 mpFS->endElementNS(XML_p, XML_timing);
1277 sal_Int32 PPTXAnimationExport::GetNextAnimationNodeId(const Reference<XAnimationNode>& xNode)
1279 sal_Int32 nId = mrPowerPointExport.GetNextAnimationNodeID();
1280 maAnimationNodeIdMap[xNode] = nId;
1281 return nId;
1284 sal_Int32 PPTXAnimationExport::GetAnimationNodeId(const Reference<XAnimationNode>& xNode)
1286 sal_Int32 nId = -1;
1287 const auto& aIter = maAnimationNodeIdMap.find(xNode);
1288 if (aIter != maAnimationNodeIdMap.end())
1290 nId = aIter->second;
1292 return nId;
1295 NodeContext::NodeContext(const Reference<XAnimationNode>& xNode, bool bMainSeqChild,
1296 bool bIsIterateChild)
1297 : mxNode(xNode)
1298 , mbMainSeqChild(bMainSeqChild)
1299 , mbValid(true)
1300 , mnEffectNodeType(-1)
1301 , mnEffectPresetClass(DFF_ANIM_PRESS_CLASS_USER_DEFINED)
1303 assert(xNode.is());
1305 initUserData();
1307 initValid(initChildNodes(), bIsIterateChild);
1310 void NodeContext::initUserData()
1312 assert(mxNode.is());
1314 Sequence<NamedValue> aUserData = mxNode->getUserData();
1315 const Any* aIndexedData[DFF_ANIM_PROPERTY_ID_COUNT];
1316 AnimationExporter::GetUserData(aUserData, aIndexedData, sizeof(aIndexedData));
1318 const Any* pAny = aIndexedData[DFF_ANIM_NODE_TYPE];
1319 if (pAny)
1320 *pAny >>= mnEffectNodeType;
1322 pAny = aIndexedData[DFF_ANIM_PRESET_CLASS];
1323 if (pAny)
1324 *pAny >>= mnEffectPresetClass;
1326 pAny = aIndexedData[DFF_ANIM_PRESET_ID];
1327 if (pAny)
1328 *pAny >>= msEffectPresetId;
1330 pAny = aIndexedData[DFF_ANIM_PRESET_SUB_TYPE];
1331 if (pAny)
1332 *pAny >>= msEffectPresetSubType;
1335 void NodeContext::initValid(bool bHasValidChild, bool bIsIterateChild)
1337 sal_Int16 nType = mxNode->getType();
1339 if (nType == AnimationNodeType::ITERATE)
1341 Reference<XIterateContainer> xIterate(mxNode, UNO_QUERY);
1342 mbValid = xIterate.is() && (bIsIterateChild || isValidTarget(xIterate->getTarget()))
1343 && !maChildNodes.empty();
1345 else if (nType == AnimationNodeType::COMMAND)
1347 Reference<XCommand> xCommand(mxNode, UNO_QUERY);
1348 mbValid = xCommand.is() && (bIsIterateChild || isValidTarget(xCommand->getTarget()));
1350 else if (nType == AnimationNodeType::PAR || nType == AnimationNodeType::SEQ)
1352 mbValid = bHasValidChild;
1354 else if (nType == AnimationNodeType::AUDIO)
1356 Reference<XAudio> xAudio(mxNode, UNO_QUERY);
1357 OUString sURL;
1358 mbValid
1359 = xAudio.is() && (xAudio->getSource() >>= sURL) && sURL.endsWithIgnoreAsciiCase(".wav");
1361 else
1363 Reference<XAnimate> xAnimate(mxNode, UNO_QUERY);
1364 mbValid = xAnimate.is() && (bIsIterateChild || isValidTarget(xAnimate->getTarget()));
1368 bool NodeContext::initChildNodes()
1370 bool bValid = false;
1371 Reference<XEnumerationAccess> xEnumerationAccess(mxNode, UNO_QUERY);
1372 if (xEnumerationAccess.is())
1374 Reference<XEnumeration> xEnumeration = xEnumerationAccess->createEnumeration();
1375 bool bIsMainSeq = mnEffectNodeType == EffectNodeType::MAIN_SEQUENCE;
1376 bool bIsIterateChild = mxNode->getType() == AnimationNodeType::ITERATE;
1377 if (xEnumeration.is())
1379 while (xEnumeration->hasMoreElements())
1381 Reference<XAnimationNode> xChildNode(xEnumeration->nextElement(), UNO_QUERY);
1382 if (xChildNode.is())
1384 auto pChildContext
1385 = std::make_unique<NodeContext>(xChildNode, bIsMainSeq, bIsIterateChild);
1386 if (pChildContext->isValid())
1387 bValid = true;
1388 maChildNodes.push_back(std::move(pChildContext));
1393 return bValid;
1396 Any NodeContext::getCondition(bool bBegin) const
1398 const bool bParent
1399 = (mnEffectNodeType != EffectNodeType::INTERACTIVE_SEQUENCE || maChildNodes.empty());
1400 const Reference<XAnimationNode>& rNode = bParent ? mxNode : maChildNodes[0]->getNode();
1402 return bBegin ? rNode->getBegin() : rNode->getEnd();
1405 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */