diff options
Diffstat (limited to 'sd/source/core')
31 files changed, 22142 insertions, 0 deletions
diff --git a/sd/source/core/CustomAnimationCloner.cxx b/sd/source/core/CustomAnimationCloner.cxx new file mode 100644 index 000000000..ea42e3bac --- /dev/null +++ b/sd/source/core/CustomAnimationCloner.cxx @@ -0,0 +1,307 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include <com/sun/star/animations/XAnimate.hpp> +#include <com/sun/star/animations/XAnimationNode.hpp> +#include <com/sun/star/animations/Event.hpp> +#include <com/sun/star/animations/XCommand.hpp> +#include <com/sun/star/animations/XIterateContainer.hpp> +#include <com/sun/star/animations/XAudio.hpp> +#include <com/sun/star/animations/AnimationNodeType.hpp> +#include <com/sun/star/animations/ValuePair.hpp> +#include <com/sun/star/util/XCloneable.hpp> +#include <com/sun/star/presentation/ParagraphTarget.hpp> +#include <com/sun/star/container/XEnumerationAccess.hpp> +#include <com/sun/star/beans/NamedValue.hpp> + +#include <map> + +#include <tools/debug.hxx> +#include <tools/diagnose_ex.h> +#include <animations/animationnodehelper.hxx> + +#include <svx/svditer.hxx> + +#include <CustomAnimationCloner.hxx> +#include <sdpage.hxx> + +using namespace ::com::sun::star::uno; +using namespace ::com::sun::star::animations; +using namespace ::com::sun::star::presentation; +using namespace ::com::sun::star::container; + +using ::com::sun::star::drawing::XShape; +using ::com::sun::star::beans::NamedValue; + +namespace sd +{ + namespace { + + class CustomAnimationClonerImpl + { + public: + CustomAnimationClonerImpl(); + Reference< XAnimationNode > Clone( const Reference< XAnimationNode >& xSourceNode, const SdPage* pSource, const SdPage* pTarget ); + + private: + void transformNode( const Reference< XAnimationNode >& xNode ); + Any transformValue( const Any& rValue ); + + Reference< XShape > getClonedShape( const Reference< XShape >& xSource ) const; + Reference< XAnimationNode > getClonedNode( const Reference< XAnimationNode >& xSource ) const; + + mutable ::std::map< Reference< XShape >, Reference< XShape > > maShapeMap; + std::vector< Reference< XAnimationNode > > maSourceNodeVector; + std::vector< Reference< XAnimationNode > > maCloneNodeVector; + }; + + } + + CustomAnimationClonerImpl::CustomAnimationClonerImpl() + { + } + + Reference< XAnimationNode > Clone( const Reference< XAnimationNode >& xSourceNode, const SdPage* pSource, const SdPage* pTarget ) + { + CustomAnimationClonerImpl aCloner; + return aCloner.Clone( xSourceNode, pSource, pTarget ); + } + + Reference< XAnimationNode > CustomAnimationClonerImpl::Clone( const Reference< XAnimationNode >& xSourceNode, const SdPage* pSourcePage, const SdPage* pTargetPage ) + { + try + { + // clone animation hierarchy + Reference< css::util::XCloneable > xClonable( xSourceNode, UNO_QUERY_THROW ); + Reference< XAnimationNode > xCloneNode( xClonable->createClone(), UNO_QUERY_THROW ); + + // create a dictionary to map source to cloned shapes + if( pSourcePage && pTargetPage ) + { + SdrObjListIter aSourceIter( pSourcePage, SdrIterMode::DeepWithGroups ); + SdrObjListIter aTargetIter( pTargetPage, SdrIterMode::DeepWithGroups ); + + while( aSourceIter.IsMore() && aTargetIter.IsMore() ) + { + SdrObject* pSource = aSourceIter.Next(); + SdrObject* pTarget = aTargetIter.Next(); + + if( pSource && pTarget) + { + Reference< XShape > xSource( pSource->getUnoShape(), UNO_QUERY ); + Reference< XShape > xTarget( pTarget->getUnoShape(), UNO_QUERY ); + if( xSource.is() && xTarget.is() ) + { + maShapeMap[xSource] = xTarget; + } + } + } + } + + // create a dictionary to map source to cloned nodes + ::anim::create_deep_vector( xSourceNode, maSourceNodeVector ); + ::anim::create_deep_vector( xCloneNode, maCloneNodeVector ); + + transformNode( xCloneNode ); + + return xCloneNode; + } + catch( Exception& ) + { + TOOLS_WARN_EXCEPTION( "sd", "sd::CustomAnimationClonerImpl::Clone()" ); + Reference< XAnimationNode > xEmpty; + return xEmpty; + } + } + + void CustomAnimationClonerImpl::transformNode( const Reference< XAnimationNode >& xNode ) + { + try + { + xNode->setBegin( transformValue( xNode->getBegin() ) ); + xNode->setEnd( transformValue( xNode->getEnd() ) ); + + sal_Int16 nNodeType( xNode->getType() ); + switch( nNodeType ) + { + case AnimationNodeType::ITERATE: + { + Reference< XIterateContainer > xIter( xNode, UNO_QUERY_THROW ); + xIter->setTarget( transformValue( xIter->getTarget() ) ); + [[fallthrough]]; + } + case AnimationNodeType::PAR: + case AnimationNodeType::SEQ: + { + Reference< XEnumerationAccess > xEnumerationAccess( xNode, UNO_QUERY_THROW ); + Reference< XEnumeration > xEnumeration( xEnumerationAccess->createEnumeration(), UNO_SET_THROW ); + while( xEnumeration->hasMoreElements() ) + { + Reference< XAnimationNode > xChildNode( xEnumeration->nextElement(), UNO_QUERY_THROW ); + transformNode( xChildNode ); + } + } + break; + + case AnimationNodeType::ANIMATE: + case AnimationNodeType::SET: + case AnimationNodeType::ANIMATEMOTION: + case AnimationNodeType::ANIMATEPHYSICS: + case AnimationNodeType::ANIMATECOLOR: + case AnimationNodeType::ANIMATETRANSFORM: + case AnimationNodeType::TRANSITIONFILTER: + { + Reference< XAnimate > xAnimate( xNode, UNO_QUERY_THROW ); + xAnimate->setTarget( transformValue( xAnimate->getTarget() ) ); + } + break; + + case AnimationNodeType::COMMAND: + { + Reference< XCommand > xCommand( xNode, UNO_QUERY_THROW ); + xCommand->setTarget( transformValue( xCommand->getTarget() ) ); + } + break; + + case AnimationNodeType::AUDIO: + { + Reference< XAudio > xAudio( xNode, UNO_QUERY_THROW ); + xAudio->setSource( transformValue( xAudio->getSource() ) ); + } + break; + } + + Sequence< NamedValue > aUserData( xNode->getUserData() ); + if( aUserData.hasElements() ) + { + for( NamedValue & namedValue : asNonConstRange(aUserData) ) + { + namedValue.Value = transformValue( namedValue.Value ); + } + + xNode->setUserData( aUserData ); + } + } + catch( Exception& ) + { + TOOLS_WARN_EXCEPTION( "sd", "sd::CustomAnimationClonerImpl::transformNode()" ); + } + } + + Any CustomAnimationClonerImpl::transformValue( const Any& rValue ) + { + if( rValue.hasValue() ) try + { + if( rValue.getValueType() == cppu::UnoType<ValuePair>::get() ) + { + ValuePair aValuePair; + rValue >>= aValuePair; + + aValuePair.First = transformValue( aValuePair.First ); + aValuePair.Second = transformValue( aValuePair.Second ); + + return Any( aValuePair ); + } + else if( rValue.getValueType() == cppu::UnoType< Sequence<Any> >::get() ) + { + Sequence<Any> aSequence; + rValue >>= aSequence; + + for( Any& rAny : asNonConstRange(aSequence) ) + rAny = transformValue( rAny ); + + return Any( aSequence ); + } + else if( rValue.getValueTypeClass() == TypeClass_INTERFACE ) + { + Reference< XShape > xShape; + rValue >>= xShape; + if( xShape.is() ) + { + return Any( getClonedShape( xShape ) ); + } + else + { + Reference< XAnimationNode > xNode; + rValue >>= xNode; + if( xNode.is() ) + return Any( getClonedNode( xNode ) ); + } + } + else if( rValue.getValueType() == cppu::UnoType<ParagraphTarget>::get() ) + { + ParagraphTarget aParaTarget; + rValue >>= aParaTarget; + + aParaTarget.Shape = getClonedShape( aParaTarget.Shape ); + + return Any( aParaTarget ); + } + else if( rValue.getValueType() == cppu::UnoType<Event>::get() ) + { + Event aEvent; + rValue >>= aEvent; + + aEvent.Source = transformValue( aEvent.Source ); + + return Any( aEvent ); + } + } + catch( Exception& ) + { + TOOLS_WARN_EXCEPTION( "sd", "sd::CustomAnimationClonerImpl::transformValue()" ); + } + + return rValue; + } + + Reference< XShape > CustomAnimationClonerImpl::getClonedShape( const Reference< XShape >& xSource ) const + { + if( xSource.is() ) + { + if( maShapeMap.find(xSource) != maShapeMap.end() ) + { + return maShapeMap[xSource]; + } + + DBG_ASSERT( maShapeMap.empty(), "sd::CustomAnimationClonerImpl::getClonedShape() failed!" ); + } + return xSource; + } + + Reference< XAnimationNode > CustomAnimationClonerImpl::getClonedNode( const Reference< XAnimationNode >& xSource ) const + { + std::size_t nNodeCount = maSourceNodeVector.size(); + std::size_t nCloneNodeCount = maCloneNodeVector.size(); + + if (nNodeCount != nCloneNodeCount) + SAL_WARN("sd.core", "Sizes of maSourceNodeVector and maCloneNodeVector mismatch!"); + + for( std::size_t nNode = 0; nNode < nNodeCount && nNode < nCloneNodeCount; ++nNode ) + { + if( maSourceNodeVector[nNode] == xSource ) + return maCloneNodeVector[nNode]; + } + + OSL_FAIL( "sd::CustomAnimationClonerImpl::getClonedNode() failed!" ); + return xSource; + } +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/core/CustomAnimationEffect.cxx b/sd/source/core/CustomAnimationEffect.cxx new file mode 100644 index 000000000..b1816784f --- /dev/null +++ b/sd/source/core/CustomAnimationEffect.cxx @@ -0,0 +1,3559 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include <tools/debug.hxx> +#include <tools/diagnose_ex.h> +#include <sal/log.hxx> +#include <com/sun/star/animations/AnimationNodeType.hpp> +#include <com/sun/star/animations/AnimateColor.hpp> +#include <com/sun/star/animations/AnimateMotion.hpp> +#include <com/sun/star/animations/AnimateSet.hpp> +#include <com/sun/star/animations/AnimationFill.hpp> +#include <com/sun/star/animations/Audio.hpp> +#include <com/sun/star/animations/Command.hpp> +#include <com/sun/star/animations/Event.hpp> +#include <com/sun/star/animations/EventTrigger.hpp> +#include <com/sun/star/animations/IterateContainer.hpp> +#include <com/sun/star/animations/ParallelTimeContainer.hpp> +#include <com/sun/star/animations/SequenceTimeContainer.hpp> +#include <com/sun/star/animations/XCommand.hpp> +#include <com/sun/star/animations/XIterateContainer.hpp> +#include <com/sun/star/animations/XAnimateTransform.hpp> +#include <com/sun/star/animations/XAnimateMotion.hpp> +#include <com/sun/star/animations/XAnimate.hpp> +#include <com/sun/star/animations/AnimationRestart.hpp> +#include <com/sun/star/beans/NamedValue.hpp> +#include <com/sun/star/beans/XPropertySet.hpp> +#include <com/sun/star/container/XEnumerationAccess.hpp> +#include <com/sun/star/lang/XInitialization.hpp> +#include <com/sun/star/presentation/EffectNodeType.hpp> +#include <com/sun/star/presentation/EffectCommands.hpp> +#include <com/sun/star/presentation/EffectPresetClass.hpp> +#include <com/sun/star/presentation/ParagraphTarget.hpp> +#include <com/sun/star/presentation/ShapeAnimationSubType.hpp> +#include <com/sun/star/text/XText.hpp> +#include <com/sun/star/util/XCloneable.hpp> +#include <com/sun/star/util/XChangesNotifier.hpp> +#include <comphelper/processfactory.hxx> +#include <comphelper/sequence.hxx> +#include <com/sun/star/lang/Locale.hpp> +#include <com/sun/star/i18n/BreakIterator.hpp> +#include <com/sun/star/i18n/CharacterIteratorMode.hpp> +#include <com/sun/star/i18n/WordType.hpp> +#include <com/sun/star/presentation/TextAnimationType.hpp> + +#include <basegfx/polygon/b2dpolypolygon.hxx> +#include <basegfx/polygon/b2dpolypolygontools.hxx> +#include <basegfx/range/b2drange.hxx> +#include <basegfx/matrix/b2dhommatrixtools.hxx> + +#include <algorithm> +#include <deque> +#include <numeric> + +#include <cppuhelper/implbase.hxx> + +#include <drawinglayer/geometry/viewinformation2d.hxx> +#include <o3tl/safeint.hxx> +#include <svx/sdr/contact/viewcontact.hxx> +#include <svx/svdopath.hxx> +#include <svx/svdpage.hxx> +#include <CustomAnimationEffect.hxx> +#include <CustomAnimationPreset.hxx> +#include <animations.hxx> + +using namespace ::com::sun::star; +using namespace ::com::sun::star::uno; +using namespace ::com::sun::star::presentation; +using namespace ::com::sun::star::animations; + +using ::com::sun::star::container::XEnumerationAccess; +using ::com::sun::star::container::XEnumeration; +using ::com::sun::star::beans::NamedValue; +using ::com::sun::star::container::XChild; +using ::com::sun::star::drawing::XShape; +using ::com::sun::star::lang::XInitialization; +using ::com::sun::star::text::XText; +using ::com::sun::star::text::XTextRange; +using ::com::sun::star::beans::XPropertySet; +using ::com::sun::star::util::XCloneable; +using ::com::sun::star::lang::Locale; +using ::com::sun::star::util::XChangesNotifier; +using ::com::sun::star::util::XChangesListener; + +namespace sd +{ +class MainSequenceChangeGuard +{ +public: + explicit MainSequenceChangeGuard( EffectSequenceHelper* pSequence ) + { + mpMainSequence = dynamic_cast< MainSequence* >( pSequence ); + if( mpMainSequence == nullptr ) + { + InteractiveSequence* pI = dynamic_cast< InteractiveSequence* >( pSequence ); + if( pI ) + mpMainSequence = pI->mpMainSequence; + } + DBG_ASSERT( mpMainSequence, "sd::MainSequenceChangeGuard::MainSequenceChangeGuard(), no main sequence to guard!" ); + + if( mpMainSequence ) + mpMainSequence->mbIgnoreChanges++; + } + + ~MainSequenceChangeGuard() + { + if( mpMainSequence ) + mpMainSequence->mbIgnoreChanges++; + } + +private: + MainSequence* mpMainSequence; +}; + +CustomAnimationEffect::CustomAnimationEffect( const css::uno::Reference< css::animations::XAnimationNode >& xNode ) +: mnNodeType(-1), + mnPresetClass(-1), + mnFill(AnimationFill::HOLD), + mfBegin(-1.0), + mfDuration(-1.0), + mfAbsoluteDuration(-1.0), + mnGroupId(-1), + mnIterateType(0), + mfIterateInterval(0.0), + mnParaDepth( -1 ), + mbHasText(false), + mfAcceleration( 1.0 ), + mfDecelerate( 1.0 ), + mbAutoReverse(false), + mnTargetSubItem(0), + mnCommand(0), + mpEffectSequence( nullptr ), + mbHasAfterEffect(false), + mbAfterEffectOnNextEffect(false) +{ + setNode( xNode ); +} + +void CustomAnimationEffect::setNode( const css::uno::Reference< css::animations::XAnimationNode >& xNode ) +{ + mxNode = xNode; + mxAudio.clear(); + mnCommand = 0; + + const Sequence< NamedValue > aUserData( mxNode->getUserData() ); + + for( const NamedValue& rProp : aUserData ) + { + if ( rProp.Name == "node-type" ) + { + rProp.Value >>= mnNodeType; + } + else if ( rProp.Name == "preset-id" ) + { + rProp.Value >>= maPresetId; + } + else if ( rProp.Name == "preset-sub-type" ) + { + rProp.Value >>= maPresetSubType; + } + else if ( rProp.Name == "preset-class" ) + { + rProp.Value >>= mnPresetClass; + } + else if ( rProp.Name == "preset-property" ) + { + rProp.Value >>= maProperty; + } + else if ( rProp.Name == "group-id" ) + { + rProp.Value >>= mnGroupId; + } + } + + // get effect start time + mxNode->getBegin() >>= mfBegin; + + mfAcceleration = mxNode->getAcceleration(); + mfDecelerate = mxNode->getDecelerate(); + mbAutoReverse = mxNode->getAutoReverse(); + + mnFill = mxNode->getFill(); + + // get iteration data + Reference< XIterateContainer > xIter( mxNode, UNO_QUERY ); + if( xIter.is() ) + { + mfIterateInterval = xIter->getIterateInterval(); + mnIterateType = xIter->getIterateType(); + maTarget = xIter->getTarget(); + mnTargetSubItem = xIter->getSubItem(); + } + else + { + mfIterateInterval = 0.0f; + mnIterateType = 0; + } + + // calculate effect duration and get target shape + Reference< XEnumerationAccess > xEnumerationAccess( mxNode, UNO_QUERY ); + if( xEnumerationAccess.is() ) + { + Reference< XEnumeration > xEnumeration = xEnumerationAccess->createEnumeration(); + if( xEnumeration.is() ) + { + while( xEnumeration->hasMoreElements() ) + { + Reference< XAnimationNode > xChildNode( xEnumeration->nextElement(), UNO_QUERY ); + if( !xChildNode.is() ) + continue; + + if( xChildNode->getType() == AnimationNodeType::AUDIO ) + { + mxAudio.set( xChildNode, UNO_QUERY ); + } + else if( xChildNode->getType() == AnimationNodeType::COMMAND ) + { + Reference< XCommand > xCommand( xChildNode, UNO_QUERY ); + if( xCommand.is() ) + { + mnCommand = xCommand->getCommand(); + if( !maTarget.hasValue() ) + maTarget = xCommand->getTarget(); + } + } + else + { + double fBegin = 0.0; + double fDuration = 0.0; + xChildNode->getBegin() >>= fBegin; + xChildNode->getDuration() >>= fDuration; + + fDuration += fBegin; + if( fDuration > mfDuration ) + mfDuration = fDuration; + + // no target shape yet? + if( !maTarget.hasValue() ) + { + // go get it boys! + Reference< XAnimate > xAnimate( xChildNode, UNO_QUERY ); + if( xAnimate.is() ) + { + maTarget = xAnimate->getTarget(); + mnTargetSubItem = xAnimate->getSubItem(); + } + } + } + } + } + } + + mfAbsoluteDuration = mfDuration; + double fRepeatCount = 1.0; + if( (mxNode->getRepeatCount()) >>= fRepeatCount ) + mfAbsoluteDuration *= fRepeatCount; + + checkForText(); +} + +sal_Int32 CustomAnimationEffect::getNumberOfSubitems( const Any& aTarget, sal_Int16 nIterateType ) +{ + sal_Int32 nSubItems = 0; + + try + { + // first get target text + sal_Int32 nOnlyPara = -1; + + Reference< XText > xShape; + aTarget >>= xShape; + if( !xShape.is() ) + { + ParagraphTarget aParaTarget; + if( aTarget >>= aParaTarget ) + { + xShape.set( aParaTarget.Shape, UNO_QUERY ); + nOnlyPara = aParaTarget.Paragraph; + } + } + + // now use the break iterator to iterate over the given text + // and count the sub items + + if( xShape.is() ) + { + // TODO/LATER: Optimize this, don't create a break iterator each time + Reference< uno::XComponentContext > xContext( ::comphelper::getProcessComponentContext() ); + Reference < i18n::XBreakIterator > xBI = i18n::BreakIterator::create(xContext); + + Reference< XEnumerationAccess > xEA( xShape, UNO_QUERY_THROW ); + Reference< XEnumeration > xEnumeration( xEA->createEnumeration(), UNO_SET_THROW ); + css::lang::Locale aLocale; + static const OUStringLiteral aStrLocaleName( u"CharLocale" ); + Reference< XTextRange > xParagraph; + + sal_Int32 nPara = 0; + while( xEnumeration->hasMoreElements() ) + { + xEnumeration->nextElement() >>= xParagraph; + + // skip this if it's not the only paragraph we want to count + if( (nOnlyPara != -1) && (nOnlyPara != nPara ) ) + continue; + + if( nIterateType == TextAnimationType::BY_PARAGRAPH ) + { + nSubItems++; + } + else + { + const OUString aText( xParagraph->getString() ); + Reference< XPropertySet > xSet( xParagraph, UNO_QUERY_THROW ); + xSet->getPropertyValue( aStrLocaleName ) >>= aLocale; + + sal_Int32 nPos; + const sal_Int32 nEndPos = aText.getLength(); + + if( nIterateType == TextAnimationType::BY_WORD ) + { + for( nPos = 0; nPos < nEndPos; nPos++ ) + { + nPos = xBI->getWordBoundary(aText, nPos, aLocale, i18n::WordType::ANY_WORD, true).endPos; + nSubItems++; + } + break; + } + else + { + sal_Int32 nDone; + for( nPos = 0; nPos < nEndPos; nPos++ ) + { + nPos = xBI->nextCharacters(aText, nPos, aLocale, i18n::CharacterIteratorMode::SKIPCELL, 0, nDone); + nSubItems++; + } + } + } + + if( nPara == nOnlyPara ) + break; + + nPara++; + } + } + } + catch( Exception& ) + { + nSubItems = 0; + TOOLS_WARN_EXCEPTION( "sd", "sd::CustomAnimationEffect::getNumberOfSubitems(), exception caught!" ); + } + + return nSubItems; +} + +CustomAnimationEffect::~CustomAnimationEffect() +{ +} + +CustomAnimationEffectPtr CustomAnimationEffect::clone() const +{ + Reference< XCloneable > xCloneable( mxNode, UNO_QUERY_THROW ); + Reference< XAnimationNode > xNode( xCloneable->createClone(), UNO_QUERY_THROW ); + CustomAnimationEffectPtr pEffect = std::make_shared<CustomAnimationEffect>( xNode ); + pEffect->setEffectSequence( getEffectSequence() ); + return pEffect; +} + +sal_Int32 CustomAnimationEffect::get_node_type( const Reference< XAnimationNode >& xNode ) +{ + sal_Int16 nNodeType = -1; + + if( xNode.is() ) + { + const Sequence< NamedValue > aUserData( xNode->getUserData() ); + if( aUserData.hasElements() ) + { + const NamedValue* pProp = std::find_if(aUserData.begin(), aUserData.end(), + [](const NamedValue& rProp) { return rProp.Name == "node-type"; }); + if (pProp != aUserData.end()) + pProp->Value >>= nNodeType; + } + } + + return nNodeType; +} + +void CustomAnimationEffect::setPresetClassAndId( sal_Int16 nPresetClass, const OUString& rPresetId ) +{ + if( mnPresetClass == nPresetClass && maPresetId == rPresetId ) + return; + + mnPresetClass = nPresetClass; + maPresetId = rPresetId; + if( !mxNode.is() ) + return; + + // first try to find a "preset-class" entry in the user data + // and change it + Sequence< NamedValue > aUserData( mxNode->getUserData() ); + sal_Int32 nLength = aUserData.getLength(); + bool bFoundPresetClass = false; + bool bFoundPresetId = false; + if( nLength ) + { + auto [begin, end] = asNonConstRange(aUserData); + NamedValue* pProp = std::find_if(begin, end, + [](const NamedValue& rProp) { return rProp.Name == "preset-class"; }); + if (pProp != end) + { + pProp->Value <<= mnPresetClass; + bFoundPresetClass = true; + } + + pProp = std::find_if(begin, end, + [](const NamedValue& rProp) { return rProp.Name == "preset-id"; }); + if (pProp != end) + { + pProp->Value <<= mnPresetClass; + bFoundPresetId = true; + } + } + + // no "preset-class" entry inside user data, so add it + if( !bFoundPresetClass ) + { + aUserData.realloc( nLength + 1); + auto& el = aUserData.getArray()[nLength]; + el.Name = "preset-class"; + el.Value <<= mnPresetClass; + ++nLength; + } + + if( !bFoundPresetId && maPresetId.getLength() > 0 ) + { + aUserData.realloc( nLength + 1); + auto& el = aUserData.getArray()[nLength]; + el.Name = "preset-id"; + el.Value <<= maPresetId; + } + + mxNode->setUserData( aUserData ); +} + +void CustomAnimationEffect::setNodeType( sal_Int16 nNodeType ) +{ + if( mnNodeType == nNodeType ) + return; + + mnNodeType = nNodeType; + if( !mxNode.is() ) + return; + + // first try to find a "node-type" entry in the user data + // and change it + Sequence< NamedValue > aUserData( mxNode->getUserData() ); + sal_Int32 nLength = aUserData.getLength(); + bool bFound = false; + if( nLength ) + { + auto [begin, end] = asNonConstRange(aUserData); + NamedValue* pProp = std::find_if(begin, end, + [](const NamedValue& rProp) { return rProp.Name == "node-type"; }); + if (pProp != end) + { + pProp->Value <<= mnNodeType; + bFound = true; + } + } + + // no "node-type" entry inside user data, so add it + if( !bFound ) + { + aUserData.realloc( nLength + 1); + auto& el = aUserData.getArray()[nLength]; + el.Name = "node-type"; + el.Value <<= mnNodeType; + } + + mxNode->setUserData( aUserData ); +} + +void CustomAnimationEffect::setGroupId( sal_Int32 nGroupId ) +{ + mnGroupId = nGroupId; + if( !mxNode.is() ) + return; + + // first try to find a "group-id" entry in the user data + // and change it + Sequence< NamedValue > aUserData( mxNode->getUserData() ); + sal_Int32 nLength = aUserData.getLength(); + bool bFound = false; + if( nLength ) + { + auto [begin, end] = asNonConstRange(aUserData); + NamedValue* pProp = std::find_if(begin, end, + [](const NamedValue& rProp) { return rProp.Name == "group-id"; }); + if (pProp != end) + { + pProp->Value <<= mnGroupId; + bFound = true; + } + } + + // no "group-id" entry inside user data, so add it + if( !bFound ) + { + aUserData.realloc( nLength + 1); + auto& el = aUserData.getArray()[nLength]; + el.Name = "group-id"; + el.Value <<= mnGroupId; + } + + mxNode->setUserData( aUserData ); +} + +/** checks if the text for this effect has changed and updates internal flags. + returns true if something changed. +*/ +bool CustomAnimationEffect::checkForText( const std::vector<sal_Int32>* paragraphNumberingLevel ) +{ + bool bChange = false; + + Reference< XText > xText; + + if( maTarget.getValueType() == ::cppu::UnoType<ParagraphTarget>::get() ) + { + // calc para depth + ParagraphTarget aParaTarget; + maTarget >>= aParaTarget; + + xText.set( aParaTarget.Shape, UNO_QUERY ); + + // get paragraph + if( xText.is() ) + { + sal_Int32 nPara = aParaTarget.Paragraph; + + bool bHasText = false; + sal_Int32 nParaDepth = 0; + + if ( paragraphNumberingLevel ) + { + bHasText = !paragraphNumberingLevel->empty(); + if (nPara >= 0 && o3tl::make_unsigned(nPara) < paragraphNumberingLevel->size()) + nParaDepth = paragraphNumberingLevel->at(nPara); + } + else + { + Reference< XEnumerationAccess > xEA( xText, UNO_QUERY ); + if( xEA.is() ) + { + Reference< XEnumeration > xEnumeration = xEA->createEnumeration(); + if( xEnumeration.is() ) + { + bHasText = xEnumeration->hasMoreElements(); + + while( xEnumeration->hasMoreElements() && nPara-- ) + xEnumeration->nextElement(); + + if( xEnumeration->hasMoreElements() ) + { + Reference< XPropertySet > xParaSet; + xEnumeration->nextElement() >>= xParaSet; + if( xParaSet.is() ) + { + xParaSet->getPropertyValue( "NumberingLevel" ) >>= nParaDepth; + } + } + } + } + } + + if( bHasText ) + { + bChange |= bHasText != mbHasText; + mbHasText = bHasText; + + bChange |= nParaDepth != mnParaDepth; + mnParaDepth = nParaDepth; + } + } + } + else + { + maTarget >>= xText; + bool bHasText = xText.is() && !xText->getString().isEmpty(); + bChange |= bHasText != mbHasText; + mbHasText = bHasText; + } + + bChange |= calculateIterateDuration(); + return bChange; +} + +bool CustomAnimationEffect::calculateIterateDuration() +{ + bool bChange = false; + + // if we have an iteration, we must also calculate the + // 'true' container duration, that is + // ( ( is form animated ) ? [contained effects duration] : 0 ) + + // ( [number of animated children] - 1 ) * [interval-delay] + [contained effects duration] + Reference< XIterateContainer > xIter( mxNode, UNO_QUERY ); + if( xIter.is() ) + { + double fDuration = mfDuration; + const double fSubEffectDuration = mfDuration; + + if( mnTargetSubItem != ShapeAnimationSubType::ONLY_BACKGROUND ) // does not make sense for iterate container but better check + { + const sal_Int32 nSubItems = getNumberOfSubitems( maTarget, mnIterateType ); + if( nSubItems ) + { + const double f = (nSubItems-1) * mfIterateInterval; + fDuration += f; + } + } + + // if we also animate the form first, we have to add the + // sub effect duration to the whole effect duration + if( mnTargetSubItem == ShapeAnimationSubType::AS_WHOLE ) + fDuration += fSubEffectDuration; + + bChange |= fDuration != mfAbsoluteDuration; + mfAbsoluteDuration = fDuration; + } + + return bChange; +} + +void CustomAnimationEffect::setTarget( const css::uno::Any& rTarget ) +{ + try + { + maTarget = rTarget; + + // first, check special case for random node + Reference< XInitialization > xInit( mxNode, UNO_QUERY ); + if( xInit.is() ) + { + const Sequence< Any > aArgs( &maTarget, 1 ); + xInit->initialize( aArgs ); + } + else + { + Reference< XIterateContainer > xIter( mxNode, UNO_QUERY ); + if( xIter.is() ) + { + xIter->setTarget(maTarget); + } + else + { + Reference< XEnumerationAccess > xEnumerationAccess( mxNode, UNO_QUERY ); + if( xEnumerationAccess.is() ) + { + Reference< XEnumeration > xEnumeration = xEnumerationAccess->createEnumeration(); + if( xEnumeration.is() ) + { + while( xEnumeration->hasMoreElements() ) + { + const Any aElem( xEnumeration->nextElement() ); + Reference< XAnimate > xAnimate( aElem, UNO_QUERY ); + if( xAnimate.is() ) + xAnimate->setTarget( rTarget ); + else + { + Reference< XCommand > xCommand( aElem, UNO_QUERY ); + if( xCommand.is() ) + xCommand->setTarget( rTarget ); + } + } + } + } + } + } + checkForText(); + } + catch( Exception& ) + { + TOOLS_WARN_EXCEPTION( "sd", "sd::CustomAnimationEffect::setTarget()" ); + } +} + +void CustomAnimationEffect::setTargetSubItem( sal_Int16 nSubItem ) +{ + try + { + mnTargetSubItem = nSubItem; + + Reference< XIterateContainer > xIter( mxNode, UNO_QUERY ); + if( xIter.is() ) + { + xIter->setSubItem(mnTargetSubItem); + } + else + { + Reference< XEnumerationAccess > xEnumerationAccess( mxNode, UNO_QUERY ); + if( xEnumerationAccess.is() ) + { + Reference< XEnumeration > xEnumeration = xEnumerationAccess->createEnumeration(); + if( xEnumeration.is() ) + { + while( xEnumeration->hasMoreElements() ) + { + Reference< XAnimate > xAnimate( xEnumeration->nextElement(), UNO_QUERY ); + if( xAnimate.is() ) + xAnimate->setSubItem( mnTargetSubItem ); + } + } + } + } + } + catch( Exception& ) + { + TOOLS_WARN_EXCEPTION( "sd", "sd::CustomAnimationEffect::setTargetSubItem()" ); + } +} + +void CustomAnimationEffect::setDuration( double fDuration ) +{ + if( (mfDuration == -1.0) || (mfDuration == fDuration) ) + return; + + try + { + double fScale = fDuration / mfDuration; + mfDuration = fDuration; + double fRepeatCount = 1.0; + getRepeatCount() >>= fRepeatCount; + mfAbsoluteDuration = mfDuration * fRepeatCount; + + // calculate effect duration and get target shape + Reference< XEnumerationAccess > xEnumerationAccess( mxNode, UNO_QUERY ); + if( xEnumerationAccess.is() ) + { + Reference< XEnumeration > xEnumeration = xEnumerationAccess->createEnumeration(); + if( xEnumeration.is() ) + { + while( xEnumeration->hasMoreElements() ) + { + Reference< XAnimationNode > xChildNode( xEnumeration->nextElement(), UNO_QUERY ); + if( !xChildNode.is() ) + continue; + + double fChildBegin = 0.0; + xChildNode->getBegin() >>= fChildBegin; + if( fChildBegin != 0.0 ) + { + fChildBegin *= fScale; + xChildNode->setBegin( Any( fChildBegin ) ); + } + + double fChildDuration = 0.0; + xChildNode->getDuration() >>= fChildDuration; + if( fChildDuration != 0.0 ) + { + fChildDuration *= fScale; + xChildNode->setDuration( Any( fChildDuration ) ); + } + } + } + } + calculateIterateDuration(); + } + catch( Exception& ) + { + TOOLS_WARN_EXCEPTION( "sd", "sd::CustomAnimationEffect::setDuration()" ); + } +} + +void CustomAnimationEffect::setBegin( double fBegin ) +{ + if( mxNode.is() ) try + { + mfBegin = fBegin; + mxNode->setBegin( Any( fBegin ) ); + } + catch( Exception& ) + { + TOOLS_WARN_EXCEPTION( "sd", "sd::CustomAnimationEffect::setBegin()" ); + } +} + +void CustomAnimationEffect::setAcceleration( double fAcceleration ) +{ + if( mxNode.is() ) try + { + mfAcceleration = fAcceleration; + mxNode->setAcceleration( fAcceleration ); + } + catch( Exception& ) + { + TOOLS_WARN_EXCEPTION( "sd", "sd::CustomAnimationEffect::setAcceleration()" ); + } +} + +void CustomAnimationEffect::setDecelerate( double fDecelerate ) +{ + if( mxNode.is() ) try + { + mfDecelerate = fDecelerate; + mxNode->setDecelerate( fDecelerate ); + } + catch( Exception& ) + { + TOOLS_WARN_EXCEPTION( "sd", "sd::CustomAnimationEffect::setDecelerate()" ); + } +} + +void CustomAnimationEffect::setAutoReverse( bool bAutoReverse ) +{ + if( mxNode.is() ) try + { + mbAutoReverse = bAutoReverse; + mxNode->setAutoReverse( bAutoReverse ); + } + catch( Exception& ) + { + TOOLS_WARN_EXCEPTION( "sd", "sd::CustomAnimationEffect::setAutoReverse()" ); + } +} + +void CustomAnimationEffect::replaceNode( const css::uno::Reference< css::animations::XAnimationNode >& xNode ) +{ + sal_Int16 nNodeType = mnNodeType; + Any aTarget = maTarget; + + sal_Int16 nFill = mnFill; + double fBegin = mfBegin; + double fDuration = mfDuration; + double fAcceleration = mfAcceleration; + double fDecelerate = mfDecelerate ; + bool bAutoReverse = mbAutoReverse; + Reference< XAudio > xAudio( mxAudio ); + sal_Int16 nIterateType = mnIterateType; + double fIterateInterval = mfIterateInterval; + sal_Int16 nSubItem = mnTargetSubItem; + + setNode( xNode ); + + setAudio( xAudio ); + setNodeType( nNodeType ); + setTarget( aTarget ); + setTargetSubItem( nSubItem ); + setDuration( fDuration ); + setBegin( fBegin ); + setFill( nFill ); + + setAcceleration( fAcceleration ); + setDecelerate( fDecelerate ); + setAutoReverse( bAutoReverse ); + + if( nIterateType != mnIterateType ) + setIterateType( nIterateType ); + + if( mnIterateType && ( fIterateInterval != mfIterateInterval ) ) + setIterateInterval( fIterateInterval ); +} + +Reference< XShape > CustomAnimationEffect::getTargetShape() const +{ + Reference< XShape > xShape; + maTarget >>= xShape; + if( !xShape.is() ) + { + ParagraphTarget aParaTarget; + if( maTarget >>= aParaTarget ) + xShape = aParaTarget.Shape; + } + + return xShape; +} + +Any CustomAnimationEffect::getRepeatCount() const +{ + if( mxNode.is() ) + { + return mxNode->getRepeatCount(); + } + else + { + Any aAny; + return aAny; + } +} + +Any CustomAnimationEffect::getEnd() const +{ + if( mxNode.is() ) + { + return mxNode->getEnd(); + } + else + { + Any aAny; + return aAny; + } +} + +void CustomAnimationEffect::setRepeatCount( const Any& rRepeatCount ) +{ + if( mxNode.is() ) + { + mxNode->setRepeatCount( rRepeatCount ); + double fRepeatCount = 1.0; + rRepeatCount >>= fRepeatCount; + mfAbsoluteDuration = mfDuration * fRepeatCount; + } +} + +void CustomAnimationEffect::setEnd( const Any& rEnd ) +{ + if( mxNode.is() ) + mxNode->setEnd( rEnd ); +} + +void CustomAnimationEffect::setFill( sal_Int16 nFill ) +{ + if (mxNode.is()) + { + mnFill = nFill; + mxNode->setFill( nFill ); + } +} + +Reference< XAnimationNode > CustomAnimationEffect::createAfterEffectNode() const +{ + DBG_ASSERT( mbHasAfterEffect, "sd::CustomAnimationEffect::createAfterEffectNode(), this node has no after effect!" ); + + Reference< XComponentContext > xContext( ::comphelper::getProcessComponentContext() ); + + Reference< XAnimate > xAnimate; + if( maDimColor.hasValue() ) + xAnimate = AnimateColor::create( xContext ); + else + xAnimate = AnimateSet::create( xContext ); + + Any aTo; + OUString aAttributeName; + + if( maDimColor.hasValue() ) + { + aTo = maDimColor; + aAttributeName = "DimColor"; + } + else + { + aTo <<= false; + aAttributeName = "Visibility"; + } + + Any aBegin; + if( !mbAfterEffectOnNextEffect ) // sameClick + { + Event aEvent; + + aEvent.Source <<= getNode(); + aEvent.Trigger = EventTrigger::END_EVENT; + aEvent.Repeat = 0; + + aBegin <<= aEvent; + } + else + { + aBegin <<= 0.0; + } + + xAnimate->setBegin( aBegin ); + xAnimate->setTo( aTo ); + xAnimate->setAttributeName( aAttributeName ); + + xAnimate->setDuration( Any( 0.001 ) ); + xAnimate->setFill( AnimationFill::HOLD ); + xAnimate->setTarget( maTarget ); + + return xAnimate; +} + +void CustomAnimationEffect::setIterateType( sal_Int16 nIterateType ) +{ + if( mnIterateType == nIterateType ) + return; + + try + { + // do we need to exchange the container node? + if( (mnIterateType == 0) || (nIterateType == 0) ) + { + sal_Int16 nTargetSubItem = mnTargetSubItem; + + Reference< XComponentContext > xContext( ::comphelper::getProcessComponentContext() ); + Reference< XTimeContainer > xNewContainer; + if(nIterateType) + { + xNewContainer.set( IterateContainer::create( xContext ) ); + } + else + xNewContainer.set( ParallelTimeContainer::create( xContext ), UNO_QUERY_THROW ); + + Reference< XTimeContainer > xOldContainer( mxNode, UNO_QUERY_THROW ); + Reference< XEnumerationAccess > xEnumerationAccess( mxNode, UNO_QUERY_THROW ); + Reference< XEnumeration > xEnumeration( xEnumerationAccess->createEnumeration(), UNO_SET_THROW ); + while( xEnumeration->hasMoreElements() ) + { + Reference< XAnimationNode > xChildNode( xEnumeration->nextElement(), UNO_QUERY_THROW ); + xOldContainer->removeChild( xChildNode ); + xNewContainer->appendChild( xChildNode ); + } + + xNewContainer->setBegin( mxNode->getBegin() ); + xNewContainer->setDuration( mxNode->getDuration() ); + xNewContainer->setEnd( mxNode->getEnd() ); + xNewContainer->setEndSync( mxNode->getEndSync() ); + xNewContainer->setRepeatCount( mxNode->getRepeatCount() ); + xNewContainer->setFill( mxNode->getFill() ); + xNewContainer->setFillDefault( mxNode->getFillDefault() ); + xNewContainer->setRestart( mxNode->getRestart() ); + xNewContainer->setRestartDefault( mxNode->getRestartDefault() ); + xNewContainer->setAcceleration( mxNode->getAcceleration() ); + xNewContainer->setDecelerate( mxNode->getDecelerate() ); + xNewContainer->setAutoReverse( mxNode->getAutoReverse() ); + xNewContainer->setRepeatDuration( mxNode->getRepeatDuration() ); + xNewContainer->setEndSync( mxNode->getEndSync() ); + xNewContainer->setRepeatCount( mxNode->getRepeatCount() ); + xNewContainer->setUserData( mxNode->getUserData() ); + + mxNode = xNewContainer; + + Any aTarget; + if( nIterateType ) + { + Reference< XIterateContainer > xIter( mxNode, UNO_QUERY_THROW ); + xIter->setTarget(maTarget); + xIter->setSubItem( nTargetSubItem ); + } + else + { + aTarget = maTarget; + } + + Reference< XEnumerationAccess > xEA( mxNode, UNO_QUERY_THROW ); + Reference< XEnumeration > xE( xEA->createEnumeration(), UNO_SET_THROW ); + while( xE->hasMoreElements() ) + { + Reference< XAnimate > xAnimate( xE->nextElement(), UNO_QUERY ); + if( xAnimate.is() ) + { + xAnimate->setTarget( aTarget ); + xAnimate->setSubItem( nTargetSubItem ); + } + } + } + + mnIterateType = nIterateType; + + // if we have an iteration container, we must set its type + if( mnIterateType ) + { + Reference< XIterateContainer > xIter( mxNode, UNO_QUERY_THROW ); + xIter->setIterateType( nIterateType ); + } + + checkForText(); + } + catch( Exception& ) + { + TOOLS_WARN_EXCEPTION( "sd", "sd::CustomAnimationEffect::setIterateType()" ); + } +} + +void CustomAnimationEffect::setIterateInterval( double fIterateInterval ) +{ + if( mfIterateInterval == fIterateInterval ) + return; + + Reference< XIterateContainer > xIter( mxNode, UNO_QUERY ); + + DBG_ASSERT( xIter.is(), "sd::CustomAnimationEffect::setIterateInterval(), not an iteration node" ); + if( xIter.is() ) + { + mfIterateInterval = fIterateInterval; + xIter->setIterateInterval( fIterateInterval ); + } + + calculateIterateDuration(); +} + +OUString CustomAnimationEffect::getPath() const +{ + OUString aPath; + + if( mxNode.is() ) try + { + Reference< XEnumerationAccess > xEnumerationAccess( mxNode, UNO_QUERY_THROW ); + Reference< XEnumeration > xEnumeration( xEnumerationAccess->createEnumeration(), UNO_SET_THROW ); + while( xEnumeration->hasMoreElements() ) + { + Reference< XAnimateMotion > xMotion( xEnumeration->nextElement(), UNO_QUERY ); + if( xMotion.is() ) + { + xMotion->getPath() >>= aPath; + break; + } + } + } + catch( Exception& ) + { + TOOLS_WARN_EXCEPTION( "sd", "sd::CustomAnimationEffect::getPath()" ); + } + + return aPath; +} + +void CustomAnimationEffect::setPath( const OUString& rPath ) +{ + if( !mxNode.is() ) + return; + + try + { + Reference< XEnumerationAccess > xEnumerationAccess( mxNode, UNO_QUERY_THROW ); + Reference< XEnumeration > xEnumeration( xEnumerationAccess->createEnumeration(), UNO_SET_THROW ); + while( xEnumeration->hasMoreElements() ) + { + Reference< XAnimateMotion > xMotion( xEnumeration->nextElement(), UNO_QUERY ); + if( xMotion.is() ) + { + + MainSequenceChangeGuard aGuard( mpEffectSequence ); + xMotion->setPath( Any( rPath ) ); + break; + } + } + } + catch( Exception& ) + { + TOOLS_WARN_EXCEPTION( "sd", "sd::CustomAnimationEffect::setPath()" ); + } +} + +Any CustomAnimationEffect::getProperty( sal_Int32 nNodeType, std::u16string_view rAttributeName, EValue eValue ) +{ + Any aProperty; + if( mxNode.is() ) try + { + Reference< XEnumerationAccess > xEnumerationAccess( mxNode, UNO_QUERY ); + if( xEnumerationAccess.is() ) + { + Reference< XEnumeration > xEnumeration = xEnumerationAccess->createEnumeration(); + if( xEnumeration.is() ) + { + while( xEnumeration->hasMoreElements() && !aProperty.hasValue() ) + { + Reference< XAnimate > xAnimate( xEnumeration->nextElement(), UNO_QUERY ); + if( !xAnimate.is() ) + continue; + + if( xAnimate->getType() == nNodeType ) + { + if( xAnimate->getAttributeName() == rAttributeName ) + { + switch( eValue ) + { + case EValue::To: aProperty = xAnimate->getTo(); break; + case EValue::By: aProperty = xAnimate->getBy(); break; + } + } + } + } + } + } + } + catch( Exception& ) + { + TOOLS_WARN_EXCEPTION( "sd", "sd::CustomAnimationEffect::getProperty()" ); + } + + return aProperty; +} + +bool CustomAnimationEffect::setProperty( sal_Int32 nNodeType, std::u16string_view rAttributeName, EValue eValue, const Any& rValue ) +{ + bool bChanged = false; + if( mxNode.is() ) try + { + Reference< XEnumerationAccess > xEnumerationAccess( mxNode, UNO_QUERY ); + if( xEnumerationAccess.is() ) + { + Reference< XEnumeration > xEnumeration = xEnumerationAccess->createEnumeration(); + if( xEnumeration.is() ) + { + while( xEnumeration->hasMoreElements() ) + { + Reference< XAnimate > xAnimate( xEnumeration->nextElement(), UNO_QUERY ); + if( !xAnimate.is() ) + continue; + + if( xAnimate->getType() == nNodeType ) + { + if( xAnimate->getAttributeName() == rAttributeName ) + { + switch( eValue ) + { + case EValue::To: + if( xAnimate->getTo() != rValue ) + { + xAnimate->setTo( rValue ); + bChanged = true; + } + break; + case EValue::By: + if( xAnimate->getTo() != rValue ) + { + xAnimate->setBy( rValue ); + bChanged = true; + } + break; + } + } + } + } + } + } + } + catch( Exception& ) + { + TOOLS_WARN_EXCEPTION( "sd", "sd::CustomAnimationEffect::setProperty()" ); + } + + return bChanged; +} + +static bool implIsColorAttribute( std::u16string_view rAttributeName ) +{ + return rAttributeName == u"FillColor" || rAttributeName == u"LineColor" || rAttributeName == u"CharColor"; +} + +Any CustomAnimationEffect::getColor( sal_Int32 nIndex ) +{ + Any aColor; + if( mxNode.is() ) try + { + Reference< XEnumerationAccess > xEnumerationAccess( mxNode, UNO_QUERY ); + if( xEnumerationAccess.is() ) + { + Reference< XEnumeration > xEnumeration = xEnumerationAccess->createEnumeration(); + if( xEnumeration.is() ) + { + while( xEnumeration->hasMoreElements() && !aColor.hasValue() ) + { + Reference< XAnimate > xAnimate( xEnumeration->nextElement(), UNO_QUERY ); + if( !xAnimate.is() ) + continue; + + switch( xAnimate->getType() ) + { + case AnimationNodeType::SET: + case AnimationNodeType::ANIMATE: + if( !implIsColorAttribute( xAnimate->getAttributeName() ) ) + break; + [[fallthrough]]; + case AnimationNodeType::ANIMATECOLOR: + Sequence<Any> aValues( xAnimate->getValues() ); + if( aValues.hasElements() ) + { + if( aValues.getLength() > nIndex ) + aColor = aValues[nIndex]; + } + else if( nIndex == 0 ) + aColor = xAnimate->getFrom(); + else + aColor = xAnimate->getTo(); + } + } + } + } + } + catch( Exception& ) + { + TOOLS_WARN_EXCEPTION( "sd", "sd::CustomAnimationEffect::getColor()" ); + } + + return aColor; +} + +void CustomAnimationEffect::setColor( sal_Int32 nIndex, const Any& rColor ) +{ + if( !mxNode.is() ) + return; + + try + { + Reference< XEnumerationAccess > xEnumerationAccess( mxNode, UNO_QUERY ); + if( xEnumerationAccess.is() ) + { + Reference< XEnumeration > xEnumeration = xEnumerationAccess->createEnumeration(); + if( xEnumeration.is() ) + { + while( xEnumeration->hasMoreElements() ) + { + Reference< XAnimate > xAnimate( xEnumeration->nextElement(), UNO_QUERY ); + if( !xAnimate.is() ) + continue; + + switch( xAnimate->getType() ) + { + case AnimationNodeType::SET: + case AnimationNodeType::ANIMATE: + if( !implIsColorAttribute( xAnimate->getAttributeName() ) ) + break; + [[fallthrough]]; + case AnimationNodeType::ANIMATECOLOR: + { + Sequence<Any> aValues( xAnimate->getValues() ); + if( aValues.hasElements() ) + { + if( aValues.getLength() > nIndex ) + { + aValues.getArray()[nIndex] = rColor; + xAnimate->setValues( aValues ); + } + } + else if( (nIndex == 0) && xAnimate->getFrom().hasValue() ) + xAnimate->setFrom(rColor); + else if( (nIndex == 1) && xAnimate->getTo().hasValue() ) + xAnimate->setTo(rColor); + } + break; + + } + } + } + } + } + catch( Exception& ) + { + TOOLS_WARN_EXCEPTION( "sd", "sd::CustomAnimationEffect::setColor()" ); + } +} + +Any CustomAnimationEffect::getTransformationProperty( sal_Int32 nTransformType, EValue eValue ) +{ + Any aProperty; + if( mxNode.is() ) try + { + Reference< XEnumerationAccess > xEnumerationAccess( mxNode, UNO_QUERY ); + if( xEnumerationAccess.is() ) + { + Reference< XEnumeration > xEnumeration = xEnumerationAccess->createEnumeration(); + if( xEnumeration.is() ) + { + while( xEnumeration->hasMoreElements() && !aProperty.hasValue() ) + { + Reference< XAnimateTransform > xTransform( xEnumeration->nextElement(), UNO_QUERY ); + if( !xTransform.is() ) + continue; + + if( xTransform->getTransformType() == nTransformType ) + { + switch( eValue ) + { + case EValue::To: aProperty = xTransform->getTo(); break; + case EValue::By: aProperty = xTransform->getBy(); break; + } + } + } + } + } + } + catch( Exception& ) + { + TOOLS_WARN_EXCEPTION( "sd", "sd::CustomAnimationEffect::getTransformationProperty()" ); + } + + return aProperty; +} + +bool CustomAnimationEffect::setTransformationProperty( sal_Int32 nTransformType, EValue eValue, const Any& rValue ) +{ + bool bChanged = false; + if( mxNode.is() ) try + { + Reference< XEnumerationAccess > xEnumerationAccess( mxNode, UNO_QUERY ); + if( xEnumerationAccess.is() ) + { + Reference< XEnumeration > xEnumeration = xEnumerationAccess->createEnumeration(); + if( xEnumeration.is() ) + { + while( xEnumeration->hasMoreElements() ) + { + Reference< XAnimateTransform > xTransform( xEnumeration->nextElement(), UNO_QUERY ); + if( !xTransform.is() ) + continue; + + if( xTransform->getTransformType() == nTransformType ) + { + switch( eValue ) + { + case EValue::To: + if( xTransform->getTo() != rValue ) + { + xTransform->setTo( rValue ); + bChanged = true; + } + break; + case EValue::By: + if( xTransform->getBy() != rValue ) + { + xTransform->setBy( rValue ); + bChanged = true; + } + break; + } + } + } + } + } + } + catch( Exception& ) + { + TOOLS_WARN_EXCEPTION( "sd", "sd::CustomAnimationEffect::setTransformationProperty()" ); + } + + return bChanged; +} + +void CustomAnimationEffect::createAudio( const css::uno::Any& rSource ) +{ + DBG_ASSERT( !mxAudio.is(), "sd::CustomAnimationEffect::createAudio(), node already has an audio!" ); + + if( mxAudio.is() ) + return; + + try + { + Reference< XComponentContext > xContext( ::comphelper::getProcessComponentContext() ); + Reference< XAudio > xAudio( Audio::create( xContext ) ); + xAudio->setSource( rSource ); + xAudio->setVolume( 1.0 ); + setAudio( xAudio ); + } + catch( Exception& ) + { + TOOLS_WARN_EXCEPTION( "sd", "sd::CustomAnimationEffect::createAudio()" ); + } +} + +static Reference< XCommand > findCommandNode( const Reference< XAnimationNode >& xRootNode ) +{ + Reference< XCommand > xCommand; + + if( xRootNode.is() ) try + { + Reference< XEnumerationAccess > xEnumerationAccess( xRootNode, UNO_QUERY_THROW ); + Reference< XEnumeration > xEnumeration( xEnumerationAccess->createEnumeration(), UNO_SET_THROW ); + while( !xCommand.is() && xEnumeration->hasMoreElements() ) + { + Reference< XAnimationNode > xNode( xEnumeration->nextElement(), UNO_QUERY ); + if( xNode.is() && (xNode->getType() == AnimationNodeType::COMMAND) ) + xCommand.set( xNode, UNO_QUERY_THROW ); + } + } + catch( Exception& ) + { + TOOLS_WARN_EXCEPTION( "sd", "sd::findCommandNode()" ); + } + + return xCommand; +} + +void CustomAnimationEffect::removeAudio() +{ + try + { + Reference< XAnimationNode > xChild; + + if( mxAudio.is() ) + { + xChild = mxAudio; + mxAudio.clear(); + } + else if( mnCommand == EffectCommands::STOPAUDIO ) + { + xChild = findCommandNode( mxNode ); + mnCommand = 0; + } + + if( xChild.is() ) + { + Reference< XTimeContainer > xContainer( mxNode, UNO_QUERY ); + if( xContainer.is() ) + xContainer->removeChild( xChild ); + } + } + catch( Exception& ) + { + TOOLS_WARN_EXCEPTION( "sd", "sd::CustomAnimationEffect::removeAudio()" ); + } + +} + +void CustomAnimationEffect::setAudio( const Reference< css::animations::XAudio >& xAudio ) +{ + if( mxAudio == xAudio ) + return; + + try + { + removeAudio(); + mxAudio = xAudio; + Reference< XTimeContainer > xContainer( mxNode, UNO_QUERY ); + if( xContainer.is() && mxAudio.is() ) + xContainer->appendChild( mxAudio ); + } + catch( Exception& ) + { + TOOLS_WARN_EXCEPTION( "sd", "sd::CustomAnimationEffect::setAudio()" ); + } +} + +void CustomAnimationEffect::setStopAudio() +{ + if( mnCommand == EffectCommands::STOPAUDIO ) + return; + + try + { + if( mxAudio.is() ) + removeAudio(); + + Reference< XComponentContext > xContext( ::comphelper::getProcessComponentContext() ); + Reference< XCommand > xCommand( Command::create( xContext ) ); + + xCommand->setCommand( EffectCommands::STOPAUDIO ); + + Reference< XTimeContainer > xContainer( mxNode, UNO_QUERY_THROW ); + xContainer->appendChild( xCommand ); + + mnCommand = EffectCommands::STOPAUDIO; + } + catch( Exception& ) + { + TOOLS_WARN_EXCEPTION( "sd", "sd::CustomAnimationEffect::setStopAudio()" ); + } +} + +bool CustomAnimationEffect::getStopAudio() const +{ + return mnCommand == EffectCommands::STOPAUDIO; +} + +SdrPathObj* CustomAnimationEffect::createSdrPathObjFromPath(SdrModel& rTargetModel) +{ + SdrPathObj * pPathObj = new SdrPathObj(rTargetModel, SdrObjKind::PathLine); + updateSdrPathObjFromPath( *pPathObj ); + return pPathObj; +} + +void CustomAnimationEffect::updateSdrPathObjFromPath( SdrPathObj& rPathObj ) +{ + ::basegfx::B2DPolyPolygon aPolyPoly; + if( ::basegfx::utils::importFromSvgD( aPolyPoly, getPath(), true, nullptr ) ) + { + SdrObject* pObj = SdrObject::getSdrObjectFromXShape(getTargetShape()); + if( pObj ) + { + SdrPage* pPage = pObj->getSdrPageFromSdrObject(); + if( pPage ) + { + const Size aPageSize( pPage->GetSize() ); + aPolyPoly.transform(basegfx::utils::createScaleB2DHomMatrix(static_cast<double>(aPageSize.Width()), static_cast<double>(aPageSize.Height()))); + } + + const ::tools::Rectangle aBoundRect( pObj->GetCurrentBoundRect() ); + const Point aCenter( aBoundRect.Center() ); + aPolyPoly.transform(basegfx::utils::createTranslateB2DHomMatrix(aCenter.X(), aCenter.Y())); + } + } + + rPathObj.SetPathPoly( aPolyPoly ); +} + +void CustomAnimationEffect::updatePathFromSdrPathObj( const SdrPathObj& rPathObj ) +{ + ::basegfx::B2DPolyPolygon aPolyPoly( rPathObj.GetPathPoly() ); + + SdrObject* pObj = SdrObject::getSdrObjectFromXShape(getTargetShape()); + if( pObj ) + { + ::tools::Rectangle aBoundRect(0,0,0,0); + + drawinglayer::primitive2d::Primitive2DContainer xPrimitives; + pObj->GetViewContact().getViewIndependentPrimitive2DContainer(xPrimitives); + const drawinglayer::geometry::ViewInformation2D aViewInformation2D; + const basegfx::B2DRange aRange(xPrimitives.getB2DRange(aViewInformation2D)); + + if(!aRange.isEmpty()) + { + aBoundRect = ::tools::Rectangle( + static_cast<sal_Int32>(floor(aRange.getMinX())), static_cast<sal_Int32>(floor(aRange.getMinY())), + static_cast<sal_Int32>(ceil(aRange.getMaxX())), static_cast<sal_Int32>(ceil(aRange.getMaxY()))); + } + + const Point aCenter( aBoundRect.Center() ); + + aPolyPoly.transform(basegfx::utils::createTranslateB2DHomMatrix(-aCenter.X(), -aCenter.Y())); + + SdrPage* pPage = pObj->getSdrPageFromSdrObject(); + if( pPage ) + { + const Size aPageSize( pPage->GetSize() ); + aPolyPoly.transform(basegfx::utils::createScaleB2DHomMatrix( + 1.0 / static_cast<double>(aPageSize.Width()), 1.0 / static_cast<double>(aPageSize.Height()))); + } + } + + setPath( ::basegfx::utils::exportToSvgD( aPolyPoly, true, true, true) ); +} + +EffectSequenceHelper::EffectSequenceHelper() +: mnSequenceType( EffectNodeType::DEFAULT ) +{ +} + +EffectSequenceHelper::EffectSequenceHelper( const css::uno::Reference< css::animations::XTimeContainer >& xSequenceRoot ) +: mxSequenceRoot( xSequenceRoot ), mnSequenceType( EffectNodeType::DEFAULT ) +{ + Reference< XAnimationNode > xNode( mxSequenceRoot, UNO_QUERY_THROW ); + create( xNode ); +} + +EffectSequenceHelper::~EffectSequenceHelper() +{ + reset(); +} + +void EffectSequenceHelper::reset() +{ + for( CustomAnimationEffectPtr& pEffect : maEffects ) + { + pEffect->setEffectSequence(nullptr); + } + maEffects.clear(); +} + +Reference< XAnimationNode > EffectSequenceHelper::getRootNode() +{ + return mxSequenceRoot; +} + +void EffectSequenceHelper::append( const CustomAnimationEffectPtr& pEffect ) +{ + pEffect->setEffectSequence( this ); + maEffects.push_back(pEffect); + rebuild(); +} + +CustomAnimationEffectPtr EffectSequenceHelper::append( const CustomAnimationPresetPtr& pPreset, const Any& rTarget, double fDuration /* = -1.0 */ ) +{ + CustomAnimationEffectPtr pEffect; + + if( pPreset ) + { + Reference< XAnimationNode > xNode( pPreset->create( "" ) ); + if( xNode.is() ) + { + // first, filter all only ui relevant user data + std::vector< NamedValue > aNewUserData; + Sequence< NamedValue > aUserData( xNode->getUserData() ); + + std::copy_if(std::cbegin(aUserData), std::cend(aUserData), std::back_inserter(aNewUserData), + [](const NamedValue& rProp) { return rProp.Name != "text-only" && rProp.Name != "preset-property"; }); + + if( !aNewUserData.empty() ) + { + aUserData = ::comphelper::containerToSequence( aNewUserData ); + xNode->setUserData( aUserData ); + } + + // check target, maybe we need to force it to text + sal_Int16 nSubItem = ShapeAnimationSubType::AS_WHOLE; + + if( rTarget.getValueType() == ::cppu::UnoType<ParagraphTarget>::get() ) + { + nSubItem = ShapeAnimationSubType::ONLY_TEXT; + } + else if( pPreset->isTextOnly() ) + { + Reference< XShape > xShape; + rTarget >>= xShape; + if( xShape.is() ) + { + // that's bad, we target a shape here but the effect is only for text + // so change subitem + nSubItem = ShapeAnimationSubType::ONLY_TEXT; + } + } + + // now create effect from preset + pEffect = std::make_shared<CustomAnimationEffect>( xNode ); + pEffect->setEffectSequence( this ); + pEffect->setTarget( rTarget ); + pEffect->setTargetSubItem( nSubItem ); + if( fDuration != -1.0 ) + pEffect->setDuration( fDuration ); + + maEffects.push_back(pEffect); + + rebuild(); + } + } + + DBG_ASSERT( pEffect, "sd::EffectSequenceHelper::append(), failed!" ); + return pEffect; +} + +CustomAnimationEffectPtr EffectSequenceHelper::append( const SdrPathObj& rPathObj, const Any& rTarget, double fDuration /* = -1.0 */, const OUString& rPresetId ) +{ + CustomAnimationEffectPtr pEffect; + + if( fDuration <= 0.0 ) + fDuration = 2.0; + + try + { + Reference< XTimeContainer > xEffectContainer( ParallelTimeContainer::create( ::comphelper::getProcessComponentContext() ), UNO_QUERY_THROW ); + Reference< XAnimationNode > xAnimateMotion( AnimateMotion::create( ::comphelper::getProcessComponentContext() ) ); + + xAnimateMotion->setDuration( Any( fDuration ) ); + xAnimateMotion->setFill( AnimationFill::HOLD ); + xEffectContainer->appendChild( xAnimateMotion ); + + sal_Int16 nSubItem = ShapeAnimationSubType::AS_WHOLE; + + if( rTarget.getValueType() == ::cppu::UnoType<ParagraphTarget>::get() ) + nSubItem = ShapeAnimationSubType::ONLY_TEXT; + + pEffect = std::make_shared<CustomAnimationEffect>( xEffectContainer ); + pEffect->setEffectSequence( this ); + pEffect->setTarget( rTarget ); + pEffect->setTargetSubItem( nSubItem ); + pEffect->setNodeType( css::presentation::EffectNodeType::ON_CLICK ); + pEffect->setPresetClassAndId( css::presentation::EffectPresetClass::MOTIONPATH, rPresetId ); + pEffect->setAcceleration( 0.5 ); + pEffect->setDecelerate( 0.5 ); + pEffect->setFill( AnimationFill::HOLD ); + pEffect->setBegin( 0.0 ); + pEffect->updatePathFromSdrPathObj( rPathObj ); + if( fDuration != -1.0 ) + pEffect->setDuration( fDuration ); + + maEffects.push_back(pEffect); + + rebuild(); + } + catch( Exception& ) + { + TOOLS_WARN_EXCEPTION( "sd", "sd::EffectSequenceHelper::append()" ); + } + + return pEffect; +} + +void EffectSequenceHelper::replace( const CustomAnimationEffectPtr& pEffect, const CustomAnimationPresetPtr& pPreset, const OUString& rPresetSubType, double fDuration /* = -1.0 */ ) +{ + if( !(pEffect && pPreset) ) + return; + + try + { + Reference< XAnimationNode > xNewNode( pPreset->create( rPresetSubType ) ); + if( xNewNode.is() ) + { + pEffect->replaceNode( xNewNode ); + if( fDuration != -1.0 ) + pEffect->setDuration( fDuration ); + } + + rebuild(); + } + catch( Exception& ) + { + TOOLS_WARN_EXCEPTION( "sd", "sd::EffectSequenceHelper::replace()" ); + } +} + +void EffectSequenceHelper::replace( const CustomAnimationEffectPtr& pEffect, const CustomAnimationPresetPtr& pPreset, double fDuration /* = -1.0 */ ) +{ + replace( pEffect, pPreset, "", fDuration ); +} + +void EffectSequenceHelper::remove( const CustomAnimationEffectPtr& pEffect ) +{ + if( pEffect ) + { + pEffect->setEffectSequence( nullptr ); + maEffects.remove( pEffect ); + } + + rebuild(); +} + +void EffectSequenceHelper::moveToBeforeEffect( const CustomAnimationEffectPtr& pEffect, const CustomAnimationEffectPtr& pInsertBefore) +{ + if ( pEffect ) + { + maEffects.remove( pEffect ); + EffectSequence::iterator aInsertIter( find( pInsertBefore ) ); + + // aInsertIter being end() is OK: pInsertBefore could be null, so put at end. + maEffects.insert( aInsertIter, pEffect ); + + rebuild(); + } +} + +void EffectSequenceHelper::rebuild() +{ + implRebuild(); +} + +void EffectSequenceHelper::implRebuild() +{ + try + { + // first we delete all time containers on the first two levels + Reference< XEnumerationAccess > xEnumerationAccess( mxSequenceRoot, UNO_QUERY_THROW ); + Reference< XEnumeration > xEnumeration( xEnumerationAccess->createEnumeration(), UNO_SET_THROW ); + while( xEnumeration->hasMoreElements() ) + { + Reference< XAnimationNode > xChildNode( xEnumeration->nextElement(), UNO_QUERY_THROW ); + Reference< XTimeContainer > xChildContainer( xChildNode, UNO_QUERY_THROW ); + + Reference< XEnumerationAccess > xChildEnumerationAccess( xChildNode, UNO_QUERY_THROW ); + Reference< XEnumeration > xChildEnumeration( xChildEnumerationAccess->createEnumeration(), UNO_SET_THROW ); + while( xChildEnumeration->hasMoreElements() ) + { + Reference< XAnimationNode > xNode( xChildEnumeration->nextElement(), UNO_QUERY_THROW ); + xChildContainer->removeChild( xNode ); + } + + mxSequenceRoot->removeChild( xChildNode ); + } + + // second, rebuild main sequence + EffectSequence::iterator aIter( maEffects.begin() ); + EffectSequence::iterator aEnd( maEffects.end() ); + if( aIter != aEnd ) + { + std::vector< sd::AfterEffectNode > aAfterEffects; + + CustomAnimationEffectPtr pEffect = *aIter++; + + bool bFirst = true; + do + { + // create a par container for the next click node and all following with and after effects + Reference< XTimeContainer > xOnClickContainer( ParallelTimeContainer::create( ::comphelper::getProcessComponentContext() ), UNO_QUERY_THROW ); + + Event aEvent; + if( mxEventSource.is() ) + { + aEvent.Source <<= mxEventSource; + aEvent.Trigger = EventTrigger::ON_CLICK; + } + else + { + aEvent.Trigger = EventTrigger::ON_NEXT; + } + aEvent.Repeat = 0; + + Any aBegin( aEvent ); + if( bFirst ) + { + // if the first node is not a click action, this click container + // must not have INDEFINITE begin but start at 0s + bFirst = false; + if( pEffect->getNodeType() != EffectNodeType::ON_CLICK ) + aBegin <<= 0.0; + } + + xOnClickContainer->setBegin( aBegin ); + + mxSequenceRoot->appendChild( xOnClickContainer ); + + double fBegin = 0.0; + + do + { + // create a par container for the current click or after effect node and all following with effects + Reference< XTimeContainer > xWithContainer( ParallelTimeContainer::create( ::comphelper::getProcessComponentContext() ), UNO_QUERY_THROW ); + xWithContainer->setBegin( Any( fBegin ) ); + xOnClickContainer->appendChild( xWithContainer ); + + double fDuration = 0.0; + do + { + Reference< XAnimationNode > xEffectNode( pEffect->getNode() ); + xWithContainer->appendChild( xEffectNode ); + + if( pEffect->hasAfterEffect() ) + { + Reference< XAnimationNode > xAfterEffect( pEffect->createAfterEffectNode() ); + AfterEffectNode a( xAfterEffect, xEffectNode, pEffect->IsAfterEffectOnNext() ); + aAfterEffects.push_back( a ); + } + + double fTemp = pEffect->getBegin() + pEffect->getAbsoluteDuration(); + if( fTemp > fDuration ) + fDuration = fTemp; + + if( aIter != aEnd ) + pEffect = *aIter++; + else + pEffect.reset(); + } + while( pEffect && (pEffect->getNodeType() == EffectNodeType::WITH_PREVIOUS) ); + + fBegin += fDuration; + } + while( pEffect && (pEffect->getNodeType() != EffectNodeType::ON_CLICK) ); + } + while( pEffect ); + + // process after effect nodes + std::for_each( aAfterEffects.begin(), aAfterEffects.end(), stl_process_after_effect_node_func ); + + updateTextGroups(); + + // reset duration, might have been altered (see below) + mxSequenceRoot->setDuration( Any() ); + } + else + { + // empty sequence, set duration to 0.0 explicitly + // (otherwise, this sequence will never end) + mxSequenceRoot->setDuration( Any(0.0) ); + } + } + catch( Exception& ) + { + TOOLS_WARN_EXCEPTION( "sd", "sd::EffectSequenceHelper::rebuild()" ); + } +} + +stl_CustomAnimationEffect_search_node_predict::stl_CustomAnimationEffect_search_node_predict( const css::uno::Reference< css::animations::XAnimationNode >& xSearchNode ) +: mxSearchNode( xSearchNode ) +{ +} + +bool stl_CustomAnimationEffect_search_node_predict::operator()( const CustomAnimationEffectPtr& pEffect ) const +{ + return pEffect->getNode() == mxSearchNode; +} + +/// @throws Exception +static bool implFindNextContainer( Reference< XTimeContainer > const & xParent, Reference< XTimeContainer > const & xCurrent, Reference< XTimeContainer >& xNext ) +{ + Reference< XEnumerationAccess > xEnumerationAccess( xParent, UNO_QUERY_THROW ); + Reference< XEnumeration > xEnumeration( xEnumerationAccess->createEnumeration() ); + if( xEnumeration.is() ) + { + Reference< XInterface > x; + while( xEnumeration->hasMoreElements() && !xNext.is() ) + { + if( (xEnumeration->nextElement() >>= x) && (x == xCurrent) ) + { + if( xEnumeration->hasMoreElements() ) + xEnumeration->nextElement() >>= xNext; + } + } + } + return xNext.is(); +} + +void stl_process_after_effect_node_func(AfterEffectNode const & rNode) +{ + try + { + if( rNode.mxNode.is() && rNode.mxMaster.is() ) + { + // set master node + Reference< XAnimationNode > xMasterNode( rNode.mxMaster, UNO_SET_THROW ); + Sequence< NamedValue > aUserData( rNode.mxNode->getUserData() ); + sal_Int32 nSize = aUserData.getLength(); + aUserData.realloc(nSize+1); + auto pUserData = aUserData.getArray(); + pUserData[nSize].Name = "master-element"; + pUserData[nSize].Value <<= xMasterNode; + rNode.mxNode->setUserData( aUserData ); + + // insert after effect node into timeline + Reference< XTimeContainer > xContainer( rNode.mxMaster->getParent(), UNO_QUERY_THROW ); + + if( !rNode.mbOnNextEffect ) // sameClick + { + // insert the aftereffect after its effect is animated + xContainer->insertAfter( rNode.mxNode, rNode.mxMaster ); + } + else // nextClick + { + Reference< XComponentContext > xContext( ::comphelper::getProcessComponentContext() ); + // insert the aftereffect in the next group + + Reference< XTimeContainer > xClickContainer( xContainer->getParent(), UNO_QUERY_THROW ); + Reference< XTimeContainer > xSequenceContainer( xClickContainer->getParent(), UNO_QUERY_THROW ); + + Reference< XTimeContainer > xNextContainer; + + // first try if we have an after effect container + if( !implFindNextContainer( xClickContainer, xContainer, xNextContainer ) ) + { + Reference< XTimeContainer > xNextClickContainer; + // if not, try to find the next click effect container + if( implFindNextContainer( xSequenceContainer, xClickContainer, xNextClickContainer ) ) + { + Reference< XEnumerationAccess > xEnumerationAccess( xNextClickContainer, UNO_QUERY_THROW ); + Reference< XEnumeration > xEnumeration( xEnumerationAccess->createEnumeration(), UNO_SET_THROW ); + if( xEnumeration->hasMoreElements() ) + { + // the next container is the first child container + xEnumeration->nextElement() >>= xNextContainer; + } + else + { + // this does not yet have a child container, create one + xNextContainer.set( ParallelTimeContainer::create(xContext), UNO_QUERY_THROW ); + + xNextContainer->setBegin( Any( 0.0 ) ); + xNextClickContainer->appendChild( xNextContainer ); + } + DBG_ASSERT( xNextContainer.is(), "ppt::stl_process_after_effect_node_func::operator(), could not find/create container!" ); + } + } + + // if we don't have a next container, we add one to the sequence container + if( !xNextContainer.is() ) + { + Reference< XTimeContainer > xNewClickContainer( ParallelTimeContainer::create( xContext ), UNO_QUERY_THROW ); + + Event aEvent; + aEvent.Trigger = EventTrigger::ON_NEXT; + aEvent.Repeat = 0; + xNewClickContainer->setBegin( Any( aEvent ) ); + + xSequenceContainer->insertAfter( xNewClickContainer, xClickContainer ); + + xNextContainer.set( ParallelTimeContainer::create( xContext ), UNO_QUERY_THROW ); + + xNextContainer->setBegin( Any( 0.0 ) ); + xNewClickContainer->appendChild( xNextContainer ); + } + + if( xNextContainer.is() ) + { + // find begin time of first element + Reference< XEnumerationAccess > xEnumerationAccess( xNextContainer, UNO_QUERY_THROW ); + Reference< XEnumeration > xEnumeration( xEnumerationAccess->createEnumeration(), UNO_SET_THROW ); + if( xEnumeration->hasMoreElements() ) + { + Reference< XAnimationNode > xChild; + // the next container is the first child container + xEnumeration->nextElement() >>= xChild; + if( xChild.is() ) + { + Any aBegin( xChild->getBegin() ); + double fBegin = 0.0; + if( (aBegin >>= fBegin) && (fBegin >= 0.0)) + rNode.mxNode->setBegin( aBegin ); + } + } + + xNextContainer->appendChild( rNode.mxNode ); + } + } + } + } + catch( Exception& ) + { + TOOLS_WARN_EXCEPTION( "sd", "ppt::stl_process_after_effect_node_func::operator()" ); + } +} + +EffectSequence::iterator EffectSequenceHelper::find( const CustomAnimationEffectPtr& pEffect ) +{ + return std::find( maEffects.begin(), maEffects.end(), pEffect ); +} + +CustomAnimationEffectPtr EffectSequenceHelper::findEffect( const css::uno::Reference< css::animations::XAnimationNode >& xNode ) const +{ + CustomAnimationEffectPtr pEffect; + + EffectSequence::const_iterator aIter = std::find_if(maEffects.begin(), maEffects.end(), + [&xNode](const CustomAnimationEffectPtr& rxEffect) { return rxEffect->getNode() == xNode; }); + if (aIter != maEffects.end()) + pEffect = *aIter; + + return pEffect; +} + +sal_Int32 EffectSequenceHelper::getOffsetFromEffect( const CustomAnimationEffectPtr& xEffect ) const +{ + auto aIter = std::find(maEffects.begin(), maEffects.end(), xEffect); + if (aIter != maEffects.end()) + return static_cast<sal_Int32>(std::distance(maEffects.begin(), aIter)); + + return -1; +} + +CustomAnimationEffectPtr EffectSequenceHelper::getEffectFromOffset( sal_Int32 nOffset ) const +{ + EffectSequence::const_iterator aIter( maEffects.begin() ); + nOffset = std::min(nOffset, static_cast<sal_Int32>(maEffects.size())); + std::advance(aIter, nOffset); + + CustomAnimationEffectPtr pEffect; + if( aIter != maEffects.end() ) + pEffect = *aIter; + + return pEffect; +} + +bool EffectSequenceHelper::disposeShape( const Reference< XShape >& xShape ) +{ + bool bChanges = false; + + EffectSequence::iterator aIter( maEffects.begin() ); + while( aIter != maEffects.end() ) + { + if( (*aIter)->getTargetShape() == xShape ) + { + (*aIter)->setEffectSequence( nullptr ); + bChanges = true; + aIter = maEffects.erase( aIter ); + } + else + { + ++aIter; + } + } + + return bChanges; +} + +bool EffectSequenceHelper::hasEffect( const css::uno::Reference< css::drawing::XShape >& xShape ) +{ + return std::any_of(maEffects.begin(), maEffects.end(), + [&xShape](const CustomAnimationEffectPtr& rxEffect) { return rxEffect->getTargetShape() == xShape; }); +} + +bool EffectSequenceHelper::getParagraphNumberingLevels( const Reference< XShape >& xShape, std::vector< sal_Int32 >& rParagraphNumberingLevel ) +{ + rParagraphNumberingLevel.clear(); + + if( !hasEffect( xShape ) ) + return false; + + Reference< XText > xText( xShape, UNO_QUERY ); + if( xText.is() ) + { + Reference< XEnumerationAccess > xEA( xText, UNO_QUERY ); + if( xEA.is() ) + { + Reference< XEnumeration > xEnumeration = xEA->createEnumeration(); + + if( xEnumeration.is() ) + { + while( xEnumeration->hasMoreElements() ) + { + Reference< XPropertySet > xParaSet; + xEnumeration->nextElement() >>= xParaSet; + + sal_Int32 nParaDepth = 0; + if( xParaSet.is() ) + { + xParaSet->getPropertyValue( "NumberingLevel" ) >>= nParaDepth; + } + + rParagraphNumberingLevel.push_back( nParaDepth ); + } + } + } + } + + return true; +} + +void EffectSequenceHelper::insertTextRange( const css::uno::Any& aTarget ) +{ + ParagraphTarget aParaTarget; + if( !(aTarget >>= aParaTarget ) ) + return; + + // get map [paragraph index] -> [NumberingLevel] + // for following reusage inside all animation effects + std::vector< sal_Int32 > paragraphNumberingLevel; + std::vector< sal_Int32 >* paragraphNumberingLevelParam = nullptr; + if ( getParagraphNumberingLevels( aParaTarget.Shape, paragraphNumberingLevel ) ) + paragraphNumberingLevelParam = ¶graphNumberingLevel; + + // update internal flags for each animation effect + const bool bChanges = std::accumulate(maEffects.begin(), maEffects.end(), false, + [&aParaTarget, ¶graphNumberingLevelParam](const bool bCheck, const CustomAnimationEffectPtr& rxEffect) { + bool bRes = bCheck; + if (rxEffect->getTargetShape() == aParaTarget.Shape) + bRes |= rxEffect->checkForText( paragraphNumberingLevelParam ); + return bRes; + }); + + if( bChanges ) + rebuild(); +} + +static bool isParagraphTargetTextEmpty( ParagraphTarget aParaTarget ) +{ + // get paragraph + Reference< XText > xText ( aParaTarget.Shape, UNO_QUERY ); + if( xText.is() ) + { + Reference< XEnumerationAccess > xEA( xText, UNO_QUERY ); + if( xEA.is() ) + { + Reference< XEnumeration > xEnumeration = xEA->createEnumeration(); + if( xEnumeration.is() ) + { + // advance to the Nth paragraph + sal_Int32 nPara = aParaTarget.Paragraph; + while( xEnumeration->hasMoreElements() && nPara-- ) + xEnumeration->nextElement(); + + // get Nth paragraph's text and check if it's empty + if( xEnumeration->hasMoreElements() ) + { + Reference< XTextRange > xRange( xEnumeration->nextElement(), UNO_QUERY ); + if( xRange.is() ) + { + OUString text = xRange->getString(); + return text.isEmpty(); + } + } + } + } + } + return false; +} + +void EffectSequenceHelper::disposeTextRange( const css::uno::Any& aTarget ) +{ + ParagraphTarget aParaTarget; + if( !(aTarget >>= aParaTarget ) ) + return; + + bool bChanges = false; + + // building list of effects for target shape; process effects not on target shape + EffectSequence aTargetParagraphEffects; + for( const auto &pEffect : maEffects ) + { + Any aIterTarget( pEffect->getTarget() ); + if( aIterTarget.getValueType() == ::cppu::UnoType<ParagraphTarget>::get() ) + { + ParagraphTarget aIterParaTarget; + if( (aIterTarget >>= aIterParaTarget) && (aIterParaTarget.Shape == aParaTarget.Shape) ) + { + aTargetParagraphEffects.push_back(pEffect); + } + } + else if( pEffect->getTargetShape() == aParaTarget.Shape ) + { + bChanges |= pEffect->checkForText(); + } + } + + // select effect to delete: + // if paragraph before target is blank, then delete its animation effect (if any) instead + ParagraphTarget aPreviousParagraph = aParaTarget; + --aPreviousParagraph.Paragraph; + bool bIsPreviousParagraphEmpty = isParagraphTargetTextEmpty( aPreviousParagraph ); + sal_Int16 anParaNumToDelete = bIsPreviousParagraphEmpty ? aPreviousParagraph.Paragraph : aParaTarget.Paragraph; + + // update effects + for( const auto &pEffect : aTargetParagraphEffects ) + { + Any aIterTarget( pEffect->getTarget() ); + + ParagraphTarget aIterParaTarget; + aIterTarget >>= aIterParaTarget; + + // delete effect for target paragraph (may have effects in more than one text group) + if( aIterParaTarget.Paragraph == anParaNumToDelete ) + { + auto aItr = find( pEffect ); + DBG_ASSERT( aItr != maEffects.end(), "sd::EffectSequenceHelper::disposeTextRange(), Expected effect missing."); + if( aItr != maEffects.end() ) + { + (*aItr)->setEffectSequence( nullptr ); + maEffects.erase(aItr); + bChanges = true; + } + } + + // shift all paragraphs after disposed paragraph + if( aIterParaTarget.Paragraph > anParaNumToDelete ) + { + --aIterParaTarget.Paragraph; + pEffect->setTarget( Any( aIterParaTarget ) ); + bChanges = true; + } + } + + if( bChanges ) + { + rebuild(); + } +} + +CustomAnimationTextGroup::CustomAnimationTextGroup( const Reference< XShape >& rTarget, sal_Int32 nGroupId ) +: maTarget( rTarget ), + mnGroupId( nGroupId ) +{ + reset(); +} + +void CustomAnimationTextGroup::reset() +{ + mnTextGrouping = -1; + mbAnimateForm = false; + mbTextReverse = false; + mfGroupingAuto = -1.0; + mnLastPara = -1; // used to check for TextReverse + + for (sal_Int8 & rn : mnDepthFlags) + { + rn = 0; + } + + maEffects.clear(); +} + +void CustomAnimationTextGroup::addEffect( CustomAnimationEffectPtr const & pEffect ) +{ + maEffects.push_back( pEffect ); + + Any aTarget( pEffect->getTarget() ); + if( aTarget.getValueType() == ::cppu::UnoType<ParagraphTarget>::get() ) + { + // now look at the paragraph + ParagraphTarget aParaTarget; + aTarget >>= aParaTarget; + + if( mnLastPara != -1 ) + mbTextReverse = mnLastPara > aParaTarget.Paragraph; + + mnLastPara = aParaTarget.Paragraph; + + const sal_Int32 nParaDepth = pEffect->getParaDepth(); + + // only look at the first PARA_LEVELS levels + if( nParaDepth < PARA_LEVELS ) + { + // our first paragraph with this level? + if( mnDepthFlags[nParaDepth] == 0 ) + { + // so set it to the first found + mnDepthFlags[nParaDepth] = static_cast<sal_Int8>(pEffect->getNodeType()); + } + else if( mnDepthFlags[nParaDepth] != pEffect->getNodeType() ) + { + mnDepthFlags[nParaDepth] = -1; + } + + if( pEffect->getNodeType() == EffectNodeType::AFTER_PREVIOUS ) + mfGroupingAuto = pEffect->getBegin(); + + mnTextGrouping = PARA_LEVELS; + while( (mnTextGrouping > 0) + && (mnDepthFlags[mnTextGrouping - 1] <= 0) ) + --mnTextGrouping; + } + } + else + { + // if we have an effect with the shape as a target, we animate the background + mbAnimateForm = pEffect->getTargetSubItem() != ShapeAnimationSubType::ONLY_TEXT; + } +} + +CustomAnimationTextGroupPtr EffectSequenceHelper::findGroup( sal_Int32 nGroupId ) +{ + CustomAnimationTextGroupPtr aPtr; + + CustomAnimationTextGroupMap::iterator aIter( maGroupMap.find( nGroupId ) ); + if( aIter != maGroupMap.end() ) + aPtr = (*aIter).second; + + return aPtr; +} + +void EffectSequenceHelper::updateTextGroups() +{ + maGroupMap.clear(); + + // first create all the groups + for( const CustomAnimationEffectPtr& pEffect : maEffects ) + { + const sal_Int32 nGroupId = pEffect->getGroupId(); + + if( nGroupId == -1 ) + continue; // trivial case, no group + + CustomAnimationTextGroupPtr pGroup = findGroup( nGroupId ); + if( !pGroup ) + { + pGroup = std::make_shared<CustomAnimationTextGroup>( pEffect->getTargetShape(), nGroupId ); + maGroupMap[nGroupId] = pGroup; + } + + pGroup->addEffect( pEffect ); + } + + // Now that all the text groups have been cleared up and rebuilt, we need to update its + // text grouping. addEffect() already make mnTextGrouping the last possible level, + // so just continue to find the last level that is not EffectNodeType::WITH_PREVIOUS. + for(const auto &rGroupMapItem: maGroupMap) + { + const CustomAnimationTextGroupPtr &pGroup = rGroupMapItem.second; + while(pGroup->mnTextGrouping > 0 && pGroup->mnDepthFlags[pGroup->mnTextGrouping - 1] == EffectNodeType::WITH_PREVIOUS) + --pGroup->mnTextGrouping; + } +} + +CustomAnimationTextGroupPtr +EffectSequenceHelper::createTextGroup(const CustomAnimationEffectPtr& pEffect, + sal_Int32 nTextGrouping, double fTextGroupingAuto, + bool bAnimateForm, bool bTextReverse) +{ + // first find a free group-id + sal_Int32 nGroupId = 0; + + CustomAnimationTextGroupMap::iterator aIter( maGroupMap.begin() ); + const CustomAnimationTextGroupMap::iterator aEnd( maGroupMap.end() ); + while( aIter != aEnd ) + { + if( (*aIter).first == nGroupId ) + { + nGroupId++; + aIter = maGroupMap.begin(); + } + else + { + ++aIter; + } + } + + Reference< XShape > xTarget( pEffect->getTargetShape() ); + + CustomAnimationTextGroupPtr pTextGroup = std::make_shared<CustomAnimationTextGroup>( xTarget, nGroupId ); + maGroupMap[nGroupId] = pTextGroup; + + bool bUsed = false; + + // do we need to target the shape? + if( (nTextGrouping == 0) || bAnimateForm ) + { + sal_Int16 nSubItem; + if( nTextGrouping == 0) + nSubItem = bAnimateForm ? ShapeAnimationSubType::AS_WHOLE : ShapeAnimationSubType::ONLY_TEXT; + else + nSubItem = ShapeAnimationSubType::ONLY_BACKGROUND; + + pEffect->setTarget( Any( xTarget ) ); + pEffect->setTargetSubItem( nSubItem ); + pEffect->setEffectSequence( this ); + pEffect->setGroupId( nGroupId ); + + pTextGroup->addEffect( pEffect ); + bUsed = true; + } + + pTextGroup->mnTextGrouping = nTextGrouping; + pTextGroup->mfGroupingAuto = fTextGroupingAuto; + pTextGroup->mbTextReverse = bTextReverse; + + // now add an effect for each paragraph + createTextGroupParagraphEffects( pTextGroup, pEffect, bUsed ); + + notify_listeners(); + + return pTextGroup; +} + +void EffectSequenceHelper::createTextGroupParagraphEffects( const CustomAnimationTextGroupPtr& pTextGroup, const CustomAnimationEffectPtr& pEffect, bool bUsed ) +{ + Reference< XShape > xTarget( pTextGroup->maTarget ); + + sal_Int32 nTextGrouping = pTextGroup->mnTextGrouping; + double fTextGroupingAuto = pTextGroup->mfGroupingAuto; + bool bTextReverse = pTextGroup->mbTextReverse; + + // now add an effect for each paragraph + if( nTextGrouping < 0 ) + return; + + try + { + EffectSequence::iterator aInsertIter( find( pEffect ) ); + + Reference< XEnumerationAccess > xText( xTarget, UNO_QUERY_THROW ); + Reference< XEnumeration > xEnumeration( xText->createEnumeration(), UNO_SET_THROW ); + + std::deque< sal_Int16 > aParaList; + sal_Int16 nPara; + + // fill the list with all valid paragraphs + for( nPara = 0; xEnumeration->hasMoreElements(); nPara++ ) + { + Reference< XTextRange > xRange( xEnumeration->nextElement(), UNO_QUERY ); + if( xRange.is() && !xRange->getString().isEmpty() ) + { + if( bTextReverse ) // sort them + aParaList.push_front( nPara ); + else + aParaList.push_back( nPara ); + } + } + + ParagraphTarget aTarget; + aTarget.Shape = xTarget; + + for( const auto i : aParaList ) + { + aTarget.Paragraph = i; + + CustomAnimationEffectPtr pNewEffect; + if( bUsed ) + { + // clone a new effect from first effect + pNewEffect = pEffect->clone(); + ++aInsertIter; + aInsertIter = maEffects.insert( aInsertIter, pNewEffect ); + } + else + { + // reuse first effect if it's not yet used + pNewEffect = pEffect; + bUsed = true; + aInsertIter = find( pNewEffect ); + } + + // set target and group-id + pNewEffect->setTarget( Any( aTarget ) ); + pNewEffect->setTargetSubItem( ShapeAnimationSubType::ONLY_TEXT ); + pNewEffect->setGroupId( pTextGroup->mnGroupId ); + pNewEffect->setEffectSequence( this ); + + // set correct node type + if( pNewEffect->getParaDepth() < nTextGrouping ) + { + if( fTextGroupingAuto == -1.0 ) + { + pNewEffect->setNodeType( EffectNodeType::ON_CLICK ); + pNewEffect->setBegin( 0.0 ); + } + else + { + pNewEffect->setNodeType( EffectNodeType::AFTER_PREVIOUS ); + pNewEffect->setBegin( fTextGroupingAuto ); + } + } + else + { + pNewEffect->setNodeType( EffectNodeType::WITH_PREVIOUS ); + pNewEffect->setBegin( 0.0 ); + } + + pTextGroup->addEffect( pNewEffect ); + } + notify_listeners(); + } + catch( Exception& ) + { + TOOLS_WARN_EXCEPTION( "sd", "sd::EffectSequenceHelper::createTextGroup()" ); + } +} + +void EffectSequenceHelper::setTextGrouping( const CustomAnimationTextGroupPtr& pTextGroup, sal_Int32 nTextGrouping ) +{ + if( pTextGroup->mnTextGrouping == nTextGrouping ) + { + // first case, trivial case, do nothing + } + else if( (pTextGroup->mnTextGrouping == -1) && (nTextGrouping >= 0) ) + { + // second case, we need to add new effects for each paragraph + + CustomAnimationEffectPtr pEffect( pTextGroup->maEffects.front() ); + + pTextGroup->mnTextGrouping = nTextGrouping; + createTextGroupParagraphEffects( pTextGroup, pEffect, true ); + notify_listeners(); + } + else if( (pTextGroup->mnTextGrouping >= 0) && (nTextGrouping == -1 ) ) + { + // third case, we need to remove effects for each paragraph + + EffectSequence aEffects( pTextGroup->maEffects ); + pTextGroup->reset(); + + for( const CustomAnimationEffectPtr& pEffect : aEffects ) + { + if( pEffect->getTarget().getValueType() == ::cppu::UnoType<ParagraphTarget>::get() ) + remove( pEffect ); + else + pTextGroup->addEffect( pEffect ); + } + notify_listeners(); + } + else + { + // fourth case, we need to change the node types for the text nodes + double fTextGroupingAuto = pTextGroup->mfGroupingAuto; + + EffectSequence aEffects( pTextGroup->maEffects ); + pTextGroup->reset(); + + for( CustomAnimationEffectPtr& pEffect : aEffects ) + { + if( pEffect->getTarget().getValueType() == ::cppu::UnoType<ParagraphTarget>::get() ) + { + // set correct node type + if( pEffect->getParaDepth() < nTextGrouping ) + { + if( fTextGroupingAuto == -1.0 ) + { + pEffect->setNodeType( EffectNodeType::ON_CLICK ); + pEffect->setBegin( 0.0 ); + } + else + { + pEffect->setNodeType( EffectNodeType::AFTER_PREVIOUS ); + pEffect->setBegin( fTextGroupingAuto ); + } + } + else + { + pEffect->setNodeType( EffectNodeType::WITH_PREVIOUS ); + pEffect->setBegin( 0.0 ); + } + } + + pTextGroup->addEffect( pEffect ); + + } + notify_listeners(); + } +} + +void EffectSequenceHelper::setAnimateForm( const CustomAnimationTextGroupPtr& pTextGroup, bool bAnimateForm ) +{ + if( pTextGroup->mbAnimateForm == bAnimateForm ) + { + // trivial case, do nothing + } + else + { + EffectSequence aEffects( pTextGroup->maEffects ); + pTextGroup->reset(); + + SAL_WARN_IF(aEffects.empty(), "sd", "EffectSequenceHelper::setAnimateForm effects empty" ); + + if (aEffects.empty()) + return; + + EffectSequence::iterator aIter( aEffects.begin() ); + const EffectSequence::iterator aEnd( aEffects.end() ); + + // first insert if we have to + if( bAnimateForm ) + { + EffectSequence::iterator aInsertIter( find( *aIter ) ); + + CustomAnimationEffectPtr pEffect; + if( (aEffects.size() == 1) && ((*aIter)->getTarget().getValueType() != ::cppu::UnoType<ParagraphTarget>::get() ) ) + { + // special case, only one effect and that targets whole text, + // convert this to target whole shape + pEffect = *aIter++; + pEffect->setTargetSubItem( ShapeAnimationSubType::AS_WHOLE ); + } + else + { + pEffect = (*aIter)->clone(); + pEffect->setTarget( Any( (*aIter)->getTargetShape() ) ); + pEffect->setTargetSubItem( ShapeAnimationSubType::ONLY_BACKGROUND ); + maEffects.insert( aInsertIter, pEffect ); + } + + pTextGroup->addEffect( pEffect ); + } + + if( !bAnimateForm && (aEffects.size() == 1) ) + { + CustomAnimationEffectPtr pEffect( *aIter ); + pEffect->setTarget( Any( (*aIter)->getTargetShape() ) ); + pEffect->setTargetSubItem( ShapeAnimationSubType::ONLY_TEXT ); + pTextGroup->addEffect( pEffect ); + } + else + { + // read the rest to the group again + while( aIter != aEnd ) + { + CustomAnimationEffectPtr pEffect( *aIter++ ); + + if( pEffect->getTarget().getValueType() == ::cppu::UnoType<ParagraphTarget>::get() ) + { + pTextGroup->addEffect( pEffect ); + } + else + { + DBG_ASSERT( !bAnimateForm, "sd::EffectSequenceHelper::setAnimateForm(), something is wrong here!" ); + remove( pEffect ); + } + } + } + notify_listeners(); + } +} + +void EffectSequenceHelper::setTextGroupingAuto( const CustomAnimationTextGroupPtr& pTextGroup, double fTextGroupingAuto ) +{ + sal_Int32 nTextGrouping = pTextGroup->mnTextGrouping; + + EffectSequence aEffects( pTextGroup->maEffects ); + pTextGroup->reset(); + + for( CustomAnimationEffectPtr& pEffect : aEffects ) + { + if( pEffect->getTarget().getValueType() == ::cppu::UnoType<ParagraphTarget>::get() ) + { + // set correct node type + if( pEffect->getParaDepth() < nTextGrouping ) + { + if( fTextGroupingAuto == -1.0 ) + { + pEffect->setNodeType( EffectNodeType::ON_CLICK ); + pEffect->setBegin( 0.0 ); + } + else + { + pEffect->setNodeType( EffectNodeType::AFTER_PREVIOUS ); + pEffect->setBegin( fTextGroupingAuto ); + } + } + else + { + pEffect->setNodeType( EffectNodeType::WITH_PREVIOUS ); + pEffect->setBegin( 0.0 ); + } + } + + pTextGroup->addEffect( pEffect ); + + } + notify_listeners(); +} + +namespace { + +struct ImplStlTextGroupSortHelper +{ + explicit ImplStlTextGroupSortHelper( bool bReverse ) : mbReverse( bReverse ) {}; + bool operator()( const CustomAnimationEffectPtr& p1, const CustomAnimationEffectPtr& p2 ); + bool mbReverse; + sal_Int32 getTargetParagraph( const CustomAnimationEffectPtr& p1 ); +}; + +} + +sal_Int32 ImplStlTextGroupSortHelper::getTargetParagraph( const CustomAnimationEffectPtr& p1 ) +{ + const Any aTarget(p1->getTarget()); + if( aTarget.hasValue() && aTarget.getValueType() == ::cppu::UnoType<ParagraphTarget>::get() ) + { + ParagraphTarget aParaTarget; + aTarget >>= aParaTarget; + return aParaTarget.Paragraph; + } + else + { + return mbReverse ? 0x7fffffff : -1; + } +} + +bool ImplStlTextGroupSortHelper::operator()( const CustomAnimationEffectPtr& p1, const CustomAnimationEffectPtr& p2 ) +{ + if( mbReverse ) + { + return getTargetParagraph( p2 ) < getTargetParagraph( p1 ); + } + else + { + return getTargetParagraph( p1 ) < getTargetParagraph( p2 ); + } +} + +void EffectSequenceHelper::setTextReverse( const CustomAnimationTextGroupPtr& pTextGroup, bool bTextReverse ) +{ + if( pTextGroup->mbTextReverse == bTextReverse ) + { + // do nothing + } + else + { + std::vector< CustomAnimationEffectPtr > aSortedVector( pTextGroup->maEffects.begin(), pTextGroup->maEffects.end() ); + ImplStlTextGroupSortHelper aSortHelper( bTextReverse ); + std::sort( aSortedVector.begin(), aSortedVector.end(), aSortHelper ); + + pTextGroup->reset(); + + std::vector< CustomAnimationEffectPtr >::iterator aIter( aSortedVector.begin() ); + const std::vector< CustomAnimationEffectPtr >::iterator aEnd( aSortedVector.end() ); + + if( aIter != aEnd ) + { + pTextGroup->addEffect( *aIter ); + EffectSequence::iterator aInsertIter( find( *aIter++ ) ); + while( aIter != aEnd ) + { + CustomAnimationEffectPtr pEffect( *aIter++ ); + maEffects.erase( find( pEffect ) ); + aInsertIter = maEffects.insert( ++aInsertIter, pEffect ); + pTextGroup->addEffect( pEffect ); + } + } + notify_listeners(); + } +} + +void EffectSequenceHelper::addListener( ISequenceListener* pListener ) +{ + if( std::find( maListeners.begin(), maListeners.end(), pListener ) == maListeners.end() ) + maListeners.push_back( pListener ); +} + +void EffectSequenceHelper::removeListener( ISequenceListener* pListener ) +{ + maListeners.remove( pListener ); +} + +namespace { + +struct stl_notify_listeners_func +{ + stl_notify_listeners_func() {} + void operator()(ISequenceListener* pListener) { pListener->notify_change(); } +}; + +} + +void EffectSequenceHelper::notify_listeners() +{ + stl_notify_listeners_func aFunc; + std::for_each( maListeners.begin(), maListeners.end(), aFunc ); +} + +void EffectSequenceHelper::create( const css::uno::Reference< css::animations::XAnimationNode >& xNode ) +{ + DBG_ASSERT( xNode.is(), "sd::EffectSequenceHelper::create(), illegal argument" ); + + if( !xNode.is() ) + return; + + try + { + Reference< XEnumerationAccess > xEnumerationAccess( xNode, UNO_QUERY_THROW ); + Reference< XEnumeration > xEnumeration( xEnumerationAccess->createEnumeration(), UNO_SET_THROW ); + while( xEnumeration->hasMoreElements() ) + { + Reference< XAnimationNode > xChildNode( xEnumeration->nextElement(), UNO_QUERY_THROW ); + createEffectsequence( xChildNode ); + } + } + catch( Exception& ) + { + TOOLS_WARN_EXCEPTION( "sd", "sd::EffectSequenceHelper::create()" ); + } +} + +void EffectSequenceHelper::createEffectsequence( const Reference< XAnimationNode >& xNode ) +{ + DBG_ASSERT( xNode.is(), "sd::EffectSequenceHelper::createEffectsequence(), illegal argument" ); + + if( !xNode.is() ) + return; + + try + { + Reference< XEnumerationAccess > xEnumerationAccess( xNode, UNO_QUERY_THROW ); + Reference< XEnumeration > xEnumeration( xEnumerationAccess->createEnumeration(), UNO_SET_THROW ); + while( xEnumeration->hasMoreElements() ) + { + Reference< XAnimationNode > xChildNode( xEnumeration->nextElement(), UNO_QUERY_THROW ); + + createEffects( xChildNode ); + } + } + catch( Exception& ) + { + TOOLS_WARN_EXCEPTION( "sd", "sd::EffectSequenceHelper::createEffectsequence()" ); + } +} + +void EffectSequenceHelper::createEffects( const Reference< XAnimationNode >& xNode ) +{ + DBG_ASSERT( xNode.is(), "sd::EffectSequenceHelper::createEffects(), illegal argument" ); + + if( !xNode.is() ) + return; + + try + { + Reference< XEnumerationAccess > xEnumerationAccess( xNode, UNO_QUERY_THROW ); + Reference< XEnumeration > xEnumeration( xEnumerationAccess->createEnumeration(), UNO_SET_THROW ); + while( xEnumeration->hasMoreElements() ) + { + Reference< XAnimationNode > xChildNode( xEnumeration->nextElement(), UNO_QUERY_THROW ); + + switch( xChildNode->getType() ) + { + // found an effect + case AnimationNodeType::PAR: + case AnimationNodeType::ITERATE: + { + CustomAnimationEffectPtr pEffect = std::make_shared<CustomAnimationEffect>( xChildNode ); + + if( pEffect->mnNodeType != -1 ) + { + pEffect->setEffectSequence( this ); + maEffects.push_back(pEffect); + } + } + break; + + // found an after effect + case AnimationNodeType::SET: + case AnimationNodeType::ANIMATECOLOR: + { + processAfterEffect( xChildNode ); + } + break; + } + } + } + catch( Exception& ) + { + TOOLS_WARN_EXCEPTION( "sd", "sd::EffectSequenceHelper::createEffects()" ); + } +} + +void EffectSequenceHelper::processAfterEffect( const Reference< XAnimationNode >& xNode ) +{ + try + { + Reference< XAnimationNode > xMaster; + + const Sequence< NamedValue > aUserData( xNode->getUserData() ); + const NamedValue* pProp = std::find_if(aUserData.begin(), aUserData.end(), + [](const NamedValue& rProp) { return rProp.Name == "master-element"; }); + + if (pProp != aUserData.end()) + pProp->Value >>= xMaster; + + // only process if this is a valid after effect + if( xMaster.is() ) + { + CustomAnimationEffectPtr pMasterEffect; + + // find the master effect + stl_CustomAnimationEffect_search_node_predict aSearchPredict( xMaster ); + EffectSequence::iterator aIter( std::find_if( maEffects.begin(), maEffects.end(), aSearchPredict ) ); + if( aIter != maEffects.end() ) + pMasterEffect = *aIter; + + if( pMasterEffect ) + { + pMasterEffect->setHasAfterEffect( true ); + + // find out what kind of after effect this is + if( xNode->getType() == AnimationNodeType::ANIMATECOLOR ) + { + // it's a dim + Reference< XAnimate > xAnimate( xNode, UNO_QUERY_THROW ); + pMasterEffect->setDimColor( xAnimate->getTo() ); + pMasterEffect->setAfterEffectOnNext( true ); + } + else + { + // it's a hide + pMasterEffect->setAfterEffectOnNext( xNode->getParent() != xMaster->getParent() ); + } + } + } + } + catch( Exception& ) + { + TOOLS_WARN_EXCEPTION( "sd", "sd::EffectSequenceHelper::processAfterEffect()" ); + } +} + +namespace { + +class AnimationChangeListener : public cppu::WeakImplHelper< XChangesListener > +{ +public: + explicit AnimationChangeListener( MainSequence* pMainSequence ) : mpMainSequence( pMainSequence ) {} + + virtual void SAL_CALL changesOccurred( const css::util::ChangesEvent& Event ) override; + virtual void SAL_CALL disposing( const css::lang::EventObject& Source ) override; +private: + MainSequence* mpMainSequence; +}; + +} + +void SAL_CALL AnimationChangeListener::changesOccurred( const css::util::ChangesEvent& ) +{ + if( mpMainSequence ) + mpMainSequence->startRecreateTimer(); +} + +void SAL_CALL AnimationChangeListener::disposing( const css::lang::EventObject& ) +{ +} + +MainSequence::MainSequence() + : mxTimingRootNode(SequenceTimeContainer::create(::comphelper::getProcessComponentContext())) + , maTimer("sd MainSequence maTimer") + , mbTimerMode(false) + , mbRebuilding( false ) + , mnRebuildLockGuard( 0 ) + , mbPendingRebuildRequest( false ) + , mbIgnoreChanges( 0 ) +{ + if( mxTimingRootNode.is() ) + { + Sequence< css::beans::NamedValue > aUserData + { { "node-type", css::uno::Any(css::presentation::EffectNodeType::MAIN_SEQUENCE) } }; + mxTimingRootNode->setUserData( aUserData ); + } + init(); +} + +MainSequence::MainSequence( const css::uno::Reference< css::animations::XAnimationNode >& xNode ) + : mxTimingRootNode( xNode, UNO_QUERY ) + , maTimer("sd MainSequence maTimer") + , mbTimerMode( false ) + , mbRebuilding( false ) + , mnRebuildLockGuard( 0 ) + , mbPendingRebuildRequest( false ) + , mbIgnoreChanges( 0 ) +{ + init(); +} + +MainSequence::~MainSequence() +{ + reset(); +} + +void MainSequence::init() +{ + mnSequenceType = EffectNodeType::MAIN_SEQUENCE; + + maTimer.SetInvokeHandler( LINK(this, MainSequence, onTimerHdl) ); + maTimer.SetTimeout(50); + + mxChangesListener.set( new AnimationChangeListener( this ) ); + + createMainSequence(); +} + +void MainSequence::reset( const css::uno::Reference< css::animations::XAnimationNode >& xTimingRootNode ) +{ + reset(); + + mxTimingRootNode.set( xTimingRootNode, UNO_QUERY ); + + createMainSequence(); +} + +Reference< css::animations::XAnimationNode > MainSequence::getRootNode() +{ + DBG_ASSERT( mnRebuildLockGuard == 0, "MainSequence::getRootNode(), rebuild is locked, is this really what you want?" ); + + if( maTimer.IsActive() && mbTimerMode ) + { + // force a rebuild NOW if one is pending + maTimer.Stop(); + implRebuild(); + } + + return EffectSequenceHelper::getRootNode(); +} + +void MainSequence::createMainSequence() +{ + if( mxTimingRootNode.is() ) try + { + Reference< XEnumerationAccess > xEnumerationAccess( mxTimingRootNode, UNO_QUERY_THROW ); + Reference< XEnumeration > xEnumeration( xEnumerationAccess->createEnumeration(), UNO_SET_THROW ); + while( xEnumeration->hasMoreElements() ) + { + Reference< XAnimationNode > xChildNode( xEnumeration->nextElement(), UNO_QUERY_THROW ); + sal_Int32 nNodeType = CustomAnimationEffect::get_node_type( xChildNode ); + if( nNodeType == EffectNodeType::MAIN_SEQUENCE ) + { + mxSequenceRoot.set( xChildNode, UNO_QUERY ); + EffectSequenceHelper::create( xChildNode ); + } + else if( nNodeType == EffectNodeType::INTERACTIVE_SEQUENCE ) + { + Reference< XTimeContainer > xInteractiveRoot( xChildNode, UNO_QUERY_THROW ); + InteractiveSequencePtr pIS = std::make_shared<InteractiveSequence>( xInteractiveRoot, this ); + pIS->addListener( this ); + maInteractiveSequenceVector.push_back( pIS ); + } + } + + // see if we have a mainsequence at all. if not, create one... + if( !mxSequenceRoot.is() ) + { + mxSequenceRoot = SequenceTimeContainer::create( ::comphelper::getProcessComponentContext() ); + + uno::Sequence< css::beans::NamedValue > aUserData + { { "node-type", css::uno::Any(css::presentation::EffectNodeType::MAIN_SEQUENCE) } }; + mxSequenceRoot->setUserData( aUserData ); + + // empty sequence until now, set duration to 0.0 + // explicitly (otherwise, this sequence will never + // end) + mxSequenceRoot->setDuration( Any(0.0) ); + + Reference< XAnimationNode > xMainSequenceNode( mxSequenceRoot, UNO_QUERY_THROW ); + mxTimingRootNode->appendChild( xMainSequenceNode ); + } + + updateTextGroups(); + + notify_listeners(); + + Reference< XChangesNotifier > xNotifier( mxTimingRootNode, UNO_QUERY ); + if( xNotifier.is() ) + xNotifier->addChangesListener( mxChangesListener ); + } + catch( Exception& ) + { + TOOLS_WARN_EXCEPTION( "sd", "sd::MainSequence::create()" ); + return; + } + + DBG_ASSERT( mxSequenceRoot.is(), "sd::MainSequence::create(), found no main sequence!" ); +} + +void MainSequence::reset() +{ + EffectSequenceHelper::reset(); + + for (auto const& interactiveSequence : maInteractiveSequenceVector) + interactiveSequence->reset(); + maInteractiveSequenceVector.clear(); + + try + { + Reference< XChangesNotifier > xNotifier( mxTimingRootNode, UNO_QUERY ); + if( xNotifier.is() ) + xNotifier->removeChangesListener( mxChangesListener ); + } + catch( Exception& ) + { + + } +} + +InteractiveSequencePtr MainSequence::createInteractiveSequence( const css::uno::Reference< css::drawing::XShape >& xShape ) +{ + InteractiveSequencePtr pIS; + + // create a new interactive sequence container + Reference< XTimeContainer > xISRoot = SequenceTimeContainer::create( ::comphelper::getProcessComponentContext() ); + + uno::Sequence< css::beans::NamedValue > aUserData + { { "node-type", css::uno::Any(css::presentation::EffectNodeType::INTERACTIVE_SEQUENCE) } }; + xISRoot->setUserData( aUserData ); + xISRoot->setRestart( css::animations::AnimationRestart::WHEN_NOT_ACTIVE ); + + Reference< XChild > xChild( mxSequenceRoot, UNO_QUERY_THROW ); + Reference< XTimeContainer > xParent( xChild->getParent(), UNO_QUERY_THROW ); + xParent->appendChild( xISRoot ); + + pIS = std::make_shared<InteractiveSequence>( xISRoot, this); + pIS->setTriggerShape( xShape ); + pIS->addListener( this ); + maInteractiveSequenceVector.push_back( pIS ); + return pIS; +} + +CustomAnimationEffectPtr MainSequence::findEffect( const css::uno::Reference< css::animations::XAnimationNode >& xNode ) const +{ + CustomAnimationEffectPtr pEffect = EffectSequenceHelper::findEffect( xNode ); + + if( !pEffect ) + { + for (auto const& interactiveSequence : maInteractiveSequenceVector) + { + pEffect = interactiveSequence->findEffect( xNode ); + if (pEffect) + break; + } + } + return pEffect; +} + +sal_Int32 MainSequence::getOffsetFromEffect( const CustomAnimationEffectPtr& pEffect ) const +{ + sal_Int32 nOffset = EffectSequenceHelper::getOffsetFromEffect( pEffect ); + + if( nOffset != -1 ) + return nOffset; + + nOffset = EffectSequenceHelper::getCount(); + + for (auto const& interactiveSequence : maInteractiveSequenceVector) + { + sal_Int32 nTemp = interactiveSequence->getOffsetFromEffect( pEffect ); + if( nTemp != -1 ) + return nOffset + nTemp; + + nOffset += interactiveSequence->getCount(); + } + + return -1; +} + +CustomAnimationEffectPtr MainSequence::getEffectFromOffset( sal_Int32 nOffset ) const +{ + if( nOffset >= 0 ) + { + if( nOffset < getCount() ) + return EffectSequenceHelper::getEffectFromOffset( nOffset ); + + nOffset -= getCount(); + + auto aIter( maInteractiveSequenceVector.begin() ); + + while( (aIter != maInteractiveSequenceVector.end()) && (nOffset > (*aIter)->getCount()) ) + nOffset -= (*aIter++)->getCount(); + + if( (aIter != maInteractiveSequenceVector.end()) && (nOffset >= 0) ) + return (*aIter)->getEffectFromOffset( nOffset ); + } + + CustomAnimationEffectPtr pEffect; + return pEffect; +} + +bool MainSequence::disposeShape( const Reference< XShape >& xShape ) +{ + bool bChanges = EffectSequenceHelper::disposeShape( xShape ); + + for (auto const& iterativeSequence : maInteractiveSequenceVector) + { + bChanges |= iterativeSequence->disposeShape( xShape ); + } + + if( bChanges ) + startRebuildTimer(); + + return bChanges; +} + +bool MainSequence::hasEffect( const css::uno::Reference< css::drawing::XShape >& xShape ) +{ + if( EffectSequenceHelper::hasEffect( xShape ) ) + return true; + + for (auto const& iterativeSequence : maInteractiveSequenceVector) + { + if( iterativeSequence->getTriggerShape() == xShape ) + return true; + + if( iterativeSequence->hasEffect( xShape ) ) + return true; + } + + return false; +} + +void MainSequence::insertTextRange( const css::uno::Any& aTarget ) +{ + EffectSequenceHelper::insertTextRange( aTarget ); + + for (auto const& iterativeSequence : maInteractiveSequenceVector) + { + iterativeSequence->insertTextRange( aTarget ); + } +} + +void MainSequence::disposeTextRange( const css::uno::Any& aTarget ) +{ + EffectSequenceHelper::disposeTextRange( aTarget ); + + for (auto const& iterativeSequence : maInteractiveSequenceVector) + { + iterativeSequence->disposeTextRange( aTarget ); + } +} + +/** callback from the sd::View when an object just left text edit mode */ +void MainSequence::onTextChanged( const Reference< XShape >& xShape ) +{ + EffectSequenceHelper::onTextChanged( xShape ); + + for (auto const& iterativeSequence : maInteractiveSequenceVector) + { + iterativeSequence->onTextChanged( xShape ); + } +} + +void EffectSequenceHelper::onTextChanged( const Reference< XShape >& xShape ) +{ + // get map [paragraph index] -> [NumberingLevel] + // for following reusage inside all animation effects + std::vector< sal_Int32 > paragraphNumberingLevel; + std::vector< sal_Int32 >* paragraphNumberingLevelParam = nullptr; + if ( getParagraphNumberingLevels( xShape, paragraphNumberingLevel ) ) + paragraphNumberingLevelParam = ¶graphNumberingLevel; + + // update internal flags for each animation effect + const bool bChanges = std::accumulate(maEffects.begin(), maEffects.end(), false, + [&xShape, ¶graphNumberingLevelParam](const bool bCheck, const CustomAnimationEffectPtr& rxEffect) { + bool bRes = bCheck; + if (rxEffect->getTargetShape() == xShape) + bRes |= rxEffect->checkForText( paragraphNumberingLevelParam ); + return bRes; + }); + + if( bChanges ) + rebuild(); +} + +void MainSequence::rebuild() +{ + startRebuildTimer(); +} + +void MainSequence::lockRebuilds() +{ + mnRebuildLockGuard++; +} + +void MainSequence::unlockRebuilds() +{ + DBG_ASSERT( mnRebuildLockGuard, "sd::MainSequence::unlockRebuilds(), no corresponding lockRebuilds() call!" ); + if( mnRebuildLockGuard ) + mnRebuildLockGuard--; + + if( (mnRebuildLockGuard == 0) && mbPendingRebuildRequest ) + { + mbPendingRebuildRequest = false; + startRebuildTimer(); + } +} + +void MainSequence::implRebuild() +{ + if( mnRebuildLockGuard ) + { + mbPendingRebuildRequest = true; + return; + } + + mbRebuilding = true; + + EffectSequenceHelper::implRebuild(); + + auto aIter( maInteractiveSequenceVector.begin() ); + while( aIter != maInteractiveSequenceVector.end() ) + { + InteractiveSequencePtr pIS( *aIter ); + if( pIS->maEffects.empty() ) + { + // remove empty interactive sequences + aIter = maInteractiveSequenceVector.erase( aIter ); + + Reference< XChild > xChild( mxSequenceRoot, UNO_QUERY_THROW ); + Reference< XTimeContainer > xParent( xChild->getParent(), UNO_QUERY_THROW ); + Reference< XAnimationNode > xISNode( pIS->mxSequenceRoot, UNO_QUERY_THROW ); + xParent->removeChild( xISNode ); + } + else + { + pIS->implRebuild(); + ++aIter; + } + } + + notify_listeners(); + mbRebuilding = false; +} + +void MainSequence::notify_change() +{ + notify_listeners(); +} + +bool MainSequence::setTrigger( const CustomAnimationEffectPtr& pEffect, const css::uno::Reference< css::drawing::XShape >& xTriggerShape ) +{ + EffectSequenceHelper* pOldSequence = pEffect->getEffectSequence(); + + EffectSequenceHelper* pNewSequence = nullptr; + if( xTriggerShape.is() ) + { + for (InteractiveSequencePtr const& pIS : maInteractiveSequenceVector) + { + if( pIS->getTriggerShape() == xTriggerShape ) + { + pNewSequence = pIS.get(); + break; + } + } + + if( !pNewSequence ) + pNewSequence = createInteractiveSequence( xTriggerShape ).get(); + } + else + { + pNewSequence = this; + } + + if( pOldSequence != pNewSequence ) + { + if( pOldSequence ) + pOldSequence->maEffects.remove( pEffect ); + if( pNewSequence ) + pNewSequence->maEffects.push_back( pEffect ); + pEffect->setEffectSequence( pNewSequence ); + return true; + } + else + { + return false; + } + +} + +IMPL_LINK_NOARG(MainSequence, onTimerHdl, Timer *, void) +{ + if( mbTimerMode ) + { + implRebuild(); + } + else + { + reset(); + createMainSequence(); + } +} + +/** starts a timer that recreates the internal structure from the API core */ +void MainSequence::startRecreateTimer() +{ + if( !mbRebuilding && (mbIgnoreChanges == 0) ) + { + mbTimerMode = false; + maTimer.Start(); + } +} + +/** + * starts a timer that rebuilds the API core from the internal structure + * This is used to reduce the number of screen redraws due to animation changes. +*/ +void MainSequence::startRebuildTimer() +{ + mbTimerMode = true; + maTimer.Start(); +} + +InteractiveSequence::InteractiveSequence( const Reference< XTimeContainer >& xSequenceRoot, MainSequence* pMainSequence ) +: EffectSequenceHelper( xSequenceRoot ), mpMainSequence( pMainSequence ) +{ + mnSequenceType = EffectNodeType::INTERACTIVE_SEQUENCE; + + try + { + if( mxSequenceRoot.is() ) + { + Reference< XEnumerationAccess > xEnumerationAccess( mxSequenceRoot, UNO_QUERY_THROW ); + Reference< XEnumeration > xEnumeration( xEnumerationAccess->createEnumeration(), UNO_SET_THROW ); + while( !mxEventSource.is() && xEnumeration->hasMoreElements() ) + { + Reference< XAnimationNode > xChildNode( xEnumeration->nextElement(), UNO_QUERY_THROW ); + + Event aEvent; + if( (xChildNode->getBegin() >>= aEvent) && (aEvent.Trigger == EventTrigger::ON_CLICK) ) + aEvent.Source >>= mxEventSource; + } + } + } + catch( Exception& ) + { + TOOLS_WARN_EXCEPTION( "sd", "sd::InteractiveSequence::InteractiveSequence()" ); + return; + } +} + +void InteractiveSequence::rebuild() +{ + mpMainSequence->rebuild(); +} + +void InteractiveSequence::implRebuild() +{ + EffectSequenceHelper::implRebuild(); +} + +MainSequenceRebuildGuard::MainSequenceRebuildGuard( const MainSequencePtr& pMainSequence ) +: mpMainSequence( pMainSequence ) +{ + if( mpMainSequence ) + mpMainSequence->lockRebuilds(); +} + +MainSequenceRebuildGuard::~MainSequenceRebuildGuard() +{ + if( mpMainSequence ) + mpMainSequence->unlockRebuilds(); +} + +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/core/CustomAnimationPreset.cxx b/sd/source/core/CustomAnimationPreset.cxx new file mode 100644 index 000000000..d7c19401d --- /dev/null +++ b/sd/source/core/CustomAnimationPreset.cxx @@ -0,0 +1,514 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include <sal/config.h> + +#include <com/sun/star/util/XCloneable.hpp> +#include <com/sun/star/animations/XAnimationNodeSupplier.hpp> +#include <com/sun/star/container/XNameAccess.hpp> +#include <com/sun/star/configuration/theDefaultProvider.hpp> +#include <com/sun/star/xml/sax/InputSource.hpp> +#include <com/sun/star/xml/sax/XFastParser.hpp> +#include <com/sun/star/presentation/EffectPresetClass.hpp> +#include <com/sun/star/beans/NamedValue.hpp> +#include <unotools/streamwrap.hxx> +#include <comphelper/getexpandeduri.hxx> +#include <comphelper/processfactory.hxx> +#include <comphelper/propertysequence.hxx> +#include <comphelper/random.hxx> +#include <comphelper/lok.hxx> +#include <unotools/syslocaleoptions.hxx> +#include <tools/stream.hxx> +#include <tools/diagnose_ex.h> +#include <o3tl/string_view.hxx> + +#include <vcl/svapp.hxx> +#include <unotools/ucbstreamhelper.hxx> +#include <CustomAnimationPreset.hxx> + +#include <algorithm> +#include <vector> + +using namespace ::com::sun::star; +using namespace ::com::sun::star::uno; +using namespace ::com::sun::star::animations; +using namespace ::com::sun::star::presentation; + +using ::com::sun::star::io::XInputStream; +using ::com::sun::star::lang::XMultiServiceFactory; +using ::com::sun::star::container::XNameAccess; +using ::com::sun::star::util::XCloneable; +using ::com::sun::star::beans::NamedValue; + +namespace sd { + +static Reference< XNameAccess > getNodeAccess( const Reference< XMultiServiceFactory >& xConfigProvider, const OUString& rNodePath ) +{ + Reference< XNameAccess > xConfigAccess; + + try + { + Sequence<Any> aArgs(comphelper::InitAnyPropertySequence( + { + {"nodepath", uno::Any(rNodePath)} + })); + + xConfigAccess.set( + xConfigProvider->createInstanceWithArguments( "com.sun.star.configuration.ConfigurationAccess", aArgs ), + UNO_QUERY); + } + catch (const Exception&) + { + TOOLS_WARN_EXCEPTION( "sd", "sd::getNodeAccess()" ); + } + + return xConfigAccess; +} + +void implImportLabels( const Reference< XMultiServiceFactory >& xConfigProvider, const OUString& rNodePath, UStringMap& rStringMap ) +{ + try + { + Reference< XNameAccess > xConfigAccess( getNodeAccess( xConfigProvider, rNodePath ) ); + if( xConfigAccess.is() ) + { + Reference< XNameAccess > xNameAccess; + const Sequence< OUString > aNames( xConfigAccess->getElementNames() ); + for(const OUString& rName : aNames) + { + xConfigAccess->getByName( rName ) >>= xNameAccess; + if( xNameAccess.is() ) + { + OUString aUIName; + xNameAccess->getByName( "Label" ) >>= aUIName; + if( !aUIName.isEmpty() ) + { + rStringMap[ rName ] = aUIName; + } + } + } + } + } + catch (const Exception&) + { + TOOLS_WARN_EXCEPTION( "sd", "sd::implImportLabels()" ); + } +} + +CustomAnimationPreset::CustomAnimationPreset( const CustomAnimationEffectPtr& pEffect ) +{ + maPresetId = pEffect->getPresetId(); + maProperty = pEffect->getProperty(); + + add( pEffect ); + + mfDuration = pEffect->getDuration(); + maDefaultSubTyp = pEffect->getPresetSubType(); + + const Sequence< NamedValue > aUserData( pEffect->getNode()->getUserData() ); + + mbIsTextOnly = std::any_of(aUserData.begin(), aUserData.end(), + [](const NamedValue& rProp) { return rProp.Name == "text-only"; }); +} + +void CustomAnimationPreset::add( const CustomAnimationEffectPtr& pEffect ) +{ + maSubTypes[ pEffect->getPresetSubType() ] = pEffect; +} + +std::vector<OUString> CustomAnimationPreset::getSubTypes() +{ + std::vector<OUString> aSubTypes; + + if( maSubTypes.size() > 1 ) + { + std::transform(maSubTypes.begin(), maSubTypes.end(), std::back_inserter(aSubTypes), + [](EffectsSubTypeMap::value_type& rEntry) -> OUString { return rEntry.first; }); + } + + return aSubTypes; +} + +Reference< XAnimationNode > CustomAnimationPreset::create( const OUString& rstrSubType ) +{ + try + { + OUString strSubType( rstrSubType ); + if( strSubType.isEmpty() ) + strSubType = maDefaultSubTyp; + + CustomAnimationEffectPtr pEffect = maSubTypes[strSubType]; + if( pEffect ) + { + Reference< XCloneable > xCloneable( pEffect->getNode(), UNO_QUERY_THROW ); + Reference< XAnimationNode > xNode( xCloneable->createClone(), UNO_QUERY_THROW ); + return xNode; + } + } + catch (const Exception&) + { + TOOLS_WARN_EXCEPTION( "sd", "sd::CustomAnimationPresets::create()" ); + } + + Reference< XAnimationNode > xNode; + return xNode; +} + +std::vector<OUString> CustomAnimationPreset::getProperties() const +{ + std::vector<OUString> aPropertyList; + if (!maProperty.isEmpty()) + { + sal_Int32 nPos = 0; + do + { + aPropertyList.push_back(maProperty.getToken(0, ';', nPos)); + } + while (nPos >= 0); + } + return aPropertyList; +} + +bool CustomAnimationPreset::hasProperty( std::u16string_view rProperty )const +{ + if (maProperty.isEmpty()) + return false; + + sal_Int32 nPos = 0; + do + { + if (o3tl::getToken(maProperty, 0, ';', nPos) == rProperty) + return true; + } + while (nPos >= 0); + + return false; +} + +CustomAnimationPresets::CustomAnimationPresets() +{ +} + +CustomAnimationPresets::~CustomAnimationPresets() +{ +} + +Reference< XAnimationNode > implImportEffects( const Reference< XMultiServiceFactory >& xServiceFactory, const OUString& rPath ) +{ + Reference< XAnimationNode > xRootNode; + + try + { + // create stream + std::unique_ptr<SvStream> pIStm = ::utl::UcbStreamHelper::CreateStream( rPath, StreamMode::READ ); + Reference<XInputStream> xInputStream( new utl::OInputStreamWrapper( std::move(pIStm) ) ); + + // prepare ParserInputSource + xml::sax::InputSource aParserInput; + aParserInput.sSystemId = rPath; + aParserInput.aInputStream = xInputStream; + + // get filter + Reference< xml::sax::XFastParser > xFilter( xServiceFactory->createInstance("com.sun.star.comp.Xmloff.AnimationsImport" ), UNO_QUERY_THROW ); + + xFilter->parseStream( aParserInput ); + + Reference< XAnimationNodeSupplier > xAnimationNodeSupplier( xFilter, UNO_QUERY_THROW ); + xRootNode = xAnimationNodeSupplier->getAnimationNode(); + } + catch (const Exception&) + { + TOOLS_WARN_EXCEPTION("sd", ""); + } + + return xRootNode; +} + +void CustomAnimationPresets::importEffects() +{ + try + { + uno::Reference< uno::XComponentContext > xContext( + comphelper::getProcessComponentContext() ); + Reference< XMultiServiceFactory > xServiceFactory( + xContext->getServiceManager(), UNO_QUERY_THROW ); + + Reference< XMultiServiceFactory > xConfigProvider = + configuration::theDefaultProvider::get( xContext ); + + // read path to transition effects files from config + uno::Sequence<uno::Any> aArgs(comphelper::InitAnyPropertySequence( + { + {"nodepath", uno::Any(OUString("/org.openoffice.Office.Impress/Misc"))} + })); + Reference<container::XNameAccess> xNameAccess( + xConfigProvider->createInstanceWithArguments( + "com.sun.star.configuration.ConfigurationAccess", + aArgs ), UNO_QUERY_THROW ); + uno::Sequence< OUString > aFiles; + xNameAccess->getByName( "EffectFiles" ) >>= aFiles; + + for( const auto& rFile : std::as_const(aFiles) ) + { + OUString aURL = comphelper::getExpandedUri(xContext, rFile); + + mxRootNode = implImportEffects( xServiceFactory, aURL ); + + if( mxRootNode.is() ) + { + Reference< XTimeContainer > xRootContainer( mxRootNode, UNO_QUERY_THROW ); + EffectSequenceHelper aSequence( xRootContainer ); + + EffectSequence::iterator aIter( aSequence.getBegin() ); + const EffectSequence::iterator aEnd( aSequence.getEnd() ); + + while( aIter != aEnd ) + { + CustomAnimationEffectPtr pEffect = *aIter; + + const OUString aPresetId( pEffect->getPresetId() ); + CustomAnimationPresetPtr pDescriptor = getEffectDescriptor( aPresetId ); + if( pDescriptor ) + pDescriptor->add( pEffect ); + else + { + pDescriptor = std::make_shared<CustomAnimationPreset>( pEffect ); + pDescriptor->maLabel = getUINameForPresetId( pEffect->getPresetId() ); + maEffectDescriptorMap[aPresetId] = pDescriptor; + } + + ++aIter; + } + } + } + } + catch (const Exception&) + { + TOOLS_WARN_EXCEPTION( "sd", "sd::CustomAnimationPresets::importEffects()" ); + } +} + +void CustomAnimationPresets::importResources() +{ + try + { + // Get service factory + Reference< XComponentContext > xContext( comphelper::getProcessComponentContext() ); + + Reference< XMultiServiceFactory > xConfigProvider = + configuration::theDefaultProvider::get( xContext ); + + implImportLabels( xConfigProvider, "/org.openoffice.Office.UI.Effects/UserInterface/Properties", maPropertyNameMap ); + + implImportLabels( xConfigProvider, "/org.openoffice.Office.UI.Effects/UserInterface/Effects", maEffectNameMap ); + + importEffects(); + + importPresets( xConfigProvider, "/org.openoffice.Office.UI.Effects/Presets/Entrance", maEntrancePresets ); + + importPresets( xConfigProvider, "/org.openoffice.Office.UI.Effects/Presets/Emphasis", maEmphasisPresets ); + + importPresets( xConfigProvider, "/org.openoffice.Office.UI.Effects/Presets/Exit", maExitPresets ); + + importPresets( xConfigProvider, "/org.openoffice.Office.UI.Effects/Presets/MotionPaths", maMotionPathsPresets ); + + importPresets( xConfigProvider, "/org.openoffice.Office.UI.Effects/Presets/Misc", maMiscPresets ); + } + catch (const Exception&) + { + TOOLS_WARN_EXCEPTION( "sd", "sd::CustomAnimationPresets::importResources()" ); + } +} + +void CustomAnimationPresets::importPresets( const Reference< XMultiServiceFactory >& xConfigProvider, const OUString& rNodePath, PresetCategoryList& rPresetMap ) +{ +#ifdef DEBUG + OUString aMissedPresetIds; +#endif + + try + { + Reference< XNameAccess > xTypeAccess( getNodeAccess( xConfigProvider, rNodePath ) ); + if( xTypeAccess.is() ) + { + Reference< XNameAccess > xCategoryAccess; + + const Sequence< OUString > aNames( xTypeAccess->getElementNames() ); + for(const OUString& rName : aNames) + { + xTypeAccess->getByName( rName ) >>= xCategoryAccess; + + if( xCategoryAccess.is() && xCategoryAccess->hasByName( "Label" ) && xCategoryAccess->hasByName( "Effects" ) ) + { + OUString aLabel; + xCategoryAccess->getByName( "Label" ) >>= aLabel; + + Sequence< OUString > aEffects; + xCategoryAccess->getByName( "Effects" ) >>= aEffects; + + EffectDescriptorList aEffectsList; + + for( const OUString& rEffectName : std::as_const(aEffects) ) + { + CustomAnimationPresetPtr pEffect = getEffectDescriptor( rEffectName ); + if( pEffect ) + { + aEffectsList.push_back( pEffect ); + } +#ifdef DEBUG + else + { + aMissedPresetIds += OUString(rEffectName); + aMissedPresetIds += "\n"; + } +#endif + } + rPresetMap.push_back( std::make_shared<PresetCategory>( aLabel, std::move(aEffectsList) ) ); + } + } + } + } + catch (const Exception&) + { + TOOLS_WARN_EXCEPTION( "sd", "sd::CustomAnimationPresets::importPresets()" ); + } + +#ifdef DEBUG + SAL_WARN_IF(!aMissedPresetIds.isEmpty(), "sd", "sd::CustomAnimationPresets::importPresets(), invalid preset id: " + << aMissedPresetIds); +#endif +} + +CustomAnimationPresetPtr CustomAnimationPresets::getEffectDescriptor( const OUString& rPresetId ) const +{ + EffectDescriptorMap::const_iterator aIter( maEffectDescriptorMap.find( rPresetId ) ); + + if( aIter != maEffectDescriptorMap.end() ) + { + return (*aIter).second; + } + else + { + return CustomAnimationPresetPtr(nullptr); + } +} + +const OUString& CustomAnimationPresets::getUINameForPresetId( const OUString& rPresetId ) const +{ + return translateName( rPresetId, maEffectNameMap ); +} + +const OUString& CustomAnimationPresets::getUINameForProperty( const OUString& rPresetId ) const +{ + return translateName( rPresetId, maPropertyNameMap ); +} + +const OUString& CustomAnimationPresets::translateName( const OUString& rId, const UStringMap& rNameMap ) +{ + UStringMap::const_iterator aIter( rNameMap.find( rId ) ); + + if( aIter != rNameMap.end() ) + { + return (*aIter).second; + } + else + { + return rId; + } +} +void CustomAnimationPresets::changePresetSubType( const CustomAnimationEffectPtr& pEffect, const OUString& rPresetSubType ) const +{ + if( pEffect && pEffect->getPresetSubType() != rPresetSubType ) + { + CustomAnimationPresetPtr pDescriptor( getEffectDescriptor( pEffect->getPresetId() ) ); + + if( pDescriptor ) + { + Reference< XAnimationNode > xNewNode( pDescriptor->create( rPresetSubType ) ); + if( xNewNode.is() ) + pEffect->replaceNode( xNewNode ); + } + } +} + +std::map<OUString, CustomAnimationPresets> CustomAnimationPresets::mPresetsMap; + +const CustomAnimationPresets& CustomAnimationPresets::getCustomAnimationPresets() +{ + // Support localization per-view. Currently not useful for Desktop + // but very much critical for LOK. The cache now is per-language. + const OUString aLang = comphelper::LibreOfficeKit::isActive() + ? comphelper::LibreOfficeKit::getLanguageTag().getBcp47() + : SvtSysLocaleOptions().GetLanguageTag().getBcp47(); + + SolarMutexGuard aGuard; + const auto it = mPresetsMap.find(aLang); + if (it != mPresetsMap.end()) + return it->second; + + CustomAnimationPresets& rPresets = mPresetsMap[aLang]; + rPresets.importResources(); + return rPresets; +} + +Reference< XAnimationNode > CustomAnimationPresets::getRandomPreset( sal_Int16 nPresetClass ) const +{ + Reference< XAnimationNode > xNode; + + const PresetCategoryList* pCategoryList = nullptr; + switch( nPresetClass ) + { + case EffectPresetClass::ENTRANCE: pCategoryList = &maEntrancePresets; break; + case EffectPresetClass::EXIT: pCategoryList = &maExitPresets; break; + case EffectPresetClass::EMPHASIS: pCategoryList = &maEmphasisPresets; break; + case EffectPresetClass::MOTIONPATH: pCategoryList = &maMotionPathsPresets; break; + default: + pCategoryList = nullptr; + } + + if( pCategoryList && !pCategoryList->empty() ) + { + sal_Int32 nCategory = comphelper::rng::uniform_size_distribution(0, pCategoryList->size()-1); + + PresetCategoryPtr pCategory = (*pCategoryList)[nCategory]; + if( pCategory && !pCategory->maEffects.empty() ) + { + sal_Int32 nDescriptor = comphelper::rng::uniform_size_distribution(0, pCategory->maEffects.size()-1); + CustomAnimationPresetPtr pPreset = pCategory->maEffects[nDescriptor]; + if( pPreset ) + { + std::vector<OUString> aSubTypes = pPreset->getSubTypes(); + + OUString aSubType; + if( !aSubTypes.empty() ) + { + size_t nSubType = comphelper::rng::uniform_size_distribution(0, aSubTypes.size()-1); + aSubType = aSubTypes[nSubType]; + } + xNode = pPreset->create( aSubType ); + } + } + } + + return xNode; +} + +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/core/EffectMigration.cxx b/sd/source/core/EffectMigration.cxx new file mode 100644 index 000000000..8dd9d0905 --- /dev/null +++ b/sd/source/core/EffectMigration.cxx @@ -0,0 +1,1439 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include <com/sun/star/presentation/EffectNodeType.hpp> +#include <com/sun/star/presentation/ShapeAnimationSubType.hpp> +#include <com/sun/star/presentation/TextAnimationType.hpp> +#include <com/sun/star/presentation/ParagraphTarget.hpp> +#include <comphelper/processfactory.hxx> +#include <com/sun/star/animations/AnimationFill.hpp> +#include <com/sun/star/animations/XAnimate.hpp> +#include <com/sun/star/beans/NamedValue.hpp> +#include <o3tl/string_view.hxx> +#include <tools/debug.hxx> +#include <svx/unoshape.hxx> +#include <svx/svdotext.hxx> +#include <svx/svdopath.hxx> +#include <svx/svdogrp.hxx> +#include <svx/svditer.hxx> +#include <drawdoc.hxx> +#include <sdpage.hxx> +#include <CustomAnimationPreset.hxx> +#include <TransitionPreset.hxx> +#include <EffectMigration.hxx> +#include <anminfo.hxx> + +using namespace ::sd; +using namespace ::com::sun::star::uno; +using namespace ::com::sun::star::animations; +using namespace ::com::sun::star::presentation; +using ::com::sun::star::drawing::XShape; +using ::com::sun::star::lang::XMultiServiceFactory; +using ::com::sun::star::drawing::XShape; +using ::com::sun::star::beans::NamedValue; + +namespace { + +struct deprecated_FadeEffect_conversion_table_entry +{ + FadeEffect meFadeEffect; + const char* mpPresetId; +}; + +} + +deprecated_FadeEffect_conversion_table_entry const deprecated_FadeEffect_conversion_table[] = +{ +// OOo 1.x transitions + { FadeEffect_FADE_FROM_LEFT, "wipe-right" }, + { FadeEffect_FADE_FROM_TOP, "wipe-down" }, + { FadeEffect_FADE_FROM_RIGHT, "wipe-left" }, + { FadeEffect_FADE_FROM_BOTTOM, "wipe-up" }, + + { FadeEffect_CLOCKWISE, "wheel-clockwise-1-spoke" }, + + { FadeEffect_UNCOVER_TO_LEFT, "uncover-left" }, + { FadeEffect_UNCOVER_TO_UPPERLEFT, "uncover-left-up" }, + { FadeEffect_UNCOVER_TO_TOP, "uncover-up" }, + { FadeEffect_UNCOVER_TO_UPPERRIGHT, "uncover-right-up" }, + { FadeEffect_UNCOVER_TO_RIGHT, "uncover-right" }, + { FadeEffect_UNCOVER_TO_LOWERRIGHT, "uncover-right-down" }, + { FadeEffect_UNCOVER_TO_BOTTOM, "uncover-down" }, + { FadeEffect_UNCOVER_TO_LOWERLEFT, "uncover-left-down" }, + + { FadeEffect_VERTICAL_LINES, "random-bars-vertical" }, + { FadeEffect_HORIZONTAL_LINES, "random-bars-horizontal" }, + + { FadeEffect_VERTICAL_CHECKERBOARD, "checkerboard-down" }, + { FadeEffect_HORIZONTAL_CHECKERBOARD, "checkerboard-across" }, + + { FadeEffect_FADE_TO_CENTER, "box-in" }, + { FadeEffect_FADE_FROM_CENTER, "box-out" }, + + { FadeEffect_VERTICAL_STRIPES, "venetian-blinds-vertical" }, + { FadeEffect_HORIZONTAL_STRIPES, "venetian-blinds-horizontal" }, + + { FadeEffect_MOVE_FROM_LEFT, "cover-right" }, + { FadeEffect_MOVE_FROM_TOP, "cover-down" }, + { FadeEffect_MOVE_FROM_RIGHT, "cover-left" }, + { FadeEffect_MOVE_FROM_BOTTOM, "cover-up" }, + { FadeEffect_MOVE_FROM_UPPERLEFT, "cover-right-down" }, + { FadeEffect_MOVE_FROM_UPPERRIGHT, "cover-left-down" }, + { FadeEffect_MOVE_FROM_LOWERRIGHT, "cover-left-up" }, + { FadeEffect_MOVE_FROM_LOWERLEFT, "cover-right-up" }, + + { FadeEffect_DISSOLVE, "dissolve" }, + + { FadeEffect_RANDOM, "random-transition" }, + + { FadeEffect_ROLL_FROM_LEFT, "push-right" }, + { FadeEffect_ROLL_FROM_TOP, "push-down" }, + { FadeEffect_ROLL_FROM_RIGHT, "push-left" }, + { FadeEffect_ROLL_FROM_BOTTOM, "push-up" }, + + { FadeEffect_CLOSE_VERTICAL, "split-horizontal-in" }, + { FadeEffect_CLOSE_HORIZONTAL, "split-vertical-in" }, + { FadeEffect_OPEN_VERTICAL, "split-horizontal-out" }, + { FadeEffect_OPEN_HORIZONTAL, "split-vertical-out" }, + + { FadeEffect_FADE_FROM_UPPERLEFT, "diagonal-squares-right-down" }, + { FadeEffect_FADE_FROM_UPPERRIGHT, "diagonal-squares-left-down" }, + { FadeEffect_FADE_FROM_LOWERLEFT, "diagonal-squares-right-up" }, + { FadeEffect_FADE_FROM_LOWERRIGHT, "diagonal-squares-left-up" }, + +// OOo 1.x transitions not in OOo 2.x + { FadeEffect_CLOCKWISE, "clock-wipe-twelve" }, + { FadeEffect_COUNTERCLOCKWISE, "reverse-clock-wipe-twelve" }, + { FadeEffect_SPIRALIN_LEFT, "spiral-wipe-top-left-clockwise" }, + { FadeEffect_SPIRALIN_RIGHT, "spiral-wipe-top-right-counter-clockwise" }, + { FadeEffect_SPIRALOUT_LEFT, "spiral-wipe-out-to-bottom-right-clockwise" }, + { FadeEffect_SPIRALOUT_RIGHT, "spiral-wipe-out-to-bottom-left-counter-clockwise" }, + { FadeEffect_WAVYLINE_FROM_LEFT, "snake-wipe-top-left-vertical" }, + { FadeEffect_WAVYLINE_FROM_TOP, "snake-wipe-top-left-horizontal" }, + { FadeEffect_WAVYLINE_FROM_RIGHT, "snake-wipe-bottom-right-vertical" }, + { FadeEffect_WAVYLINE_FROM_BOTTOM, "snake-wipe-bottom-right-horizontal" }, + { FadeEffect_STRETCH_FROM_LEFT, "wipe-right" }, // todo + { FadeEffect_STRETCH_FROM_TOP, "wipe-down" }, // todo + { FadeEffect_STRETCH_FROM_RIGHT, "wipe-left" }, // todo + { FadeEffect_STRETCH_FROM_BOTTOM, "wipe-up" }, // todo + +// OOo 1.x not available transitions + + { FadeEffect_CLOCKWISE, "wheel-clockwise-2-spokes" }, + { FadeEffect_CLOCKWISE, "wheel-clockwise-3-spokes" }, + { FadeEffect_CLOCKWISE, "wheel-clockwise-4-spokes" }, + { FadeEffect_CLOCKWISE, "wheel-clockwise-8-spokes" }, + + { FadeEffect_FADE_FROM_CENTER, "shape-circle" }, + { FadeEffect_FADE_FROM_CENTER, "shape-diamond" }, + { FadeEffect_FADE_FROM_CENTER, "shape-plus" }, + + { FadeEffect_CLOCKWISE, "wedge" }, + + { FadeEffect_DISSOLVE, "fade-through-black" }, + + { FadeEffect_CLOCKWISE, "zoom-rotate-in" }, + + { FadeEffect_HORIZONTAL_LINES, "comb-horizontal" }, + { FadeEffect_VERTICAL_LINES, "comb-vertical" }, + + { FadeEffect_DISSOLVE, "fade-smoothly" }, + + { FadeEffect_NONE, nullptr } +}; + +/* todo +cut cut (same as NONE?) +cut-through-black cut toBlack +wedge wedge +*/ + +void EffectMigration::SetFadeEffect( SdPage* pPage, css::presentation::FadeEffect eNewEffect) +{ + deprecated_FadeEffect_conversion_table_entry const * pEntry = deprecated_FadeEffect_conversion_table; + while( (pEntry->meFadeEffect != FadeEffect_NONE) && (pEntry->meFadeEffect != eNewEffect) ) + pEntry++; + + if( pEntry->mpPresetId ) + { + const OUString aPresetId( OUString::createFromAscii( pEntry->mpPresetId ) ); + + const TransitionPresetList& rPresetList = TransitionPreset::getTransitionPresetList(); + + auto aIt = std::find_if(rPresetList.begin(), rPresetList.end(), + [&aPresetId](const TransitionPresetPtr& rxPreset) { return rxPreset->getPresetId() == aPresetId; }); + if (aIt != rPresetList.end()) + { + pPage->setTransitionType( (*aIt)->getTransition() ); + pPage->setTransitionSubtype( (*aIt)->getSubtype() ); + pPage->setTransitionDirection( (*aIt)->getDirection() ); + pPage->setTransitionFadeColor( (*aIt)->getFadeColor() ); + } + } + else + { + pPage->setTransitionType( 0 ); + pPage->setTransitionSubtype( 0 ); + pPage->setTransitionDirection( false ); + pPage->setTransitionFadeColor( 0 ); + } +} + +FadeEffect EffectMigration::GetFadeEffect( const SdPage* pPage ) +{ + const TransitionPresetList & rPresetList = TransitionPreset::getTransitionPresetList(); + auto aIt = std::find_if(rPresetList.begin(), rPresetList.end(), + [&pPage](const TransitionPresetPtr& rxPreset) { + return (rxPreset->getTransition() == pPage->getTransitionType()) + && (rxPreset->getSubtype() == pPage->getTransitionSubtype()) + && (rxPreset->getDirection() == pPage->getTransitionDirection()) + && (rxPreset->getFadeColor() == pPage->getTransitionFadeColor()); + }); + if (aIt != rPresetList.end()) + { + const OUString& aPresetId = (*aIt)->getPresetId(); + + deprecated_FadeEffect_conversion_table_entry const * pEntry = deprecated_FadeEffect_conversion_table; + while( (pEntry->meFadeEffect != FadeEffect_NONE) && (!aPresetId.equalsAscii( pEntry->mpPresetId ) ) ) + pEntry++; + + return pEntry->meFadeEffect; + } + return FadeEffect_NONE; +} + +namespace { + +struct deprecated_AnimationEffect_conversion_table_entry +{ + AnimationEffect meEffect; + const char* mpPresetId; + const char* mpPresetSubType; +}; + +} + +deprecated_AnimationEffect_conversion_table_entry const deprecated_AnimationEffect_conversion_table[] = +{ +// OOo 1.x entrance effects + { AnimationEffect_APPEAR, "ooo-entrance-appear",nullptr }, + + { AnimationEffect_FADE_TO_CENTER, "ooo-entrance-box","in" }, + { AnimationEffect_FADE_FROM_CENTER, "ooo-entrance-box","out" }, + + { AnimationEffect_VERTICAL_CHECKERBOARD, "ooo-entrance-checkerboard","downward" }, + { AnimationEffect_HORIZONTAL_CHECKERBOARD, "ooo-entrance-checkerboard","across" }, + + { AnimationEffect_FADE_FROM_UPPERLEFT, "ooo-entrance-diagonal-squares","right-to-bottom" }, + { AnimationEffect_FADE_FROM_UPPERRIGHT, "ooo-entrance-diagonal-squares","left-to-bottom" }, + { AnimationEffect_FADE_FROM_LOWERLEFT, "ooo-entrance-diagonal-squares","right-to-top" }, + { AnimationEffect_FADE_FROM_LOWERRIGHT, "ooo-entrance-diagonal-squares","left-to-top" }, + + { AnimationEffect_DISSOLVE, "ooo-entrance-dissolve-in",nullptr }, + + { AnimationEffect_MOVE_FROM_LEFT, "ooo-entrance-fly-in","from-left" }, + { AnimationEffect_MOVE_FROM_TOP, "ooo-entrance-fly-in","from-top" }, + { AnimationEffect_MOVE_FROM_RIGHT, "ooo-entrance-fly-in","from-right" }, + { AnimationEffect_MOVE_FROM_BOTTOM, "ooo-entrance-fly-in","from-bottom" }, + { AnimationEffect_MOVE_FROM_UPPERLEFT, "ooo-entrance-fly-in","from-top-left" }, + { AnimationEffect_MOVE_FROM_UPPERRIGHT, "ooo-entrance-fly-in","from-top-right" }, + { AnimationEffect_MOVE_FROM_LOWERRIGHT, "ooo-entrance-fly-in","from-bottom-right" }, + { AnimationEffect_MOVE_FROM_LOWERLEFT, "ooo-entrance-fly-in","from-bottom-left" }, + + { AnimationEffect_MOVE_FROM_BOTTOM, "ooo-entrance-fly-in-slow", "from-bottom" }, + { AnimationEffect_MOVE_FROM_LEFT, "ooo-entrance-fly-in-slow", "from-left" }, + { AnimationEffect_MOVE_FROM_RIGHT, "ooo-entrance-fly-in-slow", "from-right" }, + { AnimationEffect_MOVE_FROM_TOP, "ooo-entrance-fly-in-slow", "from-top" }, + + { AnimationEffect_MOVE_SHORT_FROM_LEFT, "ooo-entrance-peek-in","from-left" }, + { AnimationEffect_MOVE_SHORT_FROM_TOP, "ooo-entrance-peek-in","from-top" }, + { AnimationEffect_MOVE_SHORT_FROM_RIGHT, "ooo-entrance-peek-in","from-right" }, + { AnimationEffect_MOVE_SHORT_FROM_BOTTOM, "ooo-entrance-peek-in","from-bottom" }, + + { AnimationEffect_VERTICAL_LINES, "ooo-entrance-random-bars","horizontal" }, + { AnimationEffect_HORIZONTAL_LINES, "ooo-entrance-random-bars","vertical" }, + + { AnimationEffect_RANDOM, "ooo-entrance-random",nullptr }, + + { AnimationEffect_CLOSE_VERTICAL, "ooo-entrance-split","horizontal-in" }, + { AnimationEffect_CLOSE_HORIZONTAL, "ooo-entrance-split","vertical-in" }, + { AnimationEffect_OPEN_VERTICAL, "ooo-entrance-split","horizontal-out" }, + { AnimationEffect_OPEN_HORIZONTAL, "ooo-entrance-split","vertical-out" }, + + { AnimationEffect_VERTICAL_STRIPES, "ooo-entrance-venetian-blinds","horizontal" }, + { AnimationEffect_HORIZONTAL_STRIPES, "ooo-entrance-venetian-blinds","vertical" }, + + { AnimationEffect_FADE_FROM_LEFT, "ooo-entrance-wipe","from-left" }, + { AnimationEffect_FADE_FROM_TOP, "ooo-entrance-wipe","from-bottom" }, + { AnimationEffect_FADE_FROM_RIGHT, "ooo-entrance-wipe","from-right" }, + { AnimationEffect_FADE_FROM_BOTTOM, "ooo-entrance-wipe","from-top" }, + + { AnimationEffect_HORIZONTAL_ROTATE, "ooo-entrance-swivel","vertical" }, + { AnimationEffect_VERTICAL_ROTATE, "ooo-entrance-swivel","horizontal" }, + + { AnimationEffect_STRETCH_FROM_LEFT, "ooo-entrance-stretchy","from-left" }, + { AnimationEffect_STRETCH_FROM_UPPERLEFT, "ooo-entrance-stretchy","from-top-left" }, + { AnimationEffect_STRETCH_FROM_TOP, "ooo-entrance-stretchy","from-top" }, + { AnimationEffect_STRETCH_FROM_UPPERRIGHT, "ooo-entrance-stretchy","from-top-right" }, + { AnimationEffect_STRETCH_FROM_RIGHT, "ooo-entrance-stretchy","from-right" }, + { AnimationEffect_STRETCH_FROM_LOWERRIGHT, "ooo-entrance-stretchy","from-bottom-right" }, + { AnimationEffect_STRETCH_FROM_BOTTOM, "ooo-entrance-stretchy","from-bottom" }, + { AnimationEffect_STRETCH_FROM_LOWERLEFT, "ooo-entrance-stretchy","from-bottom-left" }, + + { AnimationEffect_HORIZONTAL_STRETCH, "ooo-entrance-expand", nullptr }, + + { AnimationEffect_CLOCKWISE, "ooo-entrance-wheel","1" }, + { AnimationEffect_COUNTERCLOCKWISE, "ooo-entrance-clock-wipe","counter-clockwise" }, + + { AnimationEffect_SPIRALIN_LEFT, "ooo-entrance-spiral-wipe", "from-top-left-clockwise" }, + { AnimationEffect_SPIRALIN_RIGHT, "ooo-entrance-spiral-wipe", "from-top-right-counter-clockwise" }, + { AnimationEffect_SPIRALOUT_LEFT, "ooo-entrance-spiral-wipe", "from-center-clockwise" }, + { AnimationEffect_SPIRALOUT_RIGHT, "ooo-entrance-spiral-wipe", "from-center-counter-clockwise" }, + + { AnimationEffect_WAVYLINE_FROM_LEFT, "ooo-entrance-snake-wipe","from-top-left-vertical" }, + { AnimationEffect_WAVYLINE_FROM_TOP, "ooo-entrance-snake-wipe","from-top-left-horizontal" }, + { AnimationEffect_WAVYLINE_FROM_RIGHT, "ooo-entrance-snake-wipe","from-bottom-right-vertical" }, + { AnimationEffect_WAVYLINE_FROM_BOTTOM, "ooo-entrance-snake-wipe","from-bottom-right-horizontal" }, + +// ooo 1.x exit effects + { AnimationEffect_HIDE, "ooo-exit-disappear",nullptr }, + { AnimationEffect_MOVE_TO_LEFT, "ooo-exit-fly-out", "from-right" }, + { AnimationEffect_MOVE_TO_TOP, "ooo-exit-fly-out", "from-bottom" }, + { AnimationEffect_MOVE_TO_RIGHT, "ooo-exit-fly-out", "from-left" }, + { AnimationEffect_MOVE_TO_BOTTOM, "ooo-exit-fly-out", "from-top" }, + { AnimationEffect_MOVE_TO_UPPERLEFT, "ooo-exit-fly-out", "from-top-right" }, + { AnimationEffect_MOVE_TO_UPPERRIGHT, "ooo-exit-fly-out", "from-top-left" }, + { AnimationEffect_MOVE_TO_LOWERRIGHT, "ooo-exit-fly-out", "from-bottom-left" }, + { AnimationEffect_MOVE_TO_LOWERLEFT, "ooo-exit-fly-out", "from-bottom-right" }, + { AnimationEffect_MOVE_SHORT_TO_LEFT, "ooo-exit-peek-out", "from-right" }, + { AnimationEffect_MOVE_SHORT_TO_UPPERLEFT, "ooo-exit-peek-out", "from-right" }, + { AnimationEffect_MOVE_SHORT_TO_TOP, "ooo-exit-peek-out", "from-bottom" }, + { AnimationEffect_MOVE_SHORT_TO_UPPERRIGHT, "ooo-exit-peek-out", "from-bottom" }, + { AnimationEffect_MOVE_SHORT_TO_RIGHT, "ooo-exit-peek-out", "from-left" }, + { AnimationEffect_MOVE_SHORT_TO_LOWERRIGHT, "ooo-exit-peek-out","from-left" }, + { AnimationEffect_MOVE_SHORT_TO_BOTTOM, "ooo-exit-peek-out", "from-top" }, + { AnimationEffect_MOVE_SHORT_TO_LOWERLEFT, "ooo-exit-peek-out", "from-top" }, + +// no matching in OOo 2.x + { AnimationEffect_MOVE_SHORT_FROM_UPPERLEFT, "ooo-entrance-peek-in","from-left" }, + { AnimationEffect_MOVE_SHORT_FROM_UPPERRIGHT, "ooo-entrance-peek-in","from-top" }, + { AnimationEffect_MOVE_SHORT_FROM_LOWERRIGHT, "ooo-entrance-peek-in","from-right" }, + { AnimationEffect_MOVE_SHORT_FROM_LOWERLEFT, "ooo-entrance-peek-in","from-bottom" }, + { AnimationEffect_LASER_FROM_LEFT, "ooo-entrance-fly-in","from-left" }, + { AnimationEffect_LASER_FROM_TOP, "ooo-entrance-fly-in","from-top" }, + { AnimationEffect_LASER_FROM_RIGHT, "ooo-entrance-fly-in","from-right" }, + { AnimationEffect_LASER_FROM_BOTTOM, "ooo-entrance-fly-in","from-bottom" }, + { AnimationEffect_LASER_FROM_UPPERLEFT, "ooo-entrance-fly-in","from-top-left" }, + { AnimationEffect_LASER_FROM_UPPERRIGHT, "ooo-entrance-fly-in","from-top-right" }, + { AnimationEffect_LASER_FROM_LOWERLEFT, "ooo-entrance-fly-in","from-bottom-left" }, + { AnimationEffect_LASER_FROM_LOWERRIGHT, "ooo-entrance-fly-in","from-bottom-right" }, + +// no matching in OOo 1.x + + { AnimationEffect_FADE_TO_CENTER, "ooo-entrance-circle", "in" }, + { AnimationEffect_FADE_FROM_CENTER, "ooo-entrance-circle", "out" }, + { AnimationEffect_FADE_TO_CENTER, "ooo-entrance-diamond", "in" }, + { AnimationEffect_FADE_FROM_CENTER, "ooo-entrance-diamond", "out" }, + { AnimationEffect_FADE_TO_CENTER, "ooo-entrance-plus", "in" }, + { AnimationEffect_FADE_FROM_CENTER, "ooo-entrance-plus", "out" }, + { AnimationEffect_CLOCKWISE, "ooo-entrance-wedge", nullptr }, + { AnimationEffect_CLOCKWISE, "ooo-entrance-wheel", "2" }, + { AnimationEffect_CLOCKWISE, "ooo-entrance-wheel", "3" }, + { AnimationEffect_CLOCKWISE, "ooo-entrance-wheel", "4" }, + { AnimationEffect_CLOCKWISE, "ooo-entrance-wheel", "8" }, + + { AnimationEffect_MOVE_FROM_RIGHT, "ooo-entrance-boomerang", nullptr }, + { AnimationEffect_MOVE_FROM_UPPERRIGHT, "ooo-entrance-bounce", nullptr }, + { AnimationEffect_MOVE_FROM_BOTTOM, "ooo-entrance-curve-up", nullptr }, + { AnimationEffect_MOVE_FROM_TOP, "ooo-entrance-float", nullptr }, + { AnimationEffect_MOVE_FROM_LEFT, "ooo-entrance-glide", nullptr }, + { AnimationEffect_MOVE_FROM_BOTTOM, "ooo-entrance-magnify", nullptr }, + { AnimationEffect_HORIZONTAL_ROTATE, "ooo-entrance-pinwheel", nullptr }, + { AnimationEffect_MOVE_FROM_LEFT, "ooo-entrance-breaks", nullptr }, + { AnimationEffect_MOVE_FROM_LEFT, "ooo-entrance-sling", nullptr }, + { AnimationEffect_MOVE_FROM_LEFT, "ooo-entrance-spiral-in", nullptr }, + { AnimationEffect_MOVE_FROM_LEFT, "ooo-entrance-thread", nullptr }, + { AnimationEffect_MOVE_FROM_BOTTOM, "ooo-entrance-ascend", nullptr }, + { AnimationEffect_MOVE_FROM_BOTTOM, "ooo-entrance-center-revolve", nullptr }, + { AnimationEffect_APPEAR, "ooo-entrance-compress", nullptr }, + { AnimationEffect_MOVE_SHORT_FROM_TOP, "ooo-entrance-descend", nullptr }, + { AnimationEffect_MOVE_SHORT_FROM_LEFT, "ooo-entrance-ease-in", nullptr }, + { AnimationEffect_MOVE_FROM_BOTTOM, "ooo-entrance-rise-up", nullptr }, + { AnimationEffect_HORIZONTAL_ROTATE, "ooo-entrance-spin-in", nullptr }, + { AnimationEffect_STRETCH_FROM_LEFT, "ooo-entrance-stretchy", "across" }, + { AnimationEffect_STRETCH_FROM_TOP, "ooo-entrance-stretchy", "downward" }, + + { AnimationEffect_FADE_FROM_CENTER, "ooo-entrance-zoom","in" }, + { AnimationEffect_FADE_FROM_CENTER, "ooo-entrance-zoom","in-slightly" }, + { AnimationEffect_FADE_FROM_CENTER, "ooo-entrance-zoom","in-from-screen-center" }, + { AnimationEffect_FADE_TO_CENTER, "ooo-entrance-zoom","out" }, + { AnimationEffect_FADE_TO_CENTER, "ooo-entrance-zoom","out-slightly" }, + { AnimationEffect_FADE_TO_CENTER, "ooo-entrance-zoom","out-from-screen-center" }, + + { AnimationEffect_DISSOLVE, "ooo-entrance-fade-in", nullptr }, + { AnimationEffect_DISSOLVE, "ooo-entrance-fade-in-and-zoom", nullptr }, + { AnimationEffect_DISSOLVE, "ooo-entrance-fade-in-and-swivel", nullptr }, + + // still open (no matching effect: AnimationEffect_ZOOM_IN_FROM_*, + // AnimationEffect_ZOOM_OUT_FROM_*, AnimationEffect_PATH + + { AnimationEffect_NONE, nullptr, nullptr } +}; + +static EffectSequence::iterator ImplFindEffect( MainSequencePtr const & pMainSequence, const Reference< XShape >& rShape, sal_Int16 nSubItem ) +{ + return std::find_if(pMainSequence->getBegin(), pMainSequence->getEnd(), + [&rShape, &nSubItem](const CustomAnimationEffectPtr& pEffect) { + return (pEffect->getTargetShape() == rShape) + && (pEffect->getTargetSubItem() == nSubItem); + }); +} + +static bool implIsInsideGroup( SdrObject const * pObj ) +{ + // TTTT for current state of transition, SdrObject has a parent* + // to a SdrObjList. That may be a SdrPage or a SdrObjGroup, both + // are already derived from SdrObjList. To finally check, use + // the method 'getSdrObjectFromSdrObjList' - if it's not a SdrPage, + // it will return SdrObjGroup or E3dScene -> SdrObject. + // For future states, test for SdrObject. Trying to get the SdrPage + // will in the future depend on the Object(this) to be inserted to a + // SdrPage, regardless of e.g. being a group member. + if(nullptr == pObj) + { + return false; + } + + SdrObjList* pSdrObjList(pObj->getParentSdrObjListFromSdrObject()); + + if(nullptr == pSdrObjList) + { + return false; + } + + return (nullptr != pSdrObjList->getSdrObjectFromSdrObjList()); +} + +void EffectMigration::SetAnimationEffect( SvxShape* pShape, AnimationEffect eEffect ) +{ + DBG_ASSERT( pShape && pShape->GetSdrObject() && pShape->GetSdrObject()->getSdrPageFromSdrObject(), + "sd::EffectMigration::SetAnimationEffect(), invalid argument!" ); + if( !pShape || !pShape->GetSdrObject() || !pShape->GetSdrObject()->getSdrPageFromSdrObject() ) + return; + + SdrObject* pObj = pShape->GetSdrObject(); + if( implIsInsideGroup( pObj ) ) + return; + + OUString aPresetId; + OUString aPresetSubType; + + if( !ConvertAnimationEffect( eEffect, aPresetId, aPresetSubType ) ) + { + OSL_FAIL( "sd::EffectMigration::SetAnimationEffect(), no mapping for given AnimationEffect value" ); + return; + } + + const CustomAnimationPresets& rPresets = CustomAnimationPresets::getCustomAnimationPresets(); + + CustomAnimationPresetPtr pPreset( rPresets.getEffectDescriptor( aPresetId ) ); + sd::MainSequencePtr pMainSequence = static_cast<SdPage*>(pObj->getSdrPageFromSdrObject())->getMainSequence(); + + if( !(pPreset && pMainSequence) ) + return; + + const Reference< XShape > xShape( pShape ); + + EffectSequence::iterator aIterOnlyBackground( ImplFindEffect( pMainSequence, xShape, ShapeAnimationSubType::ONLY_BACKGROUND ) ); + EffectSequence::iterator aIterAsWhole( ImplFindEffect( pMainSequence, xShape, ShapeAnimationSubType::AS_WHOLE ) ); + const EffectSequence::iterator aEnd( pMainSequence->getEnd() ); + + if( (aIterOnlyBackground == aEnd) && (aIterAsWhole == aEnd) ) + { + bool bEffectCreated = false; + + // check if there is already a text effect for this shape + EffectSequence::iterator aIterOnlyText( ImplFindEffect( pMainSequence, xShape, ShapeAnimationSubType::ONLY_TEXT ) ); + if( aIterOnlyText != aEnd ) + { + // check if this is an animation text group + sal_Int32 nGroupId = (*aIterOnlyText)->getGroupId(); + if( nGroupId >= 0 ) + { + CustomAnimationTextGroupPtr pGroup = pMainSequence->findGroup( nGroupId ); + if( pGroup ) + { + // add an effect to animate the shape + pMainSequence->setAnimateForm( pGroup, true ); + + // find this effect + EffectSequence::iterator aIter( ImplFindEffect( pMainSequence, xShape, ShapeAnimationSubType::ONLY_BACKGROUND ) ); + + if( aIter != aEnd ) + { + if( ((*aIter)->getPresetId() != aPresetId) || + ((*aIter)->getPresetSubType() != aPresetSubType) ) + { + (*aIter)->replaceNode( pPreset->create( aPresetSubType ) ); + pMainSequence->rebuild(); + bEffectCreated = true; + } + } + } + } + } + + if( !bEffectCreated ) + { + // if there is not yet an effect that target this shape, we generate one + // we insert the shape effect before it + Reference< XAnimationNode > xNode( pPreset->create( aPresetSubType ) ); + DBG_ASSERT( xNode.is(), "EffectMigration::SetAnimationEffect(), could not create preset!" ); + if( xNode.is() ) + { + CustomAnimationEffectPtr pEffect = std::make_shared<CustomAnimationEffect>( xNode ); + pEffect->setTarget( Any( xShape ) ); + SdPage* pPage = dynamic_cast< SdPage* >( pObj->getSdrPageFromSdrObject() ); + const bool bManual = (pPage == nullptr) || (pPage->GetPresChange() == PresChange::Manual); + if( !bManual ) + pEffect->setNodeType( EffectNodeType::AFTER_PREVIOUS ); + + pMainSequence->append( pEffect ); + + if( ( pObj->GetObjInventor() == SdrInventor::Default ) && ( pObj->GetObjIdentifier() == SdrObjKind::OutlineText ) ) + { + // special case for outline text, effects are always mapped to text group effect + pMainSequence-> + createTextGroup( pEffect, 10, bManual ? -1 : 0.0, false, false ); + } + } + } + } + else + { + // if there is already an effect targeting this shape + // just replace it + CustomAnimationEffectPtr pEffect; + if( aIterAsWhole != aEnd ) + { + pEffect = *aIterAsWhole; + } + else + { + pEffect = *aIterOnlyBackground; + } + + if( pEffect ) + { + if( (pEffect->getPresetId() != aPresetId) || + (pEffect->getPresetSubType() != aPresetSubType) ) + { + pMainSequence->replace( pEffect, pPreset, aPresetSubType, -1.0 ); + } + } + } +} + +AnimationEffect EffectMigration::GetAnimationEffect( SvxShape* pShape ) +{ + OUString aPresetId; + OUString aPresetSubType; + + SdrObject* pObj = pShape->GetSdrObject(); + sd::MainSequencePtr pMainSequence = static_cast<SdPage*>(pObj->getSdrPageFromSdrObject())->getMainSequence(); + + if( pMainSequence ) + { + const Reference< XShape > xShape( pShape ); + + EffectSequence::iterator aIter = std::find_if(pMainSequence->getBegin(), pMainSequence->getEnd(), + [&xShape](const CustomAnimationEffectPtr& pEffect) { + return (pEffect->getTargetShape() == xShape) + && ((pEffect->getTargetSubItem() == ShapeAnimationSubType::ONLY_BACKGROUND) + || (pEffect->getTargetSubItem() == ShapeAnimationSubType::AS_WHOLE)) + && (pEffect->getDuration() != 0.1); // ignore appear effects created from old text effect import + }); + + if (aIter != pMainSequence->getEnd()) + { + aPresetId = (*aIter)->getPresetId(); + aPresetSubType = (*aIter)->getPresetSubType(); + } + } + + // now find old effect + AnimationEffect eEffect = AnimationEffect_NONE; + + if( !ConvertPreset( aPresetId, &aPresetSubType, eEffect ) ) + ConvertPreset( aPresetId, nullptr, eEffect ); + + return eEffect; +} + +void EffectMigration::SetTextAnimationEffect( SvxShape* pShape, AnimationEffect eEffect ) +{ + DBG_ASSERT( pShape && pShape->GetSdrObject() && pShape->GetSdrObject()->getSdrPageFromSdrObject(), + "sd::EffectMigration::SetAnimationEffect(), invalid argument!" ); + if( !pShape || !pShape->GetSdrObject() || !pShape->GetSdrObject()->getSdrPageFromSdrObject() ) + return; + + SdrObject* pObj = pShape->GetSdrObject(); + if( implIsInsideGroup( pObj ) ) + return; + + // first map the deprecated AnimationEffect to a preset and subtype + OUString aPresetId; + OUString aPresetSubType; + + if( !ConvertAnimationEffect( eEffect, aPresetId, aPresetSubType ) ) + { + OSL_FAIL( "sd::EffectMigration::SetAnimationEffect(), no mapping for given AnimationEffect value" ); + return; + } + + SdrTextObj* pTextObj = dynamic_cast< SdrTextObj* >( pObj ); + + // ignore old text effects on shape without text + if( (pTextObj == nullptr) || (!pTextObj->HasText()) ) + return; + + const CustomAnimationPresets& rPresets = CustomAnimationPresets::getCustomAnimationPresets(); + + // create an effect from this preset + CustomAnimationPresetPtr pPreset( rPresets.getEffectDescriptor( aPresetId ) ); + + sd::MainSequencePtr pMainSequence = static_cast<SdPage*>(pObj->getSdrPageFromSdrObject())->getMainSequence(); + + if( !(pPreset && pMainSequence) ) + return; + + const Reference< XShape > xShape( pShape ); + + EffectSequence::iterator aIterOnlyText( ImplFindEffect( pMainSequence, xShape, ShapeAnimationSubType::ONLY_TEXT ) ); + const EffectSequence::iterator aEnd( pMainSequence->getEnd() ); + + CustomAnimationTextGroupPtr pGroup; + + // is there already an animation text group for this shape? + if( aIterOnlyText != aEnd ) + { + const sal_Int32 nGroupId = (*aIterOnlyText)->getGroupId(); + if( nGroupId >= 0 ) + pGroup = pMainSequence->findGroup( nGroupId ); + } + + // if there is not yet a group, create it + if( !pGroup ) + { + CustomAnimationEffectPtr pShapeEffect; + + EffectSequence::iterator aIterOnlyBackground( ImplFindEffect( pMainSequence, xShape, ShapeAnimationSubType::ONLY_BACKGROUND ) ); + if( aIterOnlyBackground != aEnd ) + { + pShapeEffect = *aIterOnlyBackground; + } + else + { + EffectSequence::iterator aIterAsWhole( ImplFindEffect( pMainSequence, xShape, ShapeAnimationSubType::AS_WHOLE ) ); + if( aIterAsWhole != aEnd ) + { + pShapeEffect = *aIterAsWhole; + } + else + { + Reference< XAnimationNode > xNode( pPreset->create( "" ) ); + DBG_ASSERT( xNode.is(), "EffectMigration::SetTextAnimationEffect(), could not create preset!" ); + if( xNode.is() ) + { + pShapeEffect = std::make_shared<CustomAnimationEffect>( xNode ); + pShapeEffect->setTarget( Any( xShape ) ); + pShapeEffect->setDuration( 0.1 ); + pMainSequence->append( pShapeEffect ); + + SdPage* pPage = dynamic_cast< SdPage* >( pObj->getSdrPageFromSdrObject() ); + if( pPage && pPage->GetPresChange() != PresChange::Manual ) + pShapeEffect->setNodeType( EffectNodeType::AFTER_PREVIOUS ); + } + } + } + + if( pShapeEffect ) + { + SdPage* pPage = dynamic_cast< SdPage* >( pObj->getSdrPageFromSdrObject() ); + const bool bManual = (pPage == nullptr) || (pPage->GetPresChange() == PresChange::Manual); + + // now create effects for each paragraph + pGroup = + pMainSequence-> + createTextGroup( pShapeEffect, 10, bManual ? -1 : 0.0, true, false ); + } + } + + if( pGroup ) + { + const bool bLaserEffect = (eEffect >= AnimationEffect_LASER_FROM_LEFT) && (eEffect <= AnimationEffect_LASER_FROM_LOWERRIGHT); + + // now we have a group, so check if all effects are same as we like to have them + const EffectSequence& rEffects = pGroup->getEffects(); + + for( auto& rxEffect : rEffects ) + { + // only work on paragraph targets + if( rxEffect->getTarget().getValueType() == ::cppu::UnoType<ParagraphTarget>::get() ) + { + if( (rxEffect->getPresetId() != aPresetId) || + (rxEffect->getPresetSubType() != aPresetSubType) ) + { + rxEffect->replaceNode( pPreset->create( aPresetSubType ) ); + } + + if( bLaserEffect ) + { + rxEffect->setIterateType( TextAnimationType::BY_LETTER ); + rxEffect->setIterateInterval( 0.5 );// TODO: + // Determine + // interval + // according + // to + // total + // effect + // duration + } + } + } + } + pMainSequence->rebuild(); +} + +AnimationEffect EffectMigration::GetTextAnimationEffect( SvxShape* pShape ) +{ + OUString aPresetId; + OUString aPresetSubType; + + SdrObject* pObj = pShape->GetSdrObject(); + if( pObj ) + { + sd::MainSequencePtr pMainSequence = static_cast<SdPage*>(pObj->getSdrPageFromSdrObject())->getMainSequence(); + + if( pMainSequence ) + { + const Reference< XShape > xShape( pShape ); + EffectSequence::iterator aIter( ImplFindEffect( pMainSequence, xShape, ShapeAnimationSubType::ONLY_TEXT ) ); + if( aIter != pMainSequence->getEnd() ) + { + aPresetId = (*aIter)->getPresetId(); + aPresetSubType = (*aIter)->getPresetSubType(); + } + } + } + + // now find old effect + AnimationEffect eEffect = AnimationEffect_NONE; + + if( !ConvertPreset( aPresetId, &aPresetSubType, eEffect ) ) + ConvertPreset( aPresetId, nullptr, eEffect ); + + return eEffect; +} + +bool EffectMigration::ConvertPreset( std::u16string_view rPresetId, const OUString* pPresetSubType, AnimationEffect& rEffect ) +{ + rEffect = AnimationEffect_NONE; + if( !rPresetId.empty() ) + { + // first try a match for preset id and subtype + deprecated_AnimationEffect_conversion_table_entry const * p = deprecated_AnimationEffect_conversion_table; + while( p->mpPresetId ) + { + if( o3tl::equalsAscii( rPresetId, p->mpPresetId ) && + (( p->mpPresetSubType == nullptr ) || + ( pPresetSubType == nullptr) || + ( pPresetSubType->equalsAscii( p->mpPresetSubType )) ) ) + { + rEffect = p->meEffect; + return true; + } + p++; + } + return false; + } + else + { + // empty preset id means AnimationEffect_NONE + return true; + } +} + +bool EffectMigration::ConvertAnimationEffect( const AnimationEffect& rEffect, OUString& rPresetId, OUString& rPresetSubType ) +{ + deprecated_AnimationEffect_conversion_table_entry const * p = deprecated_AnimationEffect_conversion_table; + while( p->mpPresetId ) + { + if( p->meEffect == rEffect ) + { + rPresetId = OUString::createFromAscii( p->mpPresetId ); + rPresetSubType = OUString::createFromAscii( p->mpPresetSubType ); + return true; + } + p++; + } + + return false; +} + +double EffectMigration::ConvertAnimationSpeed( AnimationSpeed eSpeed ) +{ + double fDuration; + switch( eSpeed ) + { + case AnimationSpeed_SLOW: fDuration = 2.0; break; + case AnimationSpeed_FAST: fDuration = 0.5; break; + default: + fDuration = 1.0; break; + } + return fDuration; +} + +void EffectMigration::SetAnimationSpeed( SvxShape* pShape, AnimationSpeed eSpeed ) +{ + DBG_ASSERT( pShape && pShape->GetSdrObject() && pShape->GetSdrObject()->getSdrPageFromSdrObject(), + "sd::EffectMigration::SetAnimationEffect(), invalid argument!" ); + if( !pShape || !pShape->GetSdrObject() || !pShape->GetSdrObject()->getSdrPageFromSdrObject() ) + return; + + SdrObject* pObj = pShape->GetSdrObject(); + if( implIsInsideGroup( pObj ) ) + return; + + double fDuration = ConvertAnimationSpeed( eSpeed ); + + sd::MainSequencePtr pMainSequence = static_cast<SdPage*>(pObj->getSdrPageFromSdrObject())->getMainSequence(); + + const Reference< XShape > xShape( pShape ); + + EffectSequence::iterator aIter; + bool bNeedRebuild = false; + + for( aIter = pMainSequence->getBegin(); aIter != pMainSequence->getEnd(); ++aIter ) + { + CustomAnimationEffectPtr pEffect( *aIter ); + if( pEffect->getTargetShape() == xShape ) + { + if( pEffect->getDuration() != 0.1 ) + pEffect->setDuration( fDuration ); + bNeedRebuild = true; + } + } + + if( bNeedRebuild ) + pMainSequence->rebuild(); +} + +AnimationSpeed EffectMigration::GetAnimationSpeed( SvxShape* pShape ) +{ + SdrObject* pObj = pShape->GetSdrObject(); + sd::MainSequencePtr pMainSequence = static_cast<SdPage*>(pObj->getSdrPageFromSdrObject())->getMainSequence(); + + const Reference< XShape > xShape( pShape ); + + double fDuration = 1.0; + + EffectSequence::iterator aIter = std::find_if(pMainSequence->getBegin(), pMainSequence->getEnd(), + [&xShape](const CustomAnimationEffectPtr& pEffect) { + return (pEffect->getTargetShape() == xShape) + && (pEffect->getDuration() != 0.1); + }); + if (aIter != pMainSequence->getEnd()) + { + CustomAnimationEffectPtr pEffect( *aIter ); + fDuration = pEffect->getDuration(); + } + + return ConvertDuration( fDuration ); +} + +AnimationSpeed EffectMigration::ConvertDuration( double fDuration ) +{ + AnimationSpeed eSpeed; + + if( fDuration < 1.0 ) + eSpeed = AnimationSpeed_FAST; + else if( fDuration > 1.5 ) + eSpeed = AnimationSpeed_SLOW; + else + eSpeed = AnimationSpeed_MEDIUM; + + return eSpeed; +} + +void EffectMigration::SetDimColor( SvxShape* pShape, sal_Int32 nColor ) +{ + DBG_ASSERT( pShape && pShape->GetSdrObject() && pShape->GetSdrObject()->getSdrPageFromSdrObject(), + "sd::EffectMigration::SetAnimationEffect(), invalid argument!" ); + if( !pShape || !pShape->GetSdrObject() || !pShape->GetSdrObject()->getSdrPageFromSdrObject() ) + return; + + SdrObject* pObj = pShape->GetSdrObject(); + if( implIsInsideGroup( pObj ) ) + return; + + sd::MainSequencePtr pMainSequence = static_cast<SdPage*>(pObj->getSdrPageFromSdrObject())->getMainSequence(); + + const Reference< XShape > xShape( pShape ); + + EffectSequence::iterator aIter; + bool bNeedRebuild = false; + + for( aIter = pMainSequence->getBegin(); aIter != pMainSequence->getEnd(); ++aIter ) + { + CustomAnimationEffectPtr pEffect( *aIter ); + if( pEffect->getTargetShape() == xShape ) + { + pEffect->setHasAfterEffect( true ); + pEffect->setDimColor( Any( nColor ) ); + pEffect->setAfterEffectOnNext( true ); + bNeedRebuild = true; + } + } + + if( bNeedRebuild ) + pMainSequence->rebuild(); +} + +sal_Int32 EffectMigration::GetDimColor( SvxShape* pShape ) +{ + sal_Int32 nColor = 0; + if( pShape ) + { + SdrObject* pObj = pShape->GetSdrObject(); + if( pObj && pObj->getSdrPageFromSdrObject() ) + { + sd::MainSequencePtr pMainSequence = static_cast<SdPage*>(pObj->getSdrPageFromSdrObject())->getMainSequence(); + + const Reference< XShape > xShape( pShape ); + EffectSequence::iterator aIter = std::find_if(pMainSequence->getBegin(), pMainSequence->getEnd(), + [&xShape](const CustomAnimationEffectPtr& pEffect) { + return (pEffect->getTargetShape() == xShape) + && pEffect->getDimColor().hasValue() + && pEffect->hasAfterEffect(); + }); + if (aIter != pMainSequence->getEnd()) + { + CustomAnimationEffectPtr pEffect( *aIter ); + pEffect->getDimColor() >>= nColor; + } + } + } + + return nColor; +} + +void EffectMigration::SetDimHide( SvxShape* pShape, bool bDimHide ) +{ + DBG_ASSERT( pShape && pShape->GetSdrObject() && pShape->GetSdrObject()->getSdrPageFromSdrObject(), + "sd::EffectMigration::SetAnimationEffect(), invalid argument!" ); + if( !pShape || !pShape->GetSdrObject() || !pShape->GetSdrObject()->getSdrPageFromSdrObject() ) + return; + + SdrObject* pObj = pShape->GetSdrObject(); + if( implIsInsideGroup( pObj ) ) + return; + + sd::MainSequencePtr pMainSequence = static_cast<SdPage*>(pObj->getSdrPageFromSdrObject())->getMainSequence(); + + const Reference< XShape > xShape( pShape ); + + EffectSequence::iterator aIter; + bool bNeedRebuild = false; + + for( aIter = pMainSequence->getBegin(); aIter != pMainSequence->getEnd(); ++aIter ) + { + CustomAnimationEffectPtr pEffect( *aIter ); + if( pEffect->getTargetShape() == xShape ) + { + pEffect->setHasAfterEffect( bDimHide ); + if( bDimHide ) { + Any aEmpty; + pEffect->setDimColor( aEmpty ); + } + pEffect->setAfterEffectOnNext( false ); + bNeedRebuild = true; + } + } + + if( bNeedRebuild ) + pMainSequence->rebuild(); +} + +bool EffectMigration::GetDimHide( SvxShape* pShape ) +{ + bool bRet = false; + if( pShape ) + { + SdrObject* pObj = pShape->GetSdrObject(); + if( pObj && pObj->getSdrPageFromSdrObject() ) + { + sd::MainSequencePtr pMainSequence = static_cast<SdPage*>(pObj->getSdrPageFromSdrObject())->getMainSequence(); + + const Reference< XShape > xShape( pShape ); + + EffectSequence::iterator aIter = std::find_if(pMainSequence->getBegin(), pMainSequence->getEnd(), + [&xShape](const CustomAnimationEffectPtr& pEffect) { return pEffect->getTargetShape() == xShape; }); + if (aIter != pMainSequence->getEnd()) + { + CustomAnimationEffectPtr pEffect( *aIter ); + bRet = pEffect->hasAfterEffect() && + !pEffect->getDimColor().hasValue() && + (!pEffect->IsAfterEffectOnNext()); + } + } + } + + return bRet; +} + +void EffectMigration::SetDimPrevious( SvxShape* pShape, bool bDimPrevious ) +{ + DBG_ASSERT( pShape && pShape->GetSdrObject() && pShape->GetSdrObject()->getSdrPageFromSdrObject(), + "sd::EffectMigration::SetAnimationEffect(), invalid argument!" ); + if( !pShape || !pShape->GetSdrObject() || !pShape->GetSdrObject()->getSdrPageFromSdrObject() ) + return; + + SdrObject* pObj = pShape->GetSdrObject(); + if( implIsInsideGroup( pObj ) ) + return; + + Any aColor; + + if( bDimPrevious ) + aColor <<= COL_LIGHTGRAY; + + sd::MainSequencePtr pMainSequence = static_cast<SdPage*>(pObj->getSdrPageFromSdrObject())->getMainSequence(); + + const Reference< XShape > xShape( pShape ); + + EffectSequence::iterator aIter; + bool bNeedRebuild = false; + + for( aIter = pMainSequence->getBegin(); aIter != pMainSequence->getEnd(); ++aIter ) + { + CustomAnimationEffectPtr pEffect( *aIter ); + if( pEffect->getTargetShape() == xShape ) + { + pEffect->setHasAfterEffect( bDimPrevious ); + if( !bDimPrevious || !pEffect->getDimColor().hasValue() ) + pEffect->setDimColor( aColor ); + pEffect->setAfterEffectOnNext( true ); + bNeedRebuild = true; + } + } + + if( bNeedRebuild ) + pMainSequence->rebuild(); +} + +bool EffectMigration::GetDimPrevious( SvxShape* pShape ) +{ + bool bRet = false; + if( pShape ) + { + SdrObject* pObj = pShape->GetSdrObject(); + if( pObj && pObj->getSdrPageFromSdrObject() ) + { + sd::MainSequencePtr pMainSequence = static_cast<SdPage*>(pObj->getSdrPageFromSdrObject())->getMainSequence(); + + const Reference< XShape > xShape( pShape ); + + EffectSequence::iterator aIter = std::find_if(pMainSequence->getBegin(), pMainSequence->getEnd(), + [&xShape](const CustomAnimationEffectPtr& pEffect) { return pEffect->getTargetShape() == xShape; }); + if (aIter != pMainSequence->getEnd()) + { + CustomAnimationEffectPtr pEffect( *aIter ); + bRet = pEffect->hasAfterEffect() && + pEffect->getDimColor().hasValue() && + pEffect->IsAfterEffectOnNext(); + } + } + } + + return bRet; +} + +void EffectMigration::SetPresentationOrder( SvxShape* pShape, sal_Int32 nNewPos ) +{ + if( !pShape || !pShape->GetSdrObject() || !pShape->GetSdrObject()->getSdrPageFromSdrObject() ) + return; + + SdrObject* pObj = pShape->GetSdrObject(); + sd::MainSequencePtr pMainSequence = static_cast<SdPage*>(pObj->getSdrPageFromSdrObject())->getMainSequence(); + + EffectSequence& rSequence = pMainSequence->getSequence(); + sal_Int32 nPos; + sal_Int32 nCurrentPos = -1; + std::vector< std::vector< EffectSequence::iterator > > aEffectVector(1); + + if( !rSequence.empty() ) + { + Reference< XShape > xThis( pShape ); + Reference< XShape > xCurrent; + + EffectSequence::iterator aIter( rSequence.begin() ); + EffectSequence::iterator aEnd( rSequence.end() ); + for( nPos = 0; aIter != aEnd; ++aIter ) + { + CustomAnimationEffectPtr pEffect = *aIter; + + if( !xCurrent.is() ) + { + xCurrent = pEffect->getTargetShape(); + } + else if( pEffect->getTargetShape() != xCurrent ) + { + nPos++; + xCurrent = pEffect->getTargetShape(); + aEffectVector.resize( nPos+1 ); + } + + // is this the first effect for xThis shape? + if(( nCurrentPos == -1 ) && ( xCurrent == xThis ) ) + { + nCurrentPos = nPos; + } + + aEffectVector[nPos].push_back( aIter ); + } + } + + // check if there is at least one effect for xThis + if( nCurrentPos == -1 ) + { + OSL_FAIL("sd::EffectMigration::SetPresentationOrder() failed cause this shape has no effect" ); + return; + } + + // check trivial case + if( nCurrentPos == nNewPos ) + return; + + std::vector< CustomAnimationEffectPtr > aEffects; + + for( const auto& rIter : aEffectVector[nCurrentPos] ) + { + aEffects.push_back( *rIter ); + rSequence.erase( rIter ); + } + + if( nNewPos > nCurrentPos ) + nNewPos++; + + if( nNewPos == static_cast<sal_Int32>(aEffectVector.size()) ) + { + rSequence.insert( rSequence.end(), aEffects.begin(), aEffects.end() ); + } + else + { + EffectSequence::iterator aPos( aEffectVector[nNewPos][0] ); + for( const auto& rEffect : aEffects ) + { + rSequence.insert( aPos, rEffect ); + } + } +} + +/** Returns the position of the given SdrObject in the Presentation order. + * This function returns -1 if the SdrObject is not in the Presentation order + * or if it's the path-object. + */ +sal_Int32 EffectMigration::GetPresentationOrder( SvxShape* pShape ) +{ + sal_Int32 nPos = -1, nFound = -1; + + SdrObject* pObj = pShape->GetSdrObject(); + sd::MainSequencePtr pMainSequence = static_cast<SdPage*>(pObj->getSdrPageFromSdrObject())->getMainSequence(); + + EffectSequence& rSequence = pMainSequence->getSequence(); + + Reference< XShape > xThis( pShape ); + Reference< XShape > xCurrent; + + for( const CustomAnimationEffectPtr& pEffect : rSequence ) + { + if( !xCurrent.is() || pEffect->getTargetShape() != xCurrent ) + { + nPos++; + xCurrent = pEffect->getTargetShape(); + + // is this the first effect for xThis shape? + if( xCurrent == xThis ) + { + nFound = nPos; + break; + } + } + } + + return nFound; +} + +void EffectMigration::UpdateSoundEffect( SvxShape* pShape, SdAnimationInfo const * pInfo ) +{ + if( !pInfo ) + return; + + SdrObject* pObj = pShape->GetSdrObject(); + sd::MainSequencePtr pMainSequence = static_cast<SdPage*>(pObj->getSdrPageFromSdrObject())->getMainSequence(); + + const Reference< XShape > xShape( pShape ); + + EffectSequence::iterator aIter; + bool bNeedRebuild = false; + + OUString aSoundFile; + if( pInfo->mbSoundOn ) + aSoundFile = pInfo->maSoundFile; + + for( aIter = pMainSequence->getBegin(); aIter != pMainSequence->getEnd(); ++aIter ) + { + CustomAnimationEffectPtr pEffect( *aIter ); + if( pEffect->getTargetShape() == xShape ) + { + if( !aSoundFile.isEmpty() ) + { + pEffect->createAudio( Any( aSoundFile ) ); + } + else + { + pEffect->removeAudio(); + } + bNeedRebuild = true; + } + } + + if( bNeedRebuild ) + pMainSequence->rebuild(); +} + +OUString EffectMigration::GetSoundFile( SvxShape* pShape ) +{ + OUString aSoundFile; + + if( pShape ) + { + SdrObject* pObj = pShape->GetSdrObject(); + if( pObj && pObj->getSdrPageFromSdrObject() ) + { + sd::MainSequencePtr pMainSequence = static_cast<SdPage*>(pObj->getSdrPageFromSdrObject())->getMainSequence(); + + const Reference< XShape > xShape( pShape ); + + EffectSequence::iterator aIter; + + for( aIter = pMainSequence->getBegin(); + (aSoundFile.isEmpty()) && (aIter != pMainSequence->getEnd()); + ++aIter ) + { + CustomAnimationEffectPtr pEffect( *aIter ); + if( pEffect->getTargetShape() == xShape ) + { + if( pEffect->getAudio().is() ) + pEffect->getAudio()->getSource() >>= aSoundFile; + } + } + } + } + return aSoundFile; +} + +bool EffectMigration::GetSoundOn( SvxShape* pShape ) +{ + return !GetSoundFile( pShape ).isEmpty(); +} + +void EffectMigration::SetAnimationPath( SvxShape* pShape, SdrPathObj const * pPathObj ) +{ + if( !(pShape && pPathObj) ) + return; + + SdrObject* pObj = pShape->GetSdrObject(); + + if( pObj ) + { + const Reference< XShape > xShape( pShape ); + SdPage* pPage = dynamic_cast< SdPage* >(pPathObj->getSdrPageFromSdrObject()); + if( pPage ) + { + std::shared_ptr< sd::MainSequence > pMainSequence( pPage->getMainSequence() ); + if( pMainSequence ) + pMainSequence->append( *pPathObj, Any( xShape ), -1.0, "" ); + } + } +} + +// #i42894# helper which creates the needed XAnimate for changing visibility and all the (currently) needed embeddings +static void createVisibilityOnOffNode(Reference< XTimeContainer > const & rxParentContainer, SdrObject& rCandidate, bool bVisible, bool bOnClick, double fDuration) +{ + Reference< XMultiServiceFactory > xMsf(::comphelper::getProcessServiceFactory()); + + // create par container node + Reference< XAnimationNode > xOuterSeqTimeContainer(xMsf->createInstance("com.sun.star.animations.ParallelTimeContainer"), UNO_QUERY_THROW); + + // set begin + xOuterSeqTimeContainer->setBegin(Any(0.0)); + + // set fill + xOuterSeqTimeContainer->setFill(AnimationFill::HOLD); + + // set named values + Sequence< NamedValue > aUserDataSequence{ + { /* Name */ "node-type", + /* Value */ Any(bOnClick ? EffectNodeType::ON_CLICK : EffectNodeType::AFTER_PREVIOUS) } + }; + + xOuterSeqTimeContainer->setUserData(aUserDataSequence); + + // create animate set to change visibility for rCandidate + Reference< XAnimationNode > xAnimateSetForLast(xMsf->createInstance("com.sun.star.animations.AnimateSet"), UNO_QUERY_THROW); + + // set begin + xAnimateSetForLast->setBegin(Any(0.0)); + + // set duration + xAnimateSetForLast->setDuration(Any(fDuration)); + + // set fill + xAnimateSetForLast->setFill(AnimationFill::HOLD); + + // set target + Reference< XAnimate > xAnimate(xAnimateSetForLast, UNO_QUERY); + Reference< XShape > xTargetShape(rCandidate.getUnoShape(), UNO_QUERY); + xAnimate->setTarget(Any(xTargetShape)); + + // set AttributeName + xAnimate->setAttributeName("Visibility"); + + // set attribute value + xAnimate->setTo(Any(bVisible)); + + // ad set node to par node + Reference< XTimeContainer > xParentContainer(xOuterSeqTimeContainer, UNO_QUERY_THROW); + xParentContainer->appendChild(xAnimateSetForLast); + + // add node + rxParentContainer->appendChild(xOuterSeqTimeContainer); +} + +// #i42894# older native formats supported animated group objects, that means all members of the group +// were shown animated by showing one after the other. This is no longer supported, but the following +// fallback will create the needed SMIL animation stuff. Unfortunately the members of the group +// have to be moved directly to the page, else the (explained to be generic, thus I expected this to +// work) animations will not work in slideshow +void EffectMigration::CreateAnimatedGroup(SdrObjGroup const & rGroupObj, SdPage& rPage) +{ + // aw080 will give a vector immediately + SdrObjListIter aIter(rGroupObj); + + if(!aIter.Count()) + return; + + std::shared_ptr< sd::MainSequence > pMainSequence(rPage.getMainSequence()); + + if(!pMainSequence) + return; + + std::vector< SdrObject* > aObjects; + aObjects.reserve(aIter.Count()); + + while(aIter.IsMore()) + { + // do move to page rough with old/current stuff, will be different in aw080 anyways + SdrObject* pCandidate = aIter.Next(); + rGroupObj.GetSubList()->NbcRemoveObject(pCandidate->GetOrdNum()); + rPage.NbcInsertObject(pCandidate); + aObjects.push_back(pCandidate); + } + + // create main node + Reference< XMultiServiceFactory > xMsf(::comphelper::getProcessServiceFactory()); + Reference< XAnimationNode > xOuterSeqTimeContainer(xMsf->createInstance("com.sun.star.animations.ParallelTimeContainer"), UNO_QUERY_THROW); + + // set begin + xOuterSeqTimeContainer->setBegin(Any(0.0)); + + // prepare parent container + Reference< XTimeContainer > xParentContainer(xOuterSeqTimeContainer, UNO_QUERY_THROW); + + // prepare loop over objects + SdrObject* pNext = nullptr; + const double fDurationShow(0.2); + const double fDurationHide(0.001); + + for(size_t a(0); a < aObjects.size(); a++) + { + SdrObject* pLast = pNext; + pNext = aObjects[a]; + + // create node + if(pLast) + { + createVisibilityOnOffNode(xParentContainer, *pLast, false, false, fDurationHide); + } + + if(pNext) + { + createVisibilityOnOffNode(xParentContainer, *pNext, true, !a, fDurationShow); + } + } + + // create end node + if(pNext) + { + createVisibilityOnOffNode(xParentContainer, *pNext, false, false, fDurationHide); + } + + // add to main sequence and rebuild + pMainSequence->createEffects(xOuterSeqTimeContainer); + pMainSequence->rebuild(); +} + +void EffectMigration::DocumentLoaded(SdDrawDocument & rDoc) +{ + if (DocumentType::Draw == rDoc.GetDocumentType()) + return; // no animations in Draw + for (sal_uInt16 n = 0; n < rDoc.GetSdPageCount(PageKind::Standard); ++n) + { + SdPage *const pPage = rDoc.GetSdPage(n, PageKind::Standard); + if (pPage->hasAnimationNode()) + { + // this will force the equivalent of the MainSequence::onTimerHdl + // so that the animations are present in export-able representation + // *before* the import is finished + pPage->getMainSequence()->getRootNode(); + } + } + for (sal_uInt16 n = 0; n < rDoc.GetMasterSdPageCount(PageKind::Standard); ++n) + { + SdPage *const pPage = rDoc.GetMasterSdPage(n, PageKind::Standard); + if (pPage->hasAnimationNode()) + { + pPage->getMainSequence()->getRootNode(); + } + } +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/core/PageListWatcher.cxx b/sd/source/core/PageListWatcher.cxx new file mode 100644 index 000000000..c3d8846fc --- /dev/null +++ b/sd/source/core/PageListWatcher.cxx @@ -0,0 +1,217 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include "PageListWatcher.hxx" + +#include <sdpage.hxx> +#include <tools/debug.hxx> +#include <svx/svdmodel.hxx> +#include <sal/log.hxx> + +void ImpPageListWatcher::ImpRecreateSortedPageListOnDemand() +{ + // clear vectors + maPageVectorStandard.clear(); + maPageVectorNotes.clear(); + mpHandoutPage = nullptr; + + // build up vectors again + const sal_uInt32 nPageCount(ImpGetPageCount()); + + for(sal_uInt32 a(0); a < nPageCount; a++) + { + SdPage* pCandidate = ImpGetPage(a); + DBG_ASSERT(pCandidate, "ImpPageListWatcher::ImpRecreateSortedPageListOnDemand: Invalid PageList in Model (!)"); + + switch(pCandidate->GetPageKind()) + { + case PageKind::Standard: + { + maPageVectorStandard.push_back(pCandidate); + break; + } + case PageKind::Notes: + { + maPageVectorNotes.push_back(pCandidate); + break; + } + case PageKind::Handout: + { + DBG_ASSERT(!mpHandoutPage, "ImpPageListWatcher::ImpRecreateSortedPageListOnDemand: Two Handout pages in PageList of Model (!)"); + mpHandoutPage = pCandidate; + break; + } + } + } + + // set to valid + mbPageListValid = true; +} + +ImpPageListWatcher::ImpPageListWatcher(const SdrModel& rModel) + : mrModel(rModel) + , mpHandoutPage(nullptr) + , mbPageListValid(false) +{ +} + +ImpPageListWatcher::~ImpPageListWatcher() +{ +} + +SdPage* ImpPageListWatcher::GetSdPage(PageKind ePgKind, sal_uInt32 nPgNum) +{ + SdPage* pRetval(nullptr); + + if(!mbPageListValid) + { + ImpRecreateSortedPageListOnDemand(); + } + + switch(ePgKind) + { + case PageKind::Standard: + { + if( nPgNum < static_cast<sal_uInt32>(maPageVectorStandard.size()) ) + pRetval = maPageVectorStandard[nPgNum]; + else + { + SAL_INFO( "sd.core", + "ImpPageListWatcher::GetSdPage(PageKind::Standard): page number " << nPgNum << " >= " << maPageVectorStandard.size() ); + } + break; + } + case PageKind::Notes: + { + if( nPgNum < static_cast<sal_uInt32>(maPageVectorNotes.size()) ) + pRetval = maPageVectorNotes[nPgNum]; + else + { + SAL_INFO( "sd.core", + "ImpPageListWatcher::GetSdPage(PageKind::Notes): page number " << nPgNum << " >= " << maPageVectorNotes.size() ); + } + break; + } + case PageKind::Handout: + { + // #11420# for models used to transfer drawing shapes via clipboard it's ok to not have a handout page + DBG_ASSERT(nPgNum == 0, "ImpPageListWatcher::GetSdPage: access to non existing handout page (!)"); + if (nPgNum == 0) + pRetval = mpHandoutPage; + else + { + DBG_ASSERT(nPgNum == 0, + "ImpPageListWatcher::GetSdPage: access to non existing handout page (!)"); + } + break; + } + } + + return pRetval; +} + +sal_uInt32 ImpPageListWatcher::GetSdPageCount(PageKind ePgKind) +{ + sal_uInt32 nRetval(0); + + if(!mbPageListValid) + { + ImpRecreateSortedPageListOnDemand(); + } + + switch(ePgKind) + { + case PageKind::Standard: + { + nRetval = maPageVectorStandard.size(); + break; + } + case PageKind::Notes: + { + nRetval = maPageVectorNotes.size(); + break; + } + case PageKind::Handout: + { + if(mpHandoutPage) + { + nRetval = 1; + } + + break; + } + } + + return nRetval; +} + +sal_uInt32 ImpPageListWatcher::GetVisibleSdPageCount() const +{ + sal_uInt32 nVisiblePageCount = 0; + + // build up vectors again + const sal_uInt32 nPageCount(ImpGetPageCount()); + + for(sal_uInt32 a(0); a < nPageCount; a++) + { + SdPage* pCandidate = ImpGetPage(a); + if ((pCandidate->GetPageKind() == PageKind::Standard)&&(!pCandidate->IsExcluded())) nVisiblePageCount++; + } + return nVisiblePageCount; +} + +sal_uInt32 ImpDrawPageListWatcher::ImpGetPageCount() const +{ + return static_cast<sal_uInt32>(mrModel.GetPageCount()); +} + +SdPage* ImpDrawPageListWatcher::ImpGetPage(sal_uInt32 nIndex) const +{ + return const_cast<SdPage*>(static_cast<const SdPage*>(mrModel.GetPage(static_cast<sal_uInt16>(nIndex)))); +} + +ImpDrawPageListWatcher::ImpDrawPageListWatcher(const SdrModel& rModel) +: ImpPageListWatcher(rModel) +{ +} + +ImpDrawPageListWatcher::~ImpDrawPageListWatcher() +{ +} + +sal_uInt32 ImpMasterPageListWatcher::ImpGetPageCount() const +{ + return static_cast<sal_uInt32>(mrModel.GetMasterPageCount()); +} + +SdPage* ImpMasterPageListWatcher::ImpGetPage(sal_uInt32 nIndex) const +{ + return const_cast<SdPage*>(static_cast<const SdPage*>(mrModel.GetMasterPage(static_cast<sal_uInt16>(nIndex)))); +} + +ImpMasterPageListWatcher::ImpMasterPageListWatcher(const SdrModel& rModel) +: ImpPageListWatcher(rModel) +{ +} + +ImpMasterPageListWatcher::~ImpMasterPageListWatcher() +{ +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/core/PageListWatcher.hxx b/sd/source/core/PageListWatcher.hxx new file mode 100644 index 000000000..252d18615 --- /dev/null +++ b/sd/source/core/PageListWatcher.hxx @@ -0,0 +1,87 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#pragma once + +#include <pres.hxx> +#include <sal/types.h> +#include <vector> + +class SdPage; +class SdrModel; + +/** Maintain a map of page indices to page objects for faster access that + remains valid during deletions and insertions of pages (#109538#). +*/ +class ImpPageListWatcher +{ +protected: + // typedefs for a vector of SdPages + typedef ::std::vector< SdPage* > SdPageVector; + + const SdrModel& mrModel; + + SdPageVector maPageVectorStandard; + SdPageVector maPageVectorNotes; + SdPage* mpHandoutPage; + + bool mbPageListValid; + + void ImpRecreateSortedPageListOnDemand(); + virtual sal_uInt32 ImpGetPageCount() const = 0; + + /** Return the page with the given index. + @param nIndex + When given an invalid index then NULL is returned. + */ + virtual SdPage* ImpGetPage (sal_uInt32 nIndex) const = 0; + +public: + explicit ImpPageListWatcher(const SdrModel& rModel); + virtual ~ImpPageListWatcher(); + + void Invalidate() { mbPageListValid = false; } + SdPage* GetSdPage(PageKind ePgKind, sal_uInt32 nPgNum); + sal_uInt32 GetSdPageCount(PageKind ePgKind); + sal_uInt32 GetVisibleSdPageCount() const; +}; + +class ImpDrawPageListWatcher : public ImpPageListWatcher +{ +protected: + virtual sal_uInt32 ImpGetPageCount() const override; + virtual SdPage* ImpGetPage(sal_uInt32 nIndex) const override; + +public: + explicit ImpDrawPageListWatcher(const SdrModel& rModel); + virtual ~ImpDrawPageListWatcher() override; +}; + +class ImpMasterPageListWatcher : public ImpPageListWatcher +{ +protected: + virtual sal_uInt32 ImpGetPageCount() const override; + virtual SdPage* ImpGetPage(sal_uInt32 nIndex) const override; + +public: + explicit ImpMasterPageListWatcher(const SdrModel& rModel); + virtual ~ImpMasterPageListWatcher() override; +}; + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/core/TransitionPreset.cxx b/sd/source/core/TransitionPreset.cxx new file mode 100644 index 000000000..8d3a9d1d1 --- /dev/null +++ b/sd/source/core/TransitionPreset.cxx @@ -0,0 +1,385 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include <set> + +#include <com/sun/star/animations/XTransitionFilter.hpp> +#include <com/sun/star/container/XEnumerationAccess.hpp> +#include <com/sun/star/container/XNameAccess.hpp> +#include <com/sun/star/configuration/theDefaultProvider.hpp> +#include <com/sun/star/beans/NamedValue.hpp> +#include <com/sun/star/animations/AnimationNodeType.hpp> +#include <com/sun/star/animations/TransitionType.hpp> +#include <com/sun/star/animations/TransitionSubType.hpp> +#include <unotools/configmgr.hxx> +#include <comphelper/getexpandeduri.hxx> +#include <comphelper/processfactory.hxx> +#include <comphelper/propertysequence.hxx> +#include <comphelper/lok.hxx> +#include <unotools/syslocaleoptions.hxx> +#include <officecfg/Office/UI/Effects.hxx> +#include <tools/diagnose_ex.h> + +#include <sal/log.hxx> +#include <vcl/svapp.hxx> + +#include <CustomAnimationPreset.hxx> +#include <TransitionPreset.hxx> + +using namespace ::com::sun::star; +using namespace ::com::sun::star::animations; + +using ::com::sun::star::uno::UNO_QUERY_THROW; +using ::com::sun::star::uno::Any; +using ::com::sun::star::uno::Sequence; +using ::com::sun::star::uno::Reference; +using ::com::sun::star::uno::Exception; +using ::com::sun::star::lang::XMultiServiceFactory; +using ::com::sun::star::container::XEnumerationAccess; +using ::com::sun::star::container::XEnumeration; +using ::com::sun::star::beans::NamedValue; + +namespace sd { + +TransitionPreset::TransitionPreset( const css::uno::Reference< css::animations::XAnimationNode >& xNode ) +{ + // first locate preset id + const Sequence< NamedValue > aUserData( xNode->getUserData() ); + const NamedValue* pProp = std::find_if(aUserData.begin(), aUserData.end(), + [](const NamedValue& rProp) { return rProp.Name == "preset-id"; }); + if (pProp != aUserData.end()) + pProp->Value >>= maPresetId; + + // second, locate transition filter element + Reference< XEnumerationAccess > xEnumerationAccess( xNode, UNO_QUERY_THROW ); + Reference< XEnumeration > xEnumeration( xEnumerationAccess->createEnumeration(), css::uno::UNO_SET_THROW ); + Reference< XTransitionFilter > xTransition( xEnumeration->nextElement(), UNO_QUERY_THROW ); + + mnTransition = xTransition->getTransition(); + mnSubtype = xTransition->getSubtype(); + mbDirection = xTransition->getDirection(); + mnFadeColor = xTransition->getFadeColor(); +} + +bool TransitionPreset::importTransitionsFile( TransitionPresetList& rList, + Reference< XMultiServiceFactory > const & xServiceFactory, + const OUString& aURL ) +{ + SAL_INFO("sd.transitions", "Importing " << aURL); + + Reference< container::XNameAccess > xTransitionSets( officecfg::Office::UI::Effects::UserInterface::TransitionSets::get() ); + Reference< container::XNameAccess > xTransitionGroups( officecfg::Office::UI::Effects::UserInterface::TransitionGroups::get() ); + Reference< container::XNameAccess > xTransitionVariants( officecfg::Office::UI::Effects::UserInterface::TransitionVariants::get() ); + Reference< container::XNameAccess > xTransitions( officecfg::Office::UI::Effects::UserInterface::Transitions::get() ); + + // import transition presets + Reference< XAnimationNode > xAnimationNode; + + const std::set<sal_Int16> LOKSupportedTransitionTypes = { + TransitionType::BARWIPE, + TransitionType::BOXWIPE, + TransitionType::FOURBOXWIPE, + TransitionType::ELLIPSEWIPE, + TransitionType::CLOCKWIPE, + TransitionType::PINWHEELWIPE, + TransitionType::PUSHWIPE, + TransitionType::SLIDEWIPE, + TransitionType::FADE, + TransitionType::RANDOMBARWIPE, + TransitionType::CHECKERBOARDWIPE, + TransitionType::DISSOLVE, + TransitionType::SNAKEWIPE, + TransitionType::PARALLELSNAKESWIPE, + TransitionType::IRISWIPE, + TransitionType::BARNDOORWIPE, + TransitionType::VEEWIPE, + TransitionType::ZIGZAGWIPE, + TransitionType::BARNZIGZAGWIPE, + TransitionType::FANWIPE, + TransitionType::SINGLESWEEPWIPE, + TransitionType::WATERFALLWIPE, + TransitionType::SPIRALWIPE, + TransitionType::MISCDIAGONALWIPE, + TransitionType::BOXSNAKESWIPE + }; + + const std::set<sal_Int16> LOKSupportedTransitionSubTypes = { + TransitionSubType::DEFAULT, + TransitionSubType::LEFTTORIGHT, + TransitionSubType::TOPTOBOTTOM, + TransitionSubType::CORNERSIN, + TransitionSubType::CORNERSOUT, + TransitionSubType::VERTICAL, + TransitionSubType::HORIZONTAL, + TransitionSubType::DOWN, + TransitionSubType::CIRCLE, + TransitionSubType::CLOCKWISETWELVE, + TransitionSubType::CLOCKWISETHREE, + TransitionSubType::CLOCKWISESIX, + TransitionSubType::CLOCKWISENINE, + TransitionSubType::TWOBLADEVERTICAL, + TransitionSubType::TWOBLADEHORIZONTAL, + TransitionSubType::FOURBLADE, + TransitionSubType::FROMLEFT, + TransitionSubType::FROMTOP, + TransitionSubType::FROMRIGHT, + TransitionSubType::FROMBOTTOM, + TransitionSubType::CROSSFADE, + TransitionSubType::FADETOCOLOR, + TransitionSubType::FADEFROMCOLOR, + TransitionSubType::FADEOVERCOLOR, + TransitionSubType::THREEBLADE, + TransitionSubType::EIGHTBLADE, + TransitionSubType::ONEBLADE, + TransitionSubType::ACROSS, + TransitionSubType::TOPLEFTVERTICAL, + TransitionSubType::TOPLEFTHORIZONTAL, + TransitionSubType::TOPLEFTDIAGONAL, + TransitionSubType::TOPRIGHTDIAGONAL, + TransitionSubType::BOTTOMRIGHTDIAGONAL, + TransitionSubType::BOTTOMLEFTDIAGONAL, + TransitionSubType::RECTANGLE, + TransitionSubType::DIAMOND, + TransitionSubType::TOPLEFT, + TransitionSubType::TOPRIGHT, + TransitionSubType::BOTTOMRIGHT, + TransitionSubType::BOTTOMLEFT, + TransitionSubType::TOPCENTER, + TransitionSubType::RIGHTCENTER, + TransitionSubType::BOTTOMCENTER, + TransitionSubType::LEFTCENTER, + TransitionSubType::LEFT, + TransitionSubType::UP, + TransitionSubType::RIGHT, + TransitionSubType::DIAGONALBOTTOMLEFT, + TransitionSubType::DIAGONALTOPLEFT, + TransitionSubType::CENTERTOP, + TransitionSubType::CENTERRIGHT, + TransitionSubType::TOP, + TransitionSubType::BOTTOM, + TransitionSubType::CLOCKWISETOP, + TransitionSubType::CLOCKWISERIGHT, + TransitionSubType::CLOCKWISEBOTTOM, + TransitionSubType::CLOCKWISELEFT, + TransitionSubType::CLOCKWISETOPLEFT, + TransitionSubType::COUNTERCLOCKWISEBOTTOMLEFT, + TransitionSubType::CLOCKWISEBOTTOMRIGHT, + TransitionSubType::COUNTERCLOCKWISETOPRIGHT, + TransitionSubType::VERTICALLEFT, + TransitionSubType::VERTICALRIGHT, + TransitionSubType::HORIZONTALLEFT, + TransitionSubType::HORIZONTALRIGHT, + TransitionSubType::TOPLEFTCLOCKWISE, + TransitionSubType::TOPRIGHTCLOCKWISE, + TransitionSubType::BOTTOMRIGHTCLOCKWISE, + TransitionSubType::BOTTOMLEFTCLOCKWISE, + TransitionSubType::TOPLEFTCOUNTERCLOCKWISE, + TransitionSubType::TOPRIGHTCOUNTERCLOCKWISE, + TransitionSubType::BOTTOMRIGHTCOUNTERCLOCKWISE, + TransitionSubType::BOTTOMLEFTCOUNTERCLOCKWISE, + TransitionSubType::DOUBLEBARNDOOR, + TransitionSubType::DOUBLEDIAMOND, + TransitionSubType::VERTICALTOPSAME, + TransitionSubType::VERTICALBOTTOMSAME, + TransitionSubType::VERTICALTOPLEFTOPPOSITE, + TransitionSubType::VERTICALBOTTOMLEFTOPPOSITE, + TransitionSubType::HORIZONTALLEFTSAME, + TransitionSubType::HORIZONTALRIGHTSAME, + TransitionSubType::HORIZONTALTOPLEFTOPPOSITE, + TransitionSubType::HORIZONTALTOPRIGHTOPPOSITE, + TransitionSubType::DIAGONALBOTTOMLEFTOPPOSITE, + TransitionSubType::DIAGONALTOPLEFTOPPOSITE, + TransitionSubType::TWOBOXTOP, + TransitionSubType::TWOBOXBOTTOM, + TransitionSubType::TWOBOXLEFT, + TransitionSubType::TWOBOXRIGHT, + TransitionSubType::FOURBOXVERTICAL, + TransitionSubType::FOURBOXHORIZONTAL + }; + + try { + xAnimationNode = implImportEffects( xServiceFactory, aURL ); + Reference< XEnumerationAccess > xEnumerationAccess( xAnimationNode, UNO_QUERY_THROW ); + Reference< XEnumeration > xEnumeration( xEnumerationAccess->createEnumeration(), css::uno::UNO_SET_THROW ); + + while( xEnumeration->hasMoreElements() ) + { + Reference< XAnimationNode > xChildNode( xEnumeration->nextElement(), UNO_QUERY_THROW ); + if( xChildNode->getType() == AnimationNodeType::PAR ) + { + TransitionPresetPtr pPreset( new TransitionPreset( xChildNode ) ); + + if( comphelper::LibreOfficeKit::isActive() ) + { + sal_Int16 eTransitionType = pPreset->getTransition(); + sal_Int16 eTransitionSubType = pPreset->getSubtype(); + if( LOKSupportedTransitionTypes.find(eTransitionType) == LOKSupportedTransitionTypes.end() + || LOKSupportedTransitionSubTypes.find(eTransitionSubType) == LOKSupportedTransitionSubTypes.end() ) + { + continue; + } + } + + OUString aPresetId( pPreset->getPresetId() ); + + if( !aPresetId.isEmpty() ) + { + Reference< container::XNameAccess > xTransitionNode; + + if (xTransitions->hasByName( aPresetId ) && + (xTransitions->getByName( aPresetId ) >>= xTransitionNode) && + xTransitionNode.is() ) + { + OUString sSet; + OUString sVariant; + + xTransitionNode->getByName( "Set" ) >>= sSet; + xTransitionNode->getByName( "Variant" ) >>= sVariant; + + Reference< container::XNameAccess > xSetNode; + + xTransitionSets->getByName( sSet ) >>= xSetNode; + if( xSetNode.is() ) + { + pPreset->maSetId = sSet; + xSetNode->getByName( "Label" ) >>= sSet; + pPreset->maSetLabel = sSet; + + OUString sGroup; + + xSetNode->getByName( "Group" ) >>= sGroup; + + Reference< container::XNameAccess > xGroupNode; + xTransitionGroups->getByName( sGroup ) >>= xGroupNode; + + if( xGroupNode.is() ) + { + xGroupNode->getByName( "Label" ) >>= sGroup; + if( !sVariant.isEmpty() ) + { + Reference< container::XNameAccess > xVariantNode; + xTransitionVariants->getByName( sVariant ) >>= xVariantNode; + if( xVariantNode.is() ) + { + xVariantNode->getByName( "Label" ) >>= sVariant; + pPreset->maVariantLabel = sVariant; + } + } + + pPreset->maSetLabel = sSet; + SAL_INFO("sd.transitions", aPresetId << ": " << sGroup << "/" << sSet << (sVariant.isEmpty() ? OUString() : OUString("/" + sVariant))); + + rList.push_back( pPreset ); + } + else + SAL_WARN("sd.transitions", "group node " << sGroup << " not found"); + } + else + SAL_WARN("sd.transitions", "set node " << sSet << " not found"); + } + else + SAL_WARN("sd.transitions", "transition node " << aPresetId << " not found"); + } + } + else + { + SAL_WARN("sd.transitions", " malformed xml configuration file " << aURL ); + break; + } + } + } catch( Exception& ) { + return false; + } + + return true; +} + +bool TransitionPreset::importTransitionPresetList( TransitionPresetList& rList ) +{ + if (utl::ConfigManager::IsFuzzing()) + return false; + + bool bRet = false; + + try + { + uno::Reference< uno::XComponentContext > xContext( + comphelper::getProcessComponentContext() ); + Reference< XMultiServiceFactory > xServiceFactory( + xContext->getServiceManager(), UNO_QUERY_THROW ); + + // import ui strings + Reference< XMultiServiceFactory > xConfigProvider = + configuration::theDefaultProvider::get( xContext ); + + // read path to transition effects files from config + uno::Sequence<uno::Any> aArgs(comphelper::InitAnyPropertySequence( + { + {"nodepath", uno::Any(OUString("/org.openoffice.Office.Impress/Misc"))} + })); + Reference<container::XNameAccess> xNameAccess( + xConfigProvider->createInstanceWithArguments( + "com.sun.star.configuration.ConfigurationAccess", + aArgs), + UNO_QUERY_THROW ); + uno::Sequence< OUString > aFiles; + xNameAccess->getByName("TransitionFiles") >>= aFiles; + + for( const auto& rFile : std::as_const(aFiles) ) + { + OUString aURL = comphelper::getExpandedUri(xContext, rFile); + + bRet |= importTransitionsFile( rList, + xServiceFactory, + aURL ); + } + + return bRet; + } + catch( Exception& ) + { + TOOLS_WARN_EXCEPTION( "sd", "sd::TransitionPreset::importResources()" ); + } + + return bRet; +} + +std::map<OUString, TransitionPresetList> sd::TransitionPreset::mPresetsMap; + +const TransitionPresetList& TransitionPreset::getTransitionPresetList() +{ + // Support localization per-view. Currently not useful for Desktop + // but very much critical for LOK. The cache now is per-language. + const OUString aLang = comphelper::LibreOfficeKit::isActive() + ? comphelper::LibreOfficeKit::getLanguageTag().getBcp47() + : SvtSysLocaleOptions().GetLanguageTag().getBcp47(); + + SolarMutexGuard aGuard; + const auto it = mPresetsMap.find(aLang); + if (it != mPresetsMap.end()) + return it->second; + + TransitionPresetList& rList = mPresetsMap[aLang]; + sd::TransitionPreset::importTransitionPresetList(rList); + return rList; +} + +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/core/anminfo.cxx b/sd/source/core/anminfo.cxx new file mode 100644 index 000000000..5f763708c --- /dev/null +++ b/sd/source/core/anminfo.cxx @@ -0,0 +1,128 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include <editeng/flditem.hxx> +#include <editeng/eeitem.hxx> +#include <tools/debug.hxx> + +#include <anminfo.hxx> +#include <glob.hxx> + +using namespace ::com::sun::star; + +SdAnimationInfo::SdAnimationInfo(SdrObject& rObject) + : SdrObjUserData(SdrInventor::StarDrawUserData, SD_ANIMATIONINFO_ID), + mePresObjKind (PresObjKind::NONE), + meEffect (presentation::AnimationEffect_NONE), + meTextEffect (presentation::AnimationEffect_NONE), + meSpeed (presentation::AnimationSpeed_SLOW), + mbActive (true), + mbDimPrevious (false), + mbIsMovie (false), + mbDimHide (false), + mbSoundOn (false), + mbPlayFull (false), + meClickAction (presentation::ClickAction_NONE), + meSecondEffect (presentation::AnimationEffect_NONE), + meSecondSpeed (presentation::AnimationSpeed_SLOW), + mbSecondSoundOn (false), + mbSecondPlayFull (false), + mnVerb (0), + mrObject (rObject) +{ + maBlueScreen = COL_LIGHTMAGENTA; + maDimColor = COL_LIGHTGRAY; +} + +SdAnimationInfo::SdAnimationInfo(const SdAnimationInfo& rAnmInfo, SdrObject& rObject) + : SdrObjUserData (rAnmInfo), + mePresObjKind (PresObjKind::NONE), + meEffect (rAnmInfo.meEffect), + meTextEffect (rAnmInfo.meTextEffect), + meSpeed (rAnmInfo.meSpeed), + mbActive (rAnmInfo.mbActive), + mbDimPrevious (rAnmInfo.mbDimPrevious), + mbIsMovie (rAnmInfo.mbIsMovie), + mbDimHide (rAnmInfo.mbDimHide), + maBlueScreen (rAnmInfo.maBlueScreen), + maDimColor (rAnmInfo.maDimColor), + maSoundFile (rAnmInfo.maSoundFile), + mbSoundOn (rAnmInfo.mbSoundOn), + mbPlayFull (rAnmInfo.mbPlayFull), + meClickAction (rAnmInfo.meClickAction), + meSecondEffect (rAnmInfo.meSecondEffect), + meSecondSpeed (rAnmInfo.meSecondSpeed), + maSecondSoundFile (rAnmInfo.maSecondSoundFile), + mbSecondSoundOn (rAnmInfo.mbSecondSoundOn), + mbSecondPlayFull (rAnmInfo.mbSecondPlayFull), + mnVerb (rAnmInfo.mnVerb), + mrObject (rObject) +{ + // can not be copied + if(meEffect == presentation::AnimationEffect_PATH) + meEffect = presentation::AnimationEffect_NONE; +} + +SdAnimationInfo::~SdAnimationInfo() +{ +} + +std::unique_ptr<SdrObjUserData> SdAnimationInfo::Clone(SdrObject* pObject) const +{ + DBG_ASSERT( pObject, "SdAnimationInfo::Clone(), pObject must not be null!" ); + if( pObject == nullptr ) + pObject = &mrObject; + + return std::unique_ptr<SdrObjUserData>(new SdAnimationInfo(*this, *pObject )); +} + +void SdAnimationInfo::SetBookmark( const OUString& rBookmark ) +{ + if( meClickAction == css::presentation::ClickAction_BOOKMARK ) + { + OUString sURL = "#" + rBookmark; + SvxFieldItem aURLItem( SvxURLField( sURL, sURL ), EE_FEATURE_FIELD ); + mrObject.SetMergedItem( aURLItem ); + } + else + { + SvxFieldItem aURLItem( SvxURLField( rBookmark, rBookmark ), EE_FEATURE_FIELD ); + mrObject.SetMergedItem( aURLItem ); + } +} + +OUString SdAnimationInfo::GetBookmark() const +{ + OUString sBookmark; + + const SvxFieldItem* pFldItem = &mrObject.GetMergedItem( EE_FEATURE_FIELD ); + if( pFldItem ) + { + SvxURLField* pURLField = const_cast< SvxURLField* >( dynamic_cast<const SvxURLField*>( pFldItem->GetField() ) ); + if( pURLField ) + sBookmark = pURLField->GetURL(); + } + + if( (meClickAction == css::presentation::ClickAction_BOOKMARK) && sBookmark.startsWith("#") ) + sBookmark = sBookmark.copy( 1 ); + + return sBookmark; +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/core/annotations/Annotation.cxx b/sd/source/core/annotations/Annotation.cxx new file mode 100644 index 000000000..991412f06 --- /dev/null +++ b/sd/source/core/annotations/Annotation.cxx @@ -0,0 +1,480 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include <sal/config.h> + +#include <Annotation.hxx> +#include <drawdoc.hxx> + +#include <com/sun/star/drawing/XDrawPage.hpp> + +#include <comphelper/processfactory.hxx> +#include <comphelper/lok.hxx> + +#include <unotools/datetime.hxx> + +#include <sfx2/viewsh.hxx> +#include <svx/svdundo.hxx> + +#include <LibreOfficeKit/LibreOfficeKitEnums.h> + +#include <notifydocumentevent.hxx> + +#include <tools/json_writer.hxx> + +using namespace css; + +namespace com::sun::star::uno { class XComponentContext; } + +namespace sd { + +namespace { + +class UndoInsertOrRemoveAnnotation : public SdrUndoAction +{ +public: + UndoInsertOrRemoveAnnotation( Annotation& rAnnotation, bool bInsert ); + + virtual void Undo() override; + virtual void Redo() override; + +protected: + rtl::Reference< Annotation > mxAnnotation; + bool mbInsert; + int mnIndex; +}; + +struct AnnotationData +{ + geometry::RealPoint2D m_Position; + geometry::RealSize2D m_Size; + OUString m_Author; + OUString m_Initials; + util::DateTime m_DateTime; + OUString m_Text; + + void get( const rtl::Reference< Annotation >& xAnnotation ) + { + m_Position = xAnnotation->getPosition(); + m_Size = xAnnotation->getSize(); + m_Author = xAnnotation->getAuthor(); + m_Initials = xAnnotation->getInitials(); + m_DateTime = xAnnotation->getDateTime(); + uno::Reference<text::XText> xText(xAnnotation->getTextRange()); + m_Text = xText->getString(); + } + + void set( const rtl::Reference< Annotation >& xAnnotation ) + { + xAnnotation->setPosition(m_Position); + xAnnotation->setSize(m_Size); + xAnnotation->setAuthor(m_Author); + xAnnotation->setInitials(m_Initials); + xAnnotation->setDateTime(m_DateTime); + uno::Reference<text::XText> xText(xAnnotation->getTextRange()); + xText->setString(m_Text); + } +}; + +class UndoAnnotation : public SdrUndoAction +{ +public: + explicit UndoAnnotation( Annotation& rAnnotation ); + + virtual void Undo() override; + virtual void Redo() override; + +protected: + rtl::Reference< Annotation > mxAnnotation; + AnnotationData maUndoData; + AnnotationData maRedoData; +}; + +} + +void createAnnotation(uno::Reference<office::XAnnotation>& xAnnotation, SdPage* pPage ) +{ + xAnnotation.set( + new Annotation(comphelper::getProcessComponentContext(), pPage)); + pPage->addAnnotation(xAnnotation, -1); +} + +sal_uInt32 Annotation::m_nLastId = 1; + +Annotation::Annotation( const uno::Reference<uno::XComponentContext>& context, SdPage* pPage ) +: ::cppu::WeakComponentImplHelper<office::XAnnotation>(m_aMutex) +, ::cppu::PropertySetMixin<office::XAnnotation>(context, IMPLEMENTS_PROPERTY_SET, uno::Sequence<OUString>()) +, m_nId( m_nLastId++ ) +, mpPage( pPage ) +{ +} + +// override WeakComponentImplHelperBase::disposing() +// This function is called upon disposing the component, +// if your component needs special work when it becomes +// disposed, do it here. +void SAL_CALL Annotation::disposing() +{ + mpPage = nullptr; + if( m_TextRange.is() ) + { + m_TextRange->dispose(); + m_TextRange.clear(); + } +} + +uno::Any Annotation::queryInterface(css::uno::Type const & type) +{ + return ::cppu::WeakComponentImplHelper<office::XAnnotation>::queryInterface(type); +} + +// com.sun.star.beans.XPropertySet: +uno::Reference<beans::XPropertySetInfo> SAL_CALL Annotation::getPropertySetInfo() +{ + return ::cppu::PropertySetMixin<office::XAnnotation>::getPropertySetInfo(); +} + +void SAL_CALL Annotation::setPropertyValue(const OUString & aPropertyName, const uno::Any & aValue) +{ + ::cppu::PropertySetMixin<office::XAnnotation>::setPropertyValue(aPropertyName, aValue); +} + +uno::Any SAL_CALL Annotation::getPropertyValue(const OUString & aPropertyName) +{ + return ::cppu::PropertySetMixin<office::XAnnotation>::getPropertyValue(aPropertyName); +} + +void SAL_CALL Annotation::addPropertyChangeListener(const OUString & aPropertyName, const uno::Reference<beans::XPropertyChangeListener> & xListener) +{ + ::cppu::PropertySetMixin<office::XAnnotation>::addPropertyChangeListener(aPropertyName, xListener); +} + +void SAL_CALL Annotation::removePropertyChangeListener(const OUString & aPropertyName, const uno::Reference<beans::XPropertyChangeListener> & xListener) +{ + ::cppu::PropertySetMixin<office::XAnnotation>::removePropertyChangeListener(aPropertyName, xListener); +} + +void SAL_CALL Annotation::addVetoableChangeListener(const OUString & aPropertyName, const uno::Reference<beans::XVetoableChangeListener> & xListener) +{ + ::cppu::PropertySetMixin<office::XAnnotation>::addVetoableChangeListener(aPropertyName, xListener); +} + +void SAL_CALL Annotation::removeVetoableChangeListener(const OUString & aPropertyName, const uno::Reference<beans::XVetoableChangeListener> & xListener) +{ + ::cppu::PropertySetMixin<office::XAnnotation>::removeVetoableChangeListener(aPropertyName, xListener); +} + +uno::Any SAL_CALL Annotation::getAnchor() +{ + osl::MutexGuard g(m_aMutex); + uno::Any aRet; + if( mpPage ) + { + uno::Reference<drawing::XDrawPage> xPage( mpPage->getUnoPage(), uno::UNO_QUERY ); + aRet <<= xPage; + } + return aRet; +} + +// css::office::XAnnotation: +geometry::RealPoint2D SAL_CALL Annotation::getPosition() +{ + osl::MutexGuard g(m_aMutex); + return m_Position; +} + +void SAL_CALL Annotation::setPosition(const geometry::RealPoint2D & the_value) +{ + prepareSet("Position", uno::Any(), uno::Any(), nullptr); + { + osl::MutexGuard g(m_aMutex); + createChangeUndo(); + m_Position = the_value; + } +} + +// css::office::XAnnotation: +geometry::RealSize2D SAL_CALL Annotation::getSize() +{ + osl::MutexGuard g(m_aMutex); + return m_Size; +} + +void SAL_CALL Annotation::setSize(const geometry::RealSize2D & the_value) +{ + prepareSet("Size", uno::Any(), uno::Any(), nullptr); + { + osl::MutexGuard g(m_aMutex); + createChangeUndo(); + m_Size = the_value; + } +} + +OUString SAL_CALL Annotation::getAuthor() +{ + osl::MutexGuard g(m_aMutex); + return m_Author; +} + +void SAL_CALL Annotation::setAuthor(const OUString & the_value) +{ + prepareSet("Author", uno::Any(), uno::Any(), nullptr); + { + osl::MutexGuard g(m_aMutex); + createChangeUndo(); + m_Author = the_value; + } +} + +OUString SAL_CALL Annotation::getInitials() +{ + osl::MutexGuard g(m_aMutex); + return m_Initials; +} + +void SAL_CALL Annotation::setInitials(const OUString & the_value) +{ + prepareSet("Initials", uno::Any(), uno::Any(), nullptr); + { + osl::MutexGuard g(m_aMutex); + createChangeUndo(); + m_Initials = the_value; + } +} + +util::DateTime SAL_CALL Annotation::getDateTime() +{ + osl::MutexGuard g(m_aMutex); + return m_DateTime; +} + +void SAL_CALL Annotation::setDateTime(const util::DateTime & the_value) +{ + prepareSet("DateTime", uno::Any(), uno::Any(), nullptr); + { + osl::MutexGuard g(m_aMutex); + createChangeUndo(); + m_DateTime = the_value; + } +} + +void Annotation::createChangeUndo() +{ + SdrModel* pModel = GetModel(); // TTTT should use reference + if( pModel && pModel->IsUndoEnabled() ) + pModel->AddUndo( std::make_unique<UndoAnnotation>( *this ) ); + + if( pModel ) + { + pModel->SetChanged(); + uno::Reference< XInterface > xSource( static_cast<uno::XWeak*>( this ) ); + NotifyDocumentEvent( + static_cast< SdDrawDocument& >( *pModel ), + "OnAnnotationChanged" , + xSource ); + } +} + +uno::Reference<text::XText> SAL_CALL Annotation::getTextRange() +{ + osl::MutexGuard g(m_aMutex); + if( !m_TextRange.is() && (mpPage != nullptr) ) + { + m_TextRange = TextApiObject::create( static_cast< SdDrawDocument* >( &mpPage->getSdrModelFromSdrPage() ) ); + } + return m_TextRange; +} + +std::unique_ptr<SdrUndoAction> CreateUndoInsertOrRemoveAnnotation( const uno::Reference<office::XAnnotation>& xAnnotation, bool bInsert ) +{ + Annotation* pAnnotation = dynamic_cast< Annotation* >( xAnnotation.get() ); + if( pAnnotation ) + { + return std::make_unique< UndoInsertOrRemoveAnnotation >( *pAnnotation, bInsert ); + } + else + { + return nullptr; + } +} + +void CreateChangeUndo(const uno::Reference<office::XAnnotation>& xAnnotation) +{ + Annotation* pAnnotation = dynamic_cast<Annotation*>(xAnnotation.get()); + if (pAnnotation) + pAnnotation->createChangeUndo(); +} + +sal_uInt32 getAnnotationId(const uno::Reference<office::XAnnotation>& xAnnotation) +{ + Annotation* pAnnotation = dynamic_cast<Annotation*>(xAnnotation.get()); + sal_uInt32 nId = 0; + if (pAnnotation) + nId = pAnnotation->GetId(); + return nId; +} + +const SdPage* getAnnotationPage(const uno::Reference<office::XAnnotation>& xAnnotation) +{ + Annotation* pAnnotation = dynamic_cast<Annotation*>(xAnnotation.get()); + if (pAnnotation) + return pAnnotation->GetPage(); + return nullptr; +} + +namespace +{ +std::string lcl_LOKGetCommentPayload(CommentNotificationType nType, uno::Reference<office::XAnnotation> const & rxAnnotation) +{ + ::tools::JsonWriter aJsonWriter; + { + auto aCommentNode = aJsonWriter.startNode("comment"); + + aJsonWriter.put("action", (nType == CommentNotificationType::Add ? "Add" : + (nType == CommentNotificationType::Remove ? "Remove" : + (nType == CommentNotificationType::Modify ? "Modify" : "???")))); + aJsonWriter.put("id", sd::getAnnotationId(rxAnnotation)); + + if (nType != CommentNotificationType::Remove && rxAnnotation.is()) + { + aJsonWriter.put("id", sd::getAnnotationId(rxAnnotation)); + aJsonWriter.put("author", rxAnnotation->getAuthor()); + aJsonWriter.put("dateTime", utl::toISO8601(rxAnnotation->getDateTime())); + uno::Reference<text::XText> xText(rxAnnotation->getTextRange()); + aJsonWriter.put("text", xText->getString()); + const SdPage* pPage = sd::getAnnotationPage(rxAnnotation); + aJsonWriter.put("parthash", pPage ? OString::number(pPage->GetHashCode()) : OString()); + geometry::RealPoint2D const & rPoint = rxAnnotation->getPosition(); + geometry::RealSize2D const & rSize = rxAnnotation->getSize(); + ::tools::Rectangle aRectangle(Point(rPoint.X * 100.0, rPoint.Y * 100.0), Size(rSize.Width * 100.0, rSize.Height * 100.0)); + aRectangle = OutputDevice::LogicToLogic(aRectangle, MapMode(MapUnit::Map100thMM), MapMode(MapUnit::MapTwip)); + OString sRectangle = aRectangle.toString(); + aJsonWriter.put("rectangle", sRectangle.getStr()); + } + } + return aJsonWriter.extractData(); +} +} // anonymous ns + +void LOKCommentNotify(CommentNotificationType nType, const SfxViewShell* pViewShell, uno::Reference<office::XAnnotation> const & rxAnnotation) +{ + // callbacks only if tiled annotations are explicitly turned off by LOK client + if (!comphelper::LibreOfficeKit::isActive() || comphelper::LibreOfficeKit::isTiledAnnotations()) + return ; + + std::string aPayload = lcl_LOKGetCommentPayload(nType, rxAnnotation); + pViewShell->libreOfficeKitViewCallback(LOK_CALLBACK_COMMENT, aPayload.c_str()); +} + +void LOKCommentNotifyAll(CommentNotificationType nType, uno::Reference<office::XAnnotation> const & rxAnnotation) +{ + // callbacks only if tiled annotations are explicitly turned off by LOK client + if (!comphelper::LibreOfficeKit::isActive() || comphelper::LibreOfficeKit::isTiledAnnotations()) + return ; + + std::string aPayload = lcl_LOKGetCommentPayload(nType, rxAnnotation); + + const SfxViewShell* pViewShell = SfxViewShell::GetFirst(); + while (pViewShell) + { + pViewShell->libreOfficeKitViewCallback(LOK_CALLBACK_COMMENT, aPayload.c_str()); + pViewShell = SfxViewShell::GetNext(*pViewShell); + } +} + +UndoInsertOrRemoveAnnotation::UndoInsertOrRemoveAnnotation( Annotation& rAnnotation, bool bInsert ) +: SdrUndoAction( *rAnnotation.GetModel() ) +, mxAnnotation( &rAnnotation ) +, mbInsert( bInsert ) +, mnIndex( 0 ) +{ + SdPage* pPage = rAnnotation.GetPage(); + if( pPage ) + { + uno::Reference<office::XAnnotation> xAnnotation( &rAnnotation ); + + const AnnotationVector& rVec = pPage->getAnnotations(); + auto iter = std::find(rVec.begin(), rVec.end(), xAnnotation); + mnIndex += std::distance(rVec.begin(), iter); + } +} + +void UndoInsertOrRemoveAnnotation::Undo() +{ + SdPage* pPage = mxAnnotation->GetPage(); + SdrModel* pModel = mxAnnotation->GetModel(); + if( !(pPage && pModel) ) + return; + + uno::Reference<office::XAnnotation> xAnnotation( mxAnnotation ); + if( mbInsert ) + { + pPage->removeAnnotation( xAnnotation ); + } + else + { + pPage->addAnnotation( xAnnotation, mnIndex ); + LOKCommentNotifyAll( CommentNotificationType::Add, xAnnotation ); + } +} + +void UndoInsertOrRemoveAnnotation::Redo() +{ + SdPage* pPage = mxAnnotation->GetPage(); + SdrModel* pModel = mxAnnotation->GetModel(); + if( !(pPage && pModel) ) + return; + + uno::Reference<office::XAnnotation> xAnnotation( mxAnnotation ); + + if( mbInsert ) + { + pPage->addAnnotation( xAnnotation, mnIndex ); + LOKCommentNotifyAll( CommentNotificationType::Add, xAnnotation ); + } + else + { + pPage->removeAnnotation( xAnnotation ); + } +} + +UndoAnnotation::UndoAnnotation( Annotation& rAnnotation ) +: SdrUndoAction( *rAnnotation.GetModel() ) +, mxAnnotation( &rAnnotation ) +{ + maUndoData.get( mxAnnotation ); +} + +void UndoAnnotation::Undo() +{ + maRedoData.get( mxAnnotation ); + maUndoData.set( mxAnnotation ); + LOKCommentNotifyAll( CommentNotificationType::Modify, mxAnnotation ); +} + +void UndoAnnotation::Redo() +{ + maUndoData.get( mxAnnotation ); + maRedoData.set( mxAnnotation ); + LOKCommentNotifyAll( CommentNotificationType::Modify, mxAnnotation ); +} + +} // namespace sd + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/core/annotations/AnnotationEnumeration.cxx b/sd/source/core/annotations/AnnotationEnumeration.cxx new file mode 100644 index 000000000..5fae2422b --- /dev/null +++ b/sd/source/core/annotations/AnnotationEnumeration.cxx @@ -0,0 +1,85 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include <sal/config.h> + +#include <cppuhelper/implbase.hxx> +#include <com/sun/star/container/NoSuchElementException.hpp> +#include <com/sun/star/office/XAnnotationEnumeration.hpp> + +#include <AnnotationEnumeration.hxx> +#include <sdpage.hxx> + +using namespace ::com::sun::star::uno; +using namespace ::com::sun::star::office; +using namespace ::com::sun::star::container; +using namespace ::com::sun::star::lang; + +namespace sd { + +namespace { + +class AnnotationEnumeration: public ::cppu::WeakImplHelper< css::office::XAnnotationEnumeration > +{ +public: + explicit AnnotationEnumeration( AnnotationVector&& rAnnotations ); + AnnotationEnumeration(const AnnotationEnumeration&) = delete; + AnnotationEnumeration& operator=(const AnnotationEnumeration&) = delete; + + // css::office::XAnnotationEnumeration: + virtual sal_Bool SAL_CALL hasMoreElements() override; + virtual css::uno::Reference< css::office::XAnnotation > SAL_CALL nextElement() override; + +private: + // destructor is private and will be called indirectly by the release call virtual ~AnnotationEnumeration() {} + + AnnotationVector maAnnotations; + AnnotationVector::iterator maIter; +}; + +} + +Reference< XAnnotationEnumeration > createAnnotationEnumeration( sd::AnnotationVector&& rAnnotations ) +{ + return new AnnotationEnumeration( std::move(rAnnotations) ); +} + +AnnotationEnumeration::AnnotationEnumeration( AnnotationVector&& rAnnotations ) +: maAnnotations(std::move(rAnnotations)) +{ + maIter = maAnnotations.begin(); +} + +// css::office::XAnnotationEnumeration: +sal_Bool SAL_CALL AnnotationEnumeration::hasMoreElements() +{ + return maIter != maAnnotations.end(); +} + +css::uno::Reference< css::office::XAnnotation > SAL_CALL AnnotationEnumeration::nextElement() +{ + if( maIter == maAnnotations.end() ) + throw css::container::NoSuchElementException(); + + return (*maIter++); +} + +} // namespace sd + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/core/cusshow.cxx b/sd/source/core/cusshow.cxx new file mode 100644 index 000000000..8b51a613d --- /dev/null +++ b/sd/source/core/cusshow.cxx @@ -0,0 +1,101 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include <com/sun/star/lang/XComponent.hpp> + +#include <createunocustomshow.hxx> +#include <cusshow.hxx> +#include <customshowlist.hxx> + +using namespace ::com::sun::star; + +/************************************************************************* +|* +|* Ctor +|* +\************************************************************************/ +SdCustomShow::SdCustomShow() +{ +} + +/************************************************************************* +|* +|* Copy-Ctor +|* +\************************************************************************/ +SdCustomShow::SdCustomShow( const SdCustomShow& rShow ) + : maPages(rShow.maPages) +{ + aName = rShow.GetName(); +} + +SdCustomShow::SdCustomShow(css::uno::Reference< css::uno::XInterface > const & xShow ) + : mxUnoCustomShow( xShow ) +{ +} + +/************************************************************************* +|* +|* Dtor +|* +\************************************************************************/ +SdCustomShow::~SdCustomShow() +{ + uno::Reference< uno::XInterface > xShow( mxUnoCustomShow ); + uno::Reference< lang::XComponent > xComponent( xShow, uno::UNO_QUERY ); + if( xComponent.is() ) + xComponent->dispose(); +} + +uno::Reference< uno::XInterface > SdCustomShow::getUnoCustomShow() +{ + // try weak reference first + uno::Reference< uno::XInterface > xShow( mxUnoCustomShow ); + + if( !xShow.is() ) + { + xShow = createUnoCustomShow( this ); + } + + return xShow; +} + +void SdCustomShow::ReplacePage( const SdPage* pOldPage, const SdPage* pNewPage ) +{ + if( !pNewPage ) + { + maPages.erase(::std::remove(maPages.begin(), maPages.end(), pOldPage), maPages.end()); + } + else + { + ::std::replace(maPages.begin(), maPages.end(), pOldPage, pNewPage); + } +} + +void SdCustomShow::SetName(const OUString& rName) +{ + aName = rName; +} + +void SdCustomShowList::erase(std::vector<std::unique_ptr<SdCustomShow>>::iterator it) +{ + mShows.erase(it); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/core/drawdoc.cxx b/sd/source/core/drawdoc.cxx new file mode 100644 index 000000000..182ffe7f2 --- /dev/null +++ b/sd/source/core/drawdoc.cxx @@ -0,0 +1,1206 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include <libxml/xmlwriter.h> + +#include "PageListWatcher.hxx" +#include <com/sun/star/document/PrinterIndependentLayout.hpp> +#include <com/sun/star/i18n/ScriptType.hpp> +#include <com/sun/star/beans/XPropertyContainer.hpp> +#include <com/sun/star/beans/PropertyAttribute.hpp> +#include <com/sun/star/document/XDocumentProperties.hpp> +#include <com/sun/star/frame/XModel.hpp> +#include <editeng/forbiddencharacterstable.hxx> + +#include <svl/srchitem.hxx> +#include <editeng/eeitem.hxx> +#include <editeng/scriptspaceitem.hxx> +#include <tools/debug.hxx> + +#include <unotools/configmgr.hxx> +#include <unotools/useroptions.hxx> +#include <officecfg/Office/Impress.hxx> + +#include <sfx2/linkmgr.hxx> +#include <Outliner.hxx> +#include <sdmod.hxx> +#include <editeng/editstat.hxx> +#include <svx/svdotext.hxx> +#include <editeng/unolingu.hxx> +#include <svl/itempool.hxx> +#include <com/sun/star/lang/XMultiServiceFactory.hpp> +#include <com/sun/star/beans/XPropertySet.hpp> +#include <editeng/outlobj.hxx> +#include <comphelper/getexpandeduri.hxx> +#include <i18nlangtag/mslangid.hxx> +#include <i18nlangtag/languagetag.hxx> +#include <unotools/charclass.hxx> +#include <comphelper/processfactory.hxx> +#include <unotools/lingucfg.hxx> +#include <com/sun/star/uno/Reference.hxx> +#include <com/sun/star/xml/dom/XDocumentBuilder.hpp> +#include <com/sun/star/xml/dom/XDocument.hpp> +#include <com/sun/star/xml/dom/XNodeList.hpp> +#include <com/sun/star/xml/dom/DocumentBuilder.hpp> +#include <com/sun/star/uno/XComponentContext.hpp> +#include <rtl/ustring.hxx> + +#include <editeng/outliner.hxx> +#include <drawdoc.hxx> +#include <sdpage.hxx> +#include <strings.hrc> +#include <glob.hxx> +#include <stlpool.hxx> +#include <sdresid.hxx> +#include <customshowlist.hxx> +#include <DrawDocShell.hxx> +#include <GraphicDocShell.hxx> +#include <sdxfer.hxx> +#include <optsitem.hxx> +#include <FrameView.hxx> +#include <undo/undomanager.hxx> +#include <sdundogr.hxx> +#include <undopage.hxx> +#include <vcl/settings.hxx> +#include <vcl/svapp.hxx> +#include <unokywds.hxx> + +namespace com::sun::star::linguistic2 { class XHyphenator; } +namespace com::sun::star::linguistic2 { class XSpellChecker1; } + +using namespace ::sd; +using namespace ::com::sun::star; +using namespace ::com::sun::star::uno; +using namespace ::com::sun::star::lang; +using namespace ::com::sun::star::linguistic2; + +using namespace com::sun::star::xml::dom; +using ::com::sun::star::uno::Reference; +using ::com::sun::star::lang::XMultiServiceFactory; + + +SdDrawDocument* SdDrawDocument::s_pDocLockedInsertingLinks = nullptr; + +PresentationSettings::PresentationSettings() +: mbAll( true ), + mbEndless( false ), + mbCustomShow(false), + mbManual( false ), + mbMouseVisible( false ), + mbMouseAsPen( false ), + mbLockedPages( false ), + mbAlwaysOnTop( false ), + mbFullScreen( true ), + mbAnimationAllowed( true ), + mnPauseTimeout( 0 ), + mbShowPauseLogo( false ), + mbStartCustomShow( false ) +{ +} + +SdDrawDocument::SdDrawDocument(DocumentType eType, SfxObjectShell* pDrDocSh) +: FmFormModel( + nullptr, + pDrDocSh) +, mpDocSh(static_cast< ::sd::DrawDocShell*>(pDrDocSh)) +, mpCreatingTransferable( nullptr ) +, mbHasOnlineSpellErrors(false) +, mbInitialOnlineSpellingEnabled(true) +, mbNewOrLoadCompleted(false) +, mbOnlineSpell(false) +, mbStartWithPresentation( false ) +, mbExitAfterPresenting( false ) +, meLanguage( LANGUAGE_SYSTEM ) +, meLanguageCJK( LANGUAGE_SYSTEM ) +, meLanguageCTL( LANGUAGE_SYSTEM ) +, mePageNumType(SVX_NUM_ARABIC) +, mbAllocDocSh(false) +, meDocType(eType) +, mbEmbedFonts(false) +, mbEmbedUsedFontsOnly(false) +, mbEmbedFontScriptLatin(true) +, mbEmbedFontScriptAsian(true) +, mbEmbedFontScriptComplex(true) +, mnImagePreferredDPI(0) +{ + mpDrawPageListWatcher.reset(new ImpDrawPageListWatcher(*this)); + mpMasterPageListWatcher.reset(new ImpMasterPageListWatcher(*this)); + + InitLayoutVector(); + InitObjectVector(); + SetObjectShell(pDrDocSh); // for VCDrawModel + + if (mpDocSh) + { + SetSwapGraphics(); + } + + // Set measuring unit (of the application) and scale (of SdMod) + sal_Int32 nX, nY; + SdOptions* pOptions = SD_MOD()->GetSdOptions(meDocType); + pOptions->GetScale( nX, nY ); + + // Allow UI scale only for draw documents. + if( eType == DocumentType::Draw ) + SetUIUnit( static_cast<FieldUnit>(pOptions->GetMetric()), Fraction( nX, nY ) ); // user-defined + else + SetUIUnit( static_cast<FieldUnit>(pOptions->GetMetric()), Fraction( 1, 1 ) ); // default + + SetScaleUnit(MapUnit::Map100thMM); + SetScaleFraction(Fraction(1, 1)); + SetDefaultFontHeight(o3tl::convert(24, o3tl::Length::pt, o3tl::Length::mm100)); + + m_pItemPool->SetDefaultMetric(MapUnit::Map100thMM); + m_pItemPool->FreezeIdRanges(); + SetTextDefaults(); + + // DrawingEngine has to know where it is... + FmFormModel::SetStyleSheetPool( new SdStyleSheetPool( GetPool(), this ) ); + + // Set StyleSheetPool for DrawOutliner, so text objects can be read correctly. + // The link to the StyleRequest handler of the document is set later, in + // NewOrLoadCompleted, because only then do all the templates exist. + SdrOutliner& rOutliner = GetDrawOutliner(); + rOutliner.SetStyleSheetPool(static_cast<SfxStyleSheetPool*>(GetStyleSheetPool())); + SetCalcFieldValueHdl( &rOutliner ); + + // set linguistic options + if (!utl::ConfigManager::IsFuzzing()) + { + const SvtLinguConfig aLinguConfig; + SvtLinguOptions aOptions; + aLinguConfig.GetOptions( aOptions ); + + SetLanguage( MsLangId::resolveSystemLanguageByScriptType(aOptions.nDefaultLanguage, + css::i18n::ScriptType::LATIN), EE_CHAR_LANGUAGE ); + SetLanguage( MsLangId::resolveSystemLanguageByScriptType(aOptions.nDefaultLanguage_CJK, + css::i18n::ScriptType::ASIAN), EE_CHAR_LANGUAGE_CJK ); + SetLanguage( MsLangId::resolveSystemLanguageByScriptType(aOptions.nDefaultLanguage_CTL, + css::i18n::ScriptType::COMPLEX), EE_CHAR_LANGUAGE_CTL ); + + mbOnlineSpell = aOptions.bIsSpellAuto; + } + + LanguageType eRealLanguage = MsLangId::getRealLanguage( meLanguage ); + mpCharClass.reset(new CharClass( LanguageTag( eRealLanguage) )); + + // If the current application language is a language that uses right-to-left text... + LanguageType eRealCTLLanguage = Application::GetSettings().GetLanguageTag().getLanguageType(); + + // for korean and japanese languages we have a different default for apply spacing between asian, latin and ctl text + if (MsLangId::isKorean(eRealCTLLanguage) || (LANGUAGE_JAPANESE == eRealCTLLanguage)) + { + GetPool().GetSecondaryPool()->SetPoolDefaultItem( SvxScriptSpaceItem( false, EE_PARA_ASIANCJKSPACING ) ); + } + + // Set DefTab and SpellOptions for the SD module + sal_uInt16 nDefTab = pOptions->GetDefTab(); + SetDefaultTabulator( nDefTab ); + + try + { + Reference< XSpellChecker1 > xSpellChecker( LinguMgr::GetSpellChecker() ); + if ( xSpellChecker.is() ) + rOutliner.SetSpeller( xSpellChecker ); + + Reference< XHyphenator > xHyphenator( LinguMgr::GetHyphenator() ); + if( xHyphenator.is() ) + rOutliner.SetHyphenator( xHyphenator ); + + SetForbiddenCharsTable(SvxForbiddenCharactersTable::makeForbiddenCharactersTable(::comphelper::getProcessComponentContext())); + } + catch(...) + { + OSL_FAIL("Can't get SpellChecker"); + } + + rOutliner.SetDefaultLanguage( Application::GetSettings().GetLanguageTag().getLanguageType() ); + + if (mpDocSh) + { + SetLinkManager( new sfx2::LinkManager(mpDocSh) ); + } + + EEControlBits nCntrl = rOutliner.GetControlWord(); + nCntrl |= EEControlBits::ALLOWBIGOBJS; + + if (mbOnlineSpell) + nCntrl |= EEControlBits::ONLINESPELLING; + else + nCntrl &= ~EEControlBits::ONLINESPELLING; + + nCntrl &= ~ EEControlBits::ULSPACESUMMATION; + if ( meDocType != DocumentType::Impress ) + SetSummationOfParagraphs( false ); + else + { + SetSummationOfParagraphs( pOptions->IsSummationOfParagraphs() ); + if ( pOptions->IsSummationOfParagraphs() ) + nCntrl |= EEControlBits::ULSPACESUMMATION; + } + rOutliner.SetControlWord(nCntrl); + + // Initialize the printer independent layout mode + SetPrinterIndependentLayout (pOptions->GetPrinterIndependentLayout()); + + // Set the StyleSheetPool for HitTestOutliner. + // The link to the StyleRequest handler of the document is set later, in + // NewOrLoadCompleted, because only then do all the templates exist. + m_pHitTestOutliner->SetStyleSheetPool( static_cast<SfxStyleSheetPool*>(GetStyleSheetPool()) ); + + SetCalcFieldValueHdl( m_pHitTestOutliner.get() ); + + try + { + Reference< XSpellChecker1 > xSpellChecker( LinguMgr::GetSpellChecker() ); + if ( xSpellChecker.is() ) + m_pHitTestOutliner->SetSpeller( xSpellChecker ); + + Reference< XHyphenator > xHyphenator( LinguMgr::GetHyphenator() ); + if( xHyphenator.is() ) + m_pHitTestOutliner->SetHyphenator( xHyphenator ); + } + catch(...) + { + OSL_FAIL("Can't get SpellChecker"); + } + + m_pHitTestOutliner->SetDefaultLanguage( Application::GetSettings().GetLanguageTag().getLanguageType() ); + + EEControlBits nCntrl2 = m_pHitTestOutliner->GetControlWord(); + nCntrl2 |= EEControlBits::ALLOWBIGOBJS; + nCntrl2 &= ~EEControlBits::ONLINESPELLING; + + nCntrl2 &= ~ EEControlBits::ULSPACESUMMATION; + if ( pOptions->IsSummationOfParagraphs() ) + nCntrl2 |= EEControlBits::ULSPACESUMMATION; + + m_pHitTestOutliner->SetControlWord( nCntrl2 ); + + /** Create layers + * + * We create the following default layers on all pages and master pages: + * + * sUNO_LayerName_layout; "layout": default layer for drawing objects of normal pages + * localized by SdResId(STR_LAYER_LAYOUT) + * + * sUNO_LayerName_background; "background": background of the master page + * localized by SdResId(STR_LAYER_BCKGRND) + * (currently unused within normal pages and not visible to users) + * + * sUNO_LayerName_background_objects; "backgroundobjects": objects on the background of master pages + * localized by SdResId(STR_LAYER_BCKGRNDOBJ) + * (currently unused within normal pages) + * + * sUNO_LayerName_controls; "controls": default layer for controls + * localized by SdResId(STR_LAYER_CONTROLS) + * (currently special handling in regard to z-order) + * + * sUNO_LayerName_measurelines; "measurelines" : default layer for measure lines + * localized by SdResId(STR_LAYER_MEASURELINES) + */ + + { + SdrLayerAdmin& rLayerAdmin = GetLayerAdmin(); + rLayerAdmin.NewLayer( sUNO_LayerName_layout ); + rLayerAdmin.NewLayer( sUNO_LayerName_background ); + rLayerAdmin.NewLayer( sUNO_LayerName_background_objects ); + rLayerAdmin.NewLayer( sUNO_LayerName_controls); + rLayerAdmin.NewLayer( sUNO_LayerName_measurelines ); + + rLayerAdmin.SetControlLayerName(sUNO_LayerName_controls); + } + +} + +// Destructor +SdDrawDocument::~SdDrawDocument() +{ + Broadcast(SdrHint(SdrHintKind::ModelCleared)); + + if (mpWorkStartupTimer) + { + if ( mpWorkStartupTimer->IsActive() ) + mpWorkStartupTimer->Stop(); + + mpWorkStartupTimer.reset(); + } + + StopOnlineSpelling(); + mpOnlineSearchItem.reset(); + + CloseBookmarkDoc(); + SetAllocDocSh(false); + + ClearModel(true); + + if (m_pLinkManager) + { + // Release BaseLinks + if ( !m_pLinkManager->GetLinks().empty() ) + { + m_pLinkManager->Remove( 0, m_pLinkManager->GetLinks().size() ); + } + + delete m_pLinkManager; + m_pLinkManager = nullptr; + } + + maFrameViewList.clear(); + mpCustomShowList.reset(); + mpOutliner.reset(); + mpInternalOutliner.reset(); + mpCharClass.reset(); +} + +void SdDrawDocument::adaptSizeAndBorderForAllPages( + const Size& rNewSize, + ::tools::Long nLeft, + ::tools::Long nRight, + ::tools::Long nUpper, + ::tools::Long nLower) +{ + const sal_uInt16 nMasterPageCnt(GetMasterSdPageCount(PageKind::Standard)); + const sal_uInt16 nPageCnt(GetSdPageCount(PageKind::Standard)); + + if(0 == nMasterPageCnt && 0 == nPageCnt) + { + return; + } + + SdPage* pPage(0 != nPageCnt ? GetSdPage(0, PageKind::Standard) : GetMasterSdPage(0, PageKind::Standard)); + + // call fully implemented local version, including getting + // some more information from one of the Pages (1st one) + AdaptPageSizeForAllPages( + rNewSize, + PageKind::Standard, + nullptr, + nLeft, + nRight, + nUpper, + nLower, + true, + pPage->GetOrientation(), + pPage->GetPaperBin(), + pPage->IsBackgroundFullSize()); + + // adjust handout page to new format of the standard page + if(0 != nPageCnt) + { + GetSdPage(0, PageKind::Handout)->CreateTitleAndLayout(true); + } +} + +void SdDrawDocument::AdaptPageSizeForAllPages( + const Size& rNewSize, + PageKind ePageKind, + SdUndoGroup* pUndoGroup, + ::tools::Long nLeft, + ::tools::Long nRight, + ::tools::Long nUpper, + ::tools::Long nLower, + bool bScaleAll, + Orientation eOrientation, + sal_uInt16 nPaperBin, + bool bBackgroundFullSize) +{ + sal_uInt16 i; + const sal_uInt16 nMasterPageCnt(GetMasterSdPageCount(ePageKind)); + const sal_uInt16 nPageCnt(GetSdPageCount(ePageKind)); + + if(0 == nMasterPageCnt && 0 == nPageCnt) + { + return; + } + + for (i = 0; i < nMasterPageCnt; i++) + { + // first, handle all master pages + SdPage* pPage(GetMasterSdPage(i, ePageKind)); + + if(pUndoGroup) + { + SdUndoAction* pUndo( + new SdPageFormatUndoAction( + this, + pPage, + pPage->GetSize(), + pPage->GetLeftBorder(), pPage->GetRightBorder(), + pPage->GetUpperBorder(), pPage->GetLowerBorder(), + pPage->GetOrientation(), + pPage->GetPaperBin(), + pPage->IsBackgroundFullSize(), + rNewSize, + nLeft, nRight, + nUpper, nLower, + bScaleAll, + eOrientation, + nPaperBin, + bBackgroundFullSize)); + pUndoGroup->AddAction(pUndo); + } + + if (rNewSize.Width() > 0 || nLeft >= 0 || nRight >= 0 || nUpper >= 0 || nLower >= 0) + { + ::tools::Rectangle aNewBorderRect(nLeft, nUpper, nRight, nLower); + pPage->ScaleObjects(rNewSize, aNewBorderRect, bScaleAll); + + if (rNewSize.Width() > 0) + { + pPage->SetSize(rNewSize); + } + } + + if( nLeft >= 0 || nRight >= 0 || nUpper >= 0 || nLower >= 0 ) + { + pPage->SetBorder(nLeft, nUpper, nRight, nLower); + } + + pPage->SetOrientation(eOrientation); + pPage->SetPaperBin( nPaperBin ); + pPage->SetBackgroundFullSize( bBackgroundFullSize ); + + if ( ePageKind == PageKind::Standard ) + { + GetMasterSdPage(i, PageKind::Notes)->CreateTitleAndLayout(); + } + + pPage->CreateTitleAndLayout(); + } + + for (i = 0; i < nPageCnt; i++) + { + // then, handle all pages + SdPage* pPage(GetSdPage(i, ePageKind)); + + if(pUndoGroup) + { + SdUndoAction* pUndo( + new SdPageFormatUndoAction( + this, + pPage, + pPage->GetSize(), + pPage->GetLeftBorder(), pPage->GetRightBorder(), + pPage->GetUpperBorder(), pPage->GetLowerBorder(), + pPage->GetOrientation(), + pPage->GetPaperBin(), + pPage->IsBackgroundFullSize(), + rNewSize, + nLeft, nRight, + nUpper, nLower, + bScaleAll, + eOrientation, + nPaperBin, + bBackgroundFullSize)); + pUndoGroup->AddAction(pUndo); + } + + if (rNewSize.Width() > 0 || nLeft >= 0 || nRight >= 0 || nUpper >= 0 || nLower >= 0) + { + ::tools::Rectangle aNewBorderRect(nLeft, nUpper, nRight, nLower); + pPage->ScaleObjects(rNewSize, aNewBorderRect, bScaleAll); + + if (rNewSize.Width() > 0) + { + pPage->SetSize(rNewSize); + } + } + + if( nLeft >= 0 || nRight >= 0 || nUpper >= 0 || nLower >= 0 ) + { + pPage->SetBorder(nLeft, nUpper, nRight, nLower); + } + + pPage->SetOrientation(eOrientation); + pPage->SetPaperBin( nPaperBin ); + pPage->SetBackgroundFullSize( bBackgroundFullSize ); + + if ( ePageKind == PageKind::Standard ) + { + SdPage* pNotesPage = GetSdPage(i, PageKind::Notes); + pNotesPage->SetAutoLayout( pNotesPage->GetAutoLayout() ); + } + + pPage->SetAutoLayout( pPage->GetAutoLayout() ); + } +} + +SdrModel* SdDrawDocument::AllocModel() const +{ + return AllocSdDrawDocument(); +} + +namespace +{ + +/// Copies all user-defined properties from pSource to pDestination. +void lcl_copyUserDefinedProperties(const SfxObjectShell* pSource, const SfxObjectShell* pDestination) +{ + if (!pSource || !pDestination) + return; + + uno::Reference<document::XDocumentProperties> xSource = pSource->getDocProperties(); + uno::Reference<document::XDocumentProperties> xDestination = pDestination->getDocProperties(); + uno::Reference<beans::XPropertyContainer> xSourcePropertyContainer = xSource->getUserDefinedProperties(); + uno::Reference<beans::XPropertyContainer> xDestinationPropertyContainer = xDestination->getUserDefinedProperties(); + uno::Reference<beans::XPropertySet> xSourcePropertySet(xSourcePropertyContainer, uno::UNO_QUERY); + const uno::Sequence<beans::Property> aProperties = xSourcePropertySet->getPropertySetInfo()->getProperties(); + + for (const beans::Property& rProperty : aProperties) + { + const OUString& rKey = rProperty.Name; + uno::Any aValue = xSourcePropertySet->getPropertyValue(rKey); + // We know that pDestination was just created, so has no properties: addProperty() will never throw. + xDestinationPropertyContainer->addProperty(rKey, beans::PropertyAttribute::REMOVABLE, aValue); + } +} + +} + +// This method creates a new document (SdDrawDocument) and returns a pointer to +// said document. The drawing engine uses this method to put the document (or +// parts of it) into the clipboard/DragServer. +SdDrawDocument* SdDrawDocument::AllocSdDrawDocument() const +{ + SdDrawDocument* pNewModel = nullptr; + + if( mpCreatingTransferable ) + { + // Document is created for drag & drop/clipboard. To be able to + // do this, the document has to know a DocShell (SvPersist). + SfxObjectShell* pObj = nullptr; + ::sd::DrawDocShell* pNewDocSh = nullptr; + + if( meDocType == DocumentType::Impress ) + mpCreatingTransferable->SetDocShell( new ::sd::DrawDocShell( + SfxObjectCreateMode::EMBEDDED, true, meDocType ) ); + else + mpCreatingTransferable->SetDocShell( new ::sd::GraphicDocShell( + SfxObjectCreateMode::EMBEDDED ) ); + + pObj = mpCreatingTransferable->GetDocShell().get(); + pNewDocSh = static_cast< ::sd::DrawDocShell*>( pObj ); + pNewDocSh->DoInitNew(); + pNewModel = pNewDocSh->GetDoc(); + + // Only necessary for clipboard - + // for drag & drop this is handled by DragServer + SdStyleSheetPool* pOldStylePool = static_cast<SdStyleSheetPool*>( GetStyleSheetPool() ); + SdStyleSheetPool* pNewStylePool = static_cast<SdStyleSheetPool*>( pNewModel->GetStyleSheetPool() ); + + pNewStylePool->CopyGraphicSheets(*pOldStylePool); + pNewStylePool->CopyCellSheets(*pOldStylePool); + pNewStylePool->CopyTableStyles(*pOldStylePool); + + for (sal_uInt16 i = 0; i < GetMasterSdPageCount(PageKind::Standard); i++) + { + // Move with all of the master page's layouts + OUString aOldLayoutName(const_cast<SdDrawDocument*>(this)->GetMasterSdPage(i, PageKind::Standard)->GetLayoutName()); + aOldLayoutName = aOldLayoutName.copy( 0, aOldLayoutName.indexOf( SD_LT_SEPARATOR ) ); + StyleSheetCopyResultVector aCreatedSheets; + pNewStylePool->CopyLayoutSheets(aOldLayoutName, *pOldStylePool, aCreatedSheets ); + } + + lcl_copyUserDefinedProperties(GetDocSh(), pNewDocSh); + + pNewModel->NewOrLoadCompleted( DocCreationMode::Loaded ); // loaded from source document + } + else if( mbAllocDocSh ) + { + // Create a DocShell which is then returned with GetAllocedDocSh() + SdDrawDocument* pDoc = const_cast<SdDrawDocument*>(this); + pDoc->SetAllocDocSh(false); + pDoc->mxAllocedDocShRef = new ::sd::DrawDocShell( + SfxObjectCreateMode::EMBEDDED, true, meDocType); + pDoc->mxAllocedDocShRef->DoInitNew(); + pNewModel = pDoc->mxAllocedDocShRef->GetDoc(); + } + else + { + pNewModel = new SdDrawDocument(meDocType, nullptr); + } + + return pNewModel; +} + +rtl::Reference<SdPage> SdDrawDocument::AllocSdPage(bool bMasterPage) +{ + return new SdPage(*this, bMasterPage); +} + +// This method creates a new page (SdPage) and returns a pointer to said page. +// The drawing engine uses this method to create pages (whose types it does +// not know, as they are _derivatives_ of SdrPage) when loading. +rtl::Reference<SdrPage> SdDrawDocument::AllocPage(bool bMasterPage) +{ + return AllocSdPage(bMasterPage); +} + +// When the model has changed +void SdDrawDocument::SetChanged(bool bFlag) +{ + if (mpDocSh) + { + if (mbNewOrLoadCompleted && mpDocSh->IsEnableSetModified()) + { + // Pass on to base class + FmFormModel::SetChanged(bFlag); + + // Forward to ObjectShell + mpDocSh->SetModified(bFlag); + } + } + else + { + // Pass on to base class + FmFormModel::SetChanged(bFlag); + } +} + +// The model changed, don't call anything else +void SdDrawDocument::NbcSetChanged(bool bFlag) +{ + // forward to baseclass + FmFormModel::SetChanged(bFlag); +} + +// NewOrLoadCompleted is called when the document is loaded, or when it is clear +// it won't load any more. +void SdDrawDocument::NewOrLoadCompleted(DocCreationMode eMode) +{ + if (eMode == DocCreationMode::New) + { + // New document: + // create slideshow and default templates, + // create pool for virtual controls + CreateLayoutTemplates(); + CreateDefaultCellStyles(); + + static_cast< SdStyleSheetPool* >( mxStyleSheetPool.get() )->CreatePseudosIfNecessary(); + } + else if (eMode == DocCreationMode::Loaded) + { + // Document has finished loading + + CheckMasterPages(); + + if ( GetMasterSdPageCount(PageKind::Standard) > 1 ) + RemoveUnnecessaryMasterPages( nullptr, true, false ); + + for ( sal_uInt16 i = 0; i < GetPageCount(); i++ ) + { + // Check for correct layout names + SdPage* pPage = static_cast<SdPage*>( GetPage( i ) ); + + if(pPage->TRG_HasMasterPage()) + { + SdPage& rMaster = static_cast<SdPage&>(pPage->TRG_GetMasterPage() ); + + if(rMaster.GetLayoutName() != pPage->GetLayoutName()) + { + pPage->SetLayoutName(rMaster.GetLayoutName()); + } + } + } + + for ( sal_uInt16 nPage = 0; nPage < GetMasterPageCount(); nPage++) + { + // LayoutName and PageName must be the same + SdPage* pPage = static_cast<SdPage*>( GetMasterPage( nPage ) ); + + OUString aName( pPage->GetLayoutName() ); + aName = aName.copy( 0, aName.indexOf( SD_LT_SEPARATOR ) ); + + if( aName != pPage->GetName() ) + pPage->SetName( aName ); + } + + // Create names of the styles in the user's language + static_cast<SdStyleSheetPool*>(mxStyleSheetPool.get())->UpdateStdNames(); + + // Create any missing styles - eg. formerly, there was no Subtitle style + static_cast<SdStyleSheetPool*>(mxStyleSheetPool.get())->CreatePseudosIfNecessary(); + } + + // Set default style of Drawing Engine + OUString aName( SdResId(STR_STANDARD_STYLESHEET_NAME)); + SetDefaultStyleSheet(static_cast<SfxStyleSheet*>(mxStyleSheetPool->Find(aName, SfxStyleFamily::Para))); + + // #i119287# Set default StyleSheet for SdrGrafObj and SdrOle2Obj + SetDefaultStyleSheetForSdrGrafObjAndSdrOle2Obj(static_cast<SfxStyleSheet*>(mxStyleSheetPool->Find(SdResId(STR_POOLSHEET_OBJNOLINENOFILL), SfxStyleFamily::Para))); + + // Initialize DrawOutliner and DocumentOutliner, but don't initialize the + // global outliner, as it is not document specific like StyleSheetPool and + // StyleRequestHandler are. + ::Outliner& rDrawOutliner = GetDrawOutliner(); + rDrawOutliner.SetStyleSheetPool(static_cast<SfxStyleSheetPool*>(GetStyleSheetPool())); + EEControlBits nCntrl = rDrawOutliner.GetControlWord(); + if (mbOnlineSpell) + nCntrl |= EEControlBits::ONLINESPELLING; + else + nCntrl &= ~EEControlBits::ONLINESPELLING; + rDrawOutliner.SetControlWord(nCntrl); + + // Initialize HitTestOutliner and DocumentOutliner, but don't initialize the + // global outliner, as it is not document specific like StyleSheetPool and + // StyleRequestHandler are. + m_pHitTestOutliner->SetStyleSheetPool(static_cast<SfxStyleSheetPool*>(GetStyleSheetPool())); + + if(mpOutliner) + { + mpOutliner->SetStyleSheetPool(static_cast<SfxStyleSheetPool*>(GetStyleSheetPool())); + } + if(mpInternalOutliner) + { + mpInternalOutliner->SetStyleSheetPool(static_cast<SfxStyleSheetPool*>(GetStyleSheetPool())); + } + + if ( eMode == DocCreationMode::Loaded ) + { + // Make presentation objects listeners of the appropriate styles + SdStyleSheetPool* pSPool = static_cast<SdStyleSheetPool*>( GetStyleSheetPool() ); + sal_uInt16 nPage, nPageCount; + + // create missing layout style sheets for broken documents + // that were created with the 5.2 + nPageCount = GetMasterSdPageCount( PageKind::Standard ); + for (nPage = 0; nPage < nPageCount; nPage++) + { + SdPage* pPage = GetMasterSdPage(nPage, PageKind::Standard); + pSPool->CreateLayoutStyleSheets( pPage->GetName(), true ); + } + + // Default and notes pages: + for (nPage = 0; nPage < GetPageCount(); nPage++) + { + SdPage* pPage = static_cast<SdPage*>(GetPage(nPage)); + NewOrLoadCompleted( pPage, pSPool ); + } + + // Master pages: + for (nPage = 0; nPage < GetMasterPageCount(); nPage++) + { + SdPage* pPage = static_cast<SdPage*>(GetMasterPage(nPage)); + + NewOrLoadCompleted( pPage, pSPool ); + } + } + + mbNewOrLoadCompleted = true; + UpdateAllLinks(); + SetChanged( false ); +} + +/** updates all links, only links in this document should by resolved */ +void SdDrawDocument::UpdateAllLinks() +{ + if (s_pDocLockedInsertingLinks || !m_pLinkManager || m_pLinkManager->GetLinks().empty()) + return; + + s_pDocLockedInsertingLinks = this; // lock inserting links. only links in this document should by resolved + + if (mpDocSh) + { + comphelper::EmbeddedObjectContainer& rEmbeddedObjectContainer = mpDocSh->getEmbeddedObjectContainer(); + rEmbeddedObjectContainer.setUserAllowsLinkUpdate(true); + } + + m_pLinkManager->UpdateAllLinks(true, false, nullptr); // query box: update all links? + + if (s_pDocLockedInsertingLinks == this) + s_pDocLockedInsertingLinks = nullptr; // unlock inserting links +} + +/** this loops over the presentation objects of a page and repairs some new settings + from old binary files and resets all default strings for empty presentation objects. +*/ +void SdDrawDocument::NewOrLoadCompleted( SdPage* pPage, SdStyleSheetPool* pSPool ) +{ + sd::ShapeList& rPresentationShapes( pPage->GetPresentationShapeList() ); + if(rPresentationShapes.isEmpty()) + return; + + // Create lists of title and outline styles + OUString aName = pPage->GetLayoutName(); + aName = aName.copy( 0, aName.indexOf( SD_LT_SEPARATOR ) ); + + std::vector<SfxStyleSheetBase*> aOutlineList; + pSPool->CreateOutlineSheetList(aName,aOutlineList); + + SfxStyleSheet* pTitleSheet = static_cast<SfxStyleSheet*>(pSPool->GetTitleSheet(aName)); + + SdrObject* pObj = nullptr; + rPresentationShapes.seekShape(0); + + // Now look for title and outline text objects, then make those objects + // listeners. + while( (pObj = rPresentationShapes.getNextShape()) ) + { + if (pObj->GetObjInventor() == SdrInventor::Default) + { + OutlinerParaObject* pOPO = pObj->GetOutlinerParaObject(); + SdrObjKind nId = pObj->GetObjIdentifier(); + + if (nId == SdrObjKind::TitleText) + { + if( pOPO && pOPO->GetOutlinerMode() == OutlinerMode::DontKnow ) + pOPO->SetOutlinerMode( OutlinerMode::TitleObject ); + + // sal_True: don't delete "hard" attributes when doing this. + if (pTitleSheet) + pObj->SetStyleSheet(pTitleSheet, true); + } + else if (nId == SdrObjKind::OutlineText) + { + if( pOPO && pOPO->GetOutlinerMode() == OutlinerMode::DontKnow ) + pOPO->SetOutlinerMode( OutlinerMode::OutlineObject ); + + std::vector<SfxStyleSheetBase*>::iterator iter; + for (iter = aOutlineList.begin(); iter != aOutlineList.end(); ++iter) + { + SfxStyleSheet* pSheet = static_cast<SfxStyleSheet*>(*iter); + + if (pSheet) + { + pObj->StartListening(*pSheet); + + if( iter == aOutlineList.begin()) + // text frame listens to stylesheet of layer 1 + pObj->NbcSetStyleSheet(pSheet, true); + } + } + } + + if( auto pTextObj = dynamic_cast<SdrTextObj *>( pObj ) ) + if (pTextObj->IsEmptyPresObj()) + { + PresObjKind ePresObjKind = pPage->GetPresObjKind(pObj); + OUString aString( pPage->GetPresObjText(ePresObjKind) ); + + if (!aString.isEmpty()) + { + SdOutliner* pInternalOutl = GetInternalOutliner(); + pPage->SetObjText( pTextObj, pInternalOutl, ePresObjKind, aString ); + pObj->NbcSetStyleSheet( pPage->GetStyleSheetForPresObj( ePresObjKind ), true ); + pInternalOutl->Clear(); + } + } + } + } +} + +// Local outliner that is used for outline mode. In this outliner, OutlinerViews +// may be inserted. +SdOutliner* SdDrawDocument::GetOutliner(bool bCreateOutliner) +{ + if (!mpOutliner && bCreateOutliner) + { + mpOutliner.reset(new SdOutliner( this, OutlinerMode::TextObject )); + + if (mpDocSh) + mpOutliner->SetRefDevice( SD_MOD()->GetVirtualRefDevice() ); + + mpOutliner->SetDefTab( m_nDefaultTabulator ); + mpOutliner->SetStyleSheetPool(static_cast<SfxStyleSheetPool*>(GetStyleSheetPool())); + } + + return mpOutliner.get(); +} + +// Internal outliner that is used to create text objects. We don't insert any +// OutlinerViews into this outliner! +SdOutliner* SdDrawDocument::GetInternalOutliner(bool bCreateOutliner) +{ + if ( !mpInternalOutliner && bCreateOutliner ) + { + mpInternalOutliner.reset( new SdOutliner( this, OutlinerMode::TextObject ) ); + + // This outliner is only used to create special text objects. As no + // information about portions is saved in this outliner, the update mode + // can/should always remain sal_False. + mpInternalOutliner->SetUpdateLayout( false ); + mpInternalOutliner->EnableUndo( false ); + + if (mpDocSh) + mpInternalOutliner->SetRefDevice( SD_MOD()->GetVirtualRefDevice() ); + + mpInternalOutliner->SetDefTab( m_nDefaultTabulator ); + mpInternalOutliner->SetStyleSheetPool(static_cast<SfxStyleSheetPool*>(GetStyleSheetPool())); + } + + DBG_ASSERT( !mpInternalOutliner || ( ! mpInternalOutliner->IsUpdateLayout() ) , "InternalOutliner: UpdateMode = sal_True !" ); + DBG_ASSERT( !mpInternalOutliner || ( ! mpInternalOutliner->IsUndoEnabled() ), "InternalOutliner: Undo = sal_True !" ); + + // If you add stuff here, always clear it out. + // Advantages: + // a) no unnecessary Clear calls + // b) no wasted memory + DBG_ASSERT( !mpInternalOutliner || ( ( mpInternalOutliner->GetParagraphCount() == 1 ) && ( mpInternalOutliner->GetText( mpInternalOutliner->GetParagraph( 0 ) ).isEmpty() ) ), "InternalOutliner: not empty!" ); + + return mpInternalOutliner.get(); +} + +// OnlineSpelling on/off +void SdDrawDocument::SetOnlineSpell(bool bIn) +{ + mbOnlineSpell = bIn; + EEControlBits nCntrl; + + if(mpOutliner) + { + nCntrl = mpOutliner->GetControlWord(); + + if(mbOnlineSpell) + nCntrl |= EEControlBits::ONLINESPELLING; + else + nCntrl &= ~EEControlBits::ONLINESPELLING; + + mpOutliner->SetControlWord(nCntrl); + } + + if (mpInternalOutliner) + { + nCntrl = mpInternalOutliner->GetControlWord(); + + if (mbOnlineSpell) + nCntrl |= EEControlBits::ONLINESPELLING; + else + nCntrl &= ~EEControlBits::ONLINESPELLING; + + mpInternalOutliner->SetControlWord(nCntrl); + } + + ::Outliner& rOutliner = GetDrawOutliner(); + + nCntrl = rOutliner.GetControlWord(); + + if (mbOnlineSpell) + nCntrl |= EEControlBits::ONLINESPELLING; + else + nCntrl &= ~EEControlBits::ONLINESPELLING; + + rOutliner.SetControlWord(nCntrl); + + if (mbOnlineSpell) + { + StartOnlineSpelling(); + } + else + { + StopOnlineSpelling(); + } +} + +// OnlineSpelling: highlighting on/off +uno::Reference< uno::XInterface > SdDrawDocument::createUnoModel() +{ + uno::Reference< uno::XInterface > xModel; + + try + { + if ( mpDocSh ) + xModel = mpDocSh->GetModel(); + } + catch( uno::RuntimeException& ) + { + } + + return xModel; +} + +SvxNumType SdDrawDocument::GetPageNumType() const +{ + return mePageNumType; +} + +void SdDrawDocument::SetPrinterIndependentLayout (sal_Int32 nMode) +{ + switch (nMode) + { + case css::document::PrinterIndependentLayout::DISABLED: + case css::document::PrinterIndependentLayout::ENABLED: + // Just store supported modes and inform the doc shell + mnPrinterIndependentLayout = nMode; + + // Since it is possible that a SdDrawDocument is constructed without a + // SdDrawDocShell the pointer member mpDocSh needs to be tested + // before the call is executed. This is e. g. used for copy/paste. + if(mpDocSh) + { + mpDocSh->UpdateRefDevice (); + } + + break; + + default: + // Ignore unknown values + break; + } +} + +void SdDrawDocument::SetStartWithPresentation( bool bStartWithPresentation ) +{ + mbStartWithPresentation = bStartWithPresentation; +} + +void SdDrawDocument::SetExitAfterPresenting( bool bExitAfterPresenting ) +{ + mbExitAfterPresenting = bExitAfterPresenting; +} + +void SdDrawDocument::PageListChanged() +{ + mpDrawPageListWatcher->Invalidate(); +} + +void SdDrawDocument::MasterPageListChanged() +{ + mpMasterPageListWatcher->Invalidate(); +} + +void SdDrawDocument::SetCalcFieldValueHdl(::Outliner* pOutliner) +{ + pOutliner->SetCalcFieldValueHdl(LINK(SD_MOD(), SdModule, CalcFieldValueHdl)); +} + +sal_uInt16 SdDrawDocument::GetAnnotationAuthorIndex( const OUString& rAuthor ) +{ + // force current user to have first color + if( maAnnotationAuthors.empty() ) + { + SvtUserOptions aUserOptions; + maAnnotationAuthors.push_back( aUserOptions.GetFullName() ); + } + + auto iter = std::find(maAnnotationAuthors.begin(), maAnnotationAuthors.end(), rAuthor); + sal_uInt16 idx = static_cast<sal_uInt16>(std::distance(maAnnotationAuthors.begin(), iter)); + + if( idx == maAnnotationAuthors.size() ) + { + maAnnotationAuthors.push_back( rAuthor ); + } + + return idx; +} + +void SdDrawDocument::InitLayoutVector() +{ + if (utl::ConfigManager::IsFuzzing()) + return; + + const Reference<css::uno::XComponentContext> xContext( + ::comphelper::getProcessComponentContext() ); + + // get file list from configuration + const Sequence< OUString > aFiles( + officecfg::Office::Impress::Misc::LayoutListFiles::get() ); + + if (aFiles.getLength() == 0) + return; + const Reference<XDocumentBuilder> xDocBuilder = DocumentBuilder::create( xContext ); + + for( const auto& rFile : aFiles ) + { + OUString sFilename = comphelper::getExpandedUri(xContext, rFile); + + // load layout file into DOM + + try + { + // loop over every layout entry in current file + const Reference<XDocument> xDoc = xDocBuilder->parseURI( sFilename ); + const Reference<XNodeList> layoutlist = xDoc->getElementsByTagName("layout"); + const int nElements = layoutlist->getLength(); + for(int index=0; index < nElements; index++) + maLayoutInfo.push_back( layoutlist->item(index) ); + } + catch (const uno::Exception &) + { + // skip missing config. files + } + } +} + +void SdDrawDocument::InitObjectVector() +{ + if (utl::ConfigManager::IsFuzzing()) + return; + + const Reference<css::uno::XComponentContext> xContext( + ::comphelper::getProcessComponentContext() ); + + // get file list from configuration + const Sequence< OUString > aFiles( + officecfg::Office::Impress::Misc::PresObjListFiles::get() ); + + if (aFiles.getLength() == 0) + return; + const Reference<XDocumentBuilder> xDocBuilder = DocumentBuilder::create( xContext ); + for( const auto& rFile : aFiles ) + { + OUString sFilename = comphelper::getExpandedUri(xContext, rFile); + + // load presentation object file into DOM + + try + { + // loop over every object entry in current file + const Reference<XDocument> xDoc = xDocBuilder->parseURI( sFilename ); + const Reference<XNodeList> objectlist = xDoc->getElementsByTagName("object"); + const int nElements = objectlist->getLength(); + for(int index=0; index < nElements; index++) + maPresObjectInfo.push_back( objectlist->item(index) ); + } + catch (const uno::Exception &) + { + // skip missing config. files + } + } +} + +void SdDrawDocument::dumpAsXml(xmlTextWriterPtr pWriter) const +{ + bool bOwns = false; + if (!pWriter) + { + pWriter = xmlNewTextWriterFilename("model.xml", 0); + xmlTextWriterSetIndent(pWriter,1); + (void)xmlTextWriterSetIndentString(pWriter, BAD_CAST(" ")); + (void)xmlTextWriterStartDocument(pWriter, nullptr, nullptr, nullptr); + bOwns = true; + } + (void)xmlTextWriterStartElement(pWriter, BAD_CAST("SdDrawDocument")); + (void)xmlTextWriterWriteFormatAttribute(pWriter, BAD_CAST("ptr"), "%p", this); + + if (mpOutliner) + mpOutliner->dumpAsXml(pWriter); + FmFormModel::dumpAsXml(pWriter); + if (GetUndoManager()) + GetUndoManager()->dumpAsXml(pWriter); + + (void)xmlTextWriterEndElement(pWriter); + if (bOwns) + { + (void)xmlTextWriterEndDocument(pWriter); + xmlFreeTextWriter(pWriter); + } +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/core/drawdoc2.cxx b/sd/source/core/drawdoc2.cxx new file mode 100644 index 000000000..d0187bab0 --- /dev/null +++ b/sd/source/core/drawdoc2.cxx @@ -0,0 +1,1382 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include <vcl/settings.hxx> + +#include <sal/log.hxx> +#include <tools/debug.hxx> +#include <sfx2/printer.hxx> +#include <editeng/paperinf.hxx> +#include <svx/svdopage.hxx> +#include <svx/svdoole2.hxx> +#include <svx/svdundo.hxx> +#include <vcl/svapp.hxx> +#include <editeng/eeitem.hxx> +#include <editeng/langitem.hxx> +#include <svl/itempool.hxx> +#include <editeng/flditem.hxx> + +#include <sfx2/linkmgr.hxx> +#include <svx/svdoutl.hxx> +#include <svx/svdlayer.hxx> + +#include <svx/svditer.hxx> +#include <comphelper/lok.hxx> +#include <xmloff/autolayout.hxx> + +#include <sdresid.hxx> +#include <drawdoc.hxx> +#include <sdpage.hxx> +#include <strings.hrc> +#include <glob.hxx> +#include <stlpool.hxx> +#include <anminfo.hxx> +#include <undo/undomanager.hxx> +#include <sfx2/lokhelper.hxx> +#include <unomodel.hxx> + +#include <DrawDocShell.hxx> + +#include "PageListWatcher.hxx" +#include <unokywds.hxx> + +using namespace ::sd; + +const ::tools::Long PRINT_OFFSET = 30; // see /svx/source/dialog/page.cxx + +using namespace com::sun::star; + +// Looks up an object by name +SdrObject* SdDrawDocument::GetObj(std::u16string_view rObjName) const +{ + SdrObject* pObj = nullptr; + SdrObject* pObjFound = nullptr; + const SdPage* pPage = nullptr; + + // First search in all pages + sal_uInt16 nPage = 0; + const sal_uInt16 nMaxPages = GetPageCount(); + + while (nPage < nMaxPages && !pObjFound) + { + pPage = static_cast<const SdPage*>( GetPage(nPage) ); + SdrObjListIter aIter(pPage, SdrIterMode::DeepWithGroups); + + while (aIter.IsMore() && !pObjFound) + { + pObj = aIter.Next(); + + if( ( pObj->GetName() == rObjName ) || + ( SdrInventor::Default == pObj->GetObjInventor() && + SdrObjKind::OLE2 == pObj->GetObjIdentifier() && + rObjName == static_cast< SdrOle2Obj* >( pObj )->GetPersistName() ) ) + { + pObjFound = pObj; + } + } + + nPage++; + } + + // If it couldn't be found, look through all master pages + nPage = 0; + const sal_uInt16 nMaxMasterPages = GetMasterPageCount(); + + while (nPage < nMaxMasterPages && !pObjFound) + { + pPage = static_cast<const SdPage*>( GetMasterPage(nPage) ); + SdrObjListIter aIter(pPage, SdrIterMode::DeepWithGroups); + + while (aIter.IsMore() && !pObjFound) + { + pObj = aIter.Next(); + + if( ( pObj->GetName() == rObjName ) || + ( SdrInventor::Default == pObj->GetObjInventor() && + SdrObjKind::OLE2 == pObj->GetObjIdentifier() && + rObjName == static_cast< SdrOle2Obj* >( pObj )->GetPersistName() ) ) + { + pObjFound = pObj; + } + } + + nPage++; + } + + return pObjFound; +} + +// Find SdPage by name +sal_uInt16 SdDrawDocument::GetPageByName(std::u16string_view rPgName, bool& rbIsMasterPage) const +{ + SdPage* pPage = nullptr; + sal_uInt16 nPage = 0; + const sal_uInt16 nMaxPages = GetPageCount(); + sal_uInt16 nPageNum = SDRPAGE_NOTFOUND; + + rbIsMasterPage = false; + + // Search all regular pages and all notes pages (handout pages are + // ignored) + while (nPage < nMaxPages && nPageNum == SDRPAGE_NOTFOUND) + { + pPage = const_cast<SdPage*>(static_cast<const SdPage*>( + GetPage(nPage))); + + if (pPage != nullptr + && pPage->GetPageKind() != PageKind::Handout + && pPage->GetName() == rPgName) + { + nPageNum = nPage; + } + + nPage++; + } + + // Search all master pages when not found among non-master pages + const sal_uInt16 nMaxMasterPages = GetMasterPageCount(); + nPage = 0; + + while (nPage < nMaxMasterPages && nPageNum == SDRPAGE_NOTFOUND) + { + pPage = const_cast<SdPage*>(static_cast<const SdPage*>( + GetMasterPage(nPage))); + + if (pPage && pPage->GetName() == rPgName) + { + nPageNum = nPage; + rbIsMasterPage = true; + } + + nPage++; + } + + return nPageNum; +} + +bool SdDrawDocument::IsPageNameUnique( std::u16string_view rPgName ) const +{ + sal_uInt16 nCount = 0; + SdPage* pPage = nullptr; + + // Search all regular pages and all notes pages (handout pages are ignored) + sal_uInt16 nPage = 0; + sal_uInt16 nMaxPages = GetPageCount(); + while (nPage < nMaxPages) + { + pPage = const_cast<SdPage*>(static_cast<const SdPage*>(GetPage(nPage))); + + if (pPage && pPage->GetName() == rPgName && pPage->GetPageKind() != PageKind::Handout) + nCount++; + + nPage++; + } + + // Search all master pages + nPage = 0; + nMaxPages = GetMasterPageCount(); + while (nPage < nMaxPages) + { + pPage = const_cast<SdPage*>(static_cast<const SdPage*>(GetMasterPage(nPage))); + + if (pPage && pPage->GetName() == rPgName) + nCount++; + + nPage++; + } + + return nCount == 1; +} + +SdPage* SdDrawDocument::GetSdPage(sal_uInt16 nPgNum, PageKind ePgKind) const +{ + return mpDrawPageListWatcher->GetSdPage(ePgKind, sal_uInt32(nPgNum)); +} + +sal_uInt16 SdDrawDocument::GetSdPageCount(PageKind ePgKind) const +{ + return static_cast<sal_uInt16>(mpDrawPageListWatcher->GetSdPageCount(ePgKind)); +} + +SdPage* SdDrawDocument::GetMasterSdPage(sal_uInt16 nPgNum, PageKind ePgKind) +{ + return mpMasterPageListWatcher->GetSdPage(ePgKind, sal_uInt32(nPgNum)); +} + +sal_uInt16 SdDrawDocument::GetMasterSdPageCount(PageKind ePgKind) const +{ + return static_cast<sal_uInt16>(mpMasterPageListWatcher->GetSdPageCount(ePgKind)); +} + +sal_uInt16 SdDrawDocument::GetActiveSdPageCount() const +{ + return static_cast<sal_uInt16>(mpDrawPageListWatcher->GetVisibleSdPageCount()); +} + +// Adapt the page numbers that are registered in the page objects of the notes +// pages +void SdDrawDocument::UpdatePageObjectsInNotes(sal_uInt16 nStartPos) +{ + sal_uInt16 nPageCount = GetPageCount(); + SdPage* pPage = nullptr; + + for (sal_uInt16 nPage = nStartPos; nPage < nPageCount; nPage++) + { + pPage = static_cast<SdPage*>( GetPage(nPage) ); + + // If this is a notes page, find its page object and correct the page + // number + if (pPage && pPage->GetPageKind() == PageKind::Notes) + { + const size_t nObjCount = pPage->GetObjCount(); + for (size_t nObj = 0; nObj < nObjCount; ++nObj) + { + SdrObject* pObj = pPage->GetObj(nObj); + if (pObj->GetObjIdentifier() == SdrObjKind::Page && + pObj->GetObjInventor() == SdrInventor::Default) + { + // The page object is the preceding page (drawing page) + SAL_WARN_IF(!nStartPos, "sd", "Position of notes page must not be 0."); + + SAL_WARN_IF(nPage <= 1, "sd", "Page object must not be a handout."); + + if (nStartPos > 0 && nPage > 1) + static_cast<SdrPageObj*>(pObj)->SetReferencedPage(GetPage(nPage - 1)); + } + } + } + } +} + +void SdDrawDocument::UpdatePageRelativeURLs(const OUString& rOldName, std::u16string_view rNewName) +{ + if (rNewName.empty()) + return; + + SfxItemPool& rPool(GetPool()); + for (const SfxPoolItem* pItem : rPool.GetItemSurrogates(EE_FEATURE_FIELD)) + { + const SvxFieldItem* pFldItem = dynamic_cast< const SvxFieldItem * > (pItem); + + if(pFldItem) + { + SvxURLField* pURLField = const_cast< SvxURLField* >( dynamic_cast<const SvxURLField*>( pFldItem->GetField() ) ); + + if(pURLField) + { + OUString aURL = pURLField->GetURL(); + + if (!aURL.isEmpty() && (aURL[0] == 35) && (aURL.indexOf(rOldName, 1) == 1)) + { + if (aURL.getLength() == rOldName.getLength() + 1) // standard page name + { + aURL = aURL.replaceAt(1, aURL.getLength() - 1, u"") + + rNewName; + pURLField->SetURL(aURL); + } + else + { + const OUString sNotes(SdResId(STR_NOTES)); + if (aURL.getLength() == rOldName.getLength() + 2 + sNotes.getLength() + && aURL.indexOf(sNotes, rOldName.getLength() + 2) == rOldName.getLength() + 2) + { + aURL = aURL.replaceAt(1, aURL.getLength() - 1, u"") + + rNewName + " " + sNotes; + pURLField->SetURL(aURL); + } + } + } + } + } + } +} + +void SdDrawDocument::UpdatePageRelativeURLs(SdPage const * pPage, sal_uInt16 nPos, sal_Int32 nIncrement) +{ + bool bNotes = (pPage->GetPageKind() == PageKind::Notes); + + SfxItemPool& rPool(GetPool()); + for (const SfxPoolItem* pItem : rPool.GetItemSurrogates(EE_FEATURE_FIELD)) + { + const SvxFieldItem* pFldItem; + + if ((pFldItem = dynamic_cast< const SvxFieldItem * > (pItem)) != nullptr) + { + SvxURLField* pURLField = const_cast< SvxURLField* >( dynamic_cast<const SvxURLField*>( pFldItem->GetField() ) ); + + if(pURLField) + { + OUString aURL = pURLField->GetURL(); + + if (!aURL.isEmpty() && (aURL[0] == 35)) + { + OUString aHashSlide = "#" + SdResId(STR_PAGE); + + if (aURL.startsWith(aHashSlide)) + { + OUString aURLCopy = aURL; + const OUString sNotes(SdResId(STR_NOTES)); + + aURLCopy = aURLCopy.replaceAt(0, aHashSlide.getLength(), u""); + + bool bNotesLink = ( aURLCopy.getLength() >= sNotes.getLength() + 3 + && aURLCopy.endsWith(sNotes) ); + + if (bNotesLink != bNotes) + continue; // no compatible link and page + + if (bNotes) + aURLCopy = aURLCopy.replaceAt(aURLCopy.getLength() - sNotes.getLength(), sNotes.getLength(), u""); + + sal_Int32 number = aURLCopy.toInt32(); + sal_uInt16 realPageNumber = (nPos + 1)/ 2; + + if ( number >= realPageNumber ) + { + // update link page number + number += nIncrement; + aURL = aURL.replaceAt(aHashSlide.getLength() + 1, aURL.getLength() - aHashSlide.getLength() - 1, u"") + + OUString::number(number); + if (bNotes) + { + aURL += " " + sNotes; + } + pURLField->SetURL(aURL); + } + } + } + } + } + } +} + +// Move page +void SdDrawDocument::MovePage(sal_uInt16 nPgNum, sal_uInt16 nNewPos) +{ + FmFormModel::MovePage(nPgNum, nNewPos); + + sal_uInt16 nMin = std::min(nPgNum, nNewPos); + + UpdatePageObjectsInNotes(nMin); +} + +// Insert page +void SdDrawDocument::InsertPage(SdrPage* pPage, sal_uInt16 nPos) +{ + bool bLast = (nPos == GetPageCount()); + + FmFormModel::InsertPage(pPage, nPos); + + static_cast<SdPage*>(pPage)->ConnectLink(); + + UpdatePageObjectsInNotes(nPos); + + if (!bLast) + UpdatePageRelativeURLs(static_cast<SdPage*>( pPage ), nPos, 1); + + if (comphelper::LibreOfficeKit::isActive() && static_cast<SdPage*>(pPage)->GetPageKind() == PageKind::Standard) + { + SdXImpressDocument* pDoc = comphelper::getFromUnoTunnel<SdXImpressDocument>(this->getUnoModel()); + SfxLokHelper::notifyDocumentSizeChangedAllViews(pDoc); + } +} + +// Delete page +void SdDrawDocument::DeletePage(sal_uInt16 nPgNum) +{ + FmFormModel::DeletePage(nPgNum); + + UpdatePageObjectsInNotes(nPgNum); +} + +// Remove page +rtl::Reference<SdrPage> SdDrawDocument::RemovePage(sal_uInt16 nPgNum) +{ + rtl::Reference<SdrPage> pPage = FmFormModel::RemovePage(nPgNum); + + bool bLast = ((nPgNum+1)/2 == (GetPageCount()+1)/2); + + auto pSdPage = static_cast<SdPage*>(pPage.get()); + pSdPage->DisconnectLink(); + ReplacePageInCustomShows( pSdPage, nullptr ); + UpdatePageObjectsInNotes(nPgNum); + + if (!bLast) + UpdatePageRelativeURLs(pSdPage, nPgNum, -1); + + if (comphelper::LibreOfficeKit::isActive() && pSdPage->GetPageKind() == PageKind::Standard) + { + SdXImpressDocument* pDoc = comphelper::getFromUnoTunnel<SdXImpressDocument>(this->getUnoModel()); + SfxLokHelper::notifyDocumentSizeChangedAllViews(pDoc); + } + + return pPage; +} + +// Warning: This is not called for new master pages created from SdrModel::Merge, +// you also have to modify code in SdDrawDocument::Merge! +void SdDrawDocument::InsertMasterPage(SdrPage* pPage, sal_uInt16 nPos ) +{ + FmFormModel::InsertMasterPage( pPage, nPos ); + if( pPage->IsMasterPage() && (static_cast<SdPage*>(pPage)->GetPageKind() == PageKind::Standard) ) + { + // new master page created, add its style family + SdStyleSheetPool* pStylePool = static_cast<SdStyleSheetPool*>( GetStyleSheetPool() ); + if( pStylePool ) + pStylePool->AddStyleFamily( static_cast<SdPage*>(pPage) ); + } +} + +rtl::Reference<SdrPage> SdDrawDocument::RemoveMasterPage(sal_uInt16 nPgNum) +{ + SdPage* pPage = static_cast<SdPage*>(GetMasterPage(nPgNum )); + if( pPage && pPage->IsMasterPage() && (pPage->GetPageKind() == PageKind::Standard) ) + { + // master page removed, remove its style family + SdStyleSheetPool* pStylePool = static_cast<SdStyleSheetPool*>( GetStyleSheetPool() ); + if( pStylePool ) + pStylePool->RemoveStyleFamily( pPage ); + } + + return FmFormModel::RemoveMasterPage(nPgNum); +} + +//Select pages +void SdDrawDocument::SetSelected(SdPage* pPage, bool bSelect) +{ + PageKind ePageKind = pPage->GetPageKind(); + + if (ePageKind == PageKind::Standard) + { + pPage->SetSelected(bSelect); + + const sal_uInt16 nDestPageNum(pPage->GetPageNum() + 1); + SdPage* pNotesPage = nullptr; + + if(nDestPageNum < GetPageCount()) + { + pNotesPage = static_cast<SdPage*>(GetPage(nDestPageNum)); + } + + if (pNotesPage && pNotesPage->GetPageKind() == PageKind::Notes) + { + pNotesPage->SetSelected(bSelect); + } + } + else if (ePageKind == PageKind::Notes) + { + pPage->SetSelected(bSelect); + SdPage* pStandardPage = static_cast<SdPage*>( GetPage( pPage->GetPageNum() - 1 ) ); + + if (pStandardPage && pStandardPage->GetPageKind() == PageKind::Standard) + pStandardPage->SetSelected(bSelect); + } +} + +// If no pages exist yet, create them now +void SdDrawDocument::CreateFirstPages( SdDrawDocument const * pRefDocument /* = 0 */ ) +{ + // If no page exists yet in the model, (File -> New), insert a page + sal_uInt16 nPageCount = GetPageCount(); + + if (nPageCount > 1) + return; + + // #i57181# Paper size depends on Language, like in Writer + Size aDefSize = SvxPaperInfo::GetDefaultPaperSize( MapUnit::Map100thMM ); + + // Insert handout page + rtl::Reference<SdPage> pHandoutPage = AllocSdPage(false); + + SdPage* pRefPage = nullptr; + + if( pRefDocument ) + pRefPage = pRefDocument->GetSdPage( 0, PageKind::Handout ); + + if( pRefPage ) + { + pHandoutPage->SetSize(pRefPage->GetSize()); + pHandoutPage->SetBorder( pRefPage->GetLeftBorder(), pRefPage->GetUpperBorder(), pRefPage->GetRightBorder(), pRefPage->GetLowerBorder() ); + } + else + { + pHandoutPage->SetSize(aDefSize); + pHandoutPage->SetBorder(0, 0, 0, 0); + } + + pHandoutPage->SetPageKind(PageKind::Handout); + pHandoutPage->SetName( SdResId(STR_HANDOUT) ); + InsertPage(pHandoutPage.get(), 0); + + // Insert master page and register this with the handout page + rtl::Reference<SdPage> pHandoutMPage = AllocSdPage(true); + pHandoutMPage->SetSize( pHandoutPage->GetSize() ); + pHandoutMPage->SetPageKind(PageKind::Handout); + pHandoutMPage->SetBorder( pHandoutPage->GetLeftBorder(), + pHandoutPage->GetUpperBorder(), + pHandoutPage->GetRightBorder(), + pHandoutPage->GetLowerBorder() ); + InsertMasterPage(pHandoutMPage.get(), 0); + pHandoutPage->TRG_SetMasterPage( *pHandoutMPage ); + + // Insert page + // If nPageCount==1 is, the model for the clipboard was created, thus a + // default page must already exist + rtl::Reference<SdPage> pPage; + bool bClipboard = false; + + if( pRefDocument ) + pRefPage = pRefDocument->GetSdPage( 0, PageKind::Standard ); + + if (nPageCount == 0) + { + pPage = AllocSdPage(false); + + if( pRefPage ) + { + pPage->SetSize( pRefPage->GetSize() ); + pPage->SetBorder( pRefPage->GetLeftBorder(), pRefPage->GetUpperBorder(), pRefPage->GetRightBorder(), pRefPage->GetLowerBorder() ); + } + else if (meDocType == DocumentType::Draw) + { + // Draw: always use default size with margins + pPage->SetSize(aDefSize); + + SfxPrinter* pPrinter = mpDocSh->GetPrinter(false); + if (pPrinter && pPrinter->IsValid()) + { + Size aOutSize(pPrinter->GetOutputSize()); + Point aPageOffset(pPrinter->GetPageOffset()); + aPageOffset -= pPrinter->PixelToLogic( Point() ); + ::tools::Long nOffset = !aPageOffset.X() && !aPageOffset.Y() ? 0 : PRINT_OFFSET; + + sal_uLong nTop = aPageOffset.Y(); + sal_uLong nLeft = aPageOffset.X(); + sal_uLong nBottom = std::max(::tools::Long(aDefSize.Height() - aOutSize.Height() - nTop + nOffset), ::tools::Long(0)); + sal_uLong nRight = std::max(::tools::Long(aDefSize.Width() - aOutSize.Width() - nLeft + nOffset), ::tools::Long(0)); + + pPage->SetBorder(nLeft, nTop, nRight, nBottom); + } + else + { + // The printer is not available. Use a border of 10mm + // on each side instead. + // This has to be kept synchronized with the border + // width set in the + // SvxPageDescPage::PaperSizeSelect_Impl callback. + pPage->SetBorder(1000, 1000, 1000, 1000); + } + } + else + { + // Impress: always use screen format, landscape. + Size aSz( SvxPaperInfo::GetPaperSize(PAPER_SCREEN_16_9, MapUnit::Map100thMM) ); + pPage->SetSize( Size( aSz.Height(), aSz.Width() ) ); + pPage->SetBorder(0, 0, 0, 0); + } + + InsertPage(pPage.get(), 1); + } + else + { + bClipboard = true; + pPage = static_cast<SdPage*>( GetPage(1) ); + } + + // Insert master page, then register this with the page + rtl::Reference<SdPage> pMPage = AllocSdPage(true); + pMPage->SetSize( pPage->GetSize() ); + pMPage->SetBorder( pPage->GetLeftBorder(), + pPage->GetUpperBorder(), + pPage->GetRightBorder(), + pPage->GetLowerBorder() ); + InsertMasterPage(pMPage.get(), 1); + pPage->TRG_SetMasterPage( *pMPage ); + if( bClipboard ) + pMPage->SetLayoutName( pPage->GetLayoutName() ); + + // Insert notes page + rtl::Reference<SdPage> pNotesPage = AllocSdPage(false); + + if( pRefDocument ) + pRefPage = pRefDocument->GetSdPage( 0, PageKind::Notes ); + + if( pRefPage ) + { + pNotesPage->SetSize( pRefPage->GetSize() ); + pNotesPage->SetBorder( pRefPage->GetLeftBorder(), pRefPage->GetUpperBorder(), pRefPage->GetRightBorder(), pRefPage->GetLowerBorder() ); + } + else + { + // Always use portrait format + if (aDefSize.Height() >= aDefSize.Width()) + { + pNotesPage->SetSize(aDefSize); + } + else + { + pNotesPage->SetSize( Size(aDefSize.Height(), aDefSize.Width()) ); + } + + pNotesPage->SetBorder(0, 0, 0, 0); + } + pNotesPage->SetPageKind(PageKind::Notes); + InsertPage(pNotesPage.get(), 2); + if( bClipboard ) + pNotesPage->SetLayoutName( pPage->GetLayoutName() ); + + // Insert master page, then register this with the notes page + rtl::Reference<SdPage> pNotesMPage = AllocSdPage(true); + pNotesMPage->SetSize( pNotesPage->GetSize() ); + pNotesMPage->SetPageKind(PageKind::Notes); + pNotesMPage->SetBorder( pNotesPage->GetLeftBorder(), + pNotesPage->GetUpperBorder(), + pNotesPage->GetRightBorder(), + pNotesPage->GetLowerBorder() ); + InsertMasterPage(pNotesMPage.get(), 2); + pNotesPage->TRG_SetMasterPage( *pNotesMPage ); + if( bClipboard ) + pNotesMPage->SetLayoutName( pPage->GetLayoutName() ); + + if( !pRefPage && (meDocType != DocumentType::Draw) ) + pPage->SetAutoLayout( AUTOLAYOUT_TITLE, true, true ); + + mpWorkStartupTimer.reset( new Timer("DrawWorkStartupTimer") ); + mpWorkStartupTimer->SetInvokeHandler( LINK(this, SdDrawDocument, WorkStartupHdl) ); + mpWorkStartupTimer->SetTimeout(2000); + mpWorkStartupTimer->Start(); + + SetChanged(false); +} + +// Creates missing notes and handout pages (after PowerPoint import). +// We assume that at least one default page and one default master page exist. + +bool SdDrawDocument::CreateMissingNotesAndHandoutPages() +{ + bool bOK = false; + sal_uInt16 nPageCount = GetPageCount(); + + if (nPageCount != 0) + { + // Set PageKind + SdPage* pHandoutMPage = static_cast<SdPage*>( GetMasterPage(0) ); + pHandoutMPage->SetPageKind(PageKind::Handout); + + SdPage* pHandoutPage = static_cast<SdPage*>( GetPage(0) ); + pHandoutPage->SetPageKind(PageKind::Handout); + pHandoutPage->TRG_SetMasterPage( *pHandoutMPage ); + + for (sal_uInt16 i = 1; i < nPageCount; i = i + 2) + { + SdPage* pPage = static_cast<SdPage*>( GetPage(i) ); + + if(!pPage->TRG_HasMasterPage()) + { + // No master page set -> use first default master page + // (If there was no default page in the PPT) + pPage->TRG_SetMasterPage(*GetMasterPage(1)); + } + + SdPage* pNotesPage = static_cast<SdPage*>( GetPage(i+1) ); + pNotesPage->SetPageKind(PageKind::Notes); + + // Set notes master page + sal_uInt16 nMasterPageAfterPagesMasterPage = pPage->TRG_GetMasterPage().GetPageNum() + 1; + pNotesPage->TRG_SetMasterPage(*GetMasterPage(nMasterPageAfterPagesMasterPage)); + } + + bOK = true; + StopWorkStartupDelay(); + SetChanged(false); + } + + return bOK; +} + +void SdDrawDocument::UnselectAllPages() +{ + sal_uInt16 nNoOfPages = GetSdPageCount(PageKind::Standard); + for (sal_uInt16 nPage = 0; nPage < nNoOfPages; ++nPage) + { + SdPage* pPage = GetSdPage(nPage, PageKind::Standard); + pPage->SetSelected(false); + } +} + +// + Move selected pages after said page +// (nTargetPage = (sal_uInt16)-1 --> move before first page) +// + Returns sal_True when the page has been moved +bool SdDrawDocument::MovePages(sal_uInt16 nTargetPage) +{ + SdPage* pPage = nullptr; + sal_uInt16 nPage; + sal_uInt16 nNoOfPages = GetSdPageCount(PageKind::Standard); + bool bSomethingHappened = false; + + const bool bUndo = IsUndoEnabled(); + + if( bUndo ) + BegUndo(SdResId(STR_UNDO_MOVEPAGES)); + + // List of selected pages + std::vector<SdPage*> aPageList; + for (nPage = 0; nPage < nNoOfPages; nPage++) + { + pPage = GetSdPage(nPage, PageKind::Standard); + + if (pPage->IsSelected()) { + aPageList.push_back(pPage); + } + } + + // If necessary, look backwards, until we find a page that wasn't selected + nPage = nTargetPage; + + if (nPage != sal_uInt16(-1)) + { + pPage = GetSdPage(nPage, PageKind::Standard); + while (nPage > 0 && pPage->IsSelected()) + { + nPage--; + pPage = GetSdPage(nPage, PageKind::Standard); + } + + if (pPage->IsSelected()) + { + nPage = sal_uInt16(-1); + } + } + + // Insert before the first page + if (nPage == sal_uInt16(-1)) + { + std::vector<SdPage*>::reverse_iterator iter; + for (iter = aPageList.rbegin(); iter != aPageList.rend(); ++iter) + { + nPage = (*iter)->GetPageNum(); + if (nPage != 0) + { + SdrPage* pPg = GetPage(nPage); + if( bUndo ) + AddUndo(GetSdrUndoFactory().CreateUndoSetPageNum(*pPg, nPage, 1)); + MovePage(nPage, 1); + pPg = GetPage(nPage+1); + if( bUndo ) + AddUndo(GetSdrUndoFactory().CreateUndoSetPageNum(*pPg, nPage+1, 2)); + MovePage(nPage+1, 2); + bSomethingHappened = true; + } + } + } + // Insert after <nPage> + else + { + nTargetPage = 2 * nPage + 1; // PageKind::Standard --> absolute + + for (const auto& rpPage : aPageList) + { + nPage = rpPage->GetPageNum(); + if (nPage > nTargetPage) + { + nTargetPage += 2; // Insert _after_ the page + + if (nPage != nTargetPage) + { + SdrPage* pPg = GetPage(nPage); + if( bUndo ) + AddUndo(GetSdrUndoFactory().CreateUndoSetPageNum(*pPg, nPage, nTargetPage)); + MovePage(nPage, nTargetPage); + pPg = GetPage(nPage+1); + if( bUndo ) + AddUndo(GetSdrUndoFactory().CreateUndoSetPageNum(*pPg, nPage+1, nTargetPage+1)); + MovePage(nPage+1, nTargetPage+1); + bSomethingHappened = true; + } + } + else + { + if (nPage != nTargetPage) + { + SdrPage* pPg = GetPage(nPage+1); + if( bUndo ) + AddUndo(GetSdrUndoFactory().CreateUndoSetPageNum(*pPg, nPage+1, nTargetPage+1)); + MovePage(nPage+1, nTargetPage+1); + pPg = GetPage(nPage); + if( bUndo ) + AddUndo(GetSdrUndoFactory().CreateUndoSetPageNum(*pPg, nPage, nTargetPage)); + MovePage(nPage, nTargetPage); + bSomethingHappened = true; + } + } + nTargetPage = rpPage->GetPageNum(); + } + } + + if( bUndo ) + EndUndo(); + + return bSomethingHappened; +} + +// Return number of links in sfx2::LinkManager +sal_uLong SdDrawDocument::GetLinkCount() const +{ + return m_pLinkManager->GetLinks().size(); +} + +// Set Language +void SdDrawDocument::SetLanguage( const LanguageType eLang, const sal_uInt16 nId ) +{ + bool bChanged = false; + + if( nId == EE_CHAR_LANGUAGE && meLanguage != eLang ) + { + meLanguage = eLang; + bChanged = true; + } + else if( nId == EE_CHAR_LANGUAGE_CJK && meLanguageCJK != eLang ) + { + meLanguageCJK = eLang; + bChanged = true; + } + else if( nId == EE_CHAR_LANGUAGE_CTL && meLanguageCTL != eLang ) + { + meLanguageCTL = eLang; + bChanged = true; + } + + if( bChanged ) + { + GetDrawOutliner().SetDefaultLanguage( Application::GetSettings().GetLanguageTag().getLanguageType() ); + m_pHitTestOutliner->SetDefaultLanguage( Application::GetSettings().GetLanguageTag().getLanguageType() ); + m_pItemPool->SetPoolDefaultItem( SvxLanguageItem( eLang, nId ) ); + SetChanged( bChanged ); + } +} + +// Return language +LanguageType SdDrawDocument::GetLanguage( const sal_uInt16 nId ) const +{ + LanguageType eLangType = meLanguage; + + if( nId == EE_CHAR_LANGUAGE_CJK ) + eLangType = meLanguageCJK; + else if( nId == EE_CHAR_LANGUAGE_CTL ) + eLangType = meLanguageCTL; + + return eLangType; +} + +// Initiate WorkStartup +IMPL_LINK_NOARG(SdDrawDocument, WorkStartupHdl, Timer *, void) +{ + if (IsTransportContainer()) + return; + + if( mpDocSh ) + mpDocSh->SetWaitCursor( true ); + + bool bChanged = IsChanged(); // remember this + + // Initialize Autolayouts + SdPage* pHandoutMPage = GetMasterSdPage(0, PageKind::Handout); + + if (pHandoutMPage->GetAutoLayout() == AUTOLAYOUT_NONE) + { + // No AutoLayout yet -> initialize + pHandoutMPage->SetAutoLayout(AUTOLAYOUT_HANDOUT6, true, true); + } + + SdPage* pPage = GetSdPage(0, PageKind::Standard); + + if (pPage->GetAutoLayout() == AUTOLAYOUT_NONE) + { + // No AutoLayout yet -> initialize + pPage->SetAutoLayout(AUTOLAYOUT_NONE, true, true); + } + + SdPage* pNotesPage = GetSdPage(0, PageKind::Notes); + + if (pNotesPage->GetAutoLayout() == AUTOLAYOUT_NONE) + { + // No AutoLayout yet -> initialize + pNotesPage->SetAutoLayout(AUTOLAYOUT_NOTES, true, true); + } + + SetChanged(bChanged); + + if( mpDocSh ) + mpDocSh->SetWaitCursor( false ); +} + +// When the WorkStartupTimer has been created (this only happens in +// SdDrawViewShell::Construct() ), the timer may be stopped and the WorkStartup +// may be initiated. +void SdDrawDocument::StopWorkStartupDelay() +{ + if (mpWorkStartupTimer) + { + if ( mpWorkStartupTimer->IsActive() ) + { + // Timer not yet expired -> initiate WorkStartup + mpWorkStartupTimer->Stop(); + WorkStartupHdl(nullptr); + } + + mpWorkStartupTimer.reset(); + } +} + +// When the WorkStartupTimer has been created (this only happens in +// SdDrawViewShell::Construct() ), the timer may be stopped and the WorkStartup +// may be initiated. +SdAnimationInfo* SdDrawDocument::GetAnimationInfo(SdrObject* pObject) +{ + DBG_ASSERT(pObject, "sd::SdDrawDocument::GetAnimationInfo(), invalid argument!"); + if( pObject ) + return GetShapeUserData( *pObject ); + else + return nullptr; +} + +SdAnimationInfo* SdDrawDocument::GetShapeUserData(SdrObject& rObject, bool bCreate /* = false */ ) +{ + sal_uInt16 nUD = 0; + sal_uInt16 nUDCount = rObject.GetUserDataCount(); + SdAnimationInfo* pRet = nullptr; + + // Can we find animation information within the user data? + for (nUD = 0; nUD < nUDCount; nUD++) + { + SdrObjUserData* pUD = rObject.GetUserData(nUD); + if((pUD->GetInventor() == SdrInventor::StarDrawUserData) && (pUD->GetId() == SD_ANIMATIONINFO_ID)) + { + pRet = dynamic_cast<SdAnimationInfo*>(pUD); + break; + } + } + + if( (pRet == nullptr) && bCreate ) + { + pRet = new SdAnimationInfo( rObject ); + rObject.AppendUserData( std::unique_ptr<SdrObjUserData>(pRet) ); + } + + return pRet; +} + +/** this method enforces that the masterpages are in the correct order, + that is at position 1 is a PageKind::Standard masterpage followed by a + PageKind::Notes masterpage and so on. # +*/ +void SdDrawDocument::CheckMasterPages() +{ + sal_uInt16 nMaxPages = GetMasterPageCount(); + + // we need at least a handout master and one master page + if( nMaxPages < 2 ) + { + return; + } + + SdPage* pPage = nullptr; + + sal_uInt16 nPage; + + // first see if the page order is correct + for( nPage = 1; nPage < nMaxPages; nPage++ ) + { + pPage = static_cast<SdPage*> (GetMasterPage( nPage )); + // if an odd page is not a standard page or an even page is not a notes page + if( ((1 == (nPage & 1)) && (pPage->GetPageKind() != PageKind::Standard) ) || + ((0 == (nPage & 1)) && (pPage->GetPageKind() != PageKind::Notes) ) ) + break; // then we have a fatal error + } + + if( nPage >= nMaxPages ) + return; + + SdPage* pNotesPage = nullptr; + + // there is a fatal error in the master page order, + // we need to repair the document + bool bChanged = false; + + nPage = 1; + while( nPage < nMaxPages ) + { + pPage = static_cast<SdPage*> (GetMasterPage( nPage )); + if( pPage->GetPageKind() != PageKind::Standard ) + { + bChanged = true; + sal_uInt16 nFound = nPage + 1; + while( nFound < nMaxPages ) + { + pPage = static_cast<SdPage*>(GetMasterPage( nFound )); + if( PageKind::Standard == pPage->GetPageKind() ) + { + MoveMasterPage( nFound, nPage ); + pPage->SetInserted(); + break; + + } + + nFound++; + } + + // if we don't have any more standard pages, were done + if( nMaxPages == nFound ) + break; + } + + nPage++; + + if( nPage < nMaxPages ) + pNotesPage = static_cast<SdPage*>(GetMasterPage( nPage )); + else + pNotesPage = nullptr; + + if( (nullptr == pNotesPage) || (pNotesPage->GetPageKind() != PageKind::Notes) || ( pPage->GetLayoutName() != pNotesPage->GetLayoutName() ) ) + { + bChanged = true; + + sal_uInt16 nFound = nPage + 1; + while( nFound < nMaxPages ) + { + pNotesPage = static_cast<SdPage*>(GetMasterPage( nFound )); + if( (PageKind::Notes == pNotesPage->GetPageKind()) && ( pPage->GetLayoutName() == pNotesPage->GetLayoutName() ) ) + { + MoveMasterPage( nFound, nPage ); + pNotesPage->SetInserted(); + break; + } + + nFound++; + } + + // looks like we lost a notes page + if( nMaxPages == nFound ) + { + // so create one + + // first find a reference notes page for size + SdPage* pRefNotesPage = nullptr; + nFound = 0; + while( nFound < nMaxPages ) + { + pRefNotesPage = static_cast<SdPage*>(GetMasterPage( nFound )); + if( PageKind::Notes == pRefNotesPage->GetPageKind() ) + break; + nFound++; + } + if( nFound == nMaxPages ) + pRefNotesPage = nullptr; + + rtl::Reference<SdPage> pNewNotesPage = AllocSdPage(true); + pNewNotesPage->SetPageKind(PageKind::Notes); + if( pRefNotesPage ) + { + pNewNotesPage->SetSize( pRefNotesPage->GetSize() ); + pNewNotesPage->SetBorder( pRefNotesPage->GetLeftBorder(), + pRefNotesPage->GetUpperBorder(), + pRefNotesPage->GetRightBorder(), + pRefNotesPage->GetLowerBorder() ); + } + InsertMasterPage(pNewNotesPage.get(), nPage ); + pNewNotesPage->SetLayoutName( pPage->GetLayoutName() ); + pNewNotesPage->SetAutoLayout(AUTOLAYOUT_NOTES, true, true ); + nMaxPages++; + } + } + + nPage++; + } + + // now remove all remaining and unused non PageKind::Standard slides + while( nPage < nMaxPages ) + { + bChanged = true; + + RemoveMasterPage( nPage ); + nMaxPages--; + } + + if( bChanged ) + { + OSL_FAIL( "master pages where in a wrong order" ); + RecalcPageNums( true); + } +} + +sal_uInt16 SdDrawDocument::CreatePage ( + SdPage* pActualPage, + PageKind ePageKind, + const OUString& sStandardPageName, + const OUString& sNotesPageName, + AutoLayout eStandardLayout, + AutoLayout eNotesLayout, + bool bIsPageBack, + bool bIsPageObj, + const sal_Int32 nInsertPosition) +{ + SdPage* pPreviousStandardPage; + SdPage* pPreviousNotesPage; + rtl::Reference<SdPage> pStandardPage; + rtl::Reference<SdPage> pNotesPage; + + // From the given page determine the standard page and notes page of which + // to take the layout and the position where to insert the new pages. + if (ePageKind == PageKind::Notes) + { + pPreviousNotesPage = pActualPage; + sal_uInt16 nNotesPageNum = pPreviousNotesPage->GetPageNum() + 2; + pPreviousStandardPage = static_cast<SdPage*>( GetPage(nNotesPageNum - 3) ); + eStandardLayout = pPreviousStandardPage->GetAutoLayout(); + } + else + { + pPreviousStandardPage = pActualPage; + sal_uInt16 nStandardPageNum = pPreviousStandardPage->GetPageNum() + 2; + pPreviousNotesPage = static_cast<SdPage*>( GetPage(nStandardPageNum - 1) ); + eNotesLayout = pPreviousNotesPage->GetAutoLayout(); + } + + // Create new standard page and set it up + pStandardPage = AllocSdPage(false); + + // Set the size here since else the presobj autolayout + // will be wrong. + pStandardPage->SetSize( pPreviousStandardPage->GetSize() ); + pStandardPage->SetBorder( pPreviousStandardPage->GetLeftBorder(), + pPreviousStandardPage->GetUpperBorder(), + pPreviousStandardPage->GetRightBorder(), + pPreviousStandardPage->GetLowerBorder() ); + + // Use master page of current page. + pStandardPage->TRG_SetMasterPage(pPreviousStandardPage->TRG_GetMasterPage()); + + // User layout of current standard page + pStandardPage->SetLayoutName( pPreviousStandardPage->GetLayoutName() ); + pStandardPage->SetAutoLayout(eStandardLayout, true); + pStandardPage->setHeaderFooterSettings( pPreviousStandardPage->getHeaderFooterSettings() ); + + // transition settings of current page + pStandardPage->setTransitionType( pPreviousStandardPage->getTransitionType() ); + pStandardPage->setTransitionSubtype( pPreviousStandardPage->getTransitionSubtype() ); + pStandardPage->setTransitionDirection( pPreviousStandardPage->getTransitionDirection() ); + pStandardPage->setTransitionFadeColor( pPreviousStandardPage->getTransitionFadeColor() ); + pStandardPage->setTransitionDuration( pPreviousStandardPage->getTransitionDuration() ); + + // apply previous animation timing + pStandardPage->SetPresChange( pPreviousStandardPage->GetPresChange() ); + pStandardPage->SetTime( pPreviousStandardPage->GetTime() ); + + // Create new notes page and set it up + pNotesPage = AllocSdPage(false); + pNotesPage->SetPageKind(PageKind::Notes); + + // Use master page of current page + pNotesPage->TRG_SetMasterPage(pPreviousNotesPage->TRG_GetMasterPage()); + + // Use layout of current notes page + pNotesPage->SetLayoutName( pPreviousNotesPage->GetLayoutName() ); + pNotesPage->SetAutoLayout(eNotesLayout, true); + pNotesPage->setHeaderFooterSettings( pPreviousNotesPage->getHeaderFooterSettings() ); + + return InsertPageSet ( + pActualPage, + ePageKind, + sStandardPageName, + sNotesPageName, + bIsPageBack, + bIsPageObj, + pStandardPage.get(), + pNotesPage.get(), + nInsertPosition); +} + +sal_uInt16 SdDrawDocument::DuplicatePage (sal_uInt16 nPageNum) +{ + PageKind ePageKind = PageKind::Standard; + + // Get current page + SdPage* pActualPage = GetSdPage(nPageNum, ePageKind); + + // Get background flags + SdrLayerAdmin& rLayerAdmin = GetLayerAdmin(); + SdrLayerID aBckgrnd = rLayerAdmin.GetLayerID(sUNO_LayerName_background); + SdrLayerID aBckgrndObj = rLayerAdmin.GetLayerID(sUNO_LayerName_background_objects); + SdrLayerIDSet aVisibleLayers = pActualPage->TRG_GetMasterPageVisibleLayers(); + + return DuplicatePage ( + pActualPage, ePageKind, + // No names for the new slides + OUString(), OUString(), + aVisibleLayers.IsSet(aBckgrnd), + aVisibleLayers.IsSet(aBckgrndObj), -1); +} + +sal_uInt16 SdDrawDocument::DuplicatePage ( + SdPage* pActualPage, + PageKind ePageKind, + const OUString& sStandardPageName, + const OUString& sNotesPageName, + bool bIsPageBack, + bool bIsPageObj, + const sal_Int32 nInsertPosition) +{ + SdPage* pPreviousStandardPage; + SdPage* pPreviousNotesPage; + rtl::Reference<SdPage> pStandardPage; + rtl::Reference<SdPage> pNotesPage; + + // From the given page determine the standard page and the notes page + // of which to make copies. + if (ePageKind == PageKind::Notes) + { + pPreviousNotesPage = pActualPage; + sal_uInt16 nNotesPageNum = pPreviousNotesPage->GetPageNum() + 2; + pPreviousStandardPage = static_cast<SdPage*>( GetPage(nNotesPageNum - 3) ); + } + else + { + pPreviousStandardPage = pActualPage; + sal_uInt16 nStandardPageNum = pPreviousStandardPage->GetPageNum() + 2; + pPreviousNotesPage = static_cast<SdPage*>( GetPage(nStandardPageNum - 1) ); + } + + // Create duplicates of a standard page and the associated notes page + pStandardPage = static_cast<SdPage*>( pPreviousStandardPage->CloneSdrPage(*this).get() ); + pNotesPage = static_cast<SdPage*>( pPreviousNotesPage->CloneSdrPage(*this).get() ); + + return InsertPageSet ( + pActualPage, + ePageKind, + sStandardPageName, + sNotesPageName, + bIsPageBack, + bIsPageObj, + pStandardPage.get(), + pNotesPage.get(), + nInsertPosition); +} + +sal_uInt16 SdDrawDocument::InsertPageSet ( + SdPage* pActualPage, + PageKind ePageKind, + const OUString& sStandardPageName, + const OUString& sNotesPageName, + bool bIsPageBack, + bool bIsPageObj, + SdPage* pStandardPage, + SdPage* pNotesPage, + sal_Int32 nInsertPosition) +{ + SdPage* pPreviousStandardPage; + SdPage* pPreviousNotesPage; + sal_uInt16 nStandardPageNum; + sal_uInt16 nNotesPageNum; + OUString aNotesPageName(sNotesPageName); + + // Gather some information about the standard page and the notes page + // that are to be inserted. This makes sure that there is always one + // standard page followed by one notes page. + if (ePageKind == PageKind::Notes) + { + pPreviousNotesPage = pActualPage; + nNotesPageNum = pPreviousNotesPage->GetPageNum() + 2; + pPreviousStandardPage = static_cast<SdPage*>( GetPage(nNotesPageNum - 3) ); + nStandardPageNum = nNotesPageNum - 1; + } + else + { + pPreviousStandardPage = pActualPage; + nStandardPageNum = pPreviousStandardPage->GetPageNum() + 2; + pPreviousNotesPage = static_cast<SdPage*>( GetPage(nStandardPageNum - 1) ); + nNotesPageNum = nStandardPageNum + 1; + aNotesPageName = sStandardPageName; + } + + OSL_ASSERT(nNotesPageNum==nStandardPageNum+1); + if (nInsertPosition < 0) + nInsertPosition = nStandardPageNum; + + // Set up and insert the standard page + SetupNewPage ( + pPreviousStandardPage, + pStandardPage, + sStandardPageName, + nInsertPosition, + bIsPageBack, + bIsPageObj); + + // Set up and insert the notes page + pNotesPage->SetPageKind(PageKind::Notes); + SetupNewPage ( + pPreviousNotesPage, + pNotesPage, + aNotesPageName, + nInsertPosition+1, + bIsPageBack, + bIsPageObj); + + // Return an index that allows the caller to access the newly inserted + // pages by using GetSdPage() + return pStandardPage->GetPageNum() / 2; +} + +void SdDrawDocument::SetupNewPage ( + SdPage const * pPreviousPage, + SdPage* pPage, + const OUString& sPageName, + sal_uInt16 nInsertionPoint, + bool bIsPageBack, + bool bIsPageObj) +{ + if (pPreviousPage != nullptr) + { + pPage->SetSize( pPreviousPage->GetSize() ); + pPage->SetBorder( pPreviousPage->GetLeftBorder(), + pPreviousPage->GetUpperBorder(), + pPreviousPage->GetRightBorder(), + pPreviousPage->GetLowerBorder() ); + } + pPage->SetName(sPageName); + + InsertPage(pPage, nInsertionPoint); + + if (pPreviousPage != nullptr) + { + SdrLayerAdmin& rLayerAdmin = GetLayerAdmin(); + SdrLayerID aBckgrnd = rLayerAdmin.GetLayerID(sUNO_LayerName_background); + SdrLayerID aBckgrndObj = rLayerAdmin.GetLayerID(sUNO_LayerName_background_objects); + SdrLayerIDSet aVisibleLayers = pPreviousPage->TRG_GetMasterPageVisibleLayers(); + aVisibleLayers.Set(aBckgrnd, bIsPageBack); + aVisibleLayers.Set(aBckgrndObj, bIsPageObj); + pPage->TRG_SetMasterPageVisibleLayers(aVisibleLayers); + } +} + +sd::UndoManager* SdDrawDocument::GetUndoManager() const +{ + return mpDocSh ? dynamic_cast< sd::UndoManager* >(mpDocSh->GetUndoManager()) : nullptr; +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/core/drawdoc3.cxx b/sd/source/core/drawdoc3.cxx new file mode 100644 index 000000000..e25e8199d --- /dev/null +++ b/sd/source/core/drawdoc3.cxx @@ -0,0 +1,1873 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + + +#include <memory> +#include <string_view> + +#include <sfx2/docfile.hxx> +#include <sfx2/docfilt.hxx> +#include <sfx2/app.hxx> +#include <svl/itemset.hxx> +#include <tools/debug.hxx> +#include <tools/diagnose_ex.h> + +#include <sfx2/fcontnr.hxx> +#include <svl/style.hxx> +#include <svx/svdpagv.hxx> +#include <svx/svdundo.hxx> +#include <vcl/stdtext.hxx> +#include <vcl/svapp.hxx> +#include <vcl/weld.hxx> +#include <xmloff/autolayout.hxx> + +#include <strings.hrc> +#include <drawdoc.hxx> +#include <sdmod.hxx> +#include <sdpage.hxx> +#include <stlpool.hxx> +#include <sdresid.hxx> +#include <customshowlist.hxx> +#include <sdxfer.hxx> + +#include <unmovss.hxx> +#include <unchss.hxx> +#include <unprlout.hxx> +#include <DrawDocShell.hxx> +#include <GraphicDocShell.hxx> +#include <ViewShell.hxx> +#include <View.hxx> +#include <ViewShellBase.hxx> +#include <strings.hxx> + +using namespace ::com::sun::star; + +/** Concrete incarnations get called by lcl_IterateBookmarkPages, for + every page in the bookmark document/list + */ + +namespace { + +class InsertBookmarkAsPage_FindDuplicateLayouts +{ +public: + explicit InsertBookmarkAsPage_FindDuplicateLayouts( std::vector<OUString> &rLayoutsToTransfer ) + : mrLayoutsToTransfer(rLayoutsToTransfer) {} + void operator()( SdDrawDocument&, SdPage const *, bool, SdDrawDocument* ); +private: + std::vector<OUString> &mrLayoutsToTransfer; +}; + +} + +void InsertBookmarkAsPage_FindDuplicateLayouts::operator()( SdDrawDocument& rDoc, SdPage const * pBMMPage, bool bRenameDuplicates, SdDrawDocument* pBookmarkDoc ) +{ + // now check for duplicate masterpage and layout names + + OUString aLayout( pBMMPage->GetLayoutName() ); + sal_Int32 nIndex = aLayout.indexOf( SD_LT_SEPARATOR ); + if( nIndex != -1 ) + aLayout = aLayout.copy(0, nIndex); + + std::vector<OUString>::const_iterator pIter = + find(mrLayoutsToTransfer.begin(),mrLayoutsToTransfer.end(),aLayout); + + bool bFound = pIter != mrLayoutsToTransfer.end(); + + const sal_uInt16 nMPageCount = rDoc.GetMasterPageCount(); + for (sal_uInt16 nMPage = 0; nMPage < nMPageCount && !bFound; nMPage++) + { + // Do the layouts already exist within the document? + SdPage* pTestPage = static_cast<SdPage*>( rDoc.GetMasterPage(nMPage) ); + OUString aTest(pTestPage->GetLayoutName()); + sal_Int32 nIndex2 = aTest.indexOf( SD_LT_SEPARATOR ); + if( nIndex2 != -1 ) + aTest = aTest.copy(0, nIndex2); + + if (aTest == aLayout && pBMMPage->GetPageKind() == pTestPage->GetPageKind()) + { + // Ignore Layouts with "Default" these seem to be special - in the sense that there are lot of assumption all over Impress + // about this + if( bRenameDuplicates && aTest != SdResId( STR_LAYOUT_DEFAULT_NAME ) && !(pTestPage->Equals(*pBMMPage)) ) + { + pBookmarkDoc->RenameLayoutTemplate( + pBMMPage->GetLayoutName(), pBMMPage->GetName() + "_"); + aLayout = pBMMPage->GetName(); + + break; + } + else + bFound = true; + } + } + + if (!bFound) + mrLayoutsToTransfer.push_back(aLayout); +} + +// Inserts a bookmark as a page +static void lcl_IterateBookmarkPages( SdDrawDocument &rDoc, SdDrawDocument* pBookmarkDoc, + const std::vector<OUString> &rBookmarkList, sal_uInt16 nBMSdPageCount, + InsertBookmarkAsPage_FindDuplicateLayouts& rPageIterator, bool bRenameDuplicates ) +{ + + // Refactored copy'n'pasted layout name collection from InsertBookmarkAsPage + + int nPos, nEndPos; + + if( rBookmarkList.empty() ) + { + // no list? whole source document + nEndPos = nBMSdPageCount; + } + else + { + // bookmark list? number of entries + nEndPos = rBookmarkList.size(); + } + + SdPage* pBMPage; + + // iterate over number of pages to insert + for (nPos = 0; nPos < nEndPos; ++nPos) + { + // the master page associated to the nPos'th page to insert + SdPage* pBMMPage = nullptr; + + if( rBookmarkList.empty() ) + { + // simply take master page of nPos'th page in source document + pBMMPage = static_cast<SdPage*>(&(pBookmarkDoc->GetSdPage(static_cast<sal_uInt16>(nPos), PageKind::Standard)->TRG_GetMasterPage())); + } + else + { + // fetch nPos'th entry from bookmark list, and determine master page + OUString aBMPgName(rBookmarkList[nPos]); + bool bIsMasterPage; + + sal_uInt16 nBMPage = pBookmarkDoc->GetPageByName( aBMPgName, bIsMasterPage ); + + if (nBMPage != SDRPAGE_NOTFOUND) + { + pBMPage = static_cast<SdPage*>( pBookmarkDoc->GetPage(nBMPage) ); + } + else + { + pBMPage = nullptr; + } + + // enforce that bookmarked page is a standard page and not already a master page + if (pBMPage && pBMPage->GetPageKind()==PageKind::Standard && !pBMPage->IsMasterPage()) + { + const sal_uInt16 nBMSdPage = (nBMPage - 1) / 2; + pBMMPage = static_cast<SdPage*> (&(pBookmarkDoc->GetSdPage(nBMSdPage, PageKind::Standard)->TRG_GetMasterPage())); + } + } + + // successfully determined valid (bookmarked) page? + if( pBMMPage ) + { + // yes, call functor + rPageIterator( rDoc, pBMMPage, bRenameDuplicates, pBookmarkDoc ); + } + } +} + +// Opens a bookmark document +SdDrawDocument* SdDrawDocument::OpenBookmarkDoc(SfxMedium* pMedium) +{ + bool bOK = true; + SdDrawDocument* pBookmarkDoc = nullptr; + OUString aBookmarkName = pMedium->GetName(); + std::shared_ptr<const SfxFilter> pFilter = pMedium->GetFilter(); + if ( !pFilter ) + { + pMedium->UseInteractionHandler( true ); + SfxGetpApp()->GetFilterMatcher().GuessFilter(*pMedium, pFilter); + } + + if ( !pFilter ) + { + bOK = false; + } + else if ( !aBookmarkName.isEmpty() && maBookmarkFile != aBookmarkName ) + { + bool bCreateGraphicShell = pFilter->GetServiceName() == "com.sun.star.drawing.DrawingDocument"; + bool bCreateImpressShell = pFilter->GetServiceName() == "com.sun.star.presentation.PresentationDocument"; + if ( bCreateGraphicShell || bCreateImpressShell ) + { + CloseBookmarkDoc(); + + // Create a DocShell, as OLE objects might be contained in the + // document. (Persist) + // If that wasn't the case, we could load the model directly. + if ( bCreateGraphicShell ) + // Draw + mxBookmarkDocShRef = new ::sd::GraphicDocShell(SfxObjectCreateMode::STANDARD); + else + // Impress + mxBookmarkDocShRef = new ::sd::DrawDocShell(SfxObjectCreateMode::STANDARD, true, DocumentType::Impress); + + bOK = mxBookmarkDocShRef->DoLoad(pMedium); + if( bOK ) + { + maBookmarkFile = aBookmarkName; + pBookmarkDoc = mxBookmarkDocShRef->GetDoc(); + } + } + } + + DBG_ASSERT(!aBookmarkName.isEmpty(), "Empty document name!"); + + if (!bOK) + { + std::unique_ptr<weld::MessageDialog> xErrorBox(Application::CreateMessageDialog(nullptr, + VclMessageType::Warning, VclButtonsType::Ok, SdResId(STR_READ_DATA_ERROR))); + xErrorBox->run(); + + CloseBookmarkDoc(); + pBookmarkDoc = nullptr; + } + else if (mxBookmarkDocShRef.is()) + { + pBookmarkDoc = mxBookmarkDocShRef->GetDoc(); + } + + return pBookmarkDoc; +} + +// Opens a bookmark document +SdDrawDocument* SdDrawDocument::OpenBookmarkDoc(const OUString& rBookmarkFile) +{ + SdDrawDocument* pBookmarkDoc = nullptr; + + if (!rBookmarkFile.isEmpty() && maBookmarkFile != rBookmarkFile) + { + std::unique_ptr<SfxMedium> xMedium(new SfxMedium(rBookmarkFile, StreamMode::READ)); + pBookmarkDoc = OpenBookmarkDoc(xMedium.release()); + } + else if (mxBookmarkDocShRef.is()) + { + pBookmarkDoc = mxBookmarkDocShRef->GetDoc(); + } + + return pBookmarkDoc; +} + +// Inserts a bookmark (page or object) +void SdDrawDocument::InsertBookmark( + const std::vector<OUString> &rBookmarkList, // List of names of the bookmarks to be inserted + std::vector<OUString> &rExchangeList, // List of the names to be used + bool bLink, // Insert bookmarks as links? + sal_uInt16 nInsertPos, // Insertion position of pages + ::sd::DrawDocShell* pBookmarkDocSh, // If set, this is the source document + Point const * pObjPos) // Insertion position of objects +{ + bool bOK = true; + bool bInsertPages = false; + + if (rBookmarkList.empty()) + { + // Insert all pages + bInsertPages = true; + } + else + { + SdDrawDocument* pBookmarkDoc = nullptr; + + if (pBookmarkDocSh) + { + pBookmarkDoc = pBookmarkDocSh->GetDoc(); + } + else if ( mxBookmarkDocShRef.is() ) + { + pBookmarkDoc = mxBookmarkDocShRef->GetDoc(); + } + else + bOK = false; + + bInsertPages = bOK && std::any_of(rBookmarkList.begin(), rBookmarkList.end(), + [&pBookmarkDoc](const OUString& rBookmark) { + // Is there a page name in the bookmark list? + bool bIsMasterPage; + return pBookmarkDoc->GetPageByName(rBookmark, bIsMasterPage) != SDRPAGE_NOTFOUND; + }); + } + + bool bCalcObjCount = !rExchangeList.empty(); + + if ( bOK && bInsertPages ) + { + // Insert all page bookmarks + bOK = InsertBookmarkAsPage(rBookmarkList, &rExchangeList, bLink, false/*bReplace*/, + nInsertPos, false/*bNoDialogs*/, pBookmarkDocSh, true/*bCopy*/, true, false); + } + + if ( bOK && !rBookmarkList.empty() ) + { + // Insert all object bookmarks + InsertBookmarkAsObject(rBookmarkList, rExchangeList, + pBookmarkDocSh, pObjPos, bCalcObjCount); + } +} + +namespace +{ + +void +lcl_removeUnusedStyles(SfxStyleSheetBasePool* const pStyleSheetPool, StyleSheetCopyResultVector& rStyles) +{ + StyleSheetCopyResultVector aUsedStyles; + aUsedStyles.reserve(rStyles.size()); + for (const auto& a : rStyles) + { + if (a.m_xStyleSheet->IsUsed()) + aUsedStyles.push_back(a); + else + pStyleSheetPool->Remove(a.m_xStyleSheet.get()); + } + rStyles = aUsedStyles; +} + +SfxStyleSheet *lcl_findStyle(StyleSheetCopyResultVector& rStyles, std::u16string_view aStyleName) +{ + for (const auto& a : rStyles) + { + if (a.m_xStyleSheet->GetName().startsWith(aStyleName)) + return a.m_xStyleSheet.get(); + } + return nullptr; +} + +} + +bool SdDrawDocument::InsertBookmarkAsPage( + const std::vector<OUString> &rBookmarkList, + std::vector<OUString> *pExchangeList, // List of names to be used + bool bLink, + bool bReplace, + sal_uInt16 nInsertPos, + bool bNoDialogs, + ::sd::DrawDocShell* pBookmarkDocSh, + bool bCopy, + bool bMergeMasterPages, + bool bPreservePageNames) +{ + bool bContinue = true; + bool bScaleObjects = false; + sal_uInt16 nReplacedStandardPages = 0; + + SdDrawDocument* pBookmarkDoc = nullptr; + OUString aBookmarkName; + + if (pBookmarkDocSh) + { + pBookmarkDoc = pBookmarkDocSh->GetDoc(); + + if (pBookmarkDocSh->GetMedium()) + { + aBookmarkName = pBookmarkDocSh->GetMedium()->GetName(); + } + } + else if ( mxBookmarkDocShRef.is() ) + { + pBookmarkDoc = mxBookmarkDocShRef->GetDoc(); + aBookmarkName = maBookmarkFile; + } + else + { + return false; + } + + const sal_uInt16 nSdPageCount = GetSdPageCount(PageKind::Standard); + const sal_uInt16 nBMSdPageCount = pBookmarkDoc->GetSdPageCount(PageKind::Standard); + const sal_uInt16 nMPageCount = GetMasterPageCount(); + + if (nSdPageCount==0 || nBMSdPageCount==0 || nMPageCount==0) + { + return false; + } + + // Store the size and some other properties of the first page and notes + // page so that inserted pages can be properly scaled even when inserted + // before the first page. + // Note that the pointers are used later on as general page pointers. + SdPage* pRefPage = GetSdPage(0, PageKind::Standard); + Size aSize(pRefPage->GetSize()); + sal_Int32 nLeft = pRefPage->GetLeftBorder(); + sal_Int32 nRight = pRefPage->GetRightBorder(); + sal_Int32 nUpper = pRefPage->GetUpperBorder(); + sal_Int32 nLower = pRefPage->GetLowerBorder(); + Orientation eOrient = pRefPage->GetOrientation(); + + SdPage* pNPage = GetSdPage(0, PageKind::Notes); + Size aNSize(pNPage->GetSize()); + sal_Int32 nNLeft = pNPage->GetLeftBorder(); + sal_Int32 nNRight = pNPage->GetRightBorder(); + sal_Int32 nNUpper = pNPage->GetUpperBorder(); + sal_Int32 nNLower = pNPage->GetLowerBorder(); + Orientation eNOrient = pNPage->GetOrientation(); + + // Adapt page size and margins to those of the later pages? + pRefPage = GetSdPage(nSdPageCount - 1, PageKind::Standard); + + if( bNoDialogs ) + { + // If this is clipboard, then no need to scale objects: + // this will make copied masters to differ from the originals, + // and thus InsertBookmarkAsPage_FindDuplicateLayouts will + // duplicate masters on insert to same document + m_bTransportContainer = (SD_MOD()->pTransferClip && + SD_MOD()->pTransferClip->GetWorkDocument() == this); + if (!m_bTransportContainer) + { + if (rBookmarkList.empty()) + bScaleObjects = pRefPage->IsScaleObjects(); + else + bScaleObjects = true; + } + } + else + { + SdPage* pBMPage = pBookmarkDoc->GetSdPage(0,PageKind::Standard); + + if (pBMPage->GetSize() != pRefPage->GetSize() || + pBMPage->GetLeftBorder() != pRefPage->GetLeftBorder() || + pBMPage->GetRightBorder() != pRefPage->GetRightBorder() || + pBMPage->GetUpperBorder() != pRefPage->GetUpperBorder() || + pBMPage->GetLowerBorder() != pRefPage->GetLowerBorder()) + { + OUString aStr(SdResId(STR_SCALE_OBJECTS)); + std::unique_ptr<weld::MessageDialog> xQueryBox(Application::CreateMessageDialog(nullptr, + VclMessageType::Question, VclButtonsType::YesNo, + aStr)); + xQueryBox->add_button(GetStandardText(StandardButtonType::Cancel), RET_CANCEL); + sal_uInt16 nBut = xQueryBox->run(); + + bScaleObjects = nBut == RET_YES; + bContinue = nBut != RET_CANCEL; + + if (!bContinue) + { + return bContinue; + } + } + } + + // Get the necessary presentation stylesheets and transfer them before + // the pages, else, the text objects won't reference their styles anymore. + SfxUndoManager* pUndoMgr = nullptr; + if( mpDocSh ) + { + pUndoMgr = mpDocSh->GetUndoManager(); + ViewShellId nViewShellId(-1); + if (sd::ViewShell* pViewShell = mpDocSh->GetViewShell()) + nViewShellId = pViewShell->GetViewShellBase().GetViewShellId(); + pUndoMgr->EnterListAction(SdResId(STR_UNDO_INSERTPAGES), "", 0, nViewShellId); + } + + // Refactored copy'n'pasted layout name collection into IterateBookmarkPages + + std::vector<OUString> aLayoutsToTransfer; + InsertBookmarkAsPage_FindDuplicateLayouts aSearchFunctor( aLayoutsToTransfer ); + lcl_IterateBookmarkPages( *this, pBookmarkDoc, rBookmarkList, nBMSdPageCount, aSearchFunctor, ( rBookmarkList.empty() && pBookmarkDoc != this ) ); + + // Copy the style that we actually need. + SdStyleSheetPool& rBookmarkStyleSheetPool = dynamic_cast<SdStyleSheetPool&>(*pBookmarkDoc->GetStyleSheetPool()); + SdStyleSheetPool& rStyleSheetPool = dynamic_cast<SdStyleSheetPool&>(*GetStyleSheetPool()); + + // When copying styles, also copy the master pages! + if( !aLayoutsToTransfer.empty() ) + bMergeMasterPages = true; + + for ( const OUString& layoutName : aLayoutsToTransfer ) + { + StyleSheetCopyResultVector aCreatedStyles; + + rStyleSheetPool.CopyLayoutSheets(layoutName, rBookmarkStyleSheetPool,aCreatedStyles); + + if(!aCreatedStyles.empty()) + { + if( pUndoMgr ) + { + pUndoMgr->AddUndoAction(std::make_unique<SdMoveStyleSheetsUndoAction>(this, aCreatedStyles, true)); + } + } + } + + // Copy styles. This unconditionally copies all styles, even those + // that are not used in any of the inserted pages. The unused styles + // are then removed at the end of the function, where we also create + // undo records for the inserted styles. + StyleSheetCopyResultVector aNewGraphicStyles; + OUString aRenameStr; + if(!bReplace && !bNoDialogs) + aRenameStr = "_"; + rStyleSheetPool.RenameAndCopyGraphicSheets(rBookmarkStyleSheetPool, aNewGraphicStyles, aRenameStr); + StyleSheetCopyResultVector aNewCellStyles; + rStyleSheetPool.CopyCellSheets(rBookmarkStyleSheetPool, aNewCellStyles); + + // TODO handle undo of table styles too + rStyleSheetPool.CopyTableStyles(rBookmarkStyleSheetPool); + + // Insert document + + const bool bUndo = IsUndoEnabled(); + + if( bUndo ) + BegUndo(SdResId(STR_UNDO_INSERTPAGES)); + + if (rBookmarkList.empty()) + { + if (nInsertPos >= GetPageCount()) + { + // Add pages to the end + nInsertPos = GetPageCount(); + } + + sal_uInt16 nActualInsertPos = nInsertPos; + + sal_uInt16 nBMSdPage; + std::set<sal_uInt16> aRenameSet; + std::map<sal_uInt16,OUString> aNameMap; + + for (nBMSdPage=0; nBMSdPage < nBMSdPageCount; nBMSdPage++) + { + SdPage* pBMPage = pBookmarkDoc->GetSdPage(nBMSdPage, PageKind::Standard); + OUString sName(pBMPage->GetName()); + bool bIsMasterPage; + + if (bLink) + { + // Remember the names of all pages + aNameMap.insert(std::make_pair(nBMSdPage,sName)); + } + + // Have to check for duplicate names here, too + // don't change name if source and dest model are the same! + if( pBookmarkDoc != this && + GetPageByName(sName, bIsMasterPage ) != SDRPAGE_NOTFOUND ) + { + // delay renaming *after* pages are copied (might destroy source otherwise) + aRenameSet.insert(nBMSdPage); + } + } + + Merge(*pBookmarkDoc, + 1, // Not the handout page + 0xFFFF, // But all others + nActualInsertPos, // Insert at position ... + bMergeMasterPages, // Move master pages? + false, // But only the master pages used + true, // Create an undo action + bCopy); // Copy (or merge) pages? + + for (nBMSdPage=0; nBMSdPage < nBMSdPageCount; nBMSdPage++) + { + SdPage* pPage = static_cast<SdPage*>( GetPage(nActualInsertPos) ); + SdPage* pNotesPage = static_cast<SdPage*>( GetPage(nActualInsertPos+1) ); + + // delay renaming *after* pages are copied (might destroy source otherwise) + if( aRenameSet.find(nBMSdPage) != aRenameSet.end() ) + { + // Page name already in use -> Use default name for default and + // notes page + pPage->SetName(OUString()); + pNotesPage->SetName(OUString()); + } + + if (bLink) + { + OUString aName(aNameMap[nBMSdPage]); + + // Assemble all link names + pPage->SetFileName(aBookmarkName); + pPage->SetBookmarkName(aName); + } + + nActualInsertPos += 2; + } + } + else + { + // Insert selected pages + SdPage* pBMPage; + + if (nInsertPos >= GetPageCount()) + { + // Add pages to the end + bReplace = false; + nInsertPos = GetPageCount(); + } + + sal_uInt16 nActualInsertPos = nInsertPos; + + // Collect the bookmarked pages + ::std::vector<SdPage*> aBookmarkedPages (rBookmarkList.size(), nullptr); + for ( size_t nPos = 0, n = rBookmarkList.size(); nPos < n; ++nPos) + { + OUString aPgName(rBookmarkList[nPos]); + bool bIsMasterPage; + sal_uInt16 nBMPage = pBookmarkDoc->GetPageByName( aPgName, bIsMasterPage ); + + if (nBMPage != SDRPAGE_NOTFOUND) + { + aBookmarkedPages[nPos] = dynamic_cast<SdPage*>(pBookmarkDoc->GetPage(nBMPage)); + } + } + + for ( size_t nPos = 0, n = rBookmarkList.size(); nPos < n; ++nPos) + { + pBMPage = aBookmarkedPages[nPos]; + sal_uInt16 nBMPage = pBMPage!=nullptr ? pBMPage->GetPageNum() : SDRPAGE_NOTFOUND; + + if (pBMPage && pBMPage->GetPageKind()==PageKind::Standard && !pBMPage->IsMasterPage()) + { + // It has to be a default page + bool bMustRename = false; + + // delay renaming *after* pages are copied (might destroy source otherwise) + // don't change name if source and dest model are the same! + // avoid renaming if replacing the same page + OUString aPgName(rBookmarkList[nPos]); + bool bIsMasterPage; + sal_uInt16 nPageSameName = GetPageByName(aPgName, bIsMasterPage); + if( pBookmarkDoc != this && + nPageSameName != SDRPAGE_NOTFOUND && + ( !bReplace || + nPageSameName != nActualInsertPos ) ) + { + bMustRename = true; + } + + SdPage* pBookmarkPage = pBMPage; + if (bReplace ) + { + ReplacePageInCustomShows( dynamic_cast< SdPage* >( GetPage( nActualInsertPos ) ), pBookmarkPage ); + } + + Merge(*pBookmarkDoc, + nBMPage, // From page (default page) + nBMPage+1, // To page (notes page) + nActualInsertPos, // Insert at position + bMergeMasterPages, // Move master pages? + false, // But only the master pages used + true, // Create undo action + bCopy); // Copy (or merge) pages? + + if( bReplace ) + { + if( GetPage( nActualInsertPos ) != pBookmarkPage ) + { + // bookmark page was not moved but cloned, so update custom shows again + ReplacePageInCustomShows( pBookmarkPage, dynamic_cast< SdPage* >( GetPage( nActualInsertPos ) ) ); + } + } + + if( bMustRename ) + { + // Page name already in use -> use default name for default and + // notes page + SdPage* pPage = static_cast<SdPage*>( GetPage(nActualInsertPos) ); + pPage->SetName(OUString()); + SdPage* pNotesPage = static_cast<SdPage*>( GetPage(nActualInsertPos+1) ); + pNotesPage->SetName(OUString()); + } + + if (bLink) + { + SdPage* pPage = static_cast<SdPage*>( GetPage(nActualInsertPos) ); + pPage->SetFileName(aBookmarkName); + pPage->SetBookmarkName(aPgName); + } + + if (bReplace) + { + // Remove page and notes page. + const sal_uInt16 nDestPageNum(nActualInsertPos + 2); + SdPage* pStandardPage = nullptr; + + if(nDestPageNum < GetPageCount()) + { + pStandardPage = static_cast<SdPage*>(GetPage(nDestPageNum)); + } + + if (pStandardPage) + { + if( bPreservePageNames ) + { + // Take old slide names for inserted pages + SdPage* pPage = static_cast<SdPage*>( GetPage(nActualInsertPos) ); + pPage->SetName( pStandardPage->GetRealName() ); + } + + if( bUndo ) + AddUndo(GetSdrUndoFactory().CreateUndoDeletePage(*pStandardPage)); + + RemovePage(nDestPageNum); + } + + SdPage* pNotesPage = nullptr; + + if(nDestPageNum < GetPageCount()) + { + pNotesPage = static_cast<SdPage*>(GetPage(nDestPageNum)); + } + + if (pNotesPage) + { + if( bPreservePageNames ) + { + // Take old slide names for inserted pages + SdPage* pNewNotesPage = static_cast<SdPage*>( GetPage(nActualInsertPos+1)); + if( pNewNotesPage ) + pNewNotesPage->SetName( pStandardPage->GetRealName() ); + } + + if( bUndo ) + AddUndo(GetSdrUndoFactory().CreateUndoDeletePage(*pNotesPage)); + + RemovePage(nDestPageNum); + } + + nReplacedStandardPages++; + } + + nActualInsertPos += 2; + } + } + } + + // We might have duplicate master pages now, as the drawing engine does not + // recognize duplicates. Remove these now. + sal_uInt16 nNewMPageCount = GetMasterPageCount(); + + // Go backwards, so the numbers don't become messed up + for (sal_uInt16 nPage = nNewMPageCount - 1; nPage >= nMPageCount; nPage--) + { + pRefPage = static_cast<SdPage*>( GetMasterPage(nPage) ); + OUString aMPLayout(pRefPage->GetLayoutName()); + PageKind eKind = pRefPage->GetPageKind(); + + // Does this already exist? + for (sal_uInt16 nTest = 0; nTest < nMPageCount; nTest++) + { + SdPage* pTest = static_cast<SdPage*>( GetMasterPage(nTest) ); + OUString aTest(pTest->GetLayoutName()); + + // nInsertPos > 2 is always true when inserting into non-empty models + if ( nInsertPos > 2 && + aTest == aMPLayout && + eKind == pTest->GetPageKind() ) + { + if( bUndo ) + AddUndo(GetSdrUndoFactory().CreateUndoDeletePage(*pRefPage)); + + RemoveMasterPage(nPage); + + nNewMPageCount--; + break; + } + } + } + + // nInsertPos > 2 is always true when inserting into non-empty models + if (nInsertPos > 0) + { + sal_uInt16 nSdPageStart = (nInsertPos - 1) / 2; + sal_uInt16 nSdPageEnd = bReplace + ? nSdPageStart + nReplacedStandardPages - 1 + : GetSdPageCount(PageKind::Standard) - nSdPageCount + nSdPageStart - 1; + const bool bRemoveEmptyPresObj = + (pBookmarkDoc->GetDocumentType() == DocumentType::Impress) && + (GetDocumentType() == DocumentType::Draw); + + std::vector<OUString>::iterator pExchangeIter; + + if (pExchangeList) + pExchangeIter = pExchangeList->begin(); + + for (sal_uInt16 nSdPage = nSdPageStart; nSdPage <= nSdPageEnd; nSdPage++) + { + pRefPage = GetSdPage(nSdPage, PageKind::Standard); + + if (pExchangeList && pExchangeIter != pExchangeList->end()) + { + // Get the name to use from Exchange list + OUString aExchangeName(*pExchangeIter); + pRefPage->SetName(aExchangeName); + Broadcast(SdrHint(SdrHintKind::PageOrderChange, pRefPage)); + + SdPage* pNewNotesPage = GetSdPage(nSdPage, PageKind::Notes); + pNewNotesPage->SetName(aExchangeName); + Broadcast(SdrHint(SdrHintKind::PageOrderChange, pNewNotesPage)); + + ++pExchangeIter; + } + + OUString aLayout(pRefPage->GetLayoutName()); + sal_Int32 nIndex = aLayout.indexOf( SD_LT_SEPARATOR ); + if( nIndex != -1 ) + aLayout = aLayout.copy(0, nIndex); + + // update layout and referred master page + pRefPage->SetPresentationLayout(aLayout); + if( bUndo ) + AddUndo( GetSdrUndoFactory().CreateUndoPageChangeMasterPage( *pRefPage ) ); + + if (bScaleObjects) + { + ::tools::Rectangle aBorderRect(nLeft, nUpper, nRight, nLower); + pRefPage->ScaleObjects(aSize, aBorderRect, true); + } + pRefPage->SetSize(aSize); + pRefPage->SetBorder(nLeft, nUpper, nRight, nLower); + pRefPage->SetOrientation( eOrient ); + + if( bRemoveEmptyPresObj ) + pRefPage->RemoveEmptyPresentationObjects(); + + pRefPage = GetSdPage(nSdPage, PageKind::Notes); + + // update layout and referred master page + pRefPage->SetPresentationLayout(aLayout); + if( bUndo ) + AddUndo( GetSdrUndoFactory().CreateUndoPageChangeMasterPage( *pRefPage ) ); + + if (bScaleObjects) + { + ::tools::Rectangle aBorderRect(nNLeft, nNUpper, nNRight, nNLower); + pRefPage->ScaleObjects(aNSize, aBorderRect, true); + } + + pRefPage->SetSize(aNSize); + pRefPage->SetBorder(nNLeft, nNUpper, nNRight, nNLower); + pRefPage->SetOrientation( eNOrient ); + + if( bRemoveEmptyPresObj ) + pRefPage->RemoveEmptyPresentationObjects(); + } + + ///Remove processed elements, to avoid doing hacks in InsertBookmarkAsObject + if ( pExchangeList ) + pExchangeList->erase(pExchangeList->begin(),pExchangeIter); + + for (sal_uInt16 nPage = nMPageCount; nPage < nNewMPageCount; nPage++) + { + pRefPage = static_cast<SdPage*>( GetMasterPage(nPage) ); + if (pRefPage->GetPageKind() == PageKind::Standard) + { + if (bScaleObjects) + { + ::tools::Rectangle aBorderRect(nLeft, nUpper, nRight, nLower); + pRefPage->ScaleObjects(aSize, aBorderRect, true); + } + pRefPage->SetSize(aSize); + pRefPage->SetBorder(nLeft, nUpper, nRight, nLower); + pRefPage->SetOrientation( eOrient ); + } + else // Can only be notes + { + if (bScaleObjects) + { + ::tools::Rectangle aBorderRect(nNLeft, nNUpper, nNRight, nNLower); + pRefPage->ScaleObjects(aNSize, aBorderRect, true); + } + pRefPage->SetSize(aNSize); + pRefPage->SetBorder(nNLeft, nNUpper, nNRight, nNLower); + pRefPage->SetOrientation( eNOrient ); + } + + if( bRemoveEmptyPresObj ) + pRefPage->RemoveEmptyPresentationObjects(); + } + } + + // Make absolutely sure no double masterpages are there + RemoveUnnecessaryMasterPages(nullptr, true); + + // Rename object styles if necessary + if(!aRenameStr.isEmpty()) + { + try + { + for(sal_uInt32 p = nInsertPos; p < sal_uInt32(nInsertPos) + sal_uInt32(nBMSdPageCount); p++) + { + SdPage *pPg = static_cast<SdPage *>( GetPage(p) ); + for(size_t i = 0; pPg && (i < pPg->GetObjCount()); ++i) + { + if(pPg->GetObj(i)->GetStyleSheet()) + { + OUString aStyleName = pPg->GetObj(i)->GetStyleSheet()->GetName(); + SfxStyleSheet *pSheet = lcl_findStyle(aNewGraphicStyles, OUStringConcatenation(aStyleName + aRenameStr)); + if(pSheet != nullptr) + pPg->GetObj(i)->SetStyleSheet(pSheet, true); + } + } + } + } + catch(...) + { + TOOLS_WARN_EXCEPTION( "sd", "Exception while renaming styles @ SdDrawDocument::InsertBookmarkAsPage"); + } + } + // remove copied styles not used on any inserted page and create + // undo records + // WARNING: SdMoveStyleSheetsUndoAction clears the passed list of + // styles, so it cannot be used after this point + lcl_removeUnusedStyles(GetStyleSheetPool(), aNewGraphicStyles); + if (!aNewGraphicStyles.empty() && pUndoMgr) + pUndoMgr->AddUndoAction(std::make_unique<SdMoveStyleSheetsUndoAction>(this, aNewGraphicStyles, true)); + lcl_removeUnusedStyles(GetStyleSheetPool(), aNewCellStyles); + if (!aNewCellStyles.empty() && pUndoMgr) + pUndoMgr->AddUndoAction(std::make_unique<SdMoveStyleSheetsUndoAction>(this, aNewCellStyles, true)); + + if( bUndo ) + EndUndo(); + + if (pUndoMgr) + pUndoMgr->LeaveListAction(); + + return bContinue; +} + +// Inserts a bookmark as an object +bool SdDrawDocument::InsertBookmarkAsObject( + const std::vector<OUString> &rBookmarkList, + const std::vector<OUString> &rExchangeList, // List of names to use + ::sd::DrawDocShell* pBookmarkDocSh, + Point const * pObjPos, + bool bCalcObjCount) +{ + bool bOK = true; + bool bOLEObjFound = false; + std::unique_ptr<::sd::View> pBMView; + + SdDrawDocument* pBookmarkDoc = nullptr; + + if (pBookmarkDocSh) + { + pBookmarkDoc = pBookmarkDocSh->GetDoc(); + } + else if ( mxBookmarkDocShRef.is() ) + { + pBookmarkDoc = mxBookmarkDocShRef->GetDoc(); + } + else + { + return false; + } + + if (rBookmarkList.empty()) + { + pBMView.reset(new ::sd::View(*pBookmarkDoc, nullptr)); + pBMView->EndListening(*pBookmarkDoc); + pBMView->MarkAll(); + } + else + { + SdrPage* pPage; + SdrPageView* pPV; + + for ( const auto& rBookmark : rBookmarkList ) + { + // Get names of bookmarks from the list + SdrObject* pObj = pBookmarkDoc->GetObj(rBookmark); + + if (pObj) + { + // Found an object + if (pObj->GetObjInventor() == SdrInventor::Default && + pObj->GetObjIdentifier() == SdrObjKind::OLE2) + { + bOLEObjFound = true; + } + + if (!pBMView) + { + // Create View for the first time + pBMView.reset(new ::sd::View(*pBookmarkDoc, nullptr)); + pBMView->EndListening(*pBookmarkDoc); + } + + pPage = pObj->getSdrPageFromSdrObject(); + + if (pPage->IsMasterPage()) + { + pPV = pBMView->ShowSdrPage(pBMView->GetModel()->GetMasterPage(pPage->GetPageNum())); + } + else + { + pPV = pBMView->GetSdrPageView(); + if( !pPV || (pPV->GetPage() != pPage)) + pPV = pBMView->ShowSdrPage(pPage); + } + + pBMView->MarkObj(pObj, pPV); + } + } + } + + if (pBMView) + { + // Insert selected objects + std::optional<::sd::View> pView(std::in_place, *this, nullptr); + pView->EndListening(*this); + + // Look for the page into which the objects are supposed to be inserted + SdrPage* pPage = GetSdPage(0, PageKind::Standard); + + if (mpDocSh) + { + ::sd::ViewShell* pViewSh = mpDocSh->GetViewShell(); + + if (pViewSh) + { + // Which page is currently in view? + SdrPageView* pPV = pViewSh->GetView()->GetSdrPageView(); + + if (pPV) + { + pPage = pPV->GetPage(); + } + else if (pViewSh->GetActualPage()) + { + pPage = pViewSh->GetActualPage(); + } + } + } + + Point aObjPos; + + if (pObjPos) + { + aObjPos = *pObjPos; + } + else + { + aObjPos = ::tools::Rectangle(Point(), pPage->GetSize()).Center(); + } + + size_t nCountBefore = 0; + + if (!rExchangeList.empty() || bCalcObjCount) + { + // Sort OrdNums and get the number of objects before inserting + pPage->RecalcObjOrdNums(); + nCountBefore = pPage->GetObjCount(); + } + + if (bOLEObjFound) + pBMView->GetDoc().SetAllocDocSh(true); + + SdDrawDocument* pTmpDoc = static_cast<SdDrawDocument*>( pBMView->CreateMarkedObjModel().release() ); + bOK = pView->Paste(*pTmpDoc, aObjPos, pPage, SdrInsertFlags::NONE); + + if (bOLEObjFound) + pBMView->GetDoc().SetAllocDocSh(false); + + if (!bOLEObjFound) + delete pTmpDoc; // Would otherwise be destroyed by DocShell + + pView.reset(); + + // Get number of objects after inserting. + const size_t nCount = pPage->GetObjCount(); + if (nCountBefore < nCount) + { + size_t nObj = nCountBefore; + for (const auto& rExchange : rExchangeList) + { + // Get the name to use from the Exchange list + if (pPage->GetObj(nObj)) + { + pPage->GetObj(nObj)->SetName(rExchange); + } + + ++nObj; + if (nObj >= nCount) + break; + } + } + } + + return bOK; +} + +// Stops the bookmark insertion +void SdDrawDocument::CloseBookmarkDoc() +{ + if (mxBookmarkDocShRef.is()) + { + mxBookmarkDocShRef->DoClose(); + } + + mxBookmarkDocShRef.clear(); + maBookmarkFile.clear(); +} + +// Is this document read-only? +bool SdDrawDocument::IsReadOnly() const +{ + return false; +} + +// In the subsequent AllocModel() a DocShell (xAllocedDocShRef) is created. +// Any pre-existing DocShell is deleted +void SdDrawDocument::SetAllocDocSh(bool bAlloc) +{ + mbAllocDocSh = bAlloc; + + if(mxAllocedDocShRef.is()) + { + mxAllocedDocShRef->DoClose(); + } + + mxAllocedDocShRef.clear(); +} + +// Return list of CustomShows (create it, too, if necessary) +SdCustomShowList* SdDrawDocument::GetCustomShowList(bool bCreate) +{ + if (!mpCustomShowList && bCreate) + { + mpCustomShowList.reset(new SdCustomShowList); + } + + return mpCustomShowList.get(); +} + +// Remove unused master pages and layouts +void SdDrawDocument::RemoveUnnecessaryMasterPages(SdPage* pMasterPage, bool bOnlyDuplicatePages, bool bUndo) +{ + ::sd::View* pView = nullptr; + SfxUndoManager* pUndoMgr = nullptr; + + if( bUndo && !IsUndoEnabled() ) + bUndo = false; + + if (mpDocSh) + { + pUndoMgr = mpDocSh->GetUndoManager(); + + if (mpDocSh->GetViewShell()) + pView = mpDocSh->GetViewShell()->GetView(); + } + + // Check all master pages + sal_uInt16 nSdMasterPageCount = GetMasterSdPageCount( PageKind::Standard ); + for (sal_Int32 nMPage = nSdMasterPageCount - 1; nMPage >= 0; nMPage--) + { + SdPage* pMaster = pMasterPage; + SdPage* pNotesMaster = nullptr; + + if (!pMaster) + { + pMaster = GetMasterSdPage( static_cast<sal_uInt16>(nMPage), PageKind::Standard ); + pNotesMaster = GetMasterSdPage( static_cast<sal_uInt16>(nMPage), PageKind::Notes ); + } + else + { + for ( sal_uInt16 nMPg = 0; nMPg < GetMasterPageCount(); nMPg++ ) + { + if ( pMaster == GetMasterPage( nMPg ) ) + { + pNotesMaster = static_cast<SdPage*>( GetMasterPage( ++nMPg ) ); + break; + } + } + } + + DBG_ASSERT( pMaster->GetPageKind() == PageKind::Standard, "wrong page kind" ); + + if ( pMaster->GetPageKind() == PageKind::Standard && + GetMasterPageUserCount( pMaster ) == 0 && + pNotesMaster ) + { + // Do not delete master pages that have their precious flag set + bool bDeleteMaster = !pMaster->IsPrecious(); + OUString aLayoutName = pMaster->GetLayoutName(); + + if(bOnlyDuplicatePages ) + { + // remove only duplicate pages + bDeleteMaster = false; + for (sal_uInt16 i = 0; i < GetMasterSdPageCount( PageKind::Standard ); i++) + { + SdPage* pMPg = GetMasterSdPage( i, PageKind::Standard ); + if( pMPg != pMaster && + pMPg->GetLayoutName() == aLayoutName ) + { + // duplicate page found -> remove it + bDeleteMaster = true; + } + } + } + + if( bDeleteMaster ) + { + if (pView) + { + // if MasterPage is visible hide on pageview + SdrPageView* pPgView = pView->GetSdrPageView(); + if (pPgView) + { + SdrPage* pShownPage = pPgView->GetPage(); + if( (pShownPage == pMaster) || (pShownPage == pNotesMaster) ) + { + pView->HideSdrPage(); + pView->ShowSdrPage( GetSdPage( 0, PageKind::Standard ) ); + } + } + } + + if( bUndo ) + { + BegUndo(); + AddUndo( GetSdrUndoFactory().CreateUndoDeletePage( *pNotesMaster ) ); + } + + RemoveMasterPage( pNotesMaster->GetPageNum() ); + + if( bUndo ) + AddUndo(GetSdrUndoFactory().CreateUndoDeletePage(*pMaster)); + + RemoveMasterPage( pMaster->GetPageNum() ); + + if( bUndo ) + EndUndo(); // do this here already, so Joe's actions happen _between_ our own + + // Delete old, unused layout stylesheets + bool bDeleteOldStyleSheets = true; + for ( sal_uInt16 nMPg = 0; + nMPg < GetMasterPageCount() && bDeleteOldStyleSheets; + nMPg++ ) + { + SdPage* pMPg = static_cast<SdPage*>( GetMasterPage(nMPg) ); + if (pMPg->GetLayoutName() == aLayoutName) + { + bDeleteOldStyleSheets = false; + } + } + + if (bDeleteOldStyleSheets) + { + SdStyleSheetVector aRemove; + static_cast<SdStyleSheetPool*>( mxStyleSheetPool.get())->CreateLayoutSheetList( aLayoutName, aRemove ); + + if( bUndo ) + { + StyleSheetCopyResultVector aUndoRemove; + aUndoRemove.reserve(aRemove.size()); + for (const auto& a : aRemove) + aUndoRemove.emplace_back(a.get(), true); + + if (pUndoMgr) + pUndoMgr->AddUndoAction(std::make_unique<SdMoveStyleSheetsUndoAction>(this, aUndoRemove, false)); + } + + for( const auto& a : aRemove ) + static_cast<SdStyleSheetPool*>( mxStyleSheetPool.get())->Remove(a.get()); + } + } + } + + if (pMasterPage) + break; // Just this one master page! + } +} + +/** Exchange master page + * + * Either the nSdPageNum gets a new, own master page or the master page is + * exchanged completely (which then applies to all pages). + * + * nSdPageNum : page number that the new master page should get. + * rLayoutName : LayoutName of the new master page + * pSourceDoc : document (template) to get the master page from + * bMaster : exchange the master page of nSdPageNum + * bCheckMasters: remove unused master pages + * + * If pSourceDoc == NULL, an empty master page is applied. + * If rLayoutName is empty, the first master page is used. + */ +// #i121863# factored out functionality +static bool isMasterPageLayoutNameUnique(const SdDrawDocument& rDoc, std::u16string_view rCandidate) +{ + if (rCandidate.empty()) + { + return false; + } + + const sal_uInt16 nPageCount(rDoc.GetMasterPageCount()); + + for(sal_uInt16 a(0); a < nPageCount; a++) + { + const SdrPage* pCandidate = rDoc.GetMasterPage(a); + OUString aPageLayoutName(pCandidate->GetLayoutName()); + sal_Int32 nIndex = aPageLayoutName.indexOf(SD_LT_SEPARATOR); + if( nIndex != -1 ) + aPageLayoutName = aPageLayoutName.copy(0, nIndex); + + if(aPageLayoutName == rCandidate) + { + return false; + } + } + + return true; +} + +// #i121863# factored out functionality +static OUString createNewMasterPageLayoutName(const SdDrawDocument& rDoc) +{ + const OUString aBaseName(SdResId(STR_LAYOUT_DEFAULT_NAME)); + sal_uInt16 nCount(0); + for (;;) + { + OUString aRetval = aBaseName; + if(nCount) + { + aRetval += OUString::number(nCount); + } + if (isMasterPageLayoutNameUnique(rDoc, aRetval)) + return aRetval; + nCount++; + } +} + +void SdDrawDocument::SetMasterPage(sal_uInt16 nSdPageNum, + std::u16string_view rLayoutName, + SdDrawDocument* pSourceDoc, + bool bMaster, + bool bCheckMasters) +{ + SfxUndoManager* pUndoMgr = nullptr; + + if( mpDocSh ) + { + mpDocSh->SetWaitCursor( true ); + pUndoMgr = mpDocSh->GetUndoManager(); + } + + const bool bUndo = pUndoMgr && IsUndoEnabled(); + + if (bUndo) + { + ViewShellId nViewShellId(-1); + if (sd::ViewShell* pViewShell = mpDocSh->GetViewShell()) + nViewShellId = pViewShell->GetViewShellBase().GetViewShellId(); + pUndoMgr->EnterListAction(SdResId(STR_UNDO_SET_PRESLAYOUT), OUString(), 0, nViewShellId); + } + + SdPage* pSelectedPage = GetSdPage(nSdPageNum, PageKind::Standard); + SdPage* pNotes = static_cast<SdPage*>( GetPage(pSelectedPage->GetPageNum()+1) ); + SdPage& rOldMaster = static_cast<SdPage&>(pSelectedPage->TRG_GetMasterPage()); + SdPage& rOldNotesMaster = static_cast<SdPage&>(pNotes->TRG_GetMasterPage()); + rtl::Reference<SdPage> pMaster; + rtl::Reference<SdPage> pNotesMaster; + OUString aOldPageLayoutName(pSelectedPage->GetLayoutName()); + OUString aOldLayoutName(aOldPageLayoutName); + sal_Int32 nIndex = aOldLayoutName.indexOf( SD_LT_SEPARATOR ); + if( nIndex != -1 ) + aOldLayoutName = aOldLayoutName.copy(0, nIndex); + + if (pSourceDoc) + { + std::vector<StyleReplaceData> aReplList; // List of replaced stylesheets + bool bLayoutReloaded = false; // Was ex. layout reloaded? + + // LayoutName, Page and Notes page + if (rLayoutName.empty()) + { + // No LayoutName: take first MasterPage + pMaster = pSourceDoc->GetMasterSdPage(0, PageKind::Standard); + pNotesMaster = pSourceDoc->GetMasterSdPage(0, PageKind::Notes); + } + else + { + OUString aSearchFor + = OUString::Concat(rLayoutName) + SD_LT_SEPARATOR + STR_LAYOUT_OUTLINE; + + for (sal_uInt16 nMP = 0; nMP < pSourceDoc->GetMasterPageCount(); ++nMP) + { + SdPage* pMP = static_cast<SdPage*>( pSourceDoc->GetMasterPage(nMP) ); + + if (pMP->GetLayoutName() == aSearchFor) + { + if (pMP->GetPageKind() == PageKind::Standard) + pMaster = pMP; + if (pMP->GetPageKind() == PageKind::Notes) + pNotesMaster = pMP; + } + if (pMaster && pNotesMaster) + break; + } + DBG_ASSERT(pMaster, "MasterPage (Standard page) not found"); + DBG_ASSERT(pNotesMaster, "MasterPage (Notes page) not found"); + + // this should not happen, but looking at crash reports, it does + if( (pMaster == nullptr) || (pNotesMaster == nullptr) ) + { + // so take the first MasterPage + pMaster = pSourceDoc->GetMasterSdPage(0, PageKind::Standard); + pNotesMaster = pSourceDoc->GetMasterSdPage(0, PageKind::Notes); + } + } + + // we should never reach this, but one never knows... + if( (pMaster == nullptr) || (pNotesMaster == nullptr) ) + { + if (bUndo) + pUndoMgr->LeaveListAction(); + + if( mpDocSh ) + mpDocSh->SetWaitCursor( false ); + + OSL_FAIL( "SdDrawDocument::SetMasterPage() failed!" ); + + return; + } + + const OUString aOriginalNewLayoutName( pMaster->GetName() ); + OUString aTargetNewLayoutName(aOriginalNewLayoutName); + + if (pSourceDoc != this) + { + // #i121863# clone masterpages, they are from another model (!) + rtl::Reference<SdPage> pNewNotesMaster(dynamic_cast< SdPage* >(pNotesMaster->CloneSdrPage(*this).get())); + rtl::Reference<SdPage> pNewMaster(dynamic_cast< SdPage* >(pMaster->CloneSdrPage(*this).get())); + + if(!pNewNotesMaster || !pNewMaster) + { + OSL_FAIL("SdDrawDocument::SetMasterPage() cloning of MasterPage/NoteAmsterPage failed!" ); + return; + } + + pNotesMaster = pNewNotesMaster; + pMaster = pNewMaster; + + // layout name needs to be unique + aTargetNewLayoutName = pMaster->GetLayoutName(); + sal_Int32 nIndex2 = aTargetNewLayoutName.indexOf(SD_LT_SEPARATOR); + if( nIndex2 != -1 ) + aTargetNewLayoutName = aTargetNewLayoutName.copy(0, nIndex2); + + if(!isMasterPageLayoutNameUnique(*this, aTargetNewLayoutName)) + { + aTargetNewLayoutName = createNewMasterPageLayoutName(*this); + + OUString aTemp = aTargetNewLayoutName + SD_LT_SEPARATOR + STR_LAYOUT_OUTLINE; + + pMaster->SetName(aTargetNewLayoutName); + pMaster->SetLayoutName(aTemp); + + pNotesMaster->SetName(aTargetNewLayoutName); + pNotesMaster->SetLayoutName(aTemp); + } + } + + if (pSourceDoc != this) + { + const sal_uInt16 nMasterPageCount = GetMasterPageCount(); + for ( sal_uInt16 nMPage = 0; nMPage < nMasterPageCount; nMPage++ ) + { + SdPage* pCheckMaster = static_cast<SdPage*>(GetMasterPage(nMPage)); + if( pCheckMaster->GetName() == aTargetNewLayoutName ) + { + bLayoutReloaded = true; + break; + } + } + + // Correct or create presentation templates -- + // only worry about presentation templates + OUString aName; + SdStyleSheetPool* pSourceStyleSheetPool = static_cast<SdStyleSheetPool*>( pSourceDoc->GetStyleSheetPool() ); + + StyleSheetCopyResultVector aCreatedStyles; // List of created stylesheets + SfxStyleSheetBase* pHisSheet = pSourceStyleSheetPool->First(SfxStyleFamily::Page); + + while (pHisSheet) + { + aName = pHisSheet->GetName(); + + // #i121863# search in source styles with original style name from source of + // evtl. cloned master (not-cloned, renamed for uniqueness) + if( aName.startsWith( aOriginalNewLayoutName ) ) + { + // #i121863# build name of evtl. cloned master style to search for + if(aOriginalNewLayoutName != aTargetNewLayoutName) + { + const sal_Int32 nPos(aName.indexOf(SD_LT_SEPARATOR)); + aName = aTargetNewLayoutName + aName.subView(nPos); + } + + SfxStyleSheet* pMySheet = static_cast<SfxStyleSheet*>( mxStyleSheetPool->Find(aName, SfxStyleFamily::Page) ); + + if (pMySheet) + { + // A stylesheet of the same name already exists -> overwrite contents + bool bTest = pMySheet->SetName(pHisSheet->GetName()); + DBG_ASSERT(bTest, "Renaming StyleSheet failed."); + pMySheet->GetItemSet().ClearItem(); // Delete all + + if (bUndo) + { + pUndoMgr->AddUndoAction(std::make_unique<StyleSheetUndoAction>(this, + pMySheet, &pHisSheet->GetItemSet())); + } + pMySheet->GetItemSet().Put(pHisSheet->GetItemSet()); + pMySheet->Broadcast(SfxHint(SfxHintId::DataChanged)); + } + else + { + // create new style + OUString aHelpFile; + pMySheet = static_cast<SfxStyleSheet*>( &mxStyleSheetPool->Make(aName, SfxStyleFamily::Page, pHisSheet->GetMask()) ); + pMySheet->SetHelpId( aHelpFile, pHisSheet->GetHelpId(aHelpFile) ); + pMySheet->GetItemSet().ClearItem(); // Delete all + pMySheet->GetItemSet().Put(pHisSheet->GetItemSet()); + + aCreatedStyles.emplace_back(static_cast<SdStyleSheet*>(pMySheet), true); + } + + StyleReplaceData aReplData; + aReplData.nNewFamily = pMySheet->GetFamily(); + aReplData.nFamily = pMySheet->GetFamily(); + aReplData.aNewName = pMySheet->GetName(); + + // #i121863# re-create original name of style used at page where to replace with + // this new style + OUString aTemp(pMySheet->GetName()); + const sal_Int32 nPos(aTemp.indexOf(SD_LT_SEPARATOR)); + aTemp = aOldLayoutName + aTemp.subView(nPos); + aReplData.aName = aTemp; + aReplList.push_back(aReplData); + } + + pHisSheet = pSourceStyleSheetPool->Next(); + } + + // If new styles were created: re-create parent chaining of the item + // sets in the styles. + if(!aCreatedStyles.empty()) + { + for ( const auto& rRData : aReplList ) + { + SfxStyleSheetBase* pSOld = mxStyleSheetPool->Find(rRData.aName, SfxStyleFamily::Page); + SfxStyleSheetBase* pSNew = mxStyleSheetPool->Find(rRData.aNewName, SfxStyleFamily::Page); + + if (pSOld && pSNew) + { + const OUString& rParentOfOld = pSOld->GetParent(); + const OUString& rParentOfNew = pSNew->GetParent(); + + if (!rParentOfOld.isEmpty() && rParentOfNew.isEmpty()) + { + std::vector<StyleReplaceData>::iterator pRDIter = std::find_if(aReplList.begin(), aReplList.end(), + [&rParentOfOld](const StyleReplaceData& rRD) { return (rRD.aName == rParentOfOld) && (rRD.aName != rRD.aNewName); }); + if (pRDIter != aReplList.end()) + { + OUString aParentOfNew(pRDIter->aNewName); + pSNew->SetParent(aParentOfNew); + } + } + } + } + } + + if (bUndo && !aCreatedStyles.empty()) + { + // Add UndoAction for creating and inserting the stylesheets to + // the top of the UndoManager + pUndoMgr->AddUndoAction(std::make_unique<SdMoveStyleSheetsUndoAction>( this, aCreatedStyles, true)); + } + } + + // Create layout name based upon the name of the page layout of the + // master page + OUString aPageLayoutName(pMaster->GetLayoutName()); + OUString aLayoutName = aPageLayoutName; + sal_Int32 nIndex2 = aLayoutName.indexOf( SD_LT_SEPARATOR ); + if( nIndex2 != -1 ) + aLayoutName = aLayoutName.copy( 0, nIndex2); + + // #i121863# Do *not* remove from original document any longer, it is potentially used there + // and would lead to crashes. Rely on the automatic process of removing unused masterpages + // (see RemoveUnnecessaryMasterPages) + //if (pSourceDoc != this) + //{ + // // Remove from the source document + // pSourceDoc->RemoveMasterPage(pNotesMaster->GetPageNum()); + // pSourceDoc->RemoveMasterPage(pMaster->GetPageNum()); + //} + + // Register the new master pages with the document and then use + // the new presentation layout for the default and notes pages + if (pSourceDoc != this) + { + // Insert the master pages: + // Insert master pages from new layouts at the end. + // If a layout is being replaced, however, insert them before the + // position of the old master page, so from now on the new master + // page will be found when searching (e.g. + // SdPage::SetPresentationLayout). + sal_uInt16 nInsertPos = rOldMaster.GetPageNum(); + BegUndo(); + + if (!bLayoutReloaded) + nInsertPos = 0xFFFF; + InsertMasterPage(pMaster.get(), nInsertPos); + if( bUndo ) + AddUndo(GetSdrUndoFactory().CreateUndoNewPage(*pMaster)); + + nInsertPos++; + if (!bLayoutReloaded) + nInsertPos = 0xFFFF; + InsertMasterPage(pNotesMaster.get(), nInsertPos); + if( bUndo ) + { + AddUndo(GetSdrUndoFactory().CreateUndoNewPage(*pNotesMaster)); + + EndUndo(); // do this here already, so Joe's actions happen _between_ our own. + } + } + + // Fill list with pages + std::vector<rtl::Reference<SdPage>> aPageList; + +// #98456, this has to be removed according to CL (KA 07/08/2002) +// #109884# but we need them again to restore the styles of the presentation objects while undo + aPageList.push_back(pMaster); + aPageList.push_back(pNotesMaster); + + if (bMaster || bLayoutReloaded) + { + for (sal_uInt16 nPage = 1; nPage < GetPageCount(); nPage++) + { + SdPage* pPage = static_cast<SdPage*>( GetPage(nPage) ); + OUString aTest = pPage->GetLayoutName(); + if (aTest == aOldPageLayoutName) + { + aPageList.push_back(pPage); + } + } + + } + else + { + aPageList.push_back(pSelectedPage); + aPageList.push_back(pNotes); + } + + for (rtl::Reference<SdPage>& pPage : aPageList) + { + AutoLayout eAutoLayout = pPage->GetAutoLayout(); + + if( bUndo ) + { + pUndoMgr->AddUndoAction(std::make_unique<SdPresentationLayoutUndoAction> + (this, + pPage->IsMasterPage() ? aLayoutName : aOldLayoutName, + aLayoutName, + eAutoLayout, eAutoLayout, false, pPage.get())); + } + pPage->SetPresentationLayout(aLayoutName); + pPage->SetAutoLayout(eAutoLayout); + } + + // Adapt new master pages + if (pSourceDoc != this) + { + Size aSize(rOldMaster.GetSize()); + ::tools::Rectangle aBorderRect(rOldMaster.GetLeftBorder(), + rOldMaster.GetUpperBorder(), + rOldMaster.GetRightBorder(), + rOldMaster.GetLowerBorder()); + pMaster->ScaleObjects(aSize, aBorderRect, true); + pMaster->SetSize(aSize); + pMaster->SetBorder(rOldMaster.GetLeftBorder(), + rOldMaster.GetUpperBorder(), + rOldMaster.GetRightBorder(), + rOldMaster.GetLowerBorder()); + pMaster->SetOrientation( rOldMaster.GetOrientation() ); + pMaster->SetAutoLayout(pMaster->GetAutoLayout()); + + aSize = rOldNotesMaster.GetSize(); + ::tools::Rectangle aNotesBorderRect(rOldNotesMaster.GetLeftBorder(), + rOldNotesMaster.GetUpperBorder(), + rOldNotesMaster.GetRightBorder(), + rOldNotesMaster.GetLowerBorder()); + pNotesMaster->ScaleObjects(aSize, aNotesBorderRect, true); + pNotesMaster->SetSize(aSize); + pNotesMaster->SetBorder(rOldNotesMaster.GetLeftBorder(), + rOldNotesMaster.GetUpperBorder(), + rOldNotesMaster.GetRightBorder(), + rOldNotesMaster.GetLowerBorder()); + pNotesMaster->SetOrientation( rOldNotesMaster.GetOrientation() ); + pNotesMaster->SetAutoLayout(pNotesMaster->GetAutoLayout()); + + if( (pSourceDoc->GetDocumentType() == DocumentType::Impress) && + (GetDocumentType() == DocumentType::Draw) ) + { + pMaster->RemoveEmptyPresentationObjects(); + pNotesMaster->RemoveEmptyPresentationObjects(); + } + } + } + else + { + // Find a new name for the layout + OUString aName(createNewMasterPageLayoutName(*this)); + OUString aPageLayoutName(aName + SD_LT_SEPARATOR + STR_LAYOUT_OUTLINE); + + // Generate new stylesheets + static_cast<SdStyleSheetPool*>( mxStyleSheetPool.get())->CreateLayoutStyleSheets(aName); + SdStyleSheetVector aCreatedStyles; + static_cast<SdStyleSheetPool*>( mxStyleSheetPool.get())->CreateLayoutSheetList(aName, aCreatedStyles); + + if( bUndo ) + { + StyleSheetCopyResultVector aUndoInsert; + aUndoInsert.reserve(aCreatedStyles.size()); + for (const auto& a : aCreatedStyles) + aUndoInsert.emplace_back(a.get(), true); + pUndoMgr->AddUndoAction(std::make_unique<SdMoveStyleSheetsUndoAction>(this, aUndoInsert, true)); + // Generate new master pages and register them with the document + BegUndo(); + } + + pMaster = AllocSdPage(true); + pMaster->SetSize(pSelectedPage->GetSize()); + pMaster->SetBorder(pSelectedPage->GetLeftBorder(), + pSelectedPage->GetUpperBorder(), + pSelectedPage->GetRightBorder(), + pSelectedPage->GetLowerBorder() ); + pMaster->SetName(aName); + pMaster->SetLayoutName(aPageLayoutName); + InsertMasterPage(pMaster.get()); + + if( bUndo ) + AddUndo(GetSdrUndoFactory().CreateUndoNewPage(*pMaster)); + + pMaster->SetAutoLayout(AUTOLAYOUT_NONE, true, true); + + pNotesMaster = AllocSdPage(true); + pNotesMaster->SetPageKind(PageKind::Notes); + pNotesMaster->SetSize(pNotes->GetSize()); + pNotesMaster->SetBorder(pNotes->GetLeftBorder(), + pNotes->GetUpperBorder(), + pNotes->GetRightBorder(), + pNotes->GetLowerBorder() ); + pNotesMaster->SetName(aName); + pNotesMaster->SetLayoutName(aPageLayoutName); + InsertMasterPage(pNotesMaster.get()); + + if( bUndo ) + AddUndo(GetSdrUndoFactory().CreateUndoNewPage(*pNotesMaster)); + + pNotesMaster->SetAutoLayout(AUTOLAYOUT_NOTES, true, true); + + if( bUndo ) + EndUndo(); + + // Create a list of affected default and notes pages + std::vector<SdPage*> aPageList; + if (bMaster) + { + for (sal_uInt16 nPage = 1; nPage < GetPageCount(); nPage++) + { + SdPage* pPage = static_cast<SdPage*>( GetPage(nPage) ); + if (pPage->GetLayoutName() == aOldPageLayoutName) + { + aPageList.push_back(pPage); + } + } + } + else + { + aPageList.push_back(pSelectedPage); + aPageList.push_back(pNotes); + } + + // Set presentation layout and AutoLayout for the affected pages + for ( auto& rpPage : aPageList ) + { + AutoLayout eOldAutoLayout = rpPage->GetAutoLayout(); + AutoLayout eNewAutoLayout = + rpPage->GetPageKind() == PageKind::Standard ? AUTOLAYOUT_NONE : AUTOLAYOUT_NOTES; + + if( bUndo ) + { + pUndoMgr->AddUndoAction(std::make_unique<SdPresentationLayoutUndoAction> + (this, aOldLayoutName, aName, + eOldAutoLayout, eNewAutoLayout, true, + rpPage)); + } + + rpPage->SetPresentationLayout(aName); + rpPage->SetAutoLayout(eNewAutoLayout); + } + } + + // If the old master pages aren't used anymore, they and their styles have + // to be removed. + if (bCheckMasters) + { + // Check all + RemoveUnnecessaryMasterPages(); + } + else + { + // Check only the master page that was replaced + RemoveUnnecessaryMasterPages(&rOldMaster); + } + + if( bUndo ) + pUndoMgr->LeaveListAction(); + + if( mpDocSh ) + mpDocSh->SetWaitCursor( false ); +} + +void SdDrawDocument::Merge(SdrModel& rSourceModel, + sal_uInt16 nFirstPageNum, sal_uInt16 nLastPageNum, + sal_uInt16 nDestPos, + bool bMergeMasterPages, bool bAllMasterPages, + bool bUndo, bool bTreadSourceAsConst) +{ + sal_uInt16 nMasterPageCount = GetMasterPageCount(); + SdrModel::Merge( rSourceModel, nFirstPageNum, nLastPageNum, nDestPos, bMergeMasterPages, bAllMasterPages, bUndo, bTreadSourceAsConst ); + + // add style family for each new master page + for( sal_uInt16 nMaster = nMasterPageCount; nMaster < GetMasterPageCount(); nMaster++ ) + { + SdPage* pPage = static_cast< SdPage* >( GetMasterPage( nMaster ) ); + if( pPage && pPage->IsMasterPage() && (pPage->GetPageKind() == PageKind::Standard) ) + { + // new master page created, add its style family + SdStyleSheetPool* pStylePool = static_cast<SdStyleSheetPool*>( GetStyleSheetPool() ); + if( pStylePool ) + pStylePool->AddStyleFamily( pPage ); + } + } +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/core/drawdoc4.cxx b/sd/source/core/drawdoc4.cxx new file mode 100644 index 000000000..590450568 --- /dev/null +++ b/sd/source/core/drawdoc4.cxx @@ -0,0 +1,1399 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include <sal/config.h> + +#include <com/sun/star/style/XStyle.hpp> +#include <com/sun/star/drawing/LineStyle.hpp> +#include <i18nlangtag/languagetag.hxx> +#include <i18nlangtag/mslangid.hxx> +#include <sfx2/dispatch.hxx> +#include <Outliner.hxx> +#include <editeng/outliner.hxx> + +#include <DrawDocShell.hxx> +#include <editeng/eeitem.hxx> +#include <tools/diagnose_ex.h> +#include <tools/UnitConversion.hxx> + +#include <vcl/idle.hxx> +#include <vcl/settings.hxx> +#include <vcl/svapp.hxx> + +#include <editeng/autokernitem.hxx> + +#include <svx/svxids.hrc> +#include <svl/srchitem.hxx> +#include <editeng/lrspitem.hxx> +#include <editeng/ulspitem.hxx> +#include <editeng/lspcitem.hxx> +#include <editeng/adjustitem.hxx> +#include <editeng/numdef.hxx> +#include <svx/strings.hrc> +#include <svx/dialmgr.hxx> +#include <editeng/bulletitem.hxx> +#include <editeng/borderline.hxx> +#include <editeng/boxitem.hxx> +#include <svx/xlineit0.hxx> +#include <svx/sdshitm.hxx> +#include <svx/svdotext.hxx> +#include <svx/xfillit0.hxx> +#include <svx/sdshcitm.hxx> +#include <editeng/editstat.hxx> +#include <editeng/colritem.hxx> +#include <editeng/fhgtitem.hxx> +#include <editeng/wghtitem.hxx> +#include <editeng/postitem.hxx> +#include <editeng/crossedoutitem.hxx> +#include <editeng/udlnitem.hxx> +#include <editeng/contouritem.hxx> +#include <editeng/emphasismarkitem.hxx> +#include <editeng/fontitem.hxx> +#include <editeng/shdditem.hxx> +#include <editeng/cmapitem.hxx> +#include <svx/xbtmpit.hxx> +#include <svx/xflhtit.hxx> +#include <svx/xflgrit.hxx> +#include <svx/xflclit.hxx> +#include <svx/xlnedcit.hxx> +#include <svx/xlnstcit.hxx> +#include <svx/xlnedwit.hxx> +#include <svx/xlnstwit.hxx> +#include <svx/xlnedit.hxx> +#include <editeng/charreliefitem.hxx> +#include <svx/xlnstit.hxx> +#include <svx/xlndsit.hxx> +#include <svx/xlnwtit.hxx> +#include <svx/xlnclit.hxx> +#include <svx/svditer.hxx> +#include <svx/svdogrp.hxx> +#include <svx/sdsxyitm.hxx> +#include <svx/sdtditm.hxx> +#include <svx/sdtaitm.hxx> +#include <svx/sdynitm.hxx> +#include <editeng/numitem.hxx> +#include <editeng/unolingu.hxx> +#include <svl/itempool.hxx> +#include <editeng/outlobj.hxx> +#include <sfx2/viewfrm.hxx> +#include <editeng/frmdiritem.hxx> +#include <svx/sdasitm.hxx> + +#include <sdresid.hxx> +#include <drawdoc.hxx> +#include <sdpage.hxx> +#include <strings.hrc> +#include <glob.hxx> +#include <stlpool.hxx> +#include <shapelist.hxx> +#include <basegfx/point/b2dpoint.hxx> +#include <basegfx/polygon/b2dpolygon.hxx> +#include <svl/itemset.hxx> +#include <app.hrc> +#include <strings.hxx> + +namespace com::sun::star::linguistic2 { class XHyphenator; } + +using namespace ::com::sun::star; +using namespace ::com::sun::star::uno; +using namespace ::com::sun::star::lang; +using namespace ::com::sun::star::style; +using namespace ::com::sun::star::container; +using namespace ::com::sun::star::linguistic2; +using namespace ::sd; + +// CreateLayoutTemplates +// At the moment (31.03.1995), the StyleSheetPool only saves styleheets that +// have an ItemSet. To save all stylesheets, we force the creation of an ItemSet +// with a GetItemSet call. +// We can remove this behavior once the pool saves styleheets even without an ItemSet +void SdDrawDocument::CreateLayoutTemplates() +{ + SdStyleSheetPool* pSSPool = static_cast<SdStyleSheetPool*>(GetStyleSheetPool()); + SfxStyleSheetBase* pSheet = nullptr; + const OUString aHelpFile; + OUString aStdName(SdResId(STR_STANDARD_STYLESHEET_NAME)); + + // Default style + + SfxStyleSearchBits nMask = SfxStyleSearchBits::Auto; + + OUString aName(aStdName); + pSheet = &(pSSPool->Make(aName, SfxStyleFamily::Para, nMask)); + pSheet->SetHelpId( aHelpFile, HID_STANDARD_STYLESHEET_NAME ); + SfxItemSet& rISet = pSheet->GetItemSet(); + + ::basegfx::B2DPolyPolygon aNullPolyPolygon; + Color aNullCol(COL_DEFAULT_SHAPE_STROKE); + + XDash aNullDash; + XGradient aNullGrad(aNullCol,COL_WHITE); + aNullGrad.SetStartIntens( 100 ); + aNullGrad.SetEndIntens( 100 ); + XHatch aNullHatch(aNullCol); + + // Line attributes (Extended OutputDevice) + rISet.Put(XLineStyleItem(drawing::LineStyle_SOLID)); + rISet.Put(XLineColorItem(OUString(), COL_DEFAULT_SHAPE_STROKE)); + rISet.Put(XLineWidthItem(0)); + rISet.Put(XLineDashItem(aNullDash)); + rISet.Put(XLineStartItem(aNullPolyPolygon)); + rISet.Put(XLineEndItem(aNullPolyPolygon)); + rISet.Put(XLineStartWidthItem(200)); + rISet.Put(XLineEndWidthItem(200)); + rISet.Put(XLineStartCenterItem()); + rISet.Put(XLineEndCenterItem()); + rISet.Put(SdrTextHorzAdjustItem(SDRTEXTHORZADJUST_BLOCK)); + + // Fill attributes (Extended OutputDevice) + rISet.Put(XFillStyleItem(drawing::FillStyle_SOLID)); + rISet.Put(XFillColorItem(OUString(), COL_DEFAULT_SHAPE_FILLING)); + + rISet.Put( XFillGradientItem( aNullGrad) ); + rISet.Put(XFillHatchItem(aNullHatch)); + Size aNullSize( 32, 32 ); + Bitmap aNullBmp(aNullSize, vcl::PixelFormat::N8_BPP); + aNullBmp.Erase( COL_WHITE ); + rISet.Put(XFillBitmapItem(Graphic(BitmapEx(aNullBmp)))); + + // Shadow attributes (Drawing Engine) + rISet.Put(makeSdrShadowItem(false)); + rISet.Put(makeSdrShadowColorItem(COL_GRAY)); + rISet.Put(makeSdrShadowXDistItem(200)); // 3 mm Shadow distance + rISet.Put(makeSdrShadowYDistItem(200)); + + vcl::Font aLatinFont, aCJKFont, aCTLFont; + + getDefaultFonts( aLatinFont, aCJKFont, aCTLFont ); + + SvxFontItem aSvxFontItem( aLatinFont.GetFamilyType(), aLatinFont.GetFamilyName(), aLatinFont.GetStyleName(), aLatinFont.GetPitch(), + aLatinFont.GetCharSet(), EE_CHAR_FONTINFO ); + + SvxFontItem aSvxFontItemCJK( aCJKFont.GetFamilyType(), aCJKFont.GetFamilyName(), aCJKFont.GetStyleName(), aCJKFont.GetPitch(), + aCJKFont.GetCharSet(), EE_CHAR_FONTINFO_CJK ); + + SvxFontItem aSvxFontItemCTL( aCTLFont.GetFamilyType(), aCTLFont.GetFamilyName(), aCTLFont.GetStyleName(), aCTLFont.GetPitch(), + aCTLFont.GetCharSet(), EE_CHAR_FONTINFO_CTL ); + + rISet.Put( aSvxFontItem ); + rISet.Put( aSvxFontItemCJK ); + rISet.Put( aSvxFontItemCTL ); + + rISet.Put( SvxFontHeightItem( 635, 100, EE_CHAR_FONTHEIGHT ) ); // sj: (i33745) changed default from 24 to 18 pt + rISet.Put( SvxFontHeightItem( 635, 100, EE_CHAR_FONTHEIGHT_CJK ) ); // 18 pt + rISet.Put( SvxFontHeightItem( convertFontHeightToCTL( 635 ), 100, EE_CHAR_FONTHEIGHT_CTL ) ); // 18 pt + + rISet.Put( SvxWeightItem( WEIGHT_NORMAL, EE_CHAR_WEIGHT ) ); + rISet.Put( SvxWeightItem( WEIGHT_NORMAL, EE_CHAR_WEIGHT_CJK ) ); + rISet.Put( SvxWeightItem( WEIGHT_NORMAL, EE_CHAR_WEIGHT_CTL ) ); + + rISet.Put( SvxPostureItem( ITALIC_NONE, EE_CHAR_ITALIC ) ); + rISet.Put( SvxPostureItem( ITALIC_NONE, EE_CHAR_ITALIC_CJK ) ); + rISet.Put( SvxPostureItem( ITALIC_NONE, EE_CHAR_ITALIC_CTL ) ); + + rISet.Put(SvxContourItem(false, EE_CHAR_OUTLINE )); + rISet.Put(SvxShadowedItem(false, EE_CHAR_SHADOW )); + rISet.Put(SvxUnderlineItem(LINESTYLE_NONE, EE_CHAR_UNDERLINE)); + rISet.Put(SvxOverlineItem(LINESTYLE_NONE, EE_CHAR_OVERLINE)); + rISet.Put(SvxCrossedOutItem(STRIKEOUT_NONE, EE_CHAR_STRIKEOUT )); + rISet.Put(SvxCaseMapItem(SvxCaseMap::NotMapped, EE_CHAR_CASEMAP )); + rISet.Put(SvxEmphasisMarkItem(FontEmphasisMark::NONE, EE_CHAR_EMPHASISMARK)); + rISet.Put(SvxCharReliefItem(FontRelief::NONE, EE_CHAR_RELIEF)); + rISet.Put(SvxColorItem(COL_AUTO, EE_CHAR_COLOR )); + + // Paragraph attributes (Edit Engine) + rISet.Put(SvxLRSpaceItem(EE_PARA_LRSPACE)); + rISet.Put(SvxULSpaceItem(EE_PARA_ULSPACE)); + + rISet.Put( makeSdrTextLeftDistItem( 250 ) ); // sj: (i33745) using text frame distances seems to be a better default + rISet.Put( makeSdrTextRightDistItem( 250 ) ); + rISet.Put( makeSdrTextUpperDistItem( 125 ) ); + rISet.Put( makeSdrTextLowerDistItem( 125 ) ); + + // Set Word-wrap to true by default + rISet.Put( makeSdrTextWordWrapItem(true) ); + + rISet.Put( SvxLineSpacingItem( LINE_SPACE_DEFAULT_HEIGHT, EE_PARA_SBL ) ); + + // #i16874# enable kerning by default but only for new documents + rISet.Put( SvxAutoKernItem( true, EE_CHAR_PAIRKERNING ) ); + + // Bullet + // BulletItem and BulletFont for title and outline + SvxBulletItem aBulletItem(EE_PARA_BULLET); + // Identical in all layers + aBulletItem.SetStyle(SvxBulletStyle::BULLET); + aBulletItem.SetStart(1); + aBulletItem.SetScale(45); // In percent + + vcl::Font aBulletFont( SdStyleSheetPool::GetBulletFont() ); + + aBulletFont.SetFontSize(Size(0,635)); // sj: (i33745) changed default from 24 to 18 pt + + aBulletItem.SetFont(aBulletFont); + aBulletItem.SetSymbol( 0x25CF ); // In points + rISet.Put(aBulletItem); + + // New BulletItem + SdStyleSheetPool::PutNumBulletItem( pSheet, aBulletFont ); + + SfxItemSet* pISet = nullptr; + + // Default > Object without filling + { + aName = SdResId(STR_POOLSHEET_OBJWITHOUTFILL); + pSheet = &(pSSPool->Make(aName, SfxStyleFamily::Para, nMask)); + pSheet->SetParent(aStdName); + pSheet->SetHelpId( aHelpFile, HID_POOLSHEET_OBJWITHOUTFILL ); + pISet = &pSheet->GetItemSet(); + + pISet->Put(XFillStyleItem(drawing::FillStyle_NONE)); + } + // Default > Object no fill no line + { + aName = SdResId(STR_POOLSHEET_OBJNOLINENOFILL); + pSheet = &(pSSPool->Make(aName, SfxStyleFamily::Para, nMask)); + pSheet->SetParent(aStdName); + pSheet->SetHelpId( aHelpFile, HID_POOLSHEET_OBJNOLINENOFILL ); + pISet = &pSheet->GetItemSet(); + + pISet->Put(XFillStyleItem(drawing::FillStyle_NONE)); + pISet->Put(XLineStyleItem(drawing::LineStyle_NONE)); + } + + // tdf#94369 + + // Text + OUString aTextName; + { + aTextName = SdResId(STR_POOLSHEET_TEXT); + pSheet = &(pSSPool->Make(aTextName, SfxStyleFamily::Para, nMask)); + pSheet->SetHelpId( aHelpFile, HID_POOLSHEET_TEXT ); + pISet = &pSheet->GetItemSet(); + aSvxFontItem.SetFamilyName("Noto Sans"); + pISet->Put( aSvxFontItem ); // Noto Sans + pISet->Put(XFillStyleItem(drawing::FillStyle_SOLID)); // solid fill + pISet->Put(XFillColorItem(OUString(), Color(0xeeeeee))); // light gray 5 + pISet->Put(XLineStyleItem(drawing::LineStyle_SOLID)); // solid fill + pISet->Put(XLineColorItem(OUString(), Color(0xcccccc))); // light gray 3 + } + // Text > A4 + OUString aA4Name; + { + aA4Name = SdResId(STR_POOLSHEET_A4); + pSheet = &(pSSPool->Make(aA4Name, SfxStyleFamily::Para, nMask)); + pSheet->SetParent( aTextName ); + pSheet->SetHelpId( aHelpFile, HID_POOLSHEET_A4 ); + pISet = &pSheet->GetItemSet(); + pISet->Put(SvxFontHeightItem(635, 100, EE_CHAR_FONTHEIGHT )); // 18 pt + pISet->Put(XFillStyleItem(drawing::FillStyle_NONE)); // no filling + } + // Text > A4 > Title + { + + aName = SdResId(STR_POOLSHEET_A4_TITLE); + pSheet = &(pSSPool->Make(aName, SfxStyleFamily::Para, nMask)); + pSheet->SetParent( aA4Name ); + pSheet->SetHelpId( aHelpFile, HID_POOLSHEET_A4_TITLE ); + pISet = &pSheet->GetItemSet(); + pISet->Put(SvxFontHeightItem(1551, 100, EE_CHAR_FONTHEIGHT )); // 44 pt + pISet->Put(XLineStyleItem(drawing::LineStyle_NONE)); // no border + } + // Text > A4 > Headline + { + aName = SdResId(STR_POOLSHEET_A4_HEADLINE); + pSheet = &(pSSPool->Make(aName, SfxStyleFamily::Para, nMask)); + pSheet->SetParent( aA4Name ); + pSheet->SetHelpId( aHelpFile, HID_POOLSHEET_A4_HEADLINE ); + pISet = &pSheet->GetItemSet(); + pISet->Put(SvxFontHeightItem(847, 100, EE_CHAR_FONTHEIGHT )); // 24 pt + pISet->Put(XLineStyleItem(drawing::LineStyle_NONE)); // no border + } + // Text > A4 > Text + { + aName = SdResId(STR_POOLSHEET_A4_TEXT); + pSheet = &(pSSPool->Make(aName, SfxStyleFamily::Para, nMask)); + pSheet->SetParent(aA4Name); + pSheet->SetHelpId( aHelpFile, HID_POOLSHEET_A4_TEXT ); + pISet = &pSheet->GetItemSet(); + pISet->Put(XLineStyleItem(drawing::LineStyle_NONE)); // no border + } + // Text > A0 + OUString aA0Name; + { + aA0Name = SdResId(STR_POOLSHEET_A0); + pSheet = &(pSSPool->Make(aA0Name, SfxStyleFamily::Para, nMask)); + pSheet->SetParent(aTextName); + pSheet->SetHelpId( aHelpFile, HID_POOLSHEET_A0 ); + pISet = &pSheet->GetItemSet(); + pISet->Put(SvxFontHeightItem(1692, 100, EE_CHAR_FONTHEIGHT )); // 48 pt + pISet->Put(XFillStyleItem(drawing::FillStyle_NONE)); // no filling + } + // Text > A0 > Title + { + aName = SdResId(STR_POOLSHEET_A0_TITLE); + pSheet = &(pSSPool->Make(aName, SfxStyleFamily::Para, nMask)); + pSheet->SetParent(aA0Name); + pSheet->SetHelpId( aHelpFile, HID_POOLSHEET_A0_TITLE ); + pISet = &pSheet->GetItemSet(); + pISet->Put(SvxFontHeightItem(3385, 100, EE_CHAR_FONTHEIGHT )); // 96 pt + pISet->Put(XLineStyleItem(drawing::LineStyle_NONE)); // no border + } + // Text > A0 > Headline + { + aName = SdResId(STR_POOLSHEET_A0_HEADLINE); + pSheet = &(pSSPool->Make(aName, SfxStyleFamily::Para, nMask)); + pSheet->SetParent(aA0Name); + pSheet->SetHelpId( aHelpFile, HID_POOLSHEET_A0_HEADLINE ); + pISet = &pSheet->GetItemSet(); + pISet->Put(SvxFontHeightItem(2538, 100, EE_CHAR_FONTHEIGHT )); // 72 pt + pISet->Put(XLineStyleItem(drawing::LineStyle_NONE)); // no border + } + // Text > A0 > Text + { + aName = SdResId(STR_POOLSHEET_A0_TEXT); + pSheet = &(pSSPool->Make(aName, SfxStyleFamily::Para, nMask)); + pSheet->SetParent(aA0Name); + pSheet->SetHelpId( aHelpFile, HID_POOLSHEET_A0_TEXT ); + pISet = &pSheet->GetItemSet(); + pISet->Put(XLineStyleItem(drawing::LineStyle_NONE)); // no border + } + + // Graphic + OUString aGraphicName; + XFillGradientItem aFillGradient; + XGradient aGradient; + + { + aGraphicName = SdResId(STR_POOLSHEET_GRAPHIC); + pSheet = &(pSSPool->Make(aGraphicName, SfxStyleFamily::Para, nMask)); + pSheet->SetHelpId( aHelpFile, HID_POOLSHEET_GRAPHIC ); + pISet = &pSheet->GetItemSet(); + aSvxFontItem.SetFamilyName("Liberation Sans"); // Liberation Sans + pISet->Put( aSvxFontItem ); + pISet->Put( SvxFontHeightItem(635, 100, EE_CHAR_FONTHEIGHT) ); // 18 pt + pISet->Put( XFillStyleItem(drawing::FillStyle_SOLID) ); // solid fill + pISet->Put( XFillColorItem(OUString(), COL_WHITE) ); // filled white + + } + // Graphic > Shapes + OUString aShapesName; + { + aShapesName = SdResId(STR_POOLSHEET_SHAPES); + pSheet = &(pSSPool->Make(aShapesName, SfxStyleFamily::Para, nMask)); + pSheet->SetParent( aGraphicName ); + pSheet->SetHelpId( aHelpFile, HID_POOLSHEET_SHAPES); + pISet = &pSheet->GetItemSet(); + pISet->Put(XFillStyleItem(drawing::FillStyle_GRADIENT)); // fill with gradient + aGradient.SetGradientStyle( ::awt::GradientStyle_RECT); // square type + aGradient.SetAngle( 0_deg10 ); // 0° angle + aGradient.SetStartColor( Color(0xcccccc) ); // white + aGradient.SetEndColor( COL_WHITE ); // light gray 3 + aFillGradient.SetName( aShapesName ); + aFillGradient.SetGradientValue(aGradient); + pISet->Put( aFillGradient ); + pISet->Put( XLineStyleItem(drawing::LineStyle_NONE) ); // no border + pISet->Put( SvxFontHeightItem(494, 100, EE_CHAR_FONTHEIGHT) ); // 14 pt + pISet->Put( SvxWeightItem(WEIGHT_BOLD, EE_CHAR_WEIGHT) ); // bold + } + // Graphic > Shapes > Filled + OUString aFilledName(SdResId(STR_POOLSHEET_FILLED)); + { + aName = aFilledName; + pSheet = &(pSSPool->Make(aName, SfxStyleFamily::Para, nMask)); + pSheet->SetHelpId( aHelpFile, HID_POOLSHEET_FILLED ); + pSheet->SetParent( aShapesName ); + pISet = &pSheet->GetItemSet(); + + aGradient.SetGradientStyle( ::awt::GradientStyle_LINEAR ); + aGradient.SetAngle( 300_deg10 ); + aGradient.SetStartColor( COL_WHITE ); // white + aGradient.SetEndColor( Color(0xcccccc) ); // light gray 3 + aFillGradient.SetName( aName ); + aFillGradient.SetGradientValue(aGradient); + pISet->Put( XFillStyleItem(drawing::FillStyle_GRADIENT) ); + pISet->Put( aFillGradient ); + } + // Graphic > Shapes > Filled > Blue + { + aName =SdResId(STR_POOLSHEET_FILLED_BLUE); + pSheet = &(pSSPool->Make(aName, SfxStyleFamily::Para, nMask)); + pSheet->SetParent(aFilledName); + pSheet->SetHelpId( aHelpFile, HID_POOLSHEET_FILLED_BLUE ); + pISet = &pSheet->GetItemSet(); + + aGradient.SetStartColor( Color(0x00729fcf) ); // light blue 2 + aGradient.SetEndColor( Color(0x00355269) ); // dark blue 2 + aFillGradient.SetName( aName ); + aFillGradient.SetGradientValue(aGradient); + pISet->Put( aFillGradient ); + pISet->Put( SvxColorItem(COL_WHITE, EE_CHAR_COLOR )); // font white + } + // Graphic > Shapes > Filled > Green + { + aName =SdResId(STR_POOLSHEET_FILLED_GREEN); + pSheet = &(pSSPool->Make(aName, SfxStyleFamily::Para, nMask)); + pSheet->SetParent(aFilledName); + pSheet->SetHelpId( aHelpFile, HID_POOLSHEET_FILLED_GREEN ); + pISet = &pSheet->GetItemSet(); + + aGradient.SetStartColor( Color(0x0077bc65) ); // light green 2 + aGradient.SetEndColor( Color(0x00127622) ); // dark green 2 + aFillGradient.SetName( aName ); + aFillGradient.SetGradientValue(aGradient); + pISet->Put( aFillGradient ); + pISet->Put( aSvxFontItem ); // font name + pISet->Put( SvxColorItem(COL_WHITE, EE_CHAR_COLOR )); // font white + } + // Graphic > Shapes > Filled > Red + { + aName =SdResId(STR_POOLSHEET_FILLED_RED); + pSheet = &(pSSPool->Make(aName, SfxStyleFamily::Para, nMask)); + pSheet->SetParent(aFilledName); + pSheet->SetHelpId( aHelpFile, HID_POOLSHEET_FILLED_RED ); + pISet = &pSheet->GetItemSet(); + + aGradient.SetStartColor( Color(0x00ff6d6d) ); // light red 2 + aGradient.SetEndColor( Color(0x00c9211e) ); // dark red 2 + aFillGradient.SetName( aName ); + aFillGradient.SetGradientValue(aGradient); + pISet->Put( aFillGradient ); + pISet->Put( SvxColorItem(COL_WHITE, EE_CHAR_COLOR )); // font white + } + // Graphic > Shapes > Filled > Yellow + { + aName =SdResId(STR_POOLSHEET_FILLED_YELLOW); + pSheet = &(pSSPool->Make(aName, SfxStyleFamily::Para, nMask)); + pSheet->SetParent(aFilledName); + pSheet->SetHelpId( aHelpFile, HID_POOLSHEET_FILLED_YELLOW ); + pISet = &pSheet->GetItemSet(); + + aGradient.SetStartColor( Color(0x00ffde59) ); // light gold 2 + aGradient.SetEndColor( Color(0x00b47804) ); // dark gold 2 + aFillGradient.SetName( aName ); + aFillGradient.SetGradientValue(aGradient); + pISet->Put( aFillGradient ); + pISet->Put( SvxColorItem(COL_WHITE, EE_CHAR_COLOR )); // font white + } + // Graphic > Shapes > Outlines + OUString aOutlineName(SdResId(STR_POOLSHEET_OUTLINE)); + { + aName = aOutlineName; + pSheet = &(pSSPool->Make(aName, SfxStyleFamily::Para, nMask)); + pSheet->SetHelpId( aHelpFile, HID_POOLSHEET_OUTLINE ); + pSheet->SetParent( aShapesName ); + pISet = &pSheet->GetItemSet(); + pISet->Put( XFillStyleItem(drawing::FillStyle_NONE) ); // clear + pISet->Put( XLineStyleItem(drawing::LineStyle_SOLID) ); // solide line + pISet->Put( XLineWidthItem(81) ); // 2.3 pt + pISet->Put( XLineColorItem(OUString(), COL_BLACK) ); // b/w + } + // Graphic > Shapes > Outlines > Blue + { + aName =SdResId(STR_POOLSHEET_OUTLINE_BLUE); + pSheet = &(pSSPool->Make(aName, SfxStyleFamily::Para, nMask)); + pSheet->SetParent(aOutlineName); + pSheet->SetHelpId( aHelpFile, HID_POOLSHEET_OUTLINE_BLUE ); + pISet = &pSheet->GetItemSet(); + pISet->Put( XLineColorItem(OUString(), Color(0x00355269)) ); // dark blue 2 + pISet->Put( SvxColorItem(Color(0x00355269), EE_CHAR_COLOR )); // font color + } + // Graphic > Shapes > Outlines > Green + { + aName =SdResId(STR_POOLSHEET_OUTLINE_GREEN); + pSheet = &(pSSPool->Make(aName, SfxStyleFamily::Para, nMask)); + pSheet->SetParent(aOutlineName); + pSheet->SetHelpId( aHelpFile, HID_POOLSHEET_OUTLINE_GREEN ); + pISet = &pSheet->GetItemSet(); + pISet->Put( XLineColorItem(OUString(), Color(0x00127622)) ); // dark green 2 + pISet->Put( SvxColorItem(Color(0x00127622), EE_CHAR_COLOR )); // font color + } + // Graphic > Shapes > Outlines > Red + { + aName =SdResId(STR_POOLSHEET_OUTLINE_RED); + pSheet = &(pSSPool->Make(aName, SfxStyleFamily::Para, nMask)); + pSheet->SetParent(aOutlineName); + pSheet->SetHelpId( aHelpFile, HID_POOLSHEET_OUTLINE_RED ); + pISet = &pSheet->GetItemSet(); + pISet->Put( XLineColorItem(OUString(), Color(0x00c9211e)) ); // dark red 2 + pISet->Put( SvxColorItem(Color(0x00c9211e), EE_CHAR_COLOR )); // font color + } + // Graphic > Shapes > Outlines > Yellow + { + aName =SdResId(STR_POOLSHEET_OUTLINE_YELLOW); + pSheet = &(pSSPool->Make(aName, SfxStyleFamily::Para, nMask)); + pSheet->SetParent(aOutlineName); + pSheet->SetHelpId( aHelpFile, HID_POOLSHEET_OUTLINE_YELLOW ); + pISet = &pSheet->GetItemSet(); + pISet->Put( XLineStyleItem(drawing::LineStyle_SOLID)); + pISet->Put( XLineColorItem(OUString(), Color(0x00b47804)) ); // dark gold 2 + pISet->Put( SvxColorItem(Color(0x00b47804), EE_CHAR_COLOR )); // font color + } + // Graphic > Lines + OUString aLinesName; + { + aLinesName = SdResId(STR_POOLSHEET_LINES); + pSheet = &(pSSPool->Make(aLinesName, SfxStyleFamily::Para, nMask)); + pSheet->SetParent( aGraphicName ); + pSheet->SetHelpId( aHelpFile, HID_POOLSHEET_LINES); + pISet = &pSheet->GetItemSet(); + pISet->Put( XFillStyleItem(drawing::FillStyle_NONE) ); // clear + pISet->Put( XLineStyleItem(drawing::LineStyle_SOLID) ); // solide line + pISet->Put( XLineColorItem(OUString(), COL_BLACK) ); // b/w + } + // Graphic > Lines > Measurements + { + aName = SdResId(STR_POOLSHEET_MEASURE); + pSheet = &(pSSPool->Make(aName, SfxStyleFamily::Para, nMask)); + pSheet->SetParent(aLinesName); + pSheet->SetHelpId( aHelpFile, HID_POOLSHEET_MEASURE ); + pISet = &pSheet->GetItemSet(); + + ::basegfx::B2DPolygon aArrow; // arrows + aArrow.append(::basegfx::B2DPoint(10.0, 0.0)); + aArrow.append(::basegfx::B2DPoint(0.0, 30.0)); + aArrow.append(::basegfx::B2DPoint(20.0, 30.0)); + aArrow.setClosed(true); + + pISet->Put(XLineStartItem(SvxResId(RID_SVXSTR_ARROW),::basegfx::B2DPolyPolygon(aArrow))); + pISet->Put(XLineStartWidthItem(200)); + pISet->Put(XLineEndItem(SvxResId(RID_SVXSTR_ARROW),::basegfx::B2DPolyPolygon(aArrow))); + pISet->Put(XLineEndWidthItem(200)); + pISet->Put(SdrYesNoItem(SDRATTR_MEASURESHOWUNIT, true)); + } + // Graphic > Lines > Dashed + { + aName = SdResId(STR_POOLSHEET_LINES_DASHED); + pSheet = &(pSSPool->Make(aName, SfxStyleFamily::Para, nMask)); + pSheet->SetParent(aLinesName); + pSheet->SetHelpId( aHelpFile, HID_POOLSHEET_LINES_DASHED ); + pISet = &pSheet->GetItemSet(); + pISet->Put( XLineStyleItem(drawing::LineStyle_DASH) ); // dashed line + } + + // Generate presentation templates for default layout. + OUString aPrefix = SdResId(STR_LAYOUT_DEFAULT_NAME); + pSSPool->CreateLayoutStyleSheets(aPrefix); +} + +static Any implMakeSolidCellStyle( SdStyleSheetPool* pSSPool, const OUString& rName, const OUString& rParent, const Color& rColor ) +{ + SfxStyleSheetBase* pSheet = &(pSSPool->Make(rName, SfxStyleFamily::Frame, SfxStyleSearchBits::Auto)); + pSheet->SetParent(rParent); + SfxItemSet* pISet = &pSheet->GetItemSet(); + pISet->Put(XFillStyleItem(drawing::FillStyle_SOLID)); + pISet->Put(XFillColorItem(OUString(), rColor)); + + return Any( Reference< XStyle >( static_cast< XWeak* >( pSheet ), UNO_QUERY ) ); +} + +static void implCreateTableTemplate( const Reference< XNameContainer >& xTableFamily, const OUString& rName, const Any& rBody, const Any& rHeading, const Any& rBanding ) +{ + if( !xTableFamily.is() ) + return; + + try + { + if( !xTableFamily->hasByName( rName ) ) + { + Reference< XSingleServiceFactory > xFactory( xTableFamily, UNO_QUERY_THROW ); + Reference< XNameReplace > xDefaultTableStyle( xFactory->createInstance(), UNO_QUERY_THROW ); + xTableFamily->insertByName( rName, Any( xDefaultTableStyle ) ); + + xDefaultTableStyle->replaceByName( "body", rBody ); + xDefaultTableStyle->replaceByName( "odd-rows" , rBanding ); + xDefaultTableStyle->replaceByName( "odd-columns" , rBanding ); + xDefaultTableStyle->replaceByName( "first-row" , rHeading ); + xDefaultTableStyle->replaceByName( "first-column" , rHeading ); + xDefaultTableStyle->replaceByName( "last-row" , rHeading ); + xDefaultTableStyle->replaceByName( "last-column" , rHeading ); + } + } + catch( Exception& ) + { + TOOLS_WARN_EXCEPTION( "sd", "sd::implCreateTableTemplate()"); + } +} + +void SdDrawDocument::CreateDefaultCellStyles() +{ + SdStyleSheetPool* pSSPool = static_cast< SdStyleSheetPool* >(GetStyleSheetPool()); + SfxStyleSheetBase* pSheet = nullptr; + + Reference< XNameContainer > xTableFamily( pSSPool->getByName( "table" ), UNO_QUERY ); + + // ---- Default ----------------------------------------------- + + OUString aDefaultCellStyleName( "default" ); + + pSheet = &(pSSPool->Make(aDefaultCellStyleName, SfxStyleFamily::Frame, SfxStyleSearchBits::Auto)); + pSheet->SetHelpId( OUString(), HID_SD_CELL_STYLE_DEFAULT ); + SfxItemSet& rISet = pSheet->GetItemSet(); + + rISet.Put(XFillStyleItem(drawing::FillStyle_SOLID)); + rISet.Put(XFillColorItem(OUString(), Color(0x00ccccff))); + + vcl::Font aLatinFont, aCJKFont, aCTLFont; + + getDefaultFonts( aLatinFont, aCJKFont, aCTLFont ); + + SvxFontItem aSvxFontItem( aLatinFont.GetFamilyType(), aLatinFont.GetFamilyName(), aLatinFont.GetStyleName(), aLatinFont.GetPitch(), + aLatinFont.GetCharSet(), EE_CHAR_FONTINFO ); + + SvxFontItem aSvxFontItemCJK( aCJKFont.GetFamilyType(), aCJKFont.GetFamilyName(), aCJKFont.GetStyleName(), aCJKFont.GetPitch(), + aCJKFont.GetCharSet(), EE_CHAR_FONTINFO_CJK ); + + SvxFontItem aSvxFontItemCTL( aCTLFont.GetFamilyType(), aCTLFont.GetFamilyName(), aCTLFont.GetStyleName(), aCTLFont.GetPitch(), + aCTLFont.GetCharSet(), EE_CHAR_FONTINFO_CTL ); + + rISet.Put( aSvxFontItem ); + rISet.Put( aSvxFontItemCJK ); + rISet.Put( aSvxFontItemCTL ); + + rISet.Put( SvxFontHeightItem( 635, 100, EE_CHAR_FONTHEIGHT ) ); // sj: (i33745) changed default from 24 to 18 pt + rISet.Put( SvxFontHeightItem( 635, 100, EE_CHAR_FONTHEIGHT_CJK ) ); // 18 pt + rISet.Put( SvxFontHeightItem( convertFontHeightToCTL( 635 ), 100, EE_CHAR_FONTHEIGHT_CTL ) ); // 18 pt + + rISet.Put(SvxColorItem(COL_AUTO, EE_CHAR_COLOR )); + + // Paragraph attributes (Edit Engine) + rISet.Put(SvxLRSpaceItem(EE_PARA_LRSPACE)); + rISet.Put(SvxULSpaceItem(EE_PARA_ULSPACE)); + + rISet.Put( makeSdrTextLeftDistItem( 250 ) ); + rISet.Put( makeSdrTextRightDistItem( 250 ) ); + rISet.Put( makeSdrTextUpperDistItem( 130 ) ); + rISet.Put( makeSdrTextLowerDistItem( 130 ) ); + + rISet.Put( SvxLineSpacingItem( LINE_SPACE_DEFAULT_HEIGHT, EE_PARA_SBL ) ); + rISet.Put( SvxAutoKernItem( true, EE_CHAR_PAIRKERNING ) ); + rISet.Put( SdrTextVertAdjustItem(SDRTEXTVERTADJUST_TOP) ); + rISet.Put( SdrTextHorzAdjustItem(SDRTEXTHORZADJUST_LEFT) ); + + Color aWhite( COL_WHITE ); + ::editeng::SvxBorderLine aBorderLine( + &aWhite, 1, SvxBorderLineStyle::SOLID); + + SvxBoxItem aBoxItem( SDRATTR_TABLE_BORDER ); + aBoxItem.SetLine( &aBorderLine, SvxBoxItemLine::TOP ); + aBoxItem.SetLine( &aBorderLine, SvxBoxItemLine::BOTTOM ); + aBoxItem.SetLine( &aBorderLine, SvxBoxItemLine::LEFT ); + aBoxItem.SetLine( &aBorderLine, SvxBoxItemLine::RIGHT ); + + rISet.Put( aBoxItem ); + + // ---- default -------------------------------------------------- + + Any aGray1( implMakeSolidCellStyle( pSSPool, "gray1" , aDefaultCellStyleName, Color(230,230,230))); + Any aGray2( implMakeSolidCellStyle( pSSPool, "gray2" , aDefaultCellStyleName, Color(204,204,204))); + Any aGray3( implMakeSolidCellStyle( pSSPool, "gray3" , aDefaultCellStyleName, Color(179,179,179))); + + implCreateTableTemplate( xTableFamily, "default" , aGray1, aGray3, aGray2 ); + + // ---- BW ------------------------------------------------ + + Any aBW1( implMakeSolidCellStyle( pSSPool, "bw1" , aDefaultCellStyleName, Color(255,255,255))); + Any aBW2( implMakeSolidCellStyle( pSSPool, "bw2" , aDefaultCellStyleName, Color(230,230,230))); + Any aBW3( implMakeSolidCellStyle( pSSPool, "bw3" , aDefaultCellStyleName, Color(0,0,0))); + + implCreateTableTemplate( xTableFamily, "bw" , aBW1, aBW3, aBW2 ); + + // ---- Orange -------------------------------------------------- + + Any aOrange1( implMakeSolidCellStyle( pSSPool, "orange1" , aDefaultCellStyleName, Color(255,204,153))); + Any aOrange2( implMakeSolidCellStyle( pSSPool, "orange2" , aDefaultCellStyleName, Color(255,153,102))); + Any aOrange3( implMakeSolidCellStyle( pSSPool, "orange3" , aDefaultCellStyleName, Color(255,102,51))); + + implCreateTableTemplate( xTableFamily, "orange" , aOrange1, aOrange3, aOrange2 ); + + // ---- Turquoise -------------------------------------------------- + + Any aTurquoise1( implMakeSolidCellStyle( pSSPool, "turquoise1" , aDefaultCellStyleName, Color(71,184,184))); + Any aTurquoise2( implMakeSolidCellStyle( pSSPool, "turquoise2" , aDefaultCellStyleName, Color(51,163,163))); + Any aTurquoise3( implMakeSolidCellStyle( pSSPool, "turquoise3" , aDefaultCellStyleName, Color(25,138,138))); + + implCreateTableTemplate( xTableFamily, "turquoise" , aTurquoise1, aTurquoise3, aTurquoise2 ); + + // ---- Gray ------------------------------------------------ + + Any aBlue1( implMakeSolidCellStyle( pSSPool, "blue1" , aDefaultCellStyleName, Color(153,204,255))); + Any aBlue2( implMakeSolidCellStyle( pSSPool, "blue2" , aDefaultCellStyleName, Color(0,153,255))); + Any aBlue3( implMakeSolidCellStyle( pSSPool, "blue3" , aDefaultCellStyleName, Color(0,102,204))); + + implCreateTableTemplate( xTableFamily, "blue" , aBlue1, aBlue3, aBlue2 ); + + // ---- Sun ------------------------------------------------ + + Any aSun1( implMakeSolidCellStyle( pSSPool, "sun1" , aDefaultCellStyleName, Color(230,230,255))); + Any aSun2( implMakeSolidCellStyle( pSSPool, "sun2" , aDefaultCellStyleName, Color(204,204,255))); + Any aSun3( implMakeSolidCellStyle( pSSPool, "sun3" , aDefaultCellStyleName, Color(153,153,255))); + + implCreateTableTemplate( xTableFamily, "sun" , aSun1, aSun3, aSun2 ); + + // ---- Earth ---------------------------------------------- + + Any aEarth1( implMakeSolidCellStyle( pSSPool, "earth1" , aDefaultCellStyleName, Color(255,255,204))); + Any aEarth2( implMakeSolidCellStyle( pSSPool, "earth2" , aDefaultCellStyleName, Color(255,204,153))); + Any aEarth3( implMakeSolidCellStyle( pSSPool, "earth3" , aDefaultCellStyleName, Color(204,102,51))); + + implCreateTableTemplate( xTableFamily, "earth" , aEarth1, aEarth3, aEarth2 ); + + // ---- Green ---------------------------------------------- + + Any aGreen1( implMakeSolidCellStyle( pSSPool, "green1" , aDefaultCellStyleName, Color(255,255,204))); + Any aGreen2( implMakeSolidCellStyle( pSSPool, "green2" , aDefaultCellStyleName, Color(148,189,94))); + Any aGreen3( implMakeSolidCellStyle( pSSPool, "green3" , aDefaultCellStyleName, Color(92,133,38))); + + implCreateTableTemplate( xTableFamily, "green" , aGreen1, aGreen3, aGreen2 ); + + // ---- Seaweed ---------------------------------------------- + + Any aSeetang1( implMakeSolidCellStyle( pSSPool, "seetang1" , aDefaultCellStyleName, Color(204,255,255))); + Any aSeetang2( implMakeSolidCellStyle( pSSPool, "seetang2" , aDefaultCellStyleName, Color(71,184,184))); + Any aSeetang3( implMakeSolidCellStyle( pSSPool, "seetang3" , aDefaultCellStyleName, Color(51,163,163))); + + implCreateTableTemplate( xTableFamily, "seetang" , aSeetang1, aSeetang3, aSeetang2 ); + + // ---- LightBlue ---------------------------------------------- + + Any aLightBlue1( implMakeSolidCellStyle( pSSPool, "lightblue1" , aDefaultCellStyleName, Color(255,255,255))); + Any aLightBlue2( implMakeSolidCellStyle( pSSPool, "lightblue2" , aDefaultCellStyleName, Color(230,230,255))); + Any aLightBlue3( implMakeSolidCellStyle( pSSPool, "lightblue3" , aDefaultCellStyleName, Color(153,153,204))); + + implCreateTableTemplate( xTableFamily, "lightblue" , aLightBlue1, aLightBlue3, aLightBlue2 ); + + // ---- Yellow ---------------------------------------------- + + Any aYellow1( implMakeSolidCellStyle( pSSPool, "yellow1" , aDefaultCellStyleName, Color(255,255,204))); + Any aYellow2( implMakeSolidCellStyle( pSSPool, "yellow2" , aDefaultCellStyleName, Color(255,255,153))); + Any aYellow3( implMakeSolidCellStyle( pSSPool, "yellow3" , aDefaultCellStyleName, Color(255,204,153))); + + implCreateTableTemplate( xTableFamily, "yellow" , aYellow1, aYellow3, aYellow2 ); +} + +// Number of pages that reference a master page +sal_uInt16 SdDrawDocument::GetMasterPageUserCount(SdrPage const * pMaster) const +{ + sal_uInt16 nResult = 0; + sal_uInt16 nPage; + sal_uInt16 nPageCount = GetPageCount(); + + for (nPage = 0; nPage < nPageCount; nPage++) + { + const SdrPage* pPage = GetPage(nPage); + + if(pPage->TRG_HasMasterPage()) + { + if(&(pPage->TRG_GetMasterPage()) == pMaster) + { + nResult++; + } + } + } + return nResult; +} + +// Finish OnlineSpelling in the background + +void SdDrawDocument::StopOnlineSpelling() +{ + if (mpOnlineSpellingIdle && mpOnlineSpellingIdle->IsActive()) + { + mpOnlineSpellingIdle->Stop(); + } + + mpOnlineSpellingIdle.reset(); + mpOnlineSpellingList.reset(); +} + +// Start OnlineSpelling in the background +void SdDrawDocument::StartOnlineSpelling(bool bForceSpelling) +{ + if ( !mbOnlineSpell || !(bForceSpelling || mbInitialOnlineSpellingEnabled) || + !mpDocSh || mpDocSh->IsReadOnly() ) + return; + + StopOnlineSpelling(); + + SdOutliner* pOutl = GetInternalOutliner(); + + Reference< XSpellChecker1 > xSpellChecker( LinguMgr::GetSpellChecker() ); + if ( xSpellChecker.is() ) + pOutl->SetSpeller( xSpellChecker ); + + Reference< XHyphenator > xHyphenator( LinguMgr::GetHyphenator() ); + if( xHyphenator.is() ) + pOutl->SetHyphenator( xHyphenator ); + + pOutl->SetDefaultLanguage( meLanguage ); + + mpOnlineSpellingList.reset(new ShapeList); + sal_uInt16 nPage; + + for ( nPage = 0; nPage < GetPageCount(); nPage++ ) + { + // Search in all pages + FillOnlineSpellingList(static_cast<SdPage*>(GetPage(nPage))); + } + + for (nPage = 0; nPage < GetMasterPageCount(); nPage++) + { + // Search all master pages + FillOnlineSpellingList(static_cast<SdPage*>( GetMasterPage(nPage) )); + } + + mpOnlineSpellingList->seekShape(0); + mpOnlineSpellingIdle.reset(new Idle("OnlineSpelling")); + mpOnlineSpellingIdle->SetInvokeHandler( LINK(this, SdDrawDocument, OnlineSpellingHdl) ); + mpOnlineSpellingIdle->SetPriority(TaskPriority::LOWEST); + mpOnlineSpellingIdle->Start(); +} + +// Fill OnlineSpelling list +void SdDrawDocument::FillOnlineSpellingList(SdPage const * pPage) +{ + SdrObjListIter aIter(pPage, SdrIterMode::Flat); + + while (aIter.IsMore()) + { + SdrObject* pObj = aIter.Next(); + + if( !pObj ) + continue; + + if (pObj->GetOutlinerParaObject()) + { + // Found a text object + mpOnlineSpellingList->addShape(*pObj); + } + else if (pObj->GetObjIdentifier() == SdrObjKind::Group) + { + // Found a group object + SdrObjListIter aGroupIter(static_cast< SdrObjGroup* >(pObj)->GetSubList(), SdrIterMode::DeepNoGroups); + + bool bSubTextObjFound = false; + + while (aGroupIter.IsMore() && !bSubTextObjFound) + { + if (aGroupIter.Next()->GetOutlinerParaObject()) + { + // Found a text object in a group object + bSubTextObjFound = true; + } + } + + if (bSubTextObjFound) + { + mpOnlineSpellingList->addShape(*pObj); + } + } + } +} + +// OnlineSpelling in the background +IMPL_LINK_NOARG(SdDrawDocument, OnlineSpellingHdl, Timer *, void) +{ + if (mpOnlineSpellingList!=nullptr + && ( !mbOnlineSpell || mpOnlineSpellingList->hasMore())) + { + // Spell next object + SdrObject* pObj = mpOnlineSpellingList->getNextShape(); + + if (pObj) + { + if (pObj->GetOutlinerParaObject() && dynamic_cast< const SdrTextObj *>( pObj ) != nullptr) + { + // Spell text object + SpellObject(static_cast<SdrTextObj*>(pObj)); + } + else if (pObj->GetObjIdentifier() == SdrObjKind::Group) + { + // Found a group object + SdrObjListIter aGroupIter(static_cast< SdrObjGroup* >(pObj)->GetSubList(), SdrIterMode::DeepNoGroups); + + + while (aGroupIter.IsMore()) + { + SdrObject* pSubObj = aGroupIter.Next(); + + if (pSubObj->GetOutlinerParaObject()) + if (auto pTextObj = dynamic_cast< SdrTextObj *>( pSubObj )) + // Found a text object in a group object + SpellObject(pTextObj); + } + } + } + + // Continue search + mpOnlineSpellingIdle->Start(); + } + else + { + // Initial spelling has finished + mbInitialOnlineSpellingEnabled = false; + + // Stop search + StopOnlineSpelling(); + + mpOnlineSearchItem.reset(); + } +} + +// Spell object (for OnlineSpelling) +void SdDrawDocument::SpellObject(SdrTextObj* pObj) +{ + if (!(pObj && pObj->GetOutlinerParaObject()) /* && pObj != pView->GetTextEditObject() */) + return; + + mbHasOnlineSpellErrors = false; + SdOutliner* pOutl = GetInternalOutliner(); + pOutl->SetUpdateLayout(true); + Link<EditStatus&,void> aEvtHdl = pOutl->GetStatusEventHdl(); + pOutl->SetStatusEventHdl(LINK(this, SdDrawDocument, OnlineSpellEventHdl)); + + OutlinerMode nOldOutlMode = pOutl->GetOutlinerMode(); + OutlinerMode nOutlMode = OutlinerMode::TextObject; + if (pObj->GetObjInventor() == SdrInventor::Default && + pObj->GetObjIdentifier() == SdrObjKind::OutlineText) + { + nOutlMode = OutlinerMode::OutlineObject; + } + pOutl->Init( nOutlMode ); + + // Put text into the outliner + pOutl->SetText(*pObj->GetOutlinerParaObject()); + + if (!mpOnlineSearchItem || pOutl->HasText(*mpOnlineSearchItem)) + { + // Spelling + pOutl->CompleteOnlineSpelling(); + + if (mbHasOnlineSpellErrors) + { + std::optional<OutlinerParaObject> pOPO = pOutl->CreateParaObject(); + if (pOPO) + { + if ( *pOPO != *pObj->GetOutlinerParaObject() || + !pObj->GetOutlinerParaObject()->isWrongListEqual( *pOPO )) + { + sd::ModifyGuard aGuard( this ); + + // taking text from the outliner + // use non-broadcasting version to avoid O(n^2) + pObj->NbcSetOutlinerParaObject( std::move(pOPO) ); + } + } + } + } + + pOutl->SetStatusEventHdl(aEvtHdl); + pOutl->SetUpdateLayout(false); + pOutl->Init( nOldOutlMode ); + mbHasOnlineSpellErrors = false; +} + +// Object was inserted into model +void SdDrawDocument::InsertObject(SdrObject* pObj) +{ + if(mpOnlineSpellingList && pObj) + { + if (pObj->GetOutlinerParaObject() || (pObj->GetObjIdentifier() == SdrObjKind::Group)) + { + // Add object to OnlineSpelling list + mpOnlineSpellingList->addShape(*pObj); + } + } +} + +// Object removed from model +void SdDrawDocument::RemoveObject(SdrObject* pObj) +{ + if(mpOnlineSpellingList && pObj) + { + if (pObj->GetOutlinerParaObject() || (pObj->GetObjIdentifier() == SdrObjKind::Group)) + { + // Replace object in OnlineSpelling list by 0 pointer + mpOnlineSpellingList->removeShape(*pObj); + } + } +} + +// Callback for ExecuteSpellPopup() +IMPL_LINK(SdDrawDocument, OnlineSpellEventHdl, EditStatus&, rEditStat, void) +{ + EditStatusFlags nStat = rEditStat.GetStatusWord(); + mbHasOnlineSpellErrors = bool(nStat & EditStatusFlags::WRONGWORDCHANGED); +} + +// Callback for ExecuteSpellPopup() + +// removed link and replaced with Imp method +void SdDrawDocument::ImpOnlineSpellCallback(SpellCallbackInfo const * pInfo, SdrObject* pObj, SdrOutliner const * pOutl) +{ + mpOnlineSearchItem.reset(); + + SpellCallbackCommand nCommand = pInfo->nCommand; + + if (nCommand == SpellCallbackCommand::IGNOREWORD + // restart when add to dictionary takes place, too. + || nCommand == SpellCallbackCommand::ADDTODICTIONARY) + { + if(pOutl) + if (auto pTextObj = dynamic_cast<SdrTextObj *>( pObj )) + { + bool bModified(IsChanged()); + pTextObj->SetOutlinerParaObject(pOutl->CreateParaObject()); + SetChanged(bModified); + pObj->BroadcastObjectChange(); + } + + mpOnlineSearchItem.reset(new SvxSearchItem( SID_SEARCH_ITEM ) ); + mpOnlineSearchItem->SetSearchString(pInfo->aWord); + StartOnlineSpelling(); + } + else if (nCommand == SpellCallbackCommand::STARTSPELLDLG) + { + if (SfxViewFrame* pViewFrame = SfxViewFrame::Current()) + pViewFrame->GetDispatcher()->Execute( SID_SPELL_DIALOG, SfxCallMode::ASYNCHRON ); + } + else if (nCommand == SpellCallbackCommand::AUTOCORRECT_OPTIONS) + { + if (SfxViewFrame* pViewFrame = SfxViewFrame::Current()) + pViewFrame->GetDispatcher()->Execute( SID_AUTO_CORRECT_DLG, SfxCallMode::ASYNCHRON ); + } +} + +// Return formatted page number (1, I, i, a, etc.) +OUString SdDrawDocument::CreatePageNumValue(sal_uInt16 nNum) const +{ + OUString aPageNumValue; + bool bUpper = false; + + switch (mePageNumType) + { + case css::style::NumberingType::CHARS_UPPER_LETTER: + aPageNumValue += OUStringChar( sal_Unicode((nNum - 1) % 26 + 'A') ); + break; + case css::style::NumberingType::CHARS_LOWER_LETTER: + aPageNumValue += OUStringChar( sal_Unicode((nNum - 1) % 26 + 'a') ); + break; + case css::style::NumberingType::ROMAN_UPPER: + bUpper = true; + [[fallthrough]]; + case css::style::NumberingType::ROMAN_LOWER: + aPageNumValue += SvxNumberFormat::CreateRomanString(nNum, bUpper); + break; + case css::style::NumberingType::NUMBER_NONE: + aPageNumValue = " "; + break; + default: + aPageNumValue += OUString::number(nNum); + } + + return aPageNumValue; +} + +// Rename layout template +// Keep in mind that rOldLayoutName contains the _complete_ name of the layout +// (including ~LT~). This is unlike rNewName. +void SdDrawDocument::RenameLayoutTemplate(const OUString& rOldLayoutName, const OUString& rNewName) +{ + OUString aSep(SD_LT_SEPARATOR); + OUString aOldName(rOldLayoutName); + sal_Int32 nPos = aOldName.indexOf( aSep ); + + // erase everything after '~LT~' + if (nPos != -1) + aOldName = aOldName.copy(0, nPos + aSep.getLength()); + + std::vector<StyleReplaceData> aReplList; + SfxStyleSheetIterator aIter(mxStyleSheetPool.get(), SfxStyleFamily::Page); + SfxStyleSheetBase* pSheet = aIter.First(); + + while (pSheet) + { + OUString aSheetName = pSheet->GetName(); + + // if the sheetname starts with aOldName + "~LT~" + if (aSheetName.startsWith(aOldName)) + { + aSheetName = aSheetName.replaceAt(0, aOldName.getLength() - aSep.getLength(), rNewName); + + StyleReplaceData aReplData; + aReplData.nFamily = pSheet->GetFamily(); + aReplData.nNewFamily = pSheet->GetFamily(); + aReplData.aName = pSheet->GetName(); + aReplData.aNewName = aSheetName; + aReplList.push_back(aReplData); + + pSheet->SetName(aSheetName); + } + + pSheet = aIter.Next(); + } + + // Now set the layout name of the drawing and the notes page, as well as + // their master pages. + OUString aPageLayoutName = rNewName + aSep + STR_LAYOUT_OUTLINE; + + // Inform all text objects on pages that use the renamed layout and set the + // new name. + sal_uInt16 nPage; + for (nPage = 0; nPage < GetPageCount(); nPage++) + { + SdPage* pPage = static_cast<SdPage*>(GetPage(nPage)); + OUString aTemp(pPage->GetLayoutName()); + + if (aTemp == rOldLayoutName) + { + pPage->SetLayoutName(aPageLayoutName); + + for (size_t nObj = 0; nObj < pPage->GetObjCount(); ++nObj) + { + SdrObject* pObj = pPage->GetObj(nObj); + + if (pObj->GetObjInventor() == SdrInventor::Default) + { + switch( pObj->GetObjIdentifier() ) + { + case SdrObjKind::Text: + case SdrObjKind::OutlineText: + case SdrObjKind::TitleText: + { + OutlinerParaObject* pOPO = static_cast<SdrTextObj*>(pObj)->GetOutlinerParaObject(); + + if (pOPO) + { + for (const auto& rRepl : aReplList) + pOPO->ChangeStyleSheets( rRepl.aName, rRepl.nFamily, rRepl.aNewName, rRepl.nNewFamily ); + } + } + break; + + default: + break; + } + } + } + } + } + + // Now do this again for all master pages. + // The affected master pages get the name of the layout as their page name. + for (nPage = 0; nPage < GetMasterPageCount(); nPage++) + { + SdPage* pPage = static_cast<SdPage*>( GetMasterPage(nPage) ); + OUString aTemp(pPage->GetLayoutName()); + + if (aTemp == rOldLayoutName) + { + pPage->SetLayoutName(aPageLayoutName); + pPage->SetName(rNewName); + + for (size_t nObj = 0; nObj < pPage->GetObjCount(); ++nObj) + { + SdrObject* pObj = pPage->GetObj(nObj); + + if (pObj->GetObjInventor() == SdrInventor::Default) + { + switch(pObj->GetObjIdentifier()) + { + case SdrObjKind::Text: + case SdrObjKind::OutlineText: + case SdrObjKind::TitleText: + { + OutlinerParaObject* pOPO = static_cast<SdrTextObj*>(pObj)->GetOutlinerParaObject(); + + if (pOPO) + { + for (const auto& rRepl : aReplList) + pOPO->ChangeStyleSheets( rRepl.aName, rRepl.nFamily, rRepl.aNewName, rRepl.nNewFamily ); + } + } + break; + + default: + break; + } + } + } + } + } +} + +// Set outliner defaults (pool defaults) +void SdDrawDocument::SetTextDefaults() const +{ + // BulletItem and BulletFont for Title and Outline + SvxBulletItem aBulletItem(EE_PARA_BULLET); + vcl::Font aBulletFont( SdStyleSheetPool::GetBulletFont() ); + aBulletFont.SetFontSize(Size(0,846)); // 24 pt + aBulletItem.SetFont(aBulletFont); + aBulletItem.SetStyle(SvxBulletStyle::BULLET); + aBulletItem.SetStart(1); + aBulletItem.SetScale(45); // In percent + aBulletItem.SetSymbol( 0x25CF ); // In points + m_pItemPool->SetPoolDefaultItem( aBulletItem ); + + // New BulletItem + SvxNumberFormat aNumberFormat(SVX_NUM_CHAR_SPECIAL); + aNumberFormat.SetBulletFont(&aBulletFont); + aNumberFormat.SetBulletChar( 0x25CF ); // StarBats: 0xF000 + 34 + aNumberFormat.SetBulletRelSize(45); + aNumberFormat.SetBulletColor(COL_AUTO); + aNumberFormat.SetStart(1); + aNumberFormat.SetNumAdjust(SvxAdjust::Left); + + SvxNumRule aNumRule( SvxNumRuleFlags::BULLET_REL_SIZE | SvxNumRuleFlags::BULLET_COLOR, SVX_MAX_NUM, false); + + //aNumberFormat.SetAbsLSpace( 0 ); + //aNumberFormat.SetFirstLineOffset( 0 ); + //aNumRule.SetLevel( 0, aNumberFormat ); + + for( sal_uInt16 i = 0; i < aNumRule.GetLevelCount(); i++ ) + { + const auto nLSpace = (i + 1) * 600; + aNumberFormat.SetAbsLSpace(nLSpace); + aNumberFormat.SetFirstLineOffset(-600); + aNumRule.SetLevel( i, aNumberFormat ); + } + + SvxNumBulletItem aNumBulletItem( std::move(aNumRule), EE_PARA_NUMBULLET ); + m_pItemPool->SetPoolDefaultItem( aNumBulletItem ); +} + +css::text::WritingMode SdDrawDocument::GetDefaultWritingMode() const +{ + const SfxPoolItem* pItem = ( m_pItemPool ? m_pItemPool->GetPoolDefaultItem( EE_PARA_WRITINGDIR ) : nullptr ); + css::text::WritingMode eRet = css::text::WritingMode_LR_TB; + + if( pItem ) + { + switch( static_cast<const SvxFrameDirectionItem&>( *pItem ).GetValue() ) + { + case SvxFrameDirection::Horizontal_LR_TB: eRet = css::text::WritingMode_LR_TB; break; + case SvxFrameDirection::Horizontal_RL_TB: eRet = css::text::WritingMode_RL_TB; break; + case SvxFrameDirection::Vertical_RL_TB: eRet = css::text::WritingMode_TB_RL; break; + + default: + OSL_FAIL( "Frame direction not supported yet" ); + break; + } + } + + return eRet; +} + +void SdDrawDocument::SetDefaultWritingMode(css::text::WritingMode eMode ) +{ + if( !m_pItemPool ) + return; + + SvxFrameDirection nVal; + switch( eMode ) + { + case css::text::WritingMode_LR_TB: nVal = SvxFrameDirection::Horizontal_LR_TB; break; + case css::text::WritingMode_RL_TB: nVal = SvxFrameDirection::Horizontal_RL_TB; break; + case css::text::WritingMode_TB_RL: nVal = SvxFrameDirection::Vertical_RL_TB; break; + default: + OSL_FAIL( "Frame direction not supported yet" ); + return; + } + + SvxFrameDirectionItem aModeItem( nVal, EE_PARA_WRITINGDIR ); + m_pItemPool->SetPoolDefaultItem( aModeItem ); + + SvxAdjustItem aAdjust( SvxAdjust::Left, EE_PARA_JUST ); + + if( eMode == css::text::WritingMode_RL_TB ) + aAdjust.SetAdjust( SvxAdjust::Right ); + + m_pItemPool->SetPoolDefaultItem( aAdjust ); +} + +void SdDrawDocument::getDefaultFonts( vcl::Font& rLatinFont, vcl::Font& rCJKFont, vcl::Font& rCTLFont ) +{ + LanguageType eLatin = GetLanguage( EE_CHAR_LANGUAGE ); + + // If the UI language is Korean, the default Latin font has to + // be queried for Korean, too (the Latin language from the document can't be Korean). + // This is the same logic as in SwDocShell::InitNew. + LanguageType eUiLanguage = Application::GetSettings().GetUILanguageTag().getLanguageType(); + if (MsLangId::isKorean(eUiLanguage)) + eLatin = eUiLanguage; + + rLatinFont = OutputDevice::GetDefaultFont( DefaultFontType::LATIN_PRESENTATION, eLatin, GetDefaultFontFlags::OnlyOne ); + rCJKFont = OutputDevice::GetDefaultFont( DefaultFontType::CJK_PRESENTATION, GetLanguage( EE_CHAR_LANGUAGE_CJK ), GetDefaultFontFlags::OnlyOne ); + rCTLFont = OutputDevice::GetDefaultFont( DefaultFontType::CTL_PRESENTATION, GetLanguage( EE_CHAR_LANGUAGE_CTL ), GetDefaultFontFlags::OnlyOne ) ; +} + +/* converts the given western font height to a corresponding ctl font height, depending on the system language */ +sal_uInt32 SdDrawDocument::convertFontHeightToCTL( sal_uInt32 nWesternFontHeight ) +{ + LanguageType eRealCTLLanguage = Application::GetSettings().GetLanguageTag().getLanguageType(); + if( LANGUAGE_THAI == eRealCTLLanguage ) + { + // http://specs.openoffice.org/g11n/font_sizes/42775_42725_Individual_configurable_font_size_for_default_fonts.odt + double fTemp = double(nWesternFontHeight) * 1.333; + nWesternFontHeight = static_cast<sal_uInt32>(fTemp); + // make some nice values for UI that displays PT instead of 1/100th mm + nWesternFontHeight = convertPointToMm100(convertMm100ToPoint(nWesternFontHeight)); + } + return nWesternFontHeight; +} + +SdStyleSheetPool* SdDrawDocument::GetSdStyleSheetPool() const +{ + return dynamic_cast< SdStyleSheetPool* >( GetStyleSheetPool() ); +} + +ModifyGuard::ModifyGuard( SdDrawDocument* pDoc ) +: mpDocShell( nullptr ), mpDoc( pDoc ) +{ + init(); +} + +void ModifyGuard::init() +{ + if( mpDocShell ) + { + mpDoc = mpDocShell->GetDoc(); + } + else if( mpDoc ) + { + mpDocShell = mpDoc->GetDocSh(); + } + + mbIsEnableSetModified = mpDocShell && mpDocShell->IsEnableSetModified(); + mbIsDocumentChanged = mpDoc && mpDoc->IsChanged(); + + if( mbIsEnableSetModified ) + mpDocShell->EnableSetModified( false ); +} + +ModifyGuard::~ModifyGuard() +{ + if( mbIsEnableSetModified ) + mpDocShell->EnableSetModified(); + + if( mpDoc && (mpDoc->IsChanged() != mbIsDocumentChanged) ) + mpDoc->SetChanged(mbIsDocumentChanged); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/core/drawdoc_animations.cxx b/sd/source/core/drawdoc_animations.cxx new file mode 100644 index 000000000..b7f1bd557 --- /dev/null +++ b/sd/source/core/drawdoc_animations.cxx @@ -0,0 +1,54 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include <sal/config.h> + +#include <createpresentation.hxx> +#include <drawdoc.hxx> +#include <cusshow.hxx> +#include <customshowlist.hxx> + +using namespace ::com::sun::star::uno; +using namespace ::com::sun::star::presentation; + +/** replaces a slide from all custom shows with a new one or removes a slide from + all custom shows if pNewPage is 0. +*/ +void SdDrawDocument::ReplacePageInCustomShows(const SdPage* pOldPage, const SdPage* pNewPage) +{ + if (mpCustomShowList) + { + for (size_t i = 0; i < mpCustomShowList->size(); i++) + { + SdCustomShow* pCustomShow = (*mpCustomShowList)[i].get(); + pCustomShow->ReplacePage(pOldPage, pNewPage); + } + } +} + +const Reference<XPresentation2>& SdDrawDocument::getPresentation() const +{ + if (!mxPresentation.is()) + { + const_cast<SdDrawDocument*>(this)->mxPresentation = CreatePresentation(*this); + } + return mxPresentation; +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/core/pglink.cxx b/sd/source/core/pglink.cxx new file mode 100644 index 000000000..358012df9 --- /dev/null +++ b/sd/source/core/pglink.cxx @@ -0,0 +1,128 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include <sfx2/linkmgr.hxx> + +#include <pglink.hxx> +#include <sdpage.hxx> +#include <drawdoc.hxx> + +/************************************************************************* +|* +|* Ctor +|* +\************************************************************************/ + +SdPageLink::SdPageLink(SdPage* pPg, const OUString& rFileName, + const OUString& rBookmarkName) : + ::sfx2::SvBaseLink( ::SfxLinkUpdateMode::ONCALL, SotClipboardFormatId::SIMPLE_FILE), + pPage(pPg) +{ + pPage->SetFileName(rFileName); + pPage->SetBookmarkName(rBookmarkName); +} + +/************************************************************************* +|* +|* Dtor +|* +\************************************************************************/ + +SdPageLink::~SdPageLink() +{ +} + +/************************************************************************* +|* +|* Date have changed +|* +\************************************************************************/ + +::sfx2::SvBaseLink::UpdateResult SdPageLink::DataChanged( + const OUString&, const css::uno::Any& ) +{ + SdDrawDocument* pDoc = static_cast<SdDrawDocument*>( &pPage->getSdrModelFromSdrPage() ); + sfx2::LinkManager* pLinkManager = pDoc!=nullptr ? pDoc->GetLinkManager() : nullptr; + + if (pLinkManager) + { + /********************************************************************** + * Only standard pages are allowed to be linked + * The corresponding note pages are updated automatically + **********************************************************************/ + OUString aFileName; + OUString aBookmarkName; + OUString aFilterName; + sfx2::LinkManager::GetDisplayNames( this,nullptr, &aFileName, &aBookmarkName, + &aFilterName); + pPage->SetFileName(aFileName); + pPage->SetBookmarkName(aBookmarkName); + + SdDrawDocument* pBookmarkDoc = pDoc->OpenBookmarkDoc(aFileName); + + if (pBookmarkDoc) + { + /****************************************************************** + * the linked page is replaced in the model + ******************************************************************/ + if (aBookmarkName.isEmpty()) + { + // no page name specified: we assume it is the first page + aBookmarkName = pBookmarkDoc->GetSdPage(0, PageKind::Standard)->GetName(); + pPage->SetBookmarkName(aBookmarkName); + } + + std::vector<OUString> aBookmarkList { aBookmarkName }; + sal_uInt16 nInsertPos = pPage->GetPageNum(); + bool bNoDialogs = false; + bool bCopy = false; + + if (SdDrawDocument::s_pDocLockedInsertingLinks) + { + // resolving links while loading pDoc + bNoDialogs = true; + bCopy = true; + } + + pDoc->InsertBookmarkAsPage(aBookmarkList, nullptr, true/*bLink*/, true/*bReplace*/, + nInsertPos, bNoDialogs, nullptr, bCopy, true, true); + + if (!SdDrawDocument::s_pDocLockedInsertingLinks) + pDoc->CloseBookmarkDoc(); + } + } + return SUCCESS; +} + +/************************************************************************* +|* +|* Connect or disconnect link +|* +\************************************************************************/ + +void SdPageLink::Closed() +{ + // the connection is closed + pPage->SetFileName(OUString()); + pPage->SetBookmarkName(OUString()); + + SvBaseLink::Closed(); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/core/sdiocmpt.cxx b/sd/source/core/sdiocmpt.cxx new file mode 100644 index 000000000..67de6a64d --- /dev/null +++ b/sd/source/core/sdiocmpt.cxx @@ -0,0 +1,117 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include <tools/debug.hxx> + +#include <sdiocmpt.hxx> + +old_SdrDownCompat::old_SdrDownCompat(SvStream& rNewStream, StreamMode nNewMode) +: rStream(rNewStream), + nSubRecSiz(0), + nSubRecPos(0), + nMode(nNewMode), + bOpen(false) +{ + OpenSubRecord(); +} + +old_SdrDownCompat::~old_SdrDownCompat() +{ + if(bOpen) + CloseSubRecord(); +} + +void old_SdrDownCompat::Write() +{ + rStream.WriteUInt32( nSubRecSiz ); +} + +void old_SdrDownCompat::OpenSubRecord() +{ + if(rStream.GetError()) + return; + + nSubRecPos = rStream.Tell(); + + if(nMode == StreamMode::READ) + { + rStream.ReadUInt32( nSubRecSiz ); + } + else if(nMode == StreamMode::WRITE) + { + Write(); + } + + bOpen = true; +} + +void old_SdrDownCompat::CloseSubRecord() +{ + if(rStream.GetError()) + return; + + sal_uInt32 nCurrentPos(rStream.Tell()); + + if(nMode == StreamMode::READ) + { + sal_uInt32 nReadCnt(nCurrentPos - nSubRecPos); + if(nReadCnt != nSubRecSiz) + { + rStream.Seek(nSubRecPos + nSubRecSiz); + } + } + else if(nMode == StreamMode::WRITE) + { + nSubRecSiz = nCurrentPos - nSubRecPos; + rStream.Seek(nSubRecPos); + Write(); + rStream.Seek(nCurrentPos); + } + + bOpen = false; +} + +/************************************************************************* +|* +|* Constructor, writes and reads version number +|* +\************************************************************************/ + +SdIOCompat::SdIOCompat(SvStream& rNewStream, StreamMode nNewMode, sal_uInt16 nVersion) +: old_SdrDownCompat(rNewStream, nNewMode) +{ + if (nNewMode == StreamMode::WRITE) + { + DBG_ASSERT(nVersion != SDIOCOMPAT_VERSIONDONTKNOW, + "can't write unknown version"); + rNewStream.WriteUInt16( nVersion ); + } + else if (nNewMode == StreamMode::READ) + { + DBG_ASSERT(nVersion == SDIOCOMPAT_VERSIONDONTKNOW, + "referring to the version while reading is silly!"); + rNewStream.ReadUInt16( nVersion ); + } +} + +SdIOCompat::~SdIOCompat() +{ +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/core/sdpage.cxx b/sd/source/core/sdpage.cxx new file mode 100644 index 000000000..38318f294 --- /dev/null +++ b/sd/source/core/sdpage.cxx @@ -0,0 +1,3157 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include <algorithm> +#include <array> + +#include <comphelper/classids.hxx> +#include <comphelper/embeddedobjectcontainer.hxx> + +#include <sfx2/viewsh.hxx> +#include <vcl/svapp.hxx> +#include <editeng/outliner.hxx> +#include <editeng/eeitem.hxx> +#include <svx/svdoutl.hxx> +#include <editeng/editdata.hxx> +#include <editeng/lrspitem.hxx> +#include <editeng/bulletitem.hxx> +#include <svx/svdpagv.hxx> +#include <editeng/fhgtitem.hxx> +#include <editeng/outlobj.hxx> +#include <svx/svdoole2.hxx> +#include <svx/svdograf.hxx> +#include <svx/svdopage.hxx> +#include <editeng/pbinitem.hxx> +#include <svx/svdundo.hxx> +#include <svl/hint.hxx> +#include <editeng/adjustitem.hxx> +#include <editeng/editobj.hxx> +#include <svx/unopage.hxx> +#include <editeng/flditem.hxx> +#include <svx/sdr/contact/displayinfo.hxx> +#include <svx/svditer.hxx> +#include <svx/svdlayer.hxx> +#include <svx/sdtmfitm.hxx> +#include <svx/sdtagitm.hxx> +#include <svx/sdtcfitm.hxx> +#include <svx/xfillit0.hxx> +#include <tools/diagnose_ex.h> +#include <com/sun/star/animations/XAnimationNode.hpp> +#include <com/sun/star/animations/XTimeContainer.hpp> +#include <com/sun/star/container/XEnumerationAccess.hpp> +#include <com/sun/star/embed/XEmbeddedObject.hpp> +#include <com/sun/star/xml/dom/XNode.hpp> +#include <com/sun/star/xml/dom/XNodeList.hpp> +#include <com/sun/star/xml/dom/XNamedNodeMap.hpp> +#include <rtl/ustring.hxx> +#include <sal/log.hxx> +#include <o3tl/enumarray.hxx> +#include <o3tl/safeint.hxx> +#include <o3tl/sorted_vector.hxx> +#include <xmloff/autolayout.hxx> + +#include <Outliner.hxx> +#include <app.hrc> +#include <createunopageimpl.hxx> +#include <drawdoc.hxx> +#include <sdmod.hxx> +#include <sdpage.hxx> +#include <sdresid.hxx> +#include <stlsheet.hxx> +#include <strings.hrc> +#include <strings.hxx> +#include <bitmaps.hlst> +#include <glob.hxx> +#include <anminfo.hxx> +#include <undo/undomanager.hxx> +#include <undo/undoobjects.hxx> +#include <svx/sdr/contact/viewobjectcontact.hxx> +#include <svx/sdr/contact/viewcontact.hxx> +#include <svx/sdr/contact/objectcontact.hxx> +#include <svx/unoapi.hxx> +#include <unokywds.hxx> + +using namespace ::sd; +using namespace ::com::sun::star; +using namespace ::com::sun::star::uno; +using namespace com::sun::star::xml::dom; +using ::com::sun::star::uno::Reference; + + +sal_uInt16 SdPage::mnLastPageId = 1; + +/************************************************************************* +|* +|* Ctor +|* +\************************************************************************/ + +SdPage::SdPage(SdDrawDocument& rNewDoc, bool bMasterPage) +: FmFormPage(rNewDoc, bMasterPage) +, SdrObjUserCall() +, mePageKind(PageKind::Standard) +, meAutoLayout(AUTOLAYOUT_NONE) +, mbSelected(false) +, mePresChange(PresChange::Manual) +, mfTime(1.0) +, mbSoundOn(false) +, mbExcluded(false) +, mbLoopSound(false) +, mbStopSound(false) +, mbScaleObjects(true) +, meCharSet(osl_getThreadTextEncoding()) +, mnPaperBin(PAPERBIN_PRINTER_SETTINGS) +, mpPageLink(nullptr) +, mnTransitionType(0) +, mnTransitionSubtype(0) +, mbTransitionDirection(true) +, mnTransitionFadeColor(0) +, mfTransitionDuration(2.0) +, mbIsPrecious(true) +, mnPageId(mnLastPageId++) +{ + // The name of the layout of the page is used by SVDRAW to determine the + // presentation template of the outline objects. Therefore, it already + // contains the designator for the outline (STR_LAYOUT_OUTLINE). + maLayoutName = SdResId(STR_LAYOUT_DEFAULT_NAME)+ SD_LT_SEPARATOR + STR_LAYOUT_OUTLINE; + + // Stuff that former SetModel did also: + ConnectLink(); +} + +namespace +{ + void clearChildNodes(css::uno::Reference<css::animations::XAnimationNode> const & rAnimationNode) + { + css::uno::Reference<css::container::XEnumerationAccess > xEnumerationAccess(rAnimationNode, UNO_QUERY); + if (!xEnumerationAccess.is()) + return; + css::uno::Reference<css::container::XEnumeration> xEnumeration = xEnumerationAccess->createEnumeration(); + if (!xEnumeration.is()) + return; + while (xEnumeration->hasMoreElements()) + { + css::uno::Reference<css::animations::XAnimationNode> xChildNode(xEnumeration->nextElement(), UNO_QUERY); + if (!xChildNode.is()) + continue; + clearChildNodes(xChildNode); + css::uno::Reference<css::animations::XTimeContainer> xAnimationNode(rAnimationNode, UNO_QUERY); + if (!xAnimationNode.is()) + { + SAL_WARN("sd.core", "can't remove node child, possible leak"); + continue; + } + xAnimationNode->removeChild(xChildNode); + } + } +} + +/************************************************************************* +|* +|* Dtor +|* +\************************************************************************/ + +SdPage::~SdPage() +{ + DisconnectLink(); + + EndListenOutlineText(); + + clearChildNodes(mxAnimationNode); + + // disconnect the UserCall link + SdrObjListIter aIter( this, SdrIterMode::DeepWithGroups ); + while( aIter.IsMore() ) + { + SdrObject* pChild = aIter.Next(); + if( pChild->GetUserCall() == this ) + pChild->SetUserCall(nullptr); + } + + // clear SdrObjects with broadcasting + ClearSdrObjList(); +} + +namespace { + +struct OrdNumSorter +{ + bool operator()( SdrObject const * p1, SdrObject const * p2 ) + { + return p1->GetOrdNum() < p2->GetOrdNum(); + } +}; + +} + +/** returns the nIndex'th object from the given PresObjKind, index starts with 1 */ +SdrObject* SdPage::GetPresObj(PresObjKind eObjKind, int nIndex, bool bFuzzySearch /* = false */ ) +{ + // first sort all matching shapes with z-order + std::vector< SdrObject* > aMatches; + + SdrObject* pObj = nullptr; + maPresentationShapeList.seekShape(0); + + while( (pObj = maPresentationShapeList.getNextShape()) ) + { + SdAnimationInfo* pInfo = SdDrawDocument::GetShapeUserData(*pObj); + if( pInfo ) + { + bool bFound = false; + if( pInfo->mePresObjKind == eObjKind ) + { + bFound = true; + } + else if( bFuzzySearch && (eObjKind == PresObjKind::Outline) ) + { + switch( pInfo->mePresObjKind ) + { + case PresObjKind::Graphic: + case PresObjKind::Object: + case PresObjKind::Chart: + case PresObjKind::OrgChart: + case PresObjKind::Table: + case PresObjKind::Calc: + case PresObjKind::Media: + bFound = true; + break; + default: + break; + } + } + if( bFound ) + { + aMatches.push_back( pObj ); + } + } + } + + if( nIndex > 0 ) + nIndex--; + + if( (nIndex >= 0) && ( aMatches.size() > o3tl::make_unsigned(nIndex)) ) + { + if( aMatches.size() > 1 ) + std::nth_element( aMatches.begin(), aMatches.begin() + nIndex, aMatches.end(), + OrdNumSorter() ); + return aMatches[nIndex]; + } + + return nullptr; +} + +/** create background properties */ +void SdPage::EnsureMasterPageDefaultBackground() +{ + if(!mbMaster) + return; + + // no hard attributes on MasterPage attributes + getSdrPageProperties().ClearItem(); + SfxStyleSheet* pSheetForPresObj = GetStyleSheetForMasterPageBackground(); + + if(pSheetForPresObj) + { + // set StyleSheet for background fill attributes + getSdrPageProperties().SetStyleSheet(pSheetForPresObj); + } + else + { + // no style found, assert and set at least drawing::FillStyle_NONE + OSL_FAIL("No Style for MasterPageBackground fill found (!)"); + getSdrPageProperties().PutItem(XFillStyleItem(drawing::FillStyle_NONE)); + } +} + +/** creates a presentation object with the given PresObjKind on this page. A user call will be set +*/ +SdrObject* SdPage::CreatePresObj(PresObjKind eObjKind, bool bVertical, const ::tools::Rectangle& rRect ) +{ + SfxUndoManager* pUndoManager(static_cast< SdDrawDocument& >(getSdrModelFromSdrPage()).GetUndoManager()); + const bool bUndo = pUndoManager && pUndoManager->IsInListAction() && IsInserted(); + + SdrObject* pSdrObj = nullptr; + + bool bForceText = false; // forces the shape text to be set even if it's empty + bool bEmptyPresObj = true; + + switch( eObjKind ) + { + case PresObjKind::Title: + { + pSdrObj = new SdrRectObj(getSdrModelFromSdrPage(), SdrObjKind::TitleText); + + if (mbMaster) + { + pSdrObj->SetNotVisibleAsMaster(true); + } + } + break; + + case PresObjKind::Outline: + { + pSdrObj = new SdrRectObj(getSdrModelFromSdrPage(), SdrObjKind::OutlineText); + + if (mbMaster) + { + pSdrObj->SetNotVisibleAsMaster(true); + } + } + break; + + case PresObjKind::Notes: + { + pSdrObj = new SdrRectObj(getSdrModelFromSdrPage(), SdrObjKind::Text); + + if (mbMaster) + { + pSdrObj->SetNotVisibleAsMaster(true); + } + } + break; + + case PresObjKind::Text: + { + pSdrObj = new SdrRectObj(getSdrModelFromSdrPage(), SdrObjKind::Text); + } + break; + + case PresObjKind::Graphic: + { + BitmapEx aBmpEx(BMP_PRESOBJ_GRAPHIC); + Graphic aGraphic( aBmpEx ); + OutputDevice &aOutDev = *Application::GetDefaultDevice(); + aOutDev.Push(); + + aOutDev.SetMapMode( aGraphic.GetPrefMapMode() ); + Size aSizePix = aOutDev.LogicToPixel( aGraphic.GetPrefSize() ); + aOutDev.SetMapMode(MapMode(MapUnit::Map100thMM)); + + Size aSize = aOutDev.PixelToLogic(aSizePix); + Point aPnt (0, 0); + ::tools::Rectangle aRect (aPnt, aSize); + pSdrObj = new SdrGrafObj(getSdrModelFromSdrPage(), aGraphic, aRect); + aOutDev.Pop(); + } + break; + + case PresObjKind::Media: + case PresObjKind::Object: + { + pSdrObj = new SdrOle2Obj(getSdrModelFromSdrPage()); + BitmapEx aBmpEx(BMP_PRESOBJ_OBJECT); + Graphic aGraphic( aBmpEx ); + static_cast<SdrOle2Obj*>(pSdrObj)->SetGraphic(aGraphic); + } + break; + + case PresObjKind::Chart: + { + pSdrObj = new SdrOle2Obj(getSdrModelFromSdrPage()); + static_cast<SdrOle2Obj*>(pSdrObj)->SetProgName( "StarChart" ); + BitmapEx aBmpEx(BMP_PRESOBJ_CHART); + Graphic aGraphic( aBmpEx ); + static_cast<SdrOle2Obj*>(pSdrObj)->SetGraphic(aGraphic); + } + break; + + case PresObjKind::OrgChart: + { + pSdrObj = new SdrOle2Obj(getSdrModelFromSdrPage()); + static_cast<SdrOle2Obj*>(pSdrObj)->SetProgName( "StarOrg" ); + BitmapEx aBmpEx(BMP_PRESOBJ_ORGCHART); + Graphic aGraphic( aBmpEx ); + static_cast<SdrOle2Obj*>(pSdrObj)->SetGraphic(aGraphic); + } + break; + + case PresObjKind::Table: + case PresObjKind::Calc: + { + pSdrObj = new SdrOle2Obj(getSdrModelFromSdrPage()); + static_cast<SdrOle2Obj*>(pSdrObj)->SetProgName( "StarCalc" ); + BitmapEx aBmpEx(BMP_PRESOBJ_TABLE); + Graphic aGraphic( aBmpEx ); + static_cast<SdrOle2Obj*>(pSdrObj)->SetGraphic(aGraphic); + } + break; + + case PresObjKind::Handout: + { + // Save the first standard page at SdrPageObj + // #i105146# We want no content to be displayed for PageKind::Handout, + // so just never set a page as content + pSdrObj = new SdrPageObj(getSdrModelFromSdrPage(), nullptr); + } + break; + + case PresObjKind::Page: + { + // Save note pages at SdrPageObj + sal_uInt16 nDestPageNum(GetPageNum()); + + if(nDestPageNum) + { + // decrement only when != 0, else we get a 0xffff + nDestPageNum -= 1; + } + + if (nDestPageNum < getSdrModelFromSdrPage().GetPageCount()) + { + pSdrObj = new SdrPageObj(getSdrModelFromSdrPage(), getSdrModelFromSdrPage().GetPage(nDestPageNum)); + } + else + { + pSdrObj = new SdrPageObj(getSdrModelFromSdrPage()); + } + + pSdrObj->SetResizeProtect(true); + } + break; + + case PresObjKind::Header: + case PresObjKind::Footer: + case PresObjKind::DateTime: + case PresObjKind::SlideNumber: + { + pSdrObj = new SdrRectObj(getSdrModelFromSdrPage(), SdrObjKind::Text); + bEmptyPresObj = false; + bForceText = true; + } + break; + default: + break; + } + + if (pSdrObj) + { + pSdrObj->SetEmptyPresObj(bEmptyPresObj); + pSdrObj->SetLogicRect(rRect); + + InsertObject(pSdrObj); + + if ( auto pTextObj = dynamic_cast<SdrTextObj *>( pSdrObj ) ) + { + // Tell the object EARLY that it is vertical to have the + // defaults for AutoGrowWidth/Height reversed + if(bVertical) + pTextObj->SetVerticalWriting(true); + + SfxItemSet aTempAttr(static_cast< SdDrawDocument& >(getSdrModelFromSdrPage()).GetPool()); + if( bVertical ) + aTempAttr.Put( makeSdrTextMinFrameWidthItem( rRect.GetSize().Width() ) ); + else + aTempAttr.Put( makeSdrTextMinFrameHeightItem( rRect.GetSize().Height() ) ); + + if (mbMaster) + { + // The size of presentation objects on the master page have to + // be freely selectable by the user. + + // potential problem: This action was still NOT + // adapted for vertical text. This sure needs to be done. + if(bVertical) + aTempAttr.Put(makeSdrTextAutoGrowWidthItem(false)); + else + aTempAttr.Put(makeSdrTextAutoGrowHeightItem(false)); + } + + // check if we need another vertical adjustment than the default + SdrTextVertAdjust eV = SDRTEXTVERTADJUST_TOP; + + if( (eObjKind == PresObjKind::Footer) && (mePageKind != PageKind::Standard) ) + { + eV = SDRTEXTVERTADJUST_BOTTOM; + } + else if( (eObjKind == PresObjKind::SlideNumber) && (mePageKind != PageKind::Standard) ) + { + eV = SDRTEXTVERTADJUST_BOTTOM; + } + + if( eV != SDRTEXTVERTADJUST_TOP ) + aTempAttr.Put(SdrTextVertAdjustItem(eV)); + + pSdrObj->SetMergedItemSet(aTempAttr); + + pSdrObj->SetLogicRect(rRect); + } + + OUString aString = GetPresObjText(eObjKind); + if(!aString.isEmpty() || bForceText) + if (auto pTextObj = dynamic_cast<SdrTextObj *>( pSdrObj ) ) + { + SdrOutliner* pOutliner = static_cast< SdDrawDocument& >(getSdrModelFromSdrPage()).GetInternalOutliner(); + + OutlinerMode nOutlMode = pOutliner->GetOutlinerMode(); + pOutliner->Init( OutlinerMode::TextObject ); + pOutliner->SetStyleSheet( 0, nullptr ); + pOutliner->SetVertical( bVertical ); + + SetObjText( pTextObj, pOutliner, eObjKind, aString ); + + pOutliner->Init( nOutlMode ); + pOutliner->SetStyleSheet( 0, nullptr ); + } + + if( (eObjKind == PresObjKind::Header) || (eObjKind == PresObjKind::Footer) || (eObjKind == PresObjKind::SlideNumber) || (eObjKind == PresObjKind::DateTime) ) + { + SfxItemSet aTempAttr(static_cast< SdDrawDocument& >(getSdrModelFromSdrPage()).GetPool()); + aTempAttr.Put( SvxFontHeightItem( 493, 100, EE_CHAR_FONTHEIGHT ) ); + aTempAttr.Put( SvxFontHeightItem( 493, 100, EE_CHAR_FONTHEIGHT_CTL ) ); + aTempAttr.Put( SvxFontHeightItem( 493, 100, EE_CHAR_FONTHEIGHT_CJK ) ); + + SvxAdjust eH = SvxAdjust::Left; + + if( (eObjKind == PresObjKind::DateTime) && (mePageKind != PageKind::Standard ) ) + { + eH = SvxAdjust::Right; + } + else if( (eObjKind == PresObjKind::Footer) && (mePageKind == PageKind::Standard ) ) + { + eH = SvxAdjust::Center; + } + else if( eObjKind == PresObjKind::SlideNumber ) + { + eH = SvxAdjust::Right; + } + + if( eH != SvxAdjust::Left ) + aTempAttr.Put(SvxAdjustItem(eH, EE_PARA_JUST )); + + pSdrObj->SetMergedItemSet(aTempAttr); + } + + if (mbMaster) + { + SdrLayerAdmin& rLayerAdmin(getSdrModelFromSdrPage().GetLayerAdmin()); + + // background objects of the master page + pSdrObj->SetLayer( rLayerAdmin.GetLayerID(sUNO_LayerName_background_objects) ); + } + + // Subscribe object at the style sheet + // Set style only when one was found (as in 5.2) + if( mePageKind != PageKind::Handout ) + { + SfxStyleSheet* pSheetForPresObj = GetStyleSheetForPresObj(eObjKind); + if(pSheetForPresObj) + pSdrObj->SetStyleSheet(pSheetForPresObj, false); + } + + if (eObjKind == PresObjKind::Outline) + { + for (sal_uInt16 nLevel = 1; nLevel < 10; nLevel++) + { + OUString aName( maLayoutName + " " + OUString::number( nLevel ) ); + SfxStyleSheet* pSheet = static_cast<SfxStyleSheet*>(getSdrModelFromSdrPage().GetStyleSheetPool()->Find(aName, SfxStyleFamily::Page)); + DBG_ASSERT(pSheet, "StyleSheet for outline object not found"); + if (pSheet) + pSdrObj->StartListening(*pSheet, DuplicateHandling::Allow); + } + } + + if ( eObjKind == PresObjKind::Object || + eObjKind == PresObjKind::Chart || + eObjKind == PresObjKind::OrgChart || + eObjKind == PresObjKind::Calc || + eObjKind == PresObjKind::Graphic ) + { + SfxItemSet aSet( static_cast< SdDrawDocument& >(getSdrModelFromSdrPage()).GetPool() ); + aSet.Put( makeSdrTextContourFrameItem( true ) ); + aSet.Put( SvxAdjustItem( SvxAdjust::Center, EE_PARA_JUST ) ); + + pSdrObj->SetMergedItemSet(aSet); + } + + if( bUndo ) + { + pUndoManager->AddUndoAction(getSdrModelFromSdrPage().GetSdrUndoFactory().CreateUndoNewObject(*pSdrObj)); + + pUndoManager->AddUndoAction( std::make_unique<UndoObjectPresentationKind>( *pSdrObj ) ); + pUndoManager->AddUndoAction( std::make_unique<UndoObjectUserCall>(*pSdrObj) ); + } + + InsertPresObj(pSdrObj, eObjKind); + pSdrObj->SetUserCall(this); + + pSdrObj->RecalcBoundRect(); + } + + return pSdrObj; +} + +/************************************************************************* +|* +|* Creates presentation objects on the master page. +|* All presentation objects get a UserCall to the page. +|* +\************************************************************************/ + +SfxStyleSheet* SdPage::GetStyleSheetForMasterPageBackground() const +{ + OUString aName(GetLayoutName()); + OUString aSep( SD_LT_SEPARATOR ); + sal_Int32 nPos = aName.indexOf(aSep); + + if (nPos != -1) + { + nPos = nPos + aSep.getLength(); + aName = aName.copy(0, nPos); + } + + aName += STR_LAYOUT_BACKGROUND; + + SfxStyleSheetBasePool* pStShPool = getSdrModelFromSdrPage().GetStyleSheetPool(); + SfxStyleSheetBase* pResult = pStShPool->Find(aName, SfxStyleFamily::Page); + return static_cast<SfxStyleSheet*>(pResult); +} + +SfxStyleSheet* SdPage::GetStyleSheetForPresObj(PresObjKind eObjKind) const +{ + OUString aName(GetLayoutName()); + OUString aSep( SD_LT_SEPARATOR ); + sal_Int32 nPos = aName.indexOf(aSep); + if (nPos != -1) + { + nPos = nPos + aSep.getLength(); + aName = aName.copy(0, nPos); + } + + switch (eObjKind) + { + case PresObjKind::Outline: + { + aName = GetLayoutName() + " " + OUString::number( 1 ); + } + break; + + case PresObjKind::Title: + aName += STR_LAYOUT_TITLE; + break; + + case PresObjKind::Notes: + aName += STR_LAYOUT_NOTES; + break; + + case PresObjKind::Text: + aName += STR_LAYOUT_SUBTITLE; + break; + + case PresObjKind::Header: + case PresObjKind::Footer: + case PresObjKind::DateTime: + case PresObjKind::SlideNumber: + aName += STR_LAYOUT_BACKGROUNDOBJECTS; + break; + + default: + break; + } + + SfxStyleSheetBasePool* pStShPool = getSdrModelFromSdrPage().GetStyleSheetPool(); + SfxStyleSheetBase* pResult = pStShPool->Find(aName, SfxStyleFamily::Page); + return static_cast<SfxStyleSheet*>(pResult); +} + +/** returns the presentation style with the given helpid from this masterpage or this + slides masterpage */ +SdStyleSheet* SdPage::getPresentationStyle( sal_uInt32 nHelpId ) const +{ + OUString aStyleName( GetLayoutName() ); + const OUString aSep( SD_LT_SEPARATOR ); + sal_Int32 nIndex = aStyleName.indexOf(aSep); + if( nIndex != -1 ) + aStyleName = aStyleName.copy(0, nIndex + aSep.getLength()); + + OUString pNameId; + bool bOutline = false; + switch( nHelpId ) + { + case HID_PSEUDOSHEET_TITLE: pNameId = STR_LAYOUT_TITLE; break; + case HID_PSEUDOSHEET_SUBTITLE: pNameId = STR_LAYOUT_SUBTITLE; break; + case HID_PSEUDOSHEET_OUTLINE1: + case HID_PSEUDOSHEET_OUTLINE2: + case HID_PSEUDOSHEET_OUTLINE3: + case HID_PSEUDOSHEET_OUTLINE4: + case HID_PSEUDOSHEET_OUTLINE5: + case HID_PSEUDOSHEET_OUTLINE6: + case HID_PSEUDOSHEET_OUTLINE7: + case HID_PSEUDOSHEET_OUTLINE8: + case HID_PSEUDOSHEET_OUTLINE9: pNameId = STR_LAYOUT_OUTLINE; bOutline = true; break; + case HID_PSEUDOSHEET_BACKGROUNDOBJECTS: pNameId = STR_LAYOUT_BACKGROUNDOBJECTS; break; + case HID_PSEUDOSHEET_BACKGROUND: pNameId = STR_LAYOUT_BACKGROUND; break; + case HID_PSEUDOSHEET_NOTES: pNameId = STR_LAYOUT_NOTES; break; + + default: + OSL_FAIL( "SdPage::getPresentationStyle(), illegal argument!" ); + return nullptr; + } + aStyleName += pNameId; + if (bOutline) + { + aStyleName += " " + + OUString::number( sal_Int32( nHelpId - HID_PSEUDOSHEET_OUTLINE )); + } + + SfxStyleSheetBasePool* pStShPool = getSdrModelFromSdrPage().GetStyleSheetPool(); + SfxStyleSheetBase* pResult = pStShPool->Find(aStyleName, SfxStyleFamily::Page); + return dynamic_cast<SdStyleSheet*>(pResult); +} + +/************************************************************************* +|* +|* The presentation object rObj has changed and is no longer referenced by the +|* presentation object of the master page. +|* The UserCall is deleted. +|* +\************************************************************************/ + +void SdPage::Changed(const SdrObject& rObj, SdrUserCallType eType, const ::tools::Rectangle& ) +{ + if (maLockAutoLayoutArrangement.isLocked()) + return; + + switch (eType) + { + case SdrUserCallType::MoveOnly: + case SdrUserCallType::Resize: + { + if ( getSdrModelFromSdrPage().isLocked()) + break; + + if (!mbMaster) + { + if (rObj.GetUserCall()) + { + SdrObject& _rObj = const_cast<SdrObject&>(rObj); + SfxUndoManager* pUndoManager + = static_cast<SdDrawDocument&>(getSdrModelFromSdrPage()) + .GetUndoManager(); + const bool bUndo + = pUndoManager && pUndoManager->IsInListAction() && IsInserted(); + + if (bUndo) + pUndoManager->AddUndoAction( + std::make_unique<UndoObjectUserCall>(_rObj)); + + // Object was resized by user and does not listen to its slide anymore + _rObj.SetUserCall(nullptr); + } + } + else + { + // Object of the master page changed, therefore adjust + // object on all pages + sal_uInt16 nPageCount = static_cast<SdDrawDocument&>(getSdrModelFromSdrPage()) + .GetSdPageCount(mePageKind); + + for (sal_uInt16 i = 0; i < nPageCount; i++) + { + SdPage* pLoopPage = static_cast<SdDrawDocument&>(getSdrModelFromSdrPage()) + .GetSdPage(i, mePageKind); + + if (pLoopPage && this == &(pLoopPage->TRG_GetMasterPage())) + { + // Page listens to this master page, therefore + // adjust AutoLayout + pLoopPage->SetAutoLayout(pLoopPage->GetAutoLayout()); + } + } + } + } + break; + + case SdrUserCallType::Delete: + case SdrUserCallType::Removed: + default: + break; + } +} + +/************************************************************************* +|* +|* Creates on a master page: background, title- and layout area +|* +\************************************************************************/ + +void SdPage::CreateTitleAndLayout(bool bInit, bool bCreate ) +{ + SfxUndoManager* pUndoManager(static_cast< SdDrawDocument& >(getSdrModelFromSdrPage()).GetUndoManager()); + const bool bUndo = pUndoManager && pUndoManager->IsInListAction() && IsInserted(); + + SdPage* pMasterPage = this; + + if (!mbMaster) + { + pMasterPage = static_cast<SdPage*>(&(TRG_GetMasterPage())); + } + + if (!pMasterPage) + { + return; + } + + /************************************************************************** + * create background, title- and layout area + **************************************************************************/ + if( mePageKind == PageKind::Standard ) + { + pMasterPage->EnsureMasterPageDefaultBackground(); + } + + if (static_cast< SdDrawDocument& >(getSdrModelFromSdrPage()).GetDocumentType() != DocumentType::Impress) + return; + + if( mePageKind == PageKind::Handout && bInit ) + { + // handout template + + // delete all available handout presentation objects + SdrObject *pObj=nullptr; + while( (pObj = pMasterPage->GetPresObj(PresObjKind::Handout)) != nullptr ) + { + pMasterPage->RemoveObject(pObj->GetOrdNum()); + + if( bUndo ) + { + pUndoManager->AddUndoAction(getSdrModelFromSdrPage().GetSdrUndoFactory().CreateUndoDeleteObject(*pObj)); + } + else + { + SdrObject::Free( pObj ); + } + } + + std::vector< ::tools::Rectangle > aAreas; + CalculateHandoutAreas( static_cast< SdDrawDocument& >(getSdrModelFromSdrPage()), pMasterPage->GetAutoLayout(), false, aAreas ); + + const bool bSkip = pMasterPage->GetAutoLayout() == AUTOLAYOUT_HANDOUT3; + std::vector< ::tools::Rectangle >::iterator iter( aAreas.begin() ); + + while( iter != aAreas.end() ) + { + SdrPageObj* pPageObj = static_cast<SdrPageObj*>(pMasterPage->CreatePresObj(PresObjKind::Handout, false, (*iter++)) ); + // #i105146# We want no content to be displayed for PageKind::Handout, + // so just never set a page as content + pPageObj->SetReferencedPage(nullptr); + + if( bSkip && iter != aAreas.end() ) + ++iter; + } + } + + if( mePageKind != PageKind::Handout ) + { + SdrObject* pMasterTitle = pMasterPage->GetPresObj( PresObjKind::Title ); + if( pMasterTitle == nullptr ) + pMasterPage->CreateDefaultPresObj(PresObjKind::Title); + + SdrObject* pMasterOutline = pMasterPage->GetPresObj( mePageKind==PageKind::Notes ? PresObjKind::Notes : PresObjKind::Outline ); + if( pMasterOutline == nullptr ) + pMasterPage->CreateDefaultPresObj( mePageKind == PageKind::Standard ? PresObjKind::Outline : PresObjKind::Notes ); + } + + // create header&footer objects + + if( !bCreate ) + return; + + if( mePageKind != PageKind::Standard ) + { + SdrObject* pHeader = pMasterPage->GetPresObj( PresObjKind::Header ); + if( pHeader == nullptr ) + pMasterPage->CreateDefaultPresObj( PresObjKind::Header ); + } + + SdrObject* pDate = pMasterPage->GetPresObj( PresObjKind::DateTime ); + if( pDate == nullptr ) + pMasterPage->CreateDefaultPresObj( PresObjKind::DateTime ); + + SdrObject* pFooter = pMasterPage->GetPresObj( PresObjKind::Footer ); + if( pFooter == nullptr ) + pMasterPage->CreateDefaultPresObj( PresObjKind::Footer ); + + SdrObject* pNumber = pMasterPage->GetPresObj( PresObjKind::SlideNumber ); + if( pNumber == nullptr ) + pMasterPage->CreateDefaultPresObj( PresObjKind::SlideNumber ); +} + +namespace { + +const o3tl::enumarray<PageKind, char const *> PageKindVector = { + "PageKind::Standard", "PageKind::Notes", "PageKind::Handout" +}; + +const o3tl::enumarray<PresObjKind, const char*> PresObjKindVector = { + "PRESOBJ_NONE", "PRESOBJ_TITLE", "PRESOBJ_OUTLINE", + "PRESOBJ_TEXT" ,"PRESOBJ_GRAPHIC" , "PRESOBJ_OBJECT", + "PRESOBJ_CHART", "PRESOBJ_ORGCHART", "PRESOBJ_TABLE", + "PRESOBJ_PAGE", "PRESOBJ_HANDOUT", + "PRESOBJ_NOTES","PRESOBJ_HEADER", "PRESOBJ_FOOTER", + "PRESOBJ_DATETIME", "PRESOBJ_SLIDENUMBER", "PRESOBJ_CALC", + "PRESOBJ_MEDIA" +}; + +void getPresObjProp( const SdPage& rPage, const char* sObjKind, const char* sPageKind, double presObjPropValue[] ) +{ + bool bNoObjectFound = true; //used to break from outer loop + + const std::vector< Reference<XNode> >& objectInfo = static_cast< const SdDrawDocument& >(rPage.getSdrModelFromSdrPage()).GetObjectVector(); + for( const Reference<XNode>& objectNode : objectInfo ) + { + if(bNoObjectFound) + { + Reference<XNamedNodeMap> objectattrlist = objectNode->getAttributes(); + Reference<XNode> objectattr = objectattrlist->getNamedItem("type"); + OUString sObjType = objectattr->getNodeValue(); + + if (sObjType.equalsAscii(sObjKind)) + { + Reference<XNodeList> objectChildren = objectNode->getChildNodes(); + const int objSize = objectChildren->getLength(); + + for( int j=0; j< objSize; j++) + { + Reference<XNode> obj = objectChildren->item(j); + OUString nodename = obj->getNodeName(); + + //check whether child is blank 'text-node' or 'object-prop' node + if(nodename == "object-prop") + { + Reference<XNamedNodeMap> ObjAttributes = obj->getAttributes(); + Reference<XNode> ObjPageKind = ObjAttributes->getNamedItem("pagekind"); + OUString sObjPageKind = ObjPageKind->getNodeValue(); + + if (sObjPageKind.equalsAscii(sPageKind)) + { + Reference<XNode> ObjSizeHeight = ObjAttributes->getNamedItem("relative-height"); + OUString sValue = ObjSizeHeight->getNodeValue(); + presObjPropValue[0] = sValue.toDouble(); + + Reference<XNode> ObjSizeWidth = ObjAttributes->getNamedItem("relative-width"); + sValue = ObjSizeWidth->getNodeValue(); + presObjPropValue[1] = sValue.toDouble(); + + Reference<XNode> ObjPosX = ObjAttributes->getNamedItem("relative-posX"); + sValue = ObjPosX->getNodeValue(); + presObjPropValue[2] = sValue.toDouble(); + + Reference<XNode> ObjPosY = ObjAttributes->getNamedItem("relative-posY"); + sValue = ObjPosY->getNodeValue(); + presObjPropValue[3] = sValue.toDouble(); + + bNoObjectFound = false; + break; + } + } + } + } + } + else + break; + } +} + +} + +SdrObject* SdPage::CreateDefaultPresObj(PresObjKind eObjKind) +{ + if( eObjKind == PresObjKind::Title ) + { + ::tools::Rectangle aTitleRect( GetTitleRect() ); + return CreatePresObj(PresObjKind::Title, false, aTitleRect); + } + else if( eObjKind == PresObjKind::Outline ) + { + ::tools::Rectangle aLayoutRect( GetLayoutRect() ); + return CreatePresObj( PresObjKind::Outline, false, aLayoutRect); + } + else if( eObjKind == PresObjKind::Notes ) + { + ::tools::Rectangle aLayoutRect( GetLayoutRect() ); + return CreatePresObj( PresObjKind::Notes, false, aLayoutRect); + } + else if( (eObjKind == PresObjKind::Footer) || (eObjKind == PresObjKind::DateTime) || (eObjKind == PresObjKind::SlideNumber) || (eObjKind == PresObjKind::Header ) ) + { + double propvalue[] = {0,0,0,0}; + const char* sObjKind = PresObjKindVector[eObjKind]; + const char* sPageKind = PageKindVector[mePageKind]; + // create footer objects for standard master page + if( mePageKind == PageKind::Standard ) + { + const ::tools::Long nLftBorder = GetLeftBorder(); + const ::tools::Long nUppBorder = GetUpperBorder(); + + Point aPos ( nLftBorder, nUppBorder ); + Size aSize ( GetSize() ); + + aSize.AdjustWidth( -(nLftBorder + GetRightBorder()) ); + aSize.AdjustHeight( -(nUppBorder + GetLowerBorder()) ); + + getPresObjProp( *this, sObjKind, sPageKind, propvalue); + aPos.AdjustX(::tools::Long( aSize.Width() * propvalue[2] ) ); + aPos.AdjustY(::tools::Long( aSize.Height() * propvalue[3] ) ); + aSize.setWidth( ::tools::Long( aSize.Width() * propvalue[1] ) ); + aSize.setHeight( ::tools::Long( aSize.Height() * propvalue[0] ) ); + + if(eObjKind == PresObjKind::Header ) + { + OSL_FAIL( "SdPage::CreateDefaultPresObj() - can't create a header placeholder for a master slide" ); + return nullptr; + } + else + { + ::tools::Rectangle aRect( aPos, aSize ); + return CreatePresObj( eObjKind, false, aRect ); + } + } + else + { + // create header&footer objects for handout and notes master + Size aPageSize ( GetSize() ); + aPageSize.AdjustWidth( -(GetLeftBorder() + GetRightBorder()) ); + aPageSize.AdjustHeight( -(GetUpperBorder() + GetLowerBorder()) ); + + Point aPosition ( GetLeftBorder(), GetUpperBorder() ); + + getPresObjProp( *this, sObjKind, sPageKind, propvalue); + int NOTES_HEADER_FOOTER_WIDTH = ::tools::Long(aPageSize.Width() * propvalue[1]); + int NOTES_HEADER_FOOTER_HEIGHT = ::tools::Long(aPageSize.Height() * propvalue[0]); + Size aSize( NOTES_HEADER_FOOTER_WIDTH, NOTES_HEADER_FOOTER_HEIGHT ); + Point aPos ( 0 ,0 ); + if( propvalue[2] == 0 ) + aPos.setX( aPosition.X() ); + else + aPos.setX( aPosition.X() + ::tools::Long( aPageSize.Width() - NOTES_HEADER_FOOTER_WIDTH ) ); + if( propvalue[3] == 0 ) + aPos.setY( aPosition.Y() ); + else + aPos.setY( aPosition.Y() + ::tools::Long( aPageSize.Height() - NOTES_HEADER_FOOTER_HEIGHT ) ); + + ::tools::Rectangle aRect( aPos, aSize ); + return CreatePresObj( eObjKind, false, aRect ); + } + } + else + { + OSL_FAIL("SdPage::CreateDefaultPresObj() - unknown PRESOBJ kind" ); + return nullptr; + } +} + +void SdPage::DestroyDefaultPresObj(PresObjKind eObjKind) +{ + SdrObject* pObject = GetPresObj( eObjKind ); + + if( pObject ) + { + SdDrawDocument* pDoc(static_cast< SdDrawDocument* >(&getSdrModelFromSdrPage())); + const bool bUndo = pDoc->IsUndoEnabled(); + if( bUndo ) + pDoc->AddUndo(pDoc->GetSdrUndoFactory().CreateUndoDeleteObject(*pObject)); + SdrObjList* pOL = pObject->getParentSdrObjListFromSdrObject(); + pOL->RemoveObject(pObject->GetOrdNumDirect()); + + if( !bUndo ) + SdrObject::Free(pObject); + } +} + +/************************************************************************* +|* +|* return title area +|* +\************************************************************************/ + +::tools::Rectangle SdPage::GetTitleRect() const +{ + ::tools::Rectangle aTitleRect; + + if (mePageKind != PageKind::Handout) + { + double propvalue[] = {0,0,0,0}; + + /****************************************************************** + * standard- or note page: title area + ******************************************************************/ + Point aTitlePos ( GetLeftBorder(), GetUpperBorder() ); + Size aTitleSize ( GetSize() ); + aTitleSize.AdjustWidth( -(GetLeftBorder() + GetRightBorder()) ); + aTitleSize.AdjustHeight( -(GetUpperBorder() + GetLowerBorder()) ); + const char* sPageKind = PageKindVector[mePageKind]; + + if (mePageKind == PageKind::Standard) + { + getPresObjProp( *this , "PRESOBJ_TITLE" ,sPageKind, propvalue); + aTitlePos.AdjustX(::tools::Long( aTitleSize.Width() * propvalue[2] ) ); + aTitlePos.AdjustY(::tools::Long( aTitleSize.Height() * propvalue[3] ) ); + aTitleSize.setWidth( ::tools::Long( aTitleSize.Width() * propvalue[1] ) ); + aTitleSize.setHeight( ::tools::Long( aTitleSize.Height() * propvalue[0] ) ); + } + else if (mePageKind == PageKind::Notes) + { + Point aPos = aTitlePos; + getPresObjProp( *this, "PRESOBJ_TITLE" ,sPageKind, propvalue); + aPos.AdjustX(::tools::Long( aTitleSize.Width() * propvalue[2] ) ); + aPos.AdjustY(::tools::Long( aTitleSize.Height() * propvalue[3] ) ); + + // limit height + aTitleSize.setHeight( ::tools::Long( aTitleSize.Height() * propvalue[0] ) ); + aTitleSize.setWidth( ::tools::Long( aTitleSize.Width() * propvalue[1] ) ); + + Size aPartArea = aTitleSize; + Size aSize; + sal_uInt16 nDestPageNum(GetPageNum()); + SdrPage* pRefPage = nullptr; + + if(nDestPageNum) + { + // only decrement if != 0, else we get 0xffff + nDestPageNum -= 1; + } + + if(nDestPageNum < getSdrModelFromSdrPage().GetPageCount()) + { + pRefPage = getSdrModelFromSdrPage().GetPage(nDestPageNum); + } + + if ( pRefPage ) + { + // scale actually page size into handout rectangle + double fH = pRefPage->GetWidth() == 0 + ? 0 : static_cast<double>(aPartArea.Width()) / pRefPage->GetWidth(); + double fV = pRefPage->GetHeight() == 0 + ? 0 : static_cast<double>(aPartArea.Height()) / pRefPage->GetHeight(); + + if ( fH > fV ) + fH = fV; + aSize.setWidth( static_cast<::tools::Long>(fH * pRefPage->GetWidth()) ); + aSize.setHeight( static_cast<::tools::Long>(fH * pRefPage->GetHeight()) ); + + aPos.AdjustX((aPartArea.Width() - aSize.Width()) / 2 ); + aPos.AdjustY((aPartArea.Height()- aSize.Height())/ 2 ); + } + + aTitlePos = aPos; + aTitleSize = aSize; + } + + aTitleRect.SetPos(aTitlePos); + aTitleRect.SetSize(aTitleSize); + } + + return aTitleRect; +} + +/************************************************************************* +|* +|* return outline area +|* +\************************************************************************/ + +::tools::Rectangle SdPage::GetLayoutRect() const +{ + ::tools::Rectangle aLayoutRect; + + if (mePageKind != PageKind::Handout) + { + double propvalue[] = {0,0,0,0}; + + Point aLayoutPos ( GetLeftBorder(), GetUpperBorder() ); + Size aLayoutSize ( GetSize() ); + aLayoutSize.AdjustWidth( -(GetLeftBorder() + GetRightBorder()) ); + aLayoutSize.AdjustHeight( -(GetUpperBorder() + GetLowerBorder()) ); + const char* sPageKind = PageKindVector[mePageKind]; + + if (mePageKind == PageKind::Standard) + { + getPresObjProp( *this ,"PRESOBJ_OUTLINE", sPageKind, propvalue); + aLayoutPos.AdjustX(::tools::Long( aLayoutSize.Width() * propvalue[2] ) ); + aLayoutPos.AdjustY(::tools::Long( aLayoutSize.Height() * propvalue[3] ) ); + aLayoutSize.setWidth( ::tools::Long( aLayoutSize.Width() * propvalue[1] ) ); + aLayoutSize.setHeight( ::tools::Long( aLayoutSize.Height() * propvalue[0] ) ); + aLayoutRect.SetPos(aLayoutPos); + aLayoutRect.SetSize(aLayoutSize); + } + else if (mePageKind == PageKind::Notes) + { + getPresObjProp( *this, "PRESOBJ_NOTES", sPageKind, propvalue); + aLayoutPos.AdjustX(::tools::Long( aLayoutSize.Width() * propvalue[2] ) ); + aLayoutPos.AdjustY(::tools::Long( aLayoutSize.Height() * propvalue[3] ) ); + aLayoutSize.setWidth( ::tools::Long( aLayoutSize.Width() * propvalue[1] ) ); + aLayoutSize.setHeight( ::tools::Long( aLayoutSize.Height() * propvalue[0] ) ); + aLayoutRect.SetPos(aLayoutPos); + aLayoutRect.SetSize(aLayoutSize); + } + } + + return aLayoutRect; +} + +/************************************************************************** +|* +|* assign an AutoLayout +|* +\*************************************************************************/ + +const int MAX_PRESOBJS = 7; // maximum number of presentation objects per layout +const int VERTICAL = 0x8000; + +static constexpr PresObjKind operator|(PresObjKind e, int x) +{ + return static_cast<PresObjKind>(static_cast<int>(e) | x); +} + +namespace { + +struct LayoutDescriptor +{ + PresObjKind meKind[MAX_PRESOBJS]; + bool mbVertical[MAX_PRESOBJS]; + + LayoutDescriptor( PresObjKind k0 = PresObjKind::NONE, PresObjKind k1 = PresObjKind::NONE, PresObjKind k2 = PresObjKind::NONE, PresObjKind k3 = PresObjKind::NONE, PresObjKind k4 = PresObjKind::NONE, PresObjKind k5 = PresObjKind::NONE, PresObjKind k6 = PresObjKind::NONE ); +}; + +} + +LayoutDescriptor::LayoutDescriptor( PresObjKind k0, PresObjKind k1, PresObjKind k2, PresObjKind k3, PresObjKind k4, PresObjKind k5, PresObjKind k6 ) +{ + auto removeVertical = [] (PresObjKind k) { return static_cast<PresObjKind>(static_cast<int>(k) & ~VERTICAL); }; + auto isVertical = [] (PresObjKind k) { return bool(static_cast<int>(k) & VERTICAL); }; + meKind[0] = removeVertical(k0); mbVertical[0] = isVertical(k0); + meKind[1] = removeVertical(k1); mbVertical[1] = isVertical(k1); + meKind[2] = removeVertical(k2); mbVertical[2] = isVertical(k2); + meKind[3] = removeVertical(k3); mbVertical[3] = isVertical(k3); + meKind[4] = removeVertical(k4); mbVertical[4] = isVertical(k4); + meKind[5] = removeVertical(k5); mbVertical[5] = isVertical(k5); + meKind[6] = removeVertical(k6); mbVertical[6] = isVertical(k6); +} + +static const LayoutDescriptor& GetLayoutDescriptor( AutoLayout eLayout ) +{ + static const LayoutDescriptor aLayouts[AUTOLAYOUT_END-AUTOLAYOUT_START] = + { + LayoutDescriptor( PresObjKind::Title, PresObjKind::Text ), // AUTOLAYOUT_TITLE + LayoutDescriptor( PresObjKind::Title, PresObjKind::Outline ), // AUTOLAYOUT_TITLE_CONTENT + LayoutDescriptor( PresObjKind::Title, PresObjKind::Outline ), // AUTOLAYOUT_CHART + LayoutDescriptor( PresObjKind::Title, PresObjKind::Outline, PresObjKind::Outline ), // AUTOLAYOUT_TITLE_2CONTENT + LayoutDescriptor( PresObjKind::Title, PresObjKind::Outline, PresObjKind::Outline ), // AUTOLAYOUT_TEXTCHART + LayoutDescriptor( PresObjKind::Title, PresObjKind::Outline ), // AUTOLAYOUT_ORG + LayoutDescriptor( PresObjKind::Title, PresObjKind::Outline, PresObjKind::Outline ), // AUTOLAYOUT_TEXTCLbIP + LayoutDescriptor( PresObjKind::Title, PresObjKind::Outline, PresObjKind::Outline ), // AUTOLAYOUT_CHARTTEXT + LayoutDescriptor( PresObjKind::Title, PresObjKind::Outline ), // AUTOLAYOUT_TAB + LayoutDescriptor( PresObjKind::Title, PresObjKind::Outline, PresObjKind::Outline ), // AUTOLAYOUT_CLIPTEXT + LayoutDescriptor( PresObjKind::Title, PresObjKind::Outline, PresObjKind::Outline ), // AUTOLAYOUT_TEXTOBJ + LayoutDescriptor( PresObjKind::Title, PresObjKind::Object ), // AUTOLAYOUT_OBJ + LayoutDescriptor( PresObjKind::Title, PresObjKind::Outline, PresObjKind::Outline, PresObjKind::Outline ), // AUTOLAYOUT_TITLE_CONTENT_2CONTENT + LayoutDescriptor( PresObjKind::Title, PresObjKind::Outline, PresObjKind::Outline ), // AUTOLAYOUT_TEXTOBJ + LayoutDescriptor( PresObjKind::Title, PresObjKind::Outline, PresObjKind::Outline ), // AUTOLAYOUT_TITLE_CONTENT_OVER_CONTENT + LayoutDescriptor( PresObjKind::Title, PresObjKind::Outline, PresObjKind::Outline, PresObjKind::Outline ), // AUTOLAYOUT_TITLE_2CONTENT_CONTENT + LayoutDescriptor( PresObjKind::Title, PresObjKind::Outline, PresObjKind::Outline, PresObjKind::Outline ), // AUTOLAYOUT_TITLE_2CONTENT_OVER_CONTENT + LayoutDescriptor( PresObjKind::Title, PresObjKind::Outline, PresObjKind::Outline ), // AUTOLAYOUT_TEXTOVEROBJ + LayoutDescriptor( PresObjKind::Title, PresObjKind::Outline, PresObjKind::Outline, // AUTOLAYOUT_TITLE_4CONTENT + PresObjKind::Outline, PresObjKind::Outline ), + LayoutDescriptor( PresObjKind::Title, PresObjKind::NONE ), // AUTOLAYOUT_TITLE_ONLY + LayoutDescriptor( PresObjKind::NONE ), // AUTOLAYOUT_NONE + LayoutDescriptor( PresObjKind::Page, PresObjKind::Notes ), // AUTOLAYOUT_NOTES + LayoutDescriptor( ), // AUTOLAYOUT_HANDOUT1 + LayoutDescriptor( ), // AUTOLAYOUT_HANDOUT2 + LayoutDescriptor( ), // AUTOLAYOUT_HANDOUT3 + LayoutDescriptor( ), // AUTOLAYOUT_HANDOUT4 + LayoutDescriptor( ), // AUTOLAYOUT_HANDOUT6 + LayoutDescriptor( PresObjKind::Title|VERTICAL, PresObjKind::Outline|VERTICAL, PresObjKind::Outline ),// AUTOLAYOUT_VTITLE_VCONTENT_OVER_VCONTENT + LayoutDescriptor( PresObjKind::Title|VERTICAL, PresObjKind::Outline|VERTICAL ), // AUTOLAYOUT_VTITLE_VCONTENT + LayoutDescriptor( PresObjKind::Title, PresObjKind::Outline|VERTICAL ), // AUTOLAYOUT_TITLE_VCONTENT + LayoutDescriptor( PresObjKind::Title, PresObjKind::Outline|VERTICAL, PresObjKind::Outline|VERTICAL ), // AUTOLAYOUT_TITLE_2VTEXT + LayoutDescriptor( ), // AUTOLAYOUT_HANDOUT9 + LayoutDescriptor( PresObjKind::Text, PresObjKind::NONE ), // AUTOLAYOUT_ONLY_TEXT + LayoutDescriptor( PresObjKind::Title, PresObjKind::Outline, PresObjKind::Outline, // AUTOLAYOUT_4CLIPART + PresObjKind::Graphic, PresObjKind::Graphic ), + LayoutDescriptor( PresObjKind::Title, PresObjKind::Outline, PresObjKind::Outline, // AUTOLAYOUT_TITLE_6CONTENT + PresObjKind::Outline, PresObjKind::Outline, PresObjKind::Outline, PresObjKind::Outline ) + }; + + if( (eLayout < AUTOLAYOUT_START) || (eLayout >= AUTOLAYOUT_END) ) + eLayout = AUTOLAYOUT_NONE; + + return aLayouts[ eLayout - AUTOLAYOUT_START ]; +} + +static OUString enumtoString(AutoLayout aut) +{ + OUString retstr; + switch (aut) + { + case AUTOLAYOUT_TITLE_CONTENT: + retstr="AUTOLAYOUT_TITLE_CONTENT"; + break; + case AUTOLAYOUT_TITLE_CONTENT_OVER_CONTENT: + retstr="AUTOLAYOUT_TITLE_CONTENT_OVER_CONTENT"; + break; + case AUTOLAYOUT_TITLE_CONTENT_2CONTENT: + retstr="AUTOLAYOUT_TITLE_CONTENT_2CONTENT"; + break; + case AUTOLAYOUT_TITLE_4CONTENT: + retstr="AUTOLAYOUT_TITLE_4CONTENT"; + break; + case AUTOLAYOUT_ONLY_TEXT: + retstr="AUTOLAYOUT_ONLY_TEXT"; + break; + case AUTOLAYOUT_TITLE_ONLY: + retstr="AUTOLAYOUT_TITLE_ONLY"; + break; + case AUTOLAYOUT_TITLE_6CONTENT: + retstr="AUTOLAYOUT_TITLE_6CONTENT"; + break; + case AUTOLAYOUT_START: + retstr="AUTOLAYOUT_START"; + break; + case AUTOLAYOUT_TITLE_2CONTENT_CONTENT: + retstr="AUTOLAYOUT_TITLE_2CONTENT_CONTENT"; + break; + case AUTOLAYOUT_TITLE_2CONTENT_OVER_CONTENT: + retstr="AUTOLAYOUT_TITLE_2CONTENT_OVER_CONTENT"; + break; + case AUTOLAYOUT_TITLE_2CONTENT: + retstr="AUTOLAYOUT_TITLE_2CONTENT"; + break; + case AUTOLAYOUT_VTITLE_VCONTENT: + retstr="AUTOLAYOUT_VTITLE_VCONTENT"; + break; + case AUTOLAYOUT_VTITLE_VCONTENT_OVER_VCONTENT: + retstr="AUTOLAYOUT_VTITLE_VCONTENT_OVER_VCONTENT"; + break; + case AUTOLAYOUT_TITLE_VCONTENT: + retstr="AUTOLAYOUT_TITLE_VCONTENT"; + break; + case AUTOLAYOUT_TITLE_2VTEXT: + retstr="AUTOLAYOUT_TITLE_2VTEXT"; + break; + default: + retstr="unknown"; + break; + // case AUTOLAYOUT_TITLE_4SCONTENT: return "AUTOLAYOUT_TITLE_4SCONTENT"; + } + return retstr; +} + +static void CalcAutoLayoutRectangles( SdPage const & rPage,::tools::Rectangle* rRectangle ,const OUString& sLayoutType ) +{ + ::tools::Rectangle aTitleRect; + ::tools::Rectangle aLayoutRect; + + if( rPage.GetPageKind() != PageKind::Handout ) + { + SdPage& rMasterPage = static_cast<SdPage&>(rPage.TRG_GetMasterPage()); + SdrObject* pMasterTitle = rMasterPage.GetPresObj( PresObjKind::Title ); + SdrObject* pMasterSubTitle = rMasterPage.GetPresObj( PresObjKind::Text ); + SdrObject* pMasterOutline = rMasterPage.GetPresObj( rPage.GetPageKind()==PageKind::Notes ? PresObjKind::Notes : PresObjKind::Outline ); + + if( pMasterTitle ) + aTitleRect = pMasterTitle->GetLogicRect(); + + if (aTitleRect.IsEmpty() ) + aTitleRect = rPage.GetTitleRect(); + if( pMasterSubTitle ) + aLayoutRect = pMasterSubTitle->GetLogicRect(); + else if( pMasterOutline ) + aLayoutRect = pMasterOutline->GetLogicRect(); + + if (aLayoutRect.IsEmpty() ) + aLayoutRect = rPage.GetLayoutRect(); + } + + rRectangle[0] = aTitleRect; + for( int i = 1; i < MAX_PRESOBJS; i++ ) + rRectangle[i] = aLayoutRect; + + const Point aTitlePos( aTitleRect.TopLeft() ); + const Size aLayoutSize( aLayoutRect.GetSize() ); + const Point aLayoutPos( aLayoutRect.TopLeft() ); + double propvalue[] = {0,0,0,0}; + + const std::vector< Reference<XNode> >& layoutInfo = static_cast< const SdDrawDocument& >(rPage.getSdrModelFromSdrPage()).GetLayoutVector(); + auto aIter = std::find_if(layoutInfo.begin(), layoutInfo.end(), + [&sLayoutType](const Reference<XNode>& layoutNode) { + Reference<XNamedNodeMap> layoutAttrList = layoutNode->getAttributes(); + + // get the attribute value of layout (i.e it's type) + OUString sLayoutAttName = layoutAttrList->getNamedItem("type")->getNodeValue(); + return sLayoutAttName == sLayoutType; + }); + if (aIter == layoutInfo.end()) + return; + + int count=0; + Reference<XNode> layoutNode = *aIter; + Reference<XNodeList> layoutChildren = layoutNode->getChildNodes(); + const int presobjsize = layoutChildren->getLength(); + for( int j=0; j< presobjsize ; j++) + { + OUString nodename; + Reference<XNode> presobj = layoutChildren->item(j); + nodename=presobj->getNodeName(); + + //check whether child is blank 'text-node' or 'presobj' node + if(nodename == "presobj") + { + // TODO: rework sd to permit arbitrary number of presentation objects + assert(count < MAX_PRESOBJS); + + Reference<XNamedNodeMap> presObjAttributes = presobj->getAttributes(); + + Reference<XNode> presObjSizeHeight = presObjAttributes->getNamedItem("relative-height"); + OUString sValue = presObjSizeHeight->getNodeValue(); + propvalue[0] = sValue.toDouble(); + + Reference<XNode> presObjSizeWidth = presObjAttributes->getNamedItem("relative-width"); + sValue = presObjSizeWidth->getNodeValue(); + propvalue[1] = sValue.toDouble(); + + Reference<XNode> presObjPosX = presObjAttributes->getNamedItem("relative-posX"); + sValue = presObjPosX->getNodeValue(); + propvalue[2] = sValue.toDouble(); + + Reference<XNode> presObjPosY = presObjAttributes->getNamedItem("relative-posY"); + sValue = presObjPosY->getNodeValue(); + propvalue[3] = sValue.toDouble(); + + if(count == 0) + { + Size aSize ( aTitleRect.GetSize() ); + aSize.setHeight( basegfx::fround(aSize.Height() * propvalue[0]) ); + aSize.setWidth( basegfx::fround(aSize.Width() * propvalue[1]) ); + Point aPos( basegfx::fround(aTitlePos.X() +(aSize.Width() * propvalue[2])), + basegfx::fround(aTitlePos.Y() + (aSize.Height() * propvalue[3])) ); + rRectangle[count] = ::tools::Rectangle(aPos, aSize); + count = count+1; + } + else + { + Size aSize( basegfx::fround(aLayoutSize.Width() * propvalue[1]), + basegfx::fround(aLayoutSize.Height() * propvalue[0]) ); + Point aPos( basegfx::fround(aLayoutPos.X() +(aSize.Width() * propvalue[2])), + basegfx::fround(aLayoutPos.Y() + (aSize.Height() * propvalue[3])) ); + rRectangle[count] = ::tools::Rectangle (aPos, aSize); + count = count+1; + } + } + } +} + +static void findAutoLayoutShapesImpl( SdPage& rPage, const LayoutDescriptor& rDescriptor, std::array<SdrObject*, MAX_PRESOBJS>& rShapes, bool bInit, bool bSwitchLayout ) +{ + // init list of indexes for each presentation shape kind + // this is used to find subsequent shapes with the same presentation shape kind + o3tl::enumarray<PresObjKind,int> PresObjIndex; + PresObjIndex.fill(1); + + bool bMissing = false; + + // for each entry in the layoutdescriptor, arrange a presentation shape + for (int i = 0; (i < MAX_PRESOBJS) && (rDescriptor.meKind[i] != PresObjKind::NONE); i++) + { + PresObjKind eKind = rDescriptor.meKind[i]; + SdrObject* pObj = nullptr; + while( (pObj = rPage.GetPresObj( eKind, PresObjIndex[eKind], true )) != nullptr ) + { + PresObjIndex[eKind]++; // on next search for eKind, find next shape with same eKind + + if( !bSwitchLayout || !pObj->IsEmptyPresObj() ) + { + rShapes[i] = pObj; + break; + } + } + + if( !pObj ) + bMissing = true; + } + + if( !(bMissing && bInit) ) + return; + + // for each entry in the layoutdescriptor, look for an alternative shape + for (int i = 0; (i < MAX_PRESOBJS) && (rDescriptor.meKind[i] != PresObjKind::NONE); i++) + { + if( rShapes[i] ) + continue; + + PresObjKind eKind = rDescriptor.meKind[i]; + + SdrObject* pObj = nullptr; + bool bFound = false; + + const size_t nShapeCount = rPage.GetObjCount(); + for(size_t nShapeIndex = 0; nShapeIndex < nShapeCount && !bFound; ++nShapeIndex ) + { + pObj = rPage.GetObj(nShapeIndex); + + if( pObj->IsEmptyPresObj() ) + continue; + + if( pObj->GetObjInventor() != SdrInventor::Default ) + continue; + + // do not reuse shapes that are already part of the layout + if( std::find( rShapes.begin(), rShapes.end(), pObj ) != rShapes.end() ) + continue; + + bool bPresStyle = pObj->GetStyleSheet() && (pObj->GetStyleSheet()->GetFamily() == SfxStyleFamily::Page); + SdrObjKind eSdrObjKind = pObj->GetObjIdentifier(); + + switch( eKind ) + { + case PresObjKind::Title: + bFound = eSdrObjKind == SdrObjKind::TitleText; + break; + case PresObjKind::Table: + bFound = eSdrObjKind == SdrObjKind::Table; + break; + case PresObjKind::Media: + bFound = eSdrObjKind == SdrObjKind::Media; + break; + case PresObjKind::Outline: + bFound = (eSdrObjKind == SdrObjKind::OutlineText) || + ((eSdrObjKind == SdrObjKind::Text) && bPresStyle) || + (eSdrObjKind == SdrObjKind::Table) || (eSdrObjKind == SdrObjKind::Media) || (eSdrObjKind == SdrObjKind::Graphic) || (eSdrObjKind == SdrObjKind::OLE2); + break; + case PresObjKind::Graphic: + bFound = eSdrObjKind == SdrObjKind::Graphic; + break; + case PresObjKind::Object: + if( eSdrObjKind == SdrObjKind::OLE2 ) + { + SdrOle2Obj* pOle2 = dynamic_cast< SdrOle2Obj* >( pObj ); + if( pOle2 ) + { + if( pOle2->IsEmpty() ) + bFound = true; + else + { + ::comphelper::IEmbeddedHelper* pPersist(rPage.getSdrModelFromSdrPage().GetPersist()); + + if( pPersist ) + { + uno::Reference < embed::XEmbeddedObject > xObject = pPersist->getEmbeddedObjectContainer(). + GetEmbeddedObject( pOle2->GetPersistName() ); + + // TODO CL->KA: Why is this not working anymore? + if( xObject.is() ) + { + SvGlobalName aClassId( xObject->getClassID() ); + + const SvGlobalName aAppletClassId( SO3_APPLET_CLASSID ); + const SvGlobalName aPluginClassId( SO3_PLUGIN_CLASSID ); + const SvGlobalName aIFrameClassId( SO3_IFRAME_CLASSID ); + + if( aPluginClassId != aClassId && aAppletClassId != aClassId && aIFrameClassId != aClassId ) + { + bFound = true; + } + } + } + } + } + } + break; + case PresObjKind::Chart: + case PresObjKind::Calc: + if( eSdrObjKind == SdrObjKind::OLE2 ) + { + SdrOle2Obj* pOle2 = dynamic_cast< SdrOle2Obj* >( pObj ); + if( pOle2 ) + { + if( + ((eKind == PresObjKind::Chart) && + ( pOle2->GetProgName() == "StarChart" || pOle2->IsChart() ) ) + || + ((eKind == PresObjKind::Calc) && + ( pOle2->GetProgName() == "StarCalc" || pOle2->IsCalc() ) ) ) + { + bFound = true; + } + } + break; + } + else if( eSdrObjKind == SdrObjKind::Table ) + { + bFound = true; + } + break; + case PresObjKind::Page: + case PresObjKind::Handout: + bFound = eSdrObjKind == SdrObjKind::Page; + break; + case PresObjKind::Notes: + case PresObjKind::Text: + bFound = (bPresStyle && (eSdrObjKind == SdrObjKind::Text)) || (eSdrObjKind == SdrObjKind::OutlineText); + break; + default: + break; + } + } + + if( bFound ) + rShapes[i] = pObj; + } +} + +void SdPage::SetAutoLayout(AutoLayout eLayout, bool bInit, bool bCreate ) +{ + sd::ScopeLockGuard aGuard( maLockAutoLayoutArrangement ); + + const bool bSwitchLayout = eLayout != GetAutoLayout(); + + SfxUndoManager* pUndoManager(static_cast< SdDrawDocument& >(getSdrModelFromSdrPage()).GetUndoManager()); + const bool bUndo = pUndoManager && pUndoManager->IsInListAction() && IsInserted(); + + meAutoLayout = eLayout; + + // if needed, creates and initialises the presentation shapes on this slides master page + CreateTitleAndLayout(bInit, bCreate); + + if((meAutoLayout == AUTOLAYOUT_NONE && maPresentationShapeList.isEmpty()) || mbMaster) + { + // MasterPage or no layout and no presentation shapes available, nothing to do + return; + } + + ::tools::Rectangle aRectangle[MAX_PRESOBJS]; + const LayoutDescriptor& aDescriptor = GetLayoutDescriptor( meAutoLayout ); + OUString sLayoutName( enumtoString(meAutoLayout) ); + CalcAutoLayoutRectangles( *this, aRectangle, sLayoutName); + + o3tl::sorted_vector< SdrObject* > aUsedPresentationObjects; + + std::array<SdrObject*, MAX_PRESOBJS > aLayoutShapes; + aLayoutShapes.fill(nullptr); + findAutoLayoutShapesImpl( *this, aDescriptor, aLayoutShapes, bInit, bSwitchLayout ); + + // for each entry in the layoutdescriptor, arrange a presentation shape + for (int i = 0; (i < MAX_PRESOBJS) && (aDescriptor.meKind[i] != PresObjKind::NONE); i++) + { + PresObjKind eKind = aDescriptor.meKind[i]; + SdrObject* pObj = InsertAutoLayoutShape( aLayoutShapes[i], eKind, aDescriptor.mbVertical[i], aRectangle[i], bInit ); + if( pObj ) + aUsedPresentationObjects.insert(pObj); // remember that we used this empty shape + } + + // now delete all empty presentation objects that are no longer used by the new layout + if( !bInit ) + return; + + SdrObject* pObj = nullptr; + maPresentationShapeList.seekShape(0); + + while( (pObj = maPresentationShapeList.getNextShape()) ) + { + if( aUsedPresentationObjects.count(pObj) == 0 ) + { + + if( pObj->IsEmptyPresObj() ) + { + if( bUndo ) + pUndoManager->AddUndoAction(getSdrModelFromSdrPage().GetSdrUndoFactory().CreateUndoDeleteObject(*pObj)); + + RemoveObject( pObj->GetOrdNum() ); + + if( !bUndo ) + SdrObject::Free( pObj ); + } +/* #i108541# keep non empty pres obj as pres obj even if they are not part of the current layout */ + } + } +} + +/************************************************************************* +|* +|* insert object +|* +\************************************************************************/ + +void SdPage::NbcInsertObject(SdrObject* pObj, size_t nPos) +{ + FmFormPage::NbcInsertObject(pObj, nPos); + + static_cast< SdDrawDocument& >(getSdrModelFromSdrPage()).InsertObject(pObj); + + SdrLayerID nId = pObj->GetLayer(); + if( mbMaster ) + { + if( nId == SdrLayerID(0) ) + pObj->NbcSetLayer( SdrLayerID(2) ); // wrong layer. corrected to BackgroundObj layer + } + else + { + if( nId == SdrLayerID(2) ) + pObj->NbcSetLayer( SdrLayerID(0) ); // wrong layer. corrected to layout layer + } +} + +/************************************************************************* +|* +|* remove object +|* +\************************************************************************/ + +SdrObject* SdPage::RemoveObject(size_t nObjNum) +{ + onRemoveObject(GetObj( nObjNum )); + return FmFormPage::RemoveObject(nObjNum); +} + +/************************************************************************* +|* +|* remove object without broadcast +|* +\************************************************************************/ + +SdrObject* SdPage::NbcRemoveObject(size_t nObjNum) +{ + onRemoveObject(GetObj( nObjNum )); + return FmFormPage::NbcRemoveObject(nObjNum); +} + +// Also override ReplaceObject methods to realize when +// objects are removed with this mechanism instead of RemoveObject +SdrObject* SdPage::ReplaceObject(SdrObject* pNewObj, size_t nObjNum) +{ + onRemoveObject(GetObj( nObjNum )); + return FmFormPage::ReplaceObject(pNewObj, nObjNum); +} + +// called after a shape is removed or replaced from this slide + +void SdPage::onRemoveObject( SdrObject* pObject ) +{ + if( pObject ) + { + RemovePresObj(pObject); + + static_cast< SdDrawDocument& >(getSdrModelFromSdrPage()).RemoveObject(pObject); + + removeAnimations( pObject ); + } +} + +void SdPage::SetSize(const Size& aSize) +{ + Size aOldSize = GetSize(); + + if (aSize != aOldSize) + { + FmFormPage::SetSize(aSize); + } +} + +void SdPage::SetBorder(sal_Int32 nLft, sal_Int32 nUpp, sal_Int32 nRgt, sal_Int32 nLwr) +{ + if (nLft != GetLeftBorder() || nUpp != GetUpperBorder() || + nRgt != GetRightBorder() || nLwr != GetLowerBorder() ) + { + FmFormPage::SetBorder(nLft, nUpp, nRgt, nLwr); + } +} + +void SdPage::SetLeftBorder(sal_Int32 nBorder) +{ + if (nBorder != GetLeftBorder() ) + { + FmFormPage::SetLeftBorder(nBorder); + } +} + +void SdPage::SetRightBorder(sal_Int32 nBorder) +{ + if (nBorder != GetRightBorder() ) + { + FmFormPage::SetRightBorder(nBorder); + } +} + +void SdPage::SetUpperBorder(sal_Int32 nBorder) +{ + if (nBorder != GetUpperBorder() ) + { + FmFormPage::SetUpperBorder(nBorder); + } +} + +void SdPage::SetLowerBorder(sal_Int32 nBorder) +{ + if (nBorder != GetLowerBorder() ) + { + FmFormPage::SetLowerBorder(nBorder); + } +} + +/************************************************************************* +|* +|* Adjust all objects to new page size. +|* +|* bScaleAllObj: all objects are scaled into the new area within the page +|* margins. We scale the position and size. For presentation objects on the +|* master page, we also scale the font height of the presentation template. +|* +\************************************************************************/ + +void SdPage::ScaleObjects(const Size& rNewPageSize, const ::tools::Rectangle& rNewBorderRect, bool bScaleAllObj) +{ + sd::ScopeLockGuard aGuard( maLockAutoLayoutArrangement ); + + mbScaleObjects = bScaleAllObj; + SdrObject* pObj = nullptr; + Point aRefPnt(0, 0); + Size aNewPageSize(rNewPageSize); + sal_Int32 nLeft = rNewBorderRect.Left(); + sal_Int32 nRight = rNewBorderRect.Right(); + sal_Int32 nUpper = rNewBorderRect.Top(); + sal_Int32 nLower = rNewBorderRect.Bottom(); + + // negative values are fixed values + // -> use up to date values + if (aNewPageSize.Width() < 0) + { + aNewPageSize.setWidth( GetWidth() ); + } + if (aNewPageSize.Height() < 0) + { + aNewPageSize.setHeight( GetHeight() ); + } + if (nLeft < 0) + { + nLeft = GetLeftBorder(); + } + if (nRight < 0) + { + nRight = GetRightBorder(); + } + if (nUpper < 0) + { + nUpper = GetUpperBorder(); + } + if (nLower < 0) + { + nLower = GetLowerBorder(); + } + + Size aBackgroundSize(aNewPageSize); + + if (mbScaleObjects) + { + aBackgroundSize.AdjustWidth( -(nLeft + nRight) ); + aBackgroundSize.AdjustHeight( -(nUpper + nLower) ); + aNewPageSize = aBackgroundSize; + } + + ::tools::Long nOldWidth = GetWidth() - GetLeftBorder() - GetRightBorder(); + ::tools::Long nOldHeight = GetHeight() - GetUpperBorder() - GetLowerBorder(); + + Fraction aFractX(aNewPageSize.Width(), nOldWidth); + Fraction aFractY(aNewPageSize.Height(), nOldHeight); + + const size_t nObjCnt = (mbScaleObjects ? GetObjCount() : 0); + + for (size_t nObj = 0; nObj < nObjCnt; ++nObj) + { + bool bIsPresObjOnMaster = false; + + // all Objects + pObj = GetObj(nObj); + + if (mbMaster && IsPresObj(pObj)) + { + // There is a presentation object on the master page + bIsPresObjOnMaster = true; + } + + if (pObj) + { + // remember aTopLeft as original TopLeft + Point aTopLeft(pObj->GetCurrentBoundRect().TopLeft()); + + if (!pObj->IsEdgeObj()) + { + /************************************************************** + * Scale objects + **************************************************************/ + if (mbScaleObjects) + { + // use aTopLeft as original TopLeft + aRefPnt = aTopLeft; + } + + pObj->Resize(aRefPnt, aFractX, aFractY); + + if (mbScaleObjects) + { + SdrObjKind eObjKind = pObj->GetObjIdentifier(); + + if (bIsPresObjOnMaster) + { + /********************************************************** + * presentation template: adjust test height + **********************************************************/ + + if (pObj == GetPresObj(PresObjKind::Title, 0)) + { + SfxStyleSheet* pTitleSheet = GetStyleSheetForPresObj(PresObjKind::Title); + + if (pTitleSheet) + { + SfxItemSet& rSet = pTitleSheet->GetItemSet(); + + const SvxFontHeightItem& rOldHgt = rSet.Get(EE_CHAR_FONTHEIGHT); + sal_uLong nFontHeight = rOldHgt.GetHeight(); + nFontHeight = ::tools::Long(nFontHeight * static_cast<double>(aFractY)); + rSet.Put(SvxFontHeightItem(nFontHeight, 100, EE_CHAR_FONTHEIGHT)); + + if( SfxItemState::DEFAULT == rSet.GetItemState( EE_CHAR_FONTHEIGHT_CJK ) ) + { + const SvxFontHeightItem& rOldHgt2 = rSet.Get(EE_CHAR_FONTHEIGHT_CJK); + nFontHeight = rOldHgt2.GetHeight(); + nFontHeight = ::tools::Long(nFontHeight * static_cast<double>(aFractY)); + rSet.Put(SvxFontHeightItem(nFontHeight, 100, EE_CHAR_FONTHEIGHT_CJK)); + } + + if( SfxItemState::DEFAULT == rSet.GetItemState( EE_CHAR_FONTHEIGHT_CTL ) ) + { + const SvxFontHeightItem& rOldHgt2 = rSet.Get(EE_CHAR_FONTHEIGHT_CTL); + nFontHeight = rOldHgt2.GetHeight(); + nFontHeight = ::tools::Long(nFontHeight * static_cast<double>(aFractY)); + rSet.Put(SvxFontHeightItem(nFontHeight, 100, EE_CHAR_FONTHEIGHT_CTL)); + } + + pTitleSheet->Broadcast(SfxHint(SfxHintId::DataChanged)); + } + } + else if (pObj == GetPresObj(PresObjKind::Outline, 0)) + { + OUString aName(GetLayoutName() + " "); + + for (sal_Int32 i=1; i<=9; i++) + { + OUString sLayoutName( aName + OUString::number( i ) ); + SfxStyleSheet* pOutlineSheet = static_cast<SfxStyleSheet*>(static_cast< SdDrawDocument& >(getSdrModelFromSdrPage()).GetStyleSheetPool()->Find(sLayoutName, SfxStyleFamily::Page)); + + if (pOutlineSheet) + { + // Calculate new font height + SfxItemSet aTempSet(pOutlineSheet->GetItemSet()); + + const SvxFontHeightItem& rOldHgt = aTempSet.Get(EE_CHAR_FONTHEIGHT); + sal_uLong nFontHeight = rOldHgt.GetHeight(); + nFontHeight = ::tools::Long(nFontHeight * static_cast<double>(aFractY)); + aTempSet.Put(SvxFontHeightItem(nFontHeight, 100, EE_CHAR_FONTHEIGHT)); + + if( SfxItemState::DEFAULT == aTempSet.GetItemState( EE_CHAR_FONTHEIGHT_CJK ) ) + { + const SvxFontHeightItem& rOldHgt2 = aTempSet.Get(EE_CHAR_FONTHEIGHT_CJK); + nFontHeight = rOldHgt2.GetHeight(); + nFontHeight = ::tools::Long(nFontHeight * static_cast<double>(aFractY)); + aTempSet.Put(SvxFontHeightItem(nFontHeight, 100, EE_CHAR_FONTHEIGHT_CJK)); + } + + if( SfxItemState::DEFAULT == aTempSet.GetItemState( EE_CHAR_FONTHEIGHT_CTL ) ) + { + const SvxFontHeightItem& rOldHgt2 = aTempSet.Get(EE_CHAR_FONTHEIGHT_CTL); + nFontHeight = rOldHgt2.GetHeight(); + nFontHeight = ::tools::Long(nFontHeight * static_cast<double>(aFractY)); + aTempSet.Put(SvxFontHeightItem(nFontHeight, 100, EE_CHAR_FONTHEIGHT_CTL)); + } + + // adjust bullet + static_cast<SdStyleSheet*>(pOutlineSheet)->AdjustToFontHeight(aTempSet, false); + + // Special treatment: reset the INVALIDS to + // NULL pointer (otherwise we have INVALID's + // or pointer to the DefaultItems in the + // template; both would suppress the + // attribute inheritance) + aTempSet.ClearInvalidItems(); + + // Special treatment: only the valid parts + // of the BulletItems + if (aTempSet.GetItemState(EE_PARA_BULLET) == SfxItemState::DEFAULT) + { + SvxBulletItem aOldBulItem( pOutlineSheet->GetItemSet().Get(EE_PARA_BULLET) ); + const SvxBulletItem& rNewBulItem = aTempSet.Get(EE_PARA_BULLET); + aOldBulItem.CopyValidProperties(rNewBulItem); + aTempSet.Put(aOldBulItem); + } + + pOutlineSheet->GetItemSet().Put(aTempSet); + pOutlineSheet->Broadcast(SfxHint(SfxHintId::DataChanged)); + } + } + } + else if (pObj == GetPresObj(PresObjKind::Notes, 0)) + { + SfxStyleSheet* pNotesSheet = GetStyleSheetForPresObj(PresObjKind::Notes); + + if (pNotesSheet) + { + sal_uLong nHeight = pObj->GetLogicRect().GetSize().Height(); + sal_uLong nFontHeight = static_cast<sal_uLong>(nHeight * 0.0741); + SfxItemSet& rSet = pNotesSheet->GetItemSet(); + rSet.Put( SvxFontHeightItem(nFontHeight, 100, EE_CHAR_FONTHEIGHT )); + rSet.Put( SvxFontHeightItem(nFontHeight, 100, EE_CHAR_FONTHEIGHT_CJK )); + rSet.Put( SvxFontHeightItem(nFontHeight, 100, EE_CHAR_FONTHEIGHT_CTL )); + pNotesSheet->Broadcast(SfxHint(SfxHintId::DataChanged)); + } + } + } + else if ( eObjKind != SdrObjKind::TitleText && + eObjKind != SdrObjKind::OutlineText && + dynamic_cast< const SdrTextObj *>( pObj ) != nullptr && + pObj->GetOutlinerParaObject() ) + { + /****************************************************** + * normal text object: adjust text height + ******************************************************/ + SvtScriptType nScriptType = pObj->GetOutlinerParaObject()->GetTextObject().GetScriptType(); + sal_uInt16 nWhich = EE_CHAR_FONTHEIGHT; + if ( nScriptType == SvtScriptType::ASIAN ) + nWhich = EE_CHAR_FONTHEIGHT_CJK; + else if ( nScriptType == SvtScriptType::COMPLEX ) + nWhich = EE_CHAR_FONTHEIGHT_CTL; + + // use more modern method to scale the text height + sal_uInt32 nFontHeight = static_cast<const SvxFontHeightItem&>(pObj->GetMergedItem(nWhich)).GetHeight(); + sal_uInt32 nNewFontHeight = sal_uInt32(static_cast<double>(nFontHeight) * static_cast<double>(aFractY)); + + pObj->SetMergedItem(SvxFontHeightItem(nNewFontHeight, 100, nWhich)); + } + } + } + + if (mbScaleObjects && !pObj->IsEdgeObj()) + { + /************************************************************** + * scale object position + **************************************************************/ + Point aNewPos; + + // corrected scaling; only distances may be scaled + // use aTopLeft as original TopLeft + aNewPos.setX( ::tools::Long((aTopLeft.X() - GetLeftBorder()) * static_cast<double>(aFractX)) + nLeft ); + aNewPos.setY( ::tools::Long((aTopLeft.Y() - GetUpperBorder()) * static_cast<double>(aFractY)) + nUpper ); + + Size aVec(aNewPos.X() - aTopLeft.X(), aNewPos.Y() - aTopLeft.Y()); + + if (aVec.Height() != 0 || aVec.Width() != 0) + { + pObj->NbcMove(aVec); + } + + pObj->SetChanged(); + pObj->BroadcastObjectChange(); + } + } + } +} + +static SdrObject* convertPresentationObjectImpl(SdPage& rPage, SdrObject* pSourceObj, PresObjKind& eObjKind, bool bVertical, const ::tools::Rectangle& rRect) +{ + SdDrawDocument& rModel(static_cast< SdDrawDocument& >(rPage.getSdrModelFromSdrPage())); + if( !pSourceObj ) + return pSourceObj; + + SfxUndoManager* pUndoManager = rModel.GetUndoManager(); + const bool bUndo = pUndoManager && pUndoManager->IsInListAction() && rPage.IsInserted(); + + SdrObject* pNewObj = pSourceObj; + if((eObjKind == PresObjKind::Outline) && (pSourceObj->GetObjIdentifier() == SdrObjKind::Text) ) + { + pNewObj = rPage.CreatePresObj(PresObjKind::Outline, bVertical, rRect); + + // Set text of the subtitle into PRESOBJ_OUTLINE + OutlinerParaObject* pOutlParaObj = pSourceObj->GetOutlinerParaObject(); + + if(pOutlParaObj) + { + // assign text + SdOutliner* pOutl = rModel.GetInternalOutliner(); + pOutl->Clear(); + pOutl->SetText( *pOutlParaObj ); + pNewObj->SetOutlinerParaObject( pOutl->CreateParaObject() ); + pOutlParaObj = pNewObj->GetOutlinerParaObject(); + pOutl->Clear(); + pNewObj->SetEmptyPresObj(false); + + for (sal_uInt16 nLevel = 1; nLevel < 10; nLevel++) + { + // assign new template + OUString aName( rPage.GetLayoutName() + " " + OUString::number( nLevel ) ); + SfxStyleSheet* pSheet = static_cast<SfxStyleSheet*>( rModel.GetStyleSheetPool()->Find(aName, SfxStyleFamily::Page) ); + + if (pSheet && nLevel == 1) + { + SfxStyleSheet* pSubtitleSheet = rPage.GetStyleSheetForPresObj(PresObjKind::Text); + + if (pSubtitleSheet) + pOutlParaObj->ChangeStyleSheetName(SfxStyleFamily::Page, pSubtitleSheet->GetName(), pSheet->GetName()); + } + } + + // Remove LRSpace item + SfxItemSetFixed<EE_PARA_LRSPACE, EE_PARA_LRSPACE> aSet(rModel.GetPool()); + + aSet.Put(pNewObj->GetMergedItemSet()); + + aSet.ClearItem(EE_PARA_LRSPACE); + + pNewObj->SetMergedItemSet(aSet); + + if( bUndo ) + pUndoManager->AddUndoAction( rModel.GetSdrUndoFactory().CreateUndoDeleteObject(*pSourceObj) ); + + // Remove outline shape from page + rPage.RemoveObject( pSourceObj->GetOrdNum() ); + + if( !bUndo ) + SdrObject::Free( pSourceObj ); + } + } + else if((eObjKind == PresObjKind::Text) && (pSourceObj->GetObjIdentifier() == SdrObjKind::OutlineText) ) + { + // is there an outline shape we can use to replace empty subtitle shape? + pNewObj = rPage.CreatePresObj(PresObjKind::Text, bVertical, rRect); + + // Set text of the outline object into PRESOBJ_TITLE + OutlinerParaObject* pOutlParaObj = pSourceObj->GetOutlinerParaObject(); + + if(pOutlParaObj) + { + // assign text + SdOutliner* pOutl = rModel.GetInternalOutliner(); + pOutl->Clear(); + pOutl->SetText( *pOutlParaObj ); + pNewObj->SetOutlinerParaObject( pOutl->CreateParaObject() ); + pOutl->Clear(); + pNewObj->SetEmptyPresObj(false); + + // reset left indent + SfxItemSetFixed<EE_PARA_LRSPACE, EE_PARA_LRSPACE> aSet(rModel.GetPool()); + + aSet.Put(pNewObj->GetMergedItemSet()); + + const SvxLRSpaceItem& rLRItem = aSet.Get(EE_PARA_LRSPACE); + SvxLRSpaceItem aNewLRItem(rLRItem); + aNewLRItem.SetTextLeft(0); + aSet.Put(aNewLRItem); + + pNewObj->SetMergedItemSet(aSet); + + SfxStyleSheet* pSheet = rPage.GetStyleSheetForPresObj(PresObjKind::Text); + if (pSheet) + pNewObj->SetStyleSheet(pSheet, true); + + // Remove subtitle shape from page + if( bUndo ) + pUndoManager->AddUndoAction(rModel.GetSdrUndoFactory().CreateUndoDeleteObject(*pSourceObj)); + + rPage.RemoveObject( pSourceObj->GetOrdNum() ); + + if( !bUndo ) + SdrObject::Free( pSourceObj ); + } + } + else if((eObjKind == PresObjKind::Outline) && (pSourceObj->GetObjIdentifier() != SdrObjKind::OutlineText) ) + { + switch( pSourceObj->GetObjIdentifier() ) + { + case SdrObjKind::Table: eObjKind = PresObjKind::Table; break; + case SdrObjKind::Media: eObjKind = PresObjKind::Media; break; + case SdrObjKind::Graphic: eObjKind = PresObjKind::Graphic; break; + case SdrObjKind::OLE2: eObjKind = PresObjKind::Object; break; + default: break; + } + } + + return pNewObj; +} + +/** reuses or creates a presentation shape for an auto layout that fits the given parameter + + @param eObjKind + The kind of presentation shape we like to have + @param nIndex + If > 1 we skip the first nIndex-1 shapes with the presentation shape kind eObjKind while + looking for an existing presentation shape + @param bVertical + If true, the shape is created vertical if bInit is true + @param rRect + The rectangle that should be used to transform the shape + @param bInit + If true the shape is created if not found + @returns + A presentation shape that was either found or created with the given parameters +*/ +SdrObject* SdPage::InsertAutoLayoutShape(SdrObject* pObj, PresObjKind eObjKind, bool bVertical, const ::tools::Rectangle& rRect, bool bInit) +{ + SfxUndoManager* pUndoManager(static_cast< SdDrawDocument& >(getSdrModelFromSdrPage()).GetUndoManager()); + const bool bUndo = pUndoManager && pUndoManager->IsInListAction() && IsInserted(); + + if (!pObj && bInit) + { + pObj = CreatePresObj(eObjKind, bVertical, rRect); + } + else if ( pObj && (pObj->GetUserCall() || bInit) ) + { + // convert object if shape type does not match kind (f.e. converting outline text to subtitle text) + if( bInit ) + pObj = convertPresentationObjectImpl(*this, pObj, eObjKind, bVertical, rRect); + + if( bUndo ) + { + pUndoManager->AddUndoAction( getSdrModelFromSdrPage().GetSdrUndoFactory().CreateUndoGeoObject( *pObj ) ); + pUndoManager->AddUndoAction( getSdrModelFromSdrPage().GetSdrUndoFactory().CreateUndoAttrObject( *pObj, true, true ) ); + pUndoManager->AddUndoAction( std::make_unique<UndoObjectUserCall>( *pObj ) ); + } + + pObj->AdjustToMaxRect(rRect); + + pObj->SetUserCall(this); + + SdrTextObj* pTextObject = dynamic_cast< SdrTextObj* >(pObj); + if( pTextObject ) + { + if( pTextObject->IsVerticalWriting() != bVertical ) + { + pTextObject->SetVerticalWriting( bVertical ); + + // here make sure the correct anchoring is used when the object + // is re-used but orientation is changed + if(PresObjKind::Outline == eObjKind) + pTextObject->SetMergedItem(SdrTextHorzAdjustItem( bVertical ? SDRTEXTHORZADJUST_RIGHT : SDRTEXTHORZADJUST_BLOCK )); + } + + if( !mbMaster && (pTextObject->GetObjIdentifier() != SdrObjKind::Table) ) + { + if ( pTextObject->IsAutoGrowHeight() ) + { + // switch off AutoGrowHeight, set new MinHeight + SfxItemSet aTempAttr( static_cast< SdDrawDocument& >(getSdrModelFromSdrPage()).GetPool() ); + SdrMetricItem aMinHeight( makeSdrTextMinFrameHeightItem(rRect.GetSize().Height()) ); + aTempAttr.Put( aMinHeight ); + aTempAttr.Put( makeSdrTextAutoGrowHeightItem(false) ); + pTextObject->SetMergedItemSet(aTempAttr); + pTextObject->SetLogicRect(rRect); + + // switch on AutoGrowHeight + SfxItemSet aAttr( static_cast< SdDrawDocument& >(getSdrModelFromSdrPage()).GetPool() ); + aAttr.Put( makeSdrTextAutoGrowHeightItem(true) ); + + pTextObject->SetMergedItemSet(aAttr); + } + + if ( pTextObject->IsAutoGrowWidth() ) + { + // switch off AutoGrowWidth , set new MinWidth + SfxItemSet aTempAttr( static_cast< SdDrawDocument& >(getSdrModelFromSdrPage()).GetPool() ); + SdrMetricItem aMinWidth( makeSdrTextMinFrameWidthItem(rRect.GetSize().Width()) ); + aTempAttr.Put( aMinWidth ); + aTempAttr.Put( makeSdrTextAutoGrowWidthItem(false) ); + pTextObject->SetMergedItemSet(aTempAttr); + pTextObject->SetLogicRect(rRect); + + // switch on AutoGrowWidth + SfxItemSet aAttr( static_cast< SdDrawDocument& >(getSdrModelFromSdrPage()).GetPool() ); + aAttr.Put( makeSdrTextAutoGrowWidthItem(true) ); + pTextObject->SetMergedItemSet(aAttr); + } + } + } + } + + if(pObj && bInit ) + { + if( !IsPresObj( pObj ) ) + { + if( bUndo ) + pUndoManager->AddUndoAction( std::make_unique<UndoObjectPresentationKind>( *pObj ) ); + + InsertPresObj( pObj, eObjKind ); + } + + // make adjustments for vertical title and outline shapes + if( bVertical && (( eObjKind == PresObjKind::Title) || (eObjKind == PresObjKind::Outline))) + { + SfxItemSet aNewSet(pObj->GetMergedItemSet()); + aNewSet.Put( makeSdrTextAutoGrowWidthItem(true) ); + aNewSet.Put( makeSdrTextAutoGrowHeightItem(false) ); + if( eObjKind == PresObjKind::Outline ) + { + aNewSet.Put( SdrTextVertAdjustItem(SDRTEXTVERTADJUST_TOP) ); + aNewSet.Put( SdrTextHorzAdjustItem(SDRTEXTHORZADJUST_RIGHT) ); + } + pObj->SetMergedItemSet(aNewSet); + } + } + + if ( pObj && (pObj->GetUserCall() || bInit) && ( pObj->IsEmptyPresObj() || dynamic_cast< const SdrGrafObj *>( pObj ) == nullptr ) ) + pObj->AdjustToMaxRect(rRect); + + return pObj; +} + +/************************************************************************* +|* +|* Returns the PresObjKind of an object +|* +\************************************************************************/ + +PresObjKind SdPage::GetPresObjKind(SdrObject* pObj) const +{ + PresObjKind eKind = PresObjKind::NONE; + if( (pObj != nullptr) && (maPresentationShapeList.hasShape(*pObj)) ) + { + SdAnimationInfo* pInfo = SdDrawDocument::GetShapeUserData(*pObj); + if( pInfo ) + eKind = pInfo->mePresObjKind; + } + + return eKind; +} + +bool SdPage::IsPresObj(const SdrObject* pObj) +{ + return pObj && maPresentationShapeList.hasShape( const_cast<SdrObject&>(*pObj) ); +} + +void SdPage::RemovePresObj(const SdrObject* pObj) +{ + if( pObj && maPresentationShapeList.hasShape(const_cast<SdrObject&>(*pObj)) ) + { + SdAnimationInfo* pInfo = SdDrawDocument::GetShapeUserData(const_cast<SdrObject&>(*pObj)); + if( pInfo ) + pInfo->mePresObjKind = PresObjKind::NONE; + maPresentationShapeList.removeShape(const_cast<SdrObject&>(*pObj)); + } +} + +void SdPage::InsertPresObj(SdrObject* pObj, PresObjKind eKind ) +{ + DBG_ASSERT( pObj, "sd::SdPage::InsertPresObj(), invalid presentation object inserted!" ); + DBG_ASSERT( !IsPresObj(pObj), "sd::SdPage::InsertPresObj(), presentation object inserted twice!" ); + if( pObj ) + { + SdAnimationInfo* pInfo = SdDrawDocument::GetShapeUserData(*pObj, true); + if( pInfo ) + pInfo->mePresObjKind = eKind; + maPresentationShapeList.addShape(*pObj); + } +} + +/************************************************************************* +|* +|* Set the text of an object +|* +\************************************************************************/ + +void SdPage::SetObjText(SdrTextObj* pObj, SdrOutliner* pOutliner, PresObjKind eObjKind, std::u16string_view rString ) +{ + if ( !pObj ) + return; + + ::Outliner* pOutl = pOutliner; + + if (!pOutliner) + { + SfxItemPool* pPool(static_cast< SdDrawDocument& >(getSdrModelFromSdrPage()).GetDrawOutliner().GetEmptyItemSet().GetPool()); + pOutl = new ::Outliner( pPool, OutlinerMode::OutlineObject ); + pOutl->SetRefDevice( SD_MOD()->GetVirtualRefDevice() ); + pOutl->SetEditTextObjectPool(pPool); + pOutl->SetStyleSheetPool(static_cast<SfxStyleSheetPool*>(getSdrModelFromSdrPage().GetStyleSheetPool())); + pOutl->EnableUndo(false); + pOutl->SetUpdateLayout( false ); + } + + OutlinerMode nOutlMode = pOutl->GetOutlinerMode(); + Size aPaperSize = pOutl->GetPaperSize(); + bool bUpdateMode = pOutl->SetUpdateLayout(false); + pOutl->SetParaAttribs( 0, pOutl->GetEmptyItemSet() ); + + // Always set the object's StyleSheet at the Outliner to + // use the current objects StyleSheet. Thus it's the same as in + // SetText(...). + // Moved this implementation from where SetObjText(...) was called + // to inside this method to work even when outliner is fetched here. + pOutl->SetStyleSheet(0, pObj->GetStyleSheet()); + + OUString aString; + + switch( eObjKind ) + { + case PresObjKind::Outline: + { + pOutl->Init( OutlinerMode::OutlineObject ); + + aString += OUString::Concat("\t") + rString; + + if (mbMaster) + { + pOutl->SetStyleSheet( 0, GetStyleSheetForPresObj(eObjKind) ); + aString += "\n\t\t" + + SdResId(STR_PRESOBJ_MPOUTLLAYER2) + + "\n\t\t\t" + + SdResId(STR_PRESOBJ_MPOUTLLAYER3) + + "\n\t\t\t\t" + + SdResId(STR_PRESOBJ_MPOUTLLAYER4) + + "\n\t\t\t\t\t" + + SdResId(STR_PRESOBJ_MPOUTLLAYER5) + + "\n\t\t\t\t\t\t" + + SdResId(STR_PRESOBJ_MPOUTLLAYER6) + + "\n\t\t\t\t\t\t\t" + + SdResId(STR_PRESOBJ_MPOUTLLAYER7); + + } + } + break; + + case PresObjKind::Title: + { + pOutl->Init( OutlinerMode::TitleObject ); + aString += rString; + } + break; + + default: + { + pOutl->Init( OutlinerMode::TextObject ); + aString += rString; + + // check if we need to add a text field + std::unique_ptr<SvxFieldData> pData; + + switch( eObjKind ) + { + case PresObjKind::Header: + pData.reset(new SvxHeaderField()); + break; + case PresObjKind::Footer: + pData .reset(new SvxFooterField()); + break; + case PresObjKind::SlideNumber: + pData.reset(new SvxPageField()); + break; + case PresObjKind::DateTime: + pData.reset(new SvxDateTimeField()); + break; + default: + break; + } + + if( pData ) + { + ESelection e; + SvxFieldItem aField( *pData, EE_FEATURE_FIELD ); + pOutl->QuickInsertField(aField,e); + } + } + break; + } + + pOutl->SetPaperSize( pObj->GetLogicRect().GetSize() ); + + if( !aString.isEmpty() ) + pOutl->SetText( aString, pOutl->GetParagraph( 0 ) ); + + pObj->SetOutlinerParaObject( pOutl->CreateParaObject() ); + + if (!pOutliner) + { + delete pOutl; + pOutl = nullptr; + } + else + { + // restore the outliner + pOutl->Init( nOutlMode ); + pOutl->SetParaAttribs( 0, pOutl->GetEmptyItemSet() ); + pOutl->SetUpdateLayout( bUpdateMode ); + pOutl->SetPaperSize( aPaperSize ); + } +} + +/************************************************************************* +|* +|* Set the name of the layout +|* +\************************************************************************/ +void SdPage::SetLayoutName(const OUString& aName) +{ + maLayoutName = aName; + + if( mbMaster ) + { + sal_Int32 nPos = maLayoutName.indexOf(SD_LT_SEPARATOR); + if (nPos != -1) + FmFormPage::SetName(maLayoutName.copy(0, nPos)); + } +} + +/************************************************************************* +|* +|* Return the page name and generates it if necessary +|* +\************************************************************************/ + +const OUString& SdPage::GetName() const +{ + OUString aCreatedPageName( maCreatedPageName ); + if (GetRealName().isEmpty()) + { + if ((mePageKind == PageKind::Standard || mePageKind == PageKind::Notes) && !mbMaster) + { + // default name for handout pages + sal_uInt16 nNum = (GetPageNum() + 1) / 2; + + aCreatedPageName = SdResId(STR_PAGE) + " "; + if (static_cast<SdDrawDocument&>(getSdrModelFromSdrPage()).GetDocumentType() == DocumentType::Draw ) + aCreatedPageName = SdResId(STR_PAGE_NAME) + " "; + + if( getSdrModelFromSdrPage().GetPageNumType() == css::style::NumberingType::NUMBER_NONE ) + { + // if the document has number none as a formatting + // for page numbers we still default to arabic numbering + // to keep the default page names unique + aCreatedPageName += OUString::number( static_cast<sal_Int32>(nNum) ); + } + else + { + aCreatedPageName += static_cast< SdDrawDocument& >(getSdrModelFromSdrPage()).CreatePageNumValue(nNum); + } + } + else + { + /****************************************************************** + * default name for note pages + ******************************************************************/ + aCreatedPageName = SdResId(STR_LAYOUT_DEFAULT_NAME); + } + } + else + { + aCreatedPageName = GetRealName(); + } + + if (mePageKind == PageKind::Notes) + { + aCreatedPageName += " " + SdResId(STR_NOTES); + } + else if (mePageKind == PageKind::Handout && mbMaster) + { + aCreatedPageName += " (" + SdResId(STR_HANDOUT) + ")"; + } + + const_cast< SdPage* >(this)->maCreatedPageName = aCreatedPageName; + return maCreatedPageName; +} + +void SdPage::SetOrientation( Orientation /*eOrient*/) +{ + // Do nothing +} + +Orientation SdPage::GetOrientation() const +{ + Size aSize = GetSize(); + if ( aSize.getWidth() > aSize.getHeight() ) + { + return Orientation::Landscape; + } + else + { + return Orientation::Portrait; + } +} + +/************************************************************************* +|* +|* returns the default text of a PresObjektes +|* +\************************************************************************/ + +OUString SdPage::GetPresObjText(PresObjKind eObjKind) const +{ + OUString aString; + +#if defined(IOS) || defined(ANDROID) + bool isMobileDevice = true; +#else + bool isMobileDevice = false; + if (const SfxViewShell* pCurrentViewShell = SfxViewShell::Current()) + isMobileDevice = pCurrentViewShell->isLOKMobilePhone() || pCurrentViewShell->isLOKTablet(); +#endif + + if (eObjKind == PresObjKind::Title) + { + if (mbMaster) + { + if (mePageKind != PageKind::Notes) + { + if (isMobileDevice) + aString = SdResId(STR_PRESOBJ_MPTITLE_MOBILE); + else + aString = SdResId(STR_PRESOBJ_MPTITLE); + } + else + { + if (isMobileDevice) + aString = SdResId(STR_PRESOBJ_MPNOTESTITLE_MOBILE); + else + aString = SdResId(STR_PRESOBJ_MPNOTESTITLE); + } + } + else if (isMobileDevice) + aString = SdResId(STR_PRESOBJ_TITLE_MOBILE); + else + aString = SdResId(STR_PRESOBJ_TITLE); + } + else if (eObjKind == PresObjKind::Outline) + { + if (mbMaster) + { + if (isMobileDevice) + aString = SdResId(STR_PRESOBJ_MPOUTLINE_MOBILE); + else + aString = SdResId(STR_PRESOBJ_MPOUTLINE); + } + else if (isMobileDevice) + aString = SdResId(STR_PRESOBJ_OUTLINE_MOBILE); + else + aString = SdResId(STR_PRESOBJ_OUTLINE); + } + else if (eObjKind == PresObjKind::Notes) + { + if (mbMaster) + { + if (isMobileDevice) + aString = SdResId(STR_PRESOBJ_MPNOTESTEXT_MOBILE); + else + aString = SdResId(STR_PRESOBJ_MPNOTESTEXT); + } + else if (isMobileDevice) + aString = SdResId(STR_PRESOBJ_NOTESTEXT_MOBILE); + else + aString = SdResId(STR_PRESOBJ_NOTESTEXT); + } + else if (eObjKind == PresObjKind::Text) + { + if (isMobileDevice) + aString = SdResId(STR_PRESOBJ_TEXT_MOBILE); + else + aString = SdResId(STR_PRESOBJ_TEXT); + } + else if (eObjKind == PresObjKind::Graphic) + { + aString = SdResId( STR_PRESOBJ_GRAPHIC ); + } + else if (eObjKind == PresObjKind::Object) + { + aString = SdResId( STR_PRESOBJ_OBJECT ); + } + else if (eObjKind == PresObjKind::Chart) + { + aString = SdResId( STR_PRESOBJ_CHART ); + } + else if (eObjKind == PresObjKind::OrgChart) + { + aString = SdResId( STR_PRESOBJ_ORGCHART ); + } + else if (eObjKind == PresObjKind::Calc) + { + aString = SdResId( STR_PRESOBJ_TABLE ); + } + + return aString; +} + +uno::Reference< uno::XInterface > SdPage::createUnoPage() +{ + return createUnoPageImpl( this ); +} + +/** returns the SdPage implementation for the given XDrawPage or 0 if not available */ +SdPage* SdPage::getImplementation( const css::uno::Reference< css::drawing::XDrawPage >& xPage ) +{ + try + { + auto pUnoPage = comphelper::getFromUnoTunnel<SvxDrawPage>(xPage); + if( pUnoPage ) + return static_cast< SdPage* >( pUnoPage->GetSdrPage() ); + } + catch( css::uno::Exception& ) + { + TOOLS_WARN_EXCEPTION( "sd", "sd::SdPage::getImplementation()" ); + } + + return nullptr; +} + +sal_Int64 SdPage::GetHashCode() const +{ + return sal::static_int_cast<sal_Int64>(reinterpret_cast<sal_IntPtr>(this)); +} + +void SdPage::SetName (const OUString& rName) +{ + OUString aOldName( GetName() ); + FmFormPage::SetName (rName); + static_cast< SdDrawDocument& >(getSdrModelFromSdrPage()).UpdatePageRelativeURLs(aOldName, rName); + ActionChanged(); +} + +const HeaderFooterSettings& SdPage::getHeaderFooterSettings() const +{ + if( mePageKind == PageKind::Handout && !mbMaster ) + { + return static_cast<SdPage&>(TRG_GetMasterPage()).maHeaderFooterSettings; + } + else + { + return maHeaderFooterSettings; + } +} + +void SdPage::setHeaderFooterSettings( const sd::HeaderFooterSettings& rNewSettings ) +{ + if( mePageKind == PageKind::Handout && !mbMaster ) + { + static_cast<SdPage&>(TRG_GetMasterPage()).maHeaderFooterSettings = rNewSettings; + } + else + { + maHeaderFooterSettings = rNewSettings; + } + + SetChanged(); + + if(!TRG_HasMasterPage()) + return; + + TRG_GetMasterPageDescriptorViewContact().ActionChanged(); + + // #i119056# For HeaderFooterSettings SdrObjects are used, but the properties + // used are not part of their model data, but kept in SD. This data is applied + // using a 'backdoor' on primitive creation. Thus, the normal mechanism to detect + // object changes does not work here. It is necessary to trigger updates here + // directly. BroadcastObjectChange used for PagePreview invalidations, + // flushViewObjectContacts used to invalidate and flush all visualizations in + // edit views. + SdPage* pMasterPage = dynamic_cast< SdPage* >(&TRG_GetMasterPage()); + + if(!pMasterPage) + return; + + SdrObject* pCandidate = pMasterPage->GetPresObj( PresObjKind::Header ); + + if(pCandidate) + { + pCandidate->BroadcastObjectChange(); + pCandidate->GetViewContact().flushViewObjectContacts(); + } + + pCandidate = pMasterPage->GetPresObj( PresObjKind::DateTime ); + + if(pCandidate) + { + pCandidate->BroadcastObjectChange(); + pCandidate->GetViewContact().flushViewObjectContacts(); + } + + pCandidate = pMasterPage->GetPresObj( PresObjKind::Footer ); + + if(pCandidate) + { + pCandidate->BroadcastObjectChange(); + pCandidate->GetViewContact().flushViewObjectContacts(); + } + + pCandidate = pMasterPage->GetPresObj( PresObjKind::SlideNumber ); + + if(pCandidate) + { + pCandidate->BroadcastObjectChange(); + pCandidate->GetViewContact().flushViewObjectContacts(); + } +} + +bool SdPage::checkVisibility( + const sdr::contact::ViewObjectContact& rOriginal, + const sdr::contact::DisplayInfo& rDisplayInfo, + bool bEdit ) +{ + if( !FmFormPage::checkVisibility( rOriginal, rDisplayInfo, bEdit ) ) + return false; + + SdrObject* pObj = rOriginal.GetViewContact().TryToGetSdrObject(); + if( pObj == nullptr ) + return false; + + const SdrPage* pVisualizedPage = GetSdrPageFromXDrawPage(rOriginal.GetObjectContact().getViewInformation2D().getVisualizedPage()); + const bool bIsPrinting(rOriginal.GetObjectContact().isOutputToPrinter() || rOriginal.GetObjectContact().isOutputToPDFFile()); + const SdrPageView* pPageView = rOriginal.GetObjectContact().TryToGetSdrPageView(); + const bool bIsInsidePageObj(pPageView && pPageView->GetPage() != pVisualizedPage); + + // empty presentation objects only visible during edit mode + if( (bIsPrinting || !bEdit || bIsInsidePageObj ) && pObj->IsEmptyPresObj() ) + { + if( (pObj->GetObjInventor() != SdrInventor::Default) || ( (pObj->GetObjIdentifier() != SdrObjKind::Rectangle) && (pObj->GetObjIdentifier() != SdrObjKind::Page) ) ) + return false; + } + + if( ( pObj->GetObjInventor() == SdrInventor::Default ) && ( pObj->GetObjIdentifier() == SdrObjKind::Text ) ) + { + const SdPage* pCheckPage = dynamic_cast< const SdPage* >(pObj->getSdrPageFromSdrObject()); + + if( pCheckPage ) + { + PresObjKind eKind = pCheckPage->GetPresObjKind(pObj); + + if((eKind == PresObjKind::Footer) || (eKind == PresObjKind::Header) || (eKind == PresObjKind::DateTime) || (eKind == PresObjKind::SlideNumber) ) + { + const bool bSubContentProcessing(rDisplayInfo.GetSubContentActive()); + + if( bSubContentProcessing || ( pCheckPage->GetPageKind() == PageKind::Handout && bIsPrinting ) ) + { + // use the page that is currently processed + const SdPage* pVisualizedSdPage = dynamic_cast< const SdPage* >(pVisualizedPage); + + if( pVisualizedSdPage ) + { + // if we are not on a masterpage, see if we have to draw this header&footer object at all + const sd::HeaderFooterSettings& rSettings = pVisualizedSdPage->getHeaderFooterSettings(); + + switch( eKind ) + { + case PresObjKind::Footer: + return rSettings.mbFooterVisible; + case PresObjKind::Header: + return rSettings.mbHeaderVisible; + case PresObjKind::DateTime: + return rSettings.mbDateTimeVisible; + case PresObjKind::SlideNumber: + return rSettings.mbSlideNumberVisible; + default: + break; + } + } + } + } // check for placeholders on master + else if( (eKind != PresObjKind::NONE) && pCheckPage->IsMasterPage() && ( pVisualizedPage != pCheckPage ) ) + { + // presentation objects on master slide are always invisible if slide is shown. + return false; + } + } + } + + // i63977, do not print SdrpageObjs from master pages + if( ( pObj->GetObjInventor() == SdrInventor::Default ) && ( pObj->GetObjIdentifier() == SdrObjKind::Page ) ) + { + if( pObj->getSdrPageFromSdrObject() && pObj->getSdrPageFromSdrObject()->IsMasterPage() ) + return false; + } + + return true; +} + +bool SdPage::RestoreDefaultText( SdrObject* pObj ) +{ + bool bRet = false; + + SdrTextObj* pTextObj = dynamic_cast< SdrTextObj* >( pObj ); + + if( pTextObj ) + { + PresObjKind ePresObjKind = GetPresObjKind(pTextObj); + + if (ePresObjKind == PresObjKind::Title || + ePresObjKind == PresObjKind::Outline || + ePresObjKind == PresObjKind::Notes || + ePresObjKind == PresObjKind::Text) + { + OUString aString( GetPresObjText(ePresObjKind) ); + + if (!aString.isEmpty()) + { + bool bVertical = false; + OutlinerParaObject* pOldPara = pTextObj->GetOutlinerParaObject(); + if( pOldPara ) + bVertical = pOldPara->IsEffectivelyVertical(); // is old para object vertical? + + SetObjText( pTextObj, nullptr, ePresObjKind, aString ); + + if( pOldPara ) + { + // Here, only the vertical flag for the + // OutlinerParaObjects needs to be changed. The + // AutoGrowWidth/Height items still exist in the + // not changed object. + if(pTextObj->GetOutlinerParaObject() + && pTextObj->GetOutlinerParaObject()->IsEffectivelyVertical() != bVertical) + { + ::tools::Rectangle aObjectRect = pTextObj->GetSnapRect(); + pTextObj->GetOutlinerParaObject()->SetVertical(bVertical); + pTextObj->SetSnapRect(aObjectRect); + } + } + + pTextObj->SetTextEditOutliner( nullptr ); // to make stylesheet settings work + pTextObj->NbcSetStyleSheet( GetStyleSheetForPresObj(ePresObjKind), true ); + pTextObj->SetEmptyPresObj(true); + bRet = true; + } + } + } + return bRet; +} + +void SdPage::CalculateHandoutAreas( SdDrawDocument& rModel, AutoLayout eLayout, bool bHorizontal, std::vector< ::tools::Rectangle >& rAreas ) +{ + SdPage& rHandoutMaster = *rModel.GetMasterSdPage( 0, PageKind::Handout ); + + static const sal_uInt16 aOffsets[5][9] = + { + { 0, 1, 2, 3, 4, 5, 6, 7, 8 }, // AUTOLAYOUT_HANDOUT9, Portrait, Horizontal order + { 0, 2, 4, 1, 3, 5, 0, 0, 0 }, // AUTOLAYOUT_HANDOUT3, Landscape, Vertical + { 0, 2, 1, 3, 0, 0, 0, 0, 0 }, // AUTOLAYOUT_HANDOUT4, Landscape, Vertical + { 0, 3, 1, 4, 2, 5, 0, 0, 0 }, // AUTOLAYOUT_HANDOUT4, Portrait, Vertical + { 0, 3, 6, 1, 4, 7, 2, 5, 8 }, // AUTOLAYOUT_HANDOUT9, Landscape, Vertical + }; + + const sal_uInt16* pOffsets = aOffsets[0]; + + Size aArea = rHandoutMaster.GetSize(); + const bool bLandscape = aArea.Width() > aArea.Height(); + + if( eLayout == AUTOLAYOUT_NONE ) + { + // use layout from handout master + SdrObjListIter aShapeIter(&rHandoutMaster); + + std::vector< ::tools::Rectangle > vSlidesAreas; + while ( aShapeIter.IsMore() ) + { + SdrPageObj* pPageObj = dynamic_cast<SdrPageObj*>( aShapeIter.Next() ); + // get slide rectangles + if (pPageObj) + vSlidesAreas.push_back( pPageObj->GetCurrentBoundRect() ); + } + + if ( !bHorizontal || vSlidesAreas.size() < 4 ) + { // top to bottom, then right + rAreas.swap( vSlidesAreas ); + } + else + { // left to right, then down + switch ( vSlidesAreas.size() ) + { + case 4: + pOffsets = aOffsets[2]; + break; + + default: + [[fallthrough]]; + case 6: + pOffsets = aOffsets[ bLandscape ? 3 : 1 ]; + break; + + case 9: + pOffsets = aOffsets[4]; + break; + } + + rAreas.resize( static_cast<size_t>(vSlidesAreas.size()) ); + + for( const ::tools::Rectangle& rRect : vSlidesAreas ) + { + rAreas[*pOffsets++] = rRect; + } + } + } + else + { + const ::tools::Long nGapW = 1000; // gap is 1cm + const ::tools::Long nGapH = 1000; + + ::tools::Long nLeftBorder = rHandoutMaster.GetLeftBorder(); + ::tools::Long nRightBorder = rHandoutMaster.GetRightBorder(); + ::tools::Long nTopBorder = rHandoutMaster.GetUpperBorder(); + ::tools::Long nBottomBorder = rHandoutMaster.GetLowerBorder(); + + const ::tools::Long nHeaderFooterHeight = static_cast< ::tools::Long >( (aArea.Height() - nTopBorder - nLeftBorder) * 0.05 ); + + nTopBorder += nHeaderFooterHeight; + nBottomBorder += nHeaderFooterHeight; + + ::tools::Long nX = nGapW + nLeftBorder; + ::tools::Long nY = nGapH + nTopBorder; + + aArea.AdjustWidth( -(nGapW * 2 + nLeftBorder + nRightBorder) ); + aArea.AdjustHeight( -(nGapH * 2 + nTopBorder + nBottomBorder) ); + + sal_uInt16 nColCnt = 0, nRowCnt = 0; + switch ( eLayout ) + { + case AUTOLAYOUT_HANDOUT1: + nColCnt = 1; nRowCnt = 1; + break; + + case AUTOLAYOUT_HANDOUT2: + if( bLandscape ) + { + nColCnt = 2; nRowCnt = 1; + } + else + { + nColCnt = 1; nRowCnt = 2; + } + break; + + case AUTOLAYOUT_HANDOUT3: + if( bLandscape ) + { + nColCnt = 3; nRowCnt = 2; + } + else + { + nColCnt = 2; nRowCnt = 3; + } + pOffsets = aOffsets[ bLandscape ? 1 : 0 ]; + break; + + case AUTOLAYOUT_HANDOUT4: + nColCnt = 2; nRowCnt = 2; + pOffsets = aOffsets[ bHorizontal ? 0 : 2 ]; + break; + + case AUTOLAYOUT_HANDOUT6: + if( bLandscape ) + { + nColCnt = 3; nRowCnt = 2; + } + else + { + nColCnt = 2; nRowCnt = 3; + } + if( !bHorizontal ) + pOffsets = aOffsets[ bLandscape ? 1 : 3 ]; + break; + + default: + case AUTOLAYOUT_HANDOUT9: + nColCnt = 3; nRowCnt = 3; + + if( !bHorizontal ) + pOffsets = aOffsets[4]; + break; + } + + rAreas.resize(static_cast<size_t>(nColCnt) * nRowCnt); + + Size aPartArea, aSize; + aPartArea.setWidth( (aArea.Width() - ((nColCnt-1) * nGapW) ) / nColCnt ); + aPartArea.setHeight( (aArea.Height() - ((nRowCnt-1) * nGapH) ) / nRowCnt ); + + SdrPage* pFirstPage = rModel.GetMasterSdPage(0, PageKind::Standard); + if (pFirstPage && pFirstPage->GetWidth() && pFirstPage->GetHeight()) + { + // scale actual size into handout rect + double fScale = static_cast<double>(aPartArea.Width()) / static_cast<double>(pFirstPage->GetWidth()); + + aSize.setHeight( static_cast<::tools::Long>(fScale * pFirstPage->GetHeight() ) ); + if( aSize.Height() > aPartArea.Height() ) + { + fScale = static_cast<double>(aPartArea.Height()) / static_cast<double>(pFirstPage->GetHeight()); + aSize.setHeight( aPartArea.Height() ); + aSize.setWidth( static_cast<::tools::Long>(fScale * pFirstPage->GetWidth()) ); + } + else + { + aSize.setWidth( aPartArea.Width() ); + } + + nX += (aPartArea.Width() - aSize.Width()) / 2; + nY += (aPartArea.Height()- aSize.Height())/ 2; + } + else + { + aSize = aPartArea; + } + + Point aPos( nX, nY ); + + const bool bRTL = rModel.GetDefaultWritingMode() == css::text::WritingMode_RL_TB; + + const ::tools::Long nOffsetX = (aPartArea.Width() + nGapW) * (bRTL ? -1 : 1); + const ::tools::Long nOffsetY = aPartArea.Height() + nGapH; + const ::tools::Long nStartX = bRTL ? nOffsetX*(1 - nColCnt) + nX : nX; + + for(sal_uInt16 nRow = 0; nRow < nRowCnt; nRow++) + { + aPos.setX( nStartX ); + for(sal_uInt16 nCol = 0; nCol < nColCnt; nCol++) + { + rAreas[*pOffsets++] = ::tools::Rectangle(aPos, aSize); + aPos.AdjustX(nOffsetX ); + } + + aPos.AdjustY(nOffsetY ); + } + } +} + +void SdPage::SetPrecious (const bool bIsPrecious) +{ + mbIsPrecious = bIsPrecious; +} + +HeaderFooterSettings::HeaderFooterSettings() +{ + mbHeaderVisible = true; + mbFooterVisible = true; + mbSlideNumberVisible = false; + mbDateTimeVisible = true; + mbDateTimeIsFixed = true; + meDateFormat = SvxDateFormat::A; + meTimeFormat = SvxTimeFormat::AppDefault; +} + +bool HeaderFooterSettings::operator==( const HeaderFooterSettings& rSettings ) const +{ + return (mbHeaderVisible == rSettings.mbHeaderVisible) && + (maHeaderText == rSettings.maHeaderText) && + (mbFooterVisible == rSettings.mbFooterVisible) && + (maFooterText == rSettings.maFooterText) && + (mbSlideNumberVisible == rSettings.mbSlideNumberVisible) && + (mbDateTimeVisible == rSettings.mbDateTimeVisible) && + (mbDateTimeIsFixed == rSettings.mbDateTimeIsFixed) && + (meDateFormat == rSettings.meDateFormat) && + (meTimeFormat == rSettings.meTimeFormat) && + (maDateTimeText == rSettings.maDateTimeText); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/core/sdpage2.cxx b/sd/source/core/sdpage2.cxx new file mode 100644 index 000000000..a5c8d1d5f --- /dev/null +++ b/sd/source/core/sdpage2.cxx @@ -0,0 +1,651 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include <vector> +#include <libxml/xmlwriter.h> +#include <sfx2/docfile.hxx> +#include <sfx2/linkmgr.hxx> +#include <svx/svdundo.hxx> +#include <editeng/outlobj.hxx> +#include <editeng/xmlcnitm.hxx> +#include <svx/svditer.hxx> +#include <com/sun/star/text/XTextCopy.hpp> +#include <tools/debug.hxx> +#include <svx/svddef.hxx> +#include <rtl/math.hxx> +#include <svx/svdograf.hxx> + +#include <Annotation.hxx> +#include <notifydocumentevent.hxx> +#include <sdresid.hxx> +#include <sdpage.hxx> +#include <glob.hxx> +#include <strings.hrc> +#include <drawdoc.hxx> +#include <stlpool.hxx> +#include <pglink.hxx> + +#include <strings.hxx> +#include <DrawDocShell.hxx> + +#include <svl/itemset.hxx> + +using namespace ::sd; +using namespace ::com::sun::star; +using namespace ::com::sun::star::uno; +using namespace ::com::sun::star::office; + +/************************************************************************* +|* +|* Sets: names of layout, master page links and templates for presentation +|* objects +|* +|* Preconditions: - The page has to know the correct model! +|* - The corresponding master page has to be in the model. +|* - The corresponding style sheets have to be in the style sheet +|* pool. +|* +|* bReplaceStyleSheets = sal_True : Named style sheets are replaced +|* sal_False: All style sheets are reassigned +|* +|* bSetMasterPage = sal_True : search and assign master page +|* +|* bReverseOrder = sal_False: search master page from head to tail +|* sal_True : search master page from tail to head +|* (for undo operations) +|* +\************************************************************************/ + +void SdPage::SetPresentationLayout(std::u16string_view rLayoutName, + bool bReplaceStyleSheets, + bool bSetMasterPage, + bool bReverseOrder) +{ + /********************************************************************* + |* Name of the layout of the page + \********************************************************************/ + OUString aOldLayoutName(maLayoutName); // memorize + maLayoutName = OUString::Concat(rLayoutName) + SD_LT_SEPARATOR + STR_LAYOUT_OUTLINE; + + /********************************************************************* + |* search and replace master page if necessary + \********************************************************************/ + if (bSetMasterPage && !IsMasterPage()) + { + SdPage* pMaster; + SdPage* pFoundMaster = nullptr; + sal_uInt16 nMaster = 0; + sal_uInt16 nMasterCount = getSdrModelFromSdrPage().GetMasterPageCount(); + + if( !bReverseOrder ) + { + for ( nMaster = 0; nMaster < nMasterCount; nMaster++ ) + { + pMaster = static_cast<SdPage*>(getSdrModelFromSdrPage().GetMasterPage(nMaster)); + if (pMaster->GetPageKind() == mePageKind && pMaster->GetLayoutName() == maLayoutName) + { + pFoundMaster = pMaster; + break; + } + } + } + else + { + for ( nMaster = nMasterCount; nMaster > 0; nMaster-- ) + { + pMaster = static_cast<SdPage*>(getSdrModelFromSdrPage().GetMasterPage(nMaster - 1)); + if (pMaster->GetPageKind() == mePageKind && pMaster->GetLayoutName() == maLayoutName) + { + pFoundMaster = pMaster; + break; + } + } + } + + DBG_ASSERT(pFoundMaster, "Masterpage for presentation layout not found!"); + + // this should never happen, but we play failsafe here + if( pFoundMaster == nullptr ) + pFoundMaster = static_cast< SdDrawDocument& >(getSdrModelFromSdrPage()).GetSdPage( 0, mePageKind ); + + if( pFoundMaster ) + TRG_SetMasterPage(*pFoundMaster); + } + + /********************************************************************* + |* templates for presentation objects + \********************************************************************/ + // list with: + // - pointer to templates for outline text object (old and new templates) + // - replace-data for OutlinerParaObject + std::vector<SfxStyleSheetBase*> aOutlineStyles; + std::vector<SfxStyleSheetBase*> aOldOutlineStyles; + std::vector<StyleReplaceData> aReplList; + bool bListsFilled = false; + + const size_t nObjCount = GetObjCount(); + + for (size_t nObj = 0; nObj < nObjCount; ++nObj) + { + auto pObj = GetObj(nObj); + + if (pObj->GetObjInventor() == SdrInventor::Default && + pObj->GetObjIdentifier() == SdrObjKind::OutlineText) + { + if (!bListsFilled || !bReplaceStyleSheets) + { + OUString aFullName; + OUString aOldFullName; + SfxStyleSheetBase* pSheet = nullptr; + SfxStyleSheetBasePool* pStShPool = getSdrModelFromSdrPage().GetStyleSheetPool(); + + for (sal_Int16 i = -1; i < 9; i++) + { + aOldFullName = aOldLayoutName + " " + + OUString::number( (i <= 0 ) ? 1 : i + 1 ); + aFullName = maLayoutName + " " + + OUString::number( (i <= 0 ) ? 1 : i + 1); + pSheet = pStShPool->Find(aOldFullName, SfxStyleFamily::Page); + DBG_ASSERT(pSheet, "Old outline style sheet not found"); + aOldOutlineStyles.push_back(pSheet); + + pSheet = pStShPool->Find(aFullName, SfxStyleFamily::Page); + DBG_ASSERT(pSheet, "New outline style sheet not found"); + aOutlineStyles.push_back(pSheet); + + if (bReplaceStyleSheets && pSheet) + { + // Replace instead Set + StyleReplaceData aReplData; + aReplData.nNewFamily = pSheet->GetFamily(); + aReplData.nFamily = pSheet->GetFamily(); + aReplData.aNewName = aFullName; + aReplData.aName = aOldFullName; + aReplList.push_back(aReplData); + } + else + { + OutlinerParaObject* pOPO = pObj->GetOutlinerParaObject(); + + if( pOPO ) + pOPO->SetStyleSheets( i, aFullName, SfxStyleFamily::Page ); + } + } + + bListsFilled = true; + } + + + std::vector<SfxStyleSheetBase*>::iterator iterOldOut = aOldOutlineStyles.begin(); + + for (const auto& rpOut : aOutlineStyles) + { + SfxStyleSheet* pSheet = static_cast<SfxStyleSheet*>(rpOut); + SfxStyleSheet* pOldSheet = static_cast<SfxStyleSheet*>(*iterOldOut); + + if (pSheet != pOldSheet) + { + if (pOldSheet) + pObj->EndListening(*pOldSheet); + + if (pSheet && !pObj->IsListening(*pSheet)) + pObj->StartListening(*pSheet); + } + + ++iterOldOut; + } + + OutlinerParaObject* pOPO = pObj->GetOutlinerParaObject(); + if ( bReplaceStyleSheets && pOPO ) + { + for (const auto& rRepl : aReplList) + { + pOPO->ChangeStyleSheets( rRepl.aName, rRepl.nFamily, rRepl.aNewName, rRepl.nNewFamily ); + } + } + } + else if (pObj->GetObjInventor() == SdrInventor::Default && + pObj->GetObjIdentifier() == SdrObjKind::TitleText) + { + // We do not get PresObjKind via GetPresObjKind() since there are + // only PresObjListe considered. But we want to consider all "Title + // objects" here (paste from clipboard etc.) + SfxStyleSheet* pSheet = GetStyleSheetForPresObj(PresObjKind::Title); + + if (pSheet) + pObj->SetStyleSheet(pSheet, true); + } + else + { + SfxStyleSheet* pSheet = GetStyleSheetForPresObj(GetPresObjKind(pObj)); + + if (pSheet) + pObj->SetStyleSheet(pSheet, true); + } + } +} + +/************************************************************************* +|* +|* disconnect outline text object from templates for outline levels +|* +\************************************************************************/ + +void SdPage::EndListenOutlineText() +{ + SdrObject* pOutlineTextObj = GetPresObj(PresObjKind::Outline); + + if (!pOutlineTextObj) + return; + + SdStyleSheetPool* pSPool = static_cast<SdStyleSheetPool*>(getSdrModelFromSdrPage().GetStyleSheetPool()); + DBG_ASSERT(pSPool, "StyleSheetPool missing"); + OUString aTrueLayoutName(maLayoutName); + sal_Int32 nIndex = aTrueLayoutName.indexOf( SD_LT_SEPARATOR ); + if( nIndex != -1 ) + aTrueLayoutName = aTrueLayoutName.copy(0, nIndex); + + std::vector<SfxStyleSheetBase*> aOutlineStyles; + pSPool->CreateOutlineSheetList(aTrueLayoutName,aOutlineStyles); + + for (const auto& rpStyle : aOutlineStyles) + { + SfxStyleSheet *pSheet = static_cast<SfxStyleSheet*>(rpStyle); + pOutlineTextObj->EndListening(*pSheet); + } +} + +/************************************************************************* +|* +|* Is this page read-only? +|* +\************************************************************************/ + +bool SdPage::IsReadOnly() const +{ + return false; +} + +/************************************************************************* +|* +|* Connect to sfx2::LinkManager +|* +\************************************************************************/ + +void SdPage::ConnectLink() +{ + sfx2::LinkManager* pLinkManager(getSdrModelFromSdrPage().GetLinkManager()); + + if (!(pLinkManager && !mpPageLink && !maFileName.isEmpty() && !maBookmarkName.isEmpty() && + mePageKind==PageKind::Standard && !IsMasterPage() && + static_cast< SdDrawDocument& >(getSdrModelFromSdrPage()).IsNewOrLoadCompleted())) + return; + + /********************************************************************** + * Connect + * Only standard pages are allowed to be linked + **********************************************************************/ + ::sd::DrawDocShell* pDocSh = static_cast< SdDrawDocument& >(getSdrModelFromSdrPage()).GetDocSh(); + + if (!pDocSh || pDocSh->GetMedium()->GetOrigURL() != maFileName) + { + // No links to document owned pages! + mpPageLink = new SdPageLink(this, maFileName, maBookmarkName); + OUString aFilterName(SdResId(STR_IMPRESS)); + pLinkManager->InsertFileLink(*mpPageLink, sfx2::SvBaseLinkObjectType::ClientFile, + maFileName, &aFilterName, &maBookmarkName); + mpPageLink->Connect(); + } +} + +/************************************************************************* +|* +|* Disconnect from sfx2::LinkManager +|* +\************************************************************************/ + +void SdPage::DisconnectLink() +{ + sfx2::LinkManager* pLinkManager(getSdrModelFromSdrPage().GetLinkManager()); + + if (pLinkManager && mpPageLink) + { + /********************************************************************** + * Disconnect + * (remove deletes *pGraphicLink implicit) + **********************************************************************/ + pLinkManager->Remove(mpPageLink); + mpPageLink=nullptr; + } +} + +void SdPage::lateInit(const SdPage& rSrcPage) +{ + // call parent + FmFormPage::lateInit(rSrcPage); + + // copy local variables (former stuff from copy constructor) + mePageKind = rSrcPage.mePageKind; + meAutoLayout = rSrcPage.meAutoLayout; + mbSelected = false; + mnTransitionType = rSrcPage.mnTransitionType; + mnTransitionSubtype = rSrcPage.mnTransitionSubtype; + mbTransitionDirection = rSrcPage.mbTransitionDirection; + mnTransitionFadeColor = rSrcPage.mnTransitionFadeColor; + mfTransitionDuration = rSrcPage.mfTransitionDuration; + mePresChange = rSrcPage.mePresChange; + mfTime = rSrcPage.mfTime; + mbSoundOn = rSrcPage.mbSoundOn; + mbExcluded = rSrcPage.mbExcluded; + maLayoutName = rSrcPage.maLayoutName; + maSoundFile = rSrcPage.maSoundFile; + mbLoopSound = rSrcPage.mbLoopSound; + mbStopSound = rSrcPage.mbStopSound; + maCreatedPageName.clear(); + maFileName = rSrcPage.maFileName; + maBookmarkName = rSrcPage.maBookmarkName; + mbScaleObjects = rSrcPage.mbScaleObjects; + meCharSet = rSrcPage.meCharSet; + mnPaperBin = rSrcPage.mnPaperBin; + mpPageLink = nullptr; // is set when inserting via ConnectLink() + mbIsPrecious = false; + + // use shape list directly to preserve constness of rSrcPage + const std::list< SdrObject* >& rShapeList = rSrcPage.maPresentationShapeList.getList(); + const size_t nObjCount = GetObjCount(); + for( SdrObject* pObj : rShapeList ) + { + size_t nOrdNum = pObj->GetOrdNum(); + InsertPresObj(nOrdNum < nObjCount ? GetObj(nOrdNum) : nullptr, rSrcPage.GetPresObjKind(pObj)); + } + + // header footer + setHeaderFooterSettings( rSrcPage.getHeaderFooterSettings() ); + + // animations + rSrcPage.cloneAnimations(*this); + + // annotations + for(const Reference< XAnnotation >& srcAnnotation : rSrcPage.maAnnotations) + { + Reference< XAnnotation > ref; + createAnnotation(ref); + ref->setPosition(srcAnnotation->getPosition()); + ref->setSize(srcAnnotation->getSize()); + ref->setAuthor(srcAnnotation->getAuthor()); + ref->setInitials(srcAnnotation->getInitials()); + ref->setDateTime(srcAnnotation->getDateTime()); + Reference< ::css::text::XTextCopy > srcRange ( srcAnnotation->getTextRange(), uno::UNO_QUERY); + Reference< ::css::text::XTextCopy > range ( ref->getTextRange(), uno::UNO_QUERY); + if(srcRange.is() && range.is()) + range->copyText( srcRange ); + } + + // fix user calls for duplicated slide + SdrObjListIter aSourceIter( &rSrcPage, SdrIterMode::DeepWithGroups ); + SdrObjListIter aTargetIter( this, SdrIterMode::DeepWithGroups ); + + while( aSourceIter.IsMore() && aTargetIter.IsMore() ) + { + SdrObject* pSource = aSourceIter.Next(); + SdrObject* pTarget = aTargetIter.Next(); + + if( pSource->GetUserCall() ) + pTarget->SetUserCall(this); + } +} + +/************************************************************************* +|* +|* Clone +|* +\************************************************************************/ + +rtl::Reference<SdrPage> SdPage::CloneSdrPage(SdrModel& rTargetModel) const +{ + SdDrawDocument& rSdDrawDocument(static_cast< SdDrawDocument& >(rTargetModel)); + rtl::Reference<SdPage> pClonedSdPage( + new SdPage( + rSdDrawDocument, + IsMasterPage())); + pClonedSdPage->lateInit(*this); + return pClonedSdPage; +} + +/************************************************************************* +|* +|* GetTextStyleSheetForObject +|* +\************************************************************************/ + +SfxStyleSheet* SdPage::GetTextStyleSheetForObject( SdrObject* pObj ) const +{ + const PresObjKind eKind = GetPresObjKind(pObj); + if( eKind != PresObjKind::NONE ) + { + return GetStyleSheetForPresObj(eKind); + } + + return FmFormPage::GetTextStyleSheetForObject( pObj ); +} + +SfxItemSet* SdPage::getOrCreateItems() +{ + if( mpItems == nullptr ) + mpItems = std::make_unique<SfxItemSetFixed<SDRATTR_XMLATTRIBUTES, SDRATTR_XMLATTRIBUTES>>( getSdrModelFromSdrPage().GetItemPool()); + + return mpItems.get(); +} + +bool SdPage::setAlienAttributes( const css::uno::Any& rAttributes ) +{ + SfxItemSet* pSet = getOrCreateItems(); + + SvXMLAttrContainerItem aAlienAttributes( SDRATTR_XMLATTRIBUTES ); + if( aAlienAttributes.PutValue( rAttributes, 0 ) ) + { + pSet->Put( aAlienAttributes ); + return true; + } + + return false; +} + +void SdPage::getAlienAttributes( css::uno::Any& rAttributes ) +{ + const SvXMLAttrContainerItem* pItem; + + if( (mpItems == nullptr) || !( pItem = mpItems->GetItemIfSet( SDRATTR_XMLATTRIBUTES, false ) ) ) + { + SvXMLAttrContainerItem aAlienAttributes; + aAlienAttributes.QueryValue( rAttributes ); + } + else + { + pItem->QueryValue( rAttributes ); + } +} + +void SdPage::RemoveEmptyPresentationObjects() +{ + SdrObjListIter aShapeIter( this, SdrIterMode::DeepWithGroups ); + + for (SdrObject* pShape = aShapeIter.Next(); pShape; pShape = aShapeIter.Next()) + { + if (pShape->IsEmptyPresObj()) + { + RemoveObject( pShape->GetOrdNum() ); + SdrObject::Free( pShape ); + } + } +} + +void SdPage::setTransitionType( sal_Int16 nTransitionType ) +{ + mnTransitionType = nTransitionType; + ActionChanged(); +} + +void SdPage::setTransitionSubtype ( sal_Int16 nTransitionSubtype ) +{ + mnTransitionSubtype = nTransitionSubtype; + ActionChanged(); +} + +void SdPage::setTransitionDirection ( bool bTransitionbDirection ) +{ + mbTransitionDirection = bTransitionbDirection; + ActionChanged(); +} + +void SdPage::setTransitionFadeColor ( sal_Int32 nTransitionFadeColor ) +{ + mnTransitionFadeColor = nTransitionFadeColor; + ActionChanged(); +} + +void SdPage::setTransitionDuration ( double fTransitionDuration ) +{ + mfTransitionDuration = fTransitionDuration; + ActionChanged(); +} + +bool SdPage::Equals(const SdPage& rOtherPage) const +{ + if (GetObjCount() != rOtherPage.GetObjCount() || + mePageKind != rOtherPage.mePageKind || + meAutoLayout != rOtherPage.meAutoLayout || + mePresChange != rOtherPage.mePresChange || + !rtl::math::approxEqual(mfTime, rOtherPage.mfTime) || + mbSoundOn != rOtherPage.mbSoundOn || + mbExcluded != rOtherPage.mbExcluded || + maLayoutName != rOtherPage.maLayoutName || + maSoundFile != rOtherPage.maSoundFile || + mbLoopSound != rOtherPage.mbLoopSound || + mbStopSound != rOtherPage.mbStopSound || + maBookmarkName != rOtherPage.maBookmarkName || + mbScaleObjects != rOtherPage.mbScaleObjects || + IsBackgroundFullSize() != rOtherPage.IsBackgroundFullSize() || // ??? + meCharSet != rOtherPage.meCharSet || + mnPaperBin != rOtherPage.mnPaperBin || + mnTransitionType != rOtherPage.mnTransitionType || + mnTransitionSubtype != rOtherPage.mnTransitionSubtype || + mbTransitionDirection != rOtherPage.mbTransitionDirection || + mnTransitionFadeColor != rOtherPage.mnTransitionFadeColor || + !rtl::math::approxEqual(mfTransitionDuration, rOtherPage.mfTransitionDuration)) + return false; + + for(size_t i = 0; i < GetObjCount(); ++i) + if (!GetObj(i)->Equals(*(rOtherPage.GetObj(i)))) + return false; + + return true; + } + +void SdPage::createAnnotation( css::uno::Reference< css::office::XAnnotation >& xAnnotation ) +{ + sd::createAnnotation( xAnnotation, this ); +} + +void SdPage::addAnnotation( const Reference< XAnnotation >& xAnnotation, int nIndex ) +{ + if( (nIndex == -1) || (nIndex > static_cast<int>(maAnnotations.size())) ) + { + maAnnotations.push_back( xAnnotation ); + } + else + { + maAnnotations.insert( maAnnotations.begin() + nIndex, xAnnotation ); + } + + if( getSdrModelFromSdrPage().IsUndoEnabled() ) + { + std::unique_ptr<SdrUndoAction> pAction = CreateUndoInsertOrRemoveAnnotation( xAnnotation, true ); + if( pAction ) + getSdrModelFromSdrPage().AddUndo( std::move(pAction) ); + } + + SetChanged(); + getSdrModelFromSdrPage().SetChanged(); + NotifyDocumentEvent( + static_cast< SdDrawDocument& >(getSdrModelFromSdrPage()), + "OnAnnotationInserted", + Reference<XInterface>(xAnnotation, UNO_QUERY)); +} + +void SdPage::removeAnnotation( const Reference< XAnnotation >& xAnnotation ) +{ + if( getSdrModelFromSdrPage().IsUndoEnabled() ) + { + std::unique_ptr<SdrUndoAction> pAction = CreateUndoInsertOrRemoveAnnotation( xAnnotation, false ); + if( pAction ) + getSdrModelFromSdrPage().AddUndo( std::move(pAction) ); + } + + AnnotationVector::iterator iter = std::find( maAnnotations.begin(), maAnnotations.end(), xAnnotation ); + if( iter != maAnnotations.end() ) + maAnnotations.erase( iter ); + + getSdrModelFromSdrPage().SetChanged(); + NotifyDocumentEvent( + static_cast< SdDrawDocument& >( getSdrModelFromSdrPage() ), + "OnAnnotationRemoved", + Reference<XInterface>( xAnnotation, UNO_QUERY ) ); +} + +void SdPage::getGraphicsForPrefetch(std::vector<Graphic*>& graphics) const +{ + for( size_t i = 0; i < GetObjCount(); ++i) + { + SdrObject* obj = GetObj(i); + if( SdrGrafObj* grafObj = dynamic_cast<SdrGrafObj*>(obj)) + if(!grafObj->GetGraphic().isAvailable()) + graphics.push_back( const_cast<Graphic*>(&grafObj->GetGraphic())); + if( const Graphic* fillGraphic = obj->getFillGraphic()) + if(!fillGraphic->isAvailable()) + graphics.push_back( const_cast<Graphic*>(fillGraphic)); + } +} + +void SdPage::dumpAsXml(xmlTextWriterPtr pWriter) const +{ + (void)xmlTextWriterStartElement(pWriter, BAD_CAST("SdPage")); + + const char* pPageKind = nullptr; + switch (mePageKind) + { + case PageKind::Standard: + pPageKind = "PageKind::Standard"; + break; + case PageKind::Notes: + pPageKind = "PageKind::Notes"; + break; + case PageKind::Handout: + pPageKind = "PageKind::Handout"; + break; + } + if (pPageKind) + (void)xmlTextWriterWriteAttribute(pWriter, BAD_CAST("mePageKind"), BAD_CAST(pPageKind)); + + + FmFormPage::dumpAsXml(pWriter); + (void)xmlTextWriterEndElement(pWriter); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/core/sdpage_animations.cxx b/sd/source/core/sdpage_animations.cxx new file mode 100644 index 000000000..c52938fd8 --- /dev/null +++ b/sd/source/core/sdpage_animations.cxx @@ -0,0 +1,160 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include <com/sun/star/animations/ParallelTimeContainer.hpp> +#include <com/sun/star/presentation/EffectNodeType.hpp> +#include <com/sun/star/presentation/ParagraphTarget.hpp> +#include <comphelper/processfactory.hxx> +#include <editeng/outliner.hxx> +#include <CustomAnimationCloner.hxx> +#include <CustomAnimationEffect.hxx> +#include <sdpage.hxx> +#include <EffectMigration.hxx> + +using namespace ::sd; +using namespace ::com::sun::star::uno; +using namespace ::com::sun::star::animations; +using namespace ::com::sun::star::presentation; + +using ::com::sun::star::drawing::XShape; + +/** returns a helper class to manipulate effects inside the main sequence */ +std::shared_ptr< sd::MainSequence > const & SdPage::getMainSequence() +{ + if (nullptr == mpMainSequence) + mpMainSequence = std::make_shared<sd::MainSequence>( getAnimationNode() ); + + return mpMainSequence; +} + +/** returns the main animation node */ +Reference< XAnimationNode > const & SdPage::getAnimationNode() +{ + if( !mxAnimationNode.is() ) + { + mxAnimationNode.set( ParallelTimeContainer::create( ::comphelper::getProcessComponentContext() ), UNO_QUERY_THROW ); + Sequence< css::beans::NamedValue > aUserData + { { "node-type", css::uno::Any(css::presentation::EffectNodeType::TIMING_ROOT) } }; + mxAnimationNode->setUserData( aUserData ); + } + + return mxAnimationNode; +} + +void SdPage::setAnimationNode( Reference< XAnimationNode > const & xNode ) +{ + mxAnimationNode = xNode; + if( mpMainSequence ) + mpMainSequence->reset( xNode ); +} + +/** removes all custom animations for the given shape */ +void SdPage::removeAnimations( const SdrObject* pObj ) +{ + if( mxAnimationNode.is() ) + { + getMainSequence(); + + Reference< XShape > xShape( const_cast<SdrObject*>(pObj)->getUnoShape(), UNO_QUERY ); + + if( mpMainSequence->hasEffect( xShape ) ) + mpMainSequence->disposeShape( xShape ); + } +} + +/** Notify that the object has been renamed and the animation effect has to update. */ +void SdPage::notifyObjectRenamed(const SdrObject* pObj) +{ + if (pObj && hasAnimationNode()) + { + Reference<XShape> xShape(const_cast<SdrObject*>(pObj)->getUnoShape(), UNO_QUERY); + + if (xShape.is() && getMainSequence()->hasEffect(xShape)) + getMainSequence()->notify_change(); + } +} + +bool SdPage::hasAnimationNode() const +{ + return mxAnimationNode.is(); +} + +void SdPage::SetFadeEffect(css::presentation::FadeEffect eNewEffect) +{ + EffectMigration::SetFadeEffect( this, eNewEffect ); +} + +FadeEffect SdPage::GetFadeEffect() const +{ + return EffectMigration::GetFadeEffect( this ); +} + +/** callback from the sd::View when a new paragraph for one object on this page is created */ +void SdPage::onParagraphInserted( const ::Outliner* pOutliner, Paragraph const * pPara, SdrObject* pObj ) +{ + if( mxAnimationNode.is() ) + { + ParagraphTarget aTarget; + aTarget.Shape.set( pObj->getUnoShape(), UNO_QUERY ); + /* FIXME: Paragraph should be sal_Int32, though more than 64k + * paragraphs at a shape are unlikely... */ + aTarget.Paragraph = static_cast<sal_Int16>(pOutliner->GetAbsPos( pPara )); + + getMainSequence()->insertTextRange( Any( aTarget ) ); + } +} + +/** callback from the sd::View when a paragraph from one object on this page is removed */ +void SdPage::onParagraphRemoving( const ::Outliner* pOutliner, Paragraph const * pPara, SdrObject* pObj ) +{ + if( mxAnimationNode.is() ) + { + ParagraphTarget aTarget; + aTarget.Shape.set( pObj->getUnoShape(), UNO_QUERY ); + /* FIXME: Paragraph should be sal_Int32, though more than 64k + * paragraphs at a shape are unlikely... */ + aTarget.Paragraph = static_cast<sal_Int16>(pOutliner->GetAbsPos( pPara )); + + getMainSequence()->disposeTextRange( Any( aTarget ) ); + } +} + +/** callback from the sd::View when an object just left text edit mode */ +void SdPage::onEndTextEdit( SdrObject* pObj ) +{ + if( pObj && mxAnimationNode.is() ) + { + Reference< XShape > xObj( pObj->getUnoShape(), UNO_QUERY ); + getMainSequence()->onTextChanged( xObj ); + } +} + +void SdPage::cloneAnimations( SdPage& rTargetPage ) const +{ + if( mxAnimationNode.is() ) + { + Reference< XAnimationNode > xClonedNode( + ::sd::Clone( mxAnimationNode, this, &rTargetPage ) ); + + if( xClonedNode.is() ) + rTargetPage.setAnimationNode( xClonedNode ); + } +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/core/shapelist.cxx b/sd/source/core/shapelist.cxx new file mode 100644 index 000000000..613286c9b --- /dev/null +++ b/sd/source/core/shapelist.cxx @@ -0,0 +1,140 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include <svx/svdobj.hxx> +#include <osl/diagnose.h> +#include <shapelist.hxx> + +#include <algorithm> + +using namespace sd; + +ShapeList::ShapeList() +{ + maIter = maShapeList.end(); +} + +ShapeList::~ShapeList() +{ + clear(); +} + +/** adds the given shape to this list */ +void ShapeList::addShape( SdrObject& rObject ) +{ + ListImpl::iterator aIter( std::find( maShapeList.begin(), maShapeList.end(), &rObject ) ); + if( aIter == maShapeList.end() ) + { + maShapeList.push_back(&rObject); + rObject.AddObjectUser( *this ); + } + else + { + OSL_FAIL("sd::ShapeList::addShape(), given shape already part of list!"); + } +} + +/** removes the given shape from this list */ +void ShapeList::removeShape( SdrObject& rObject ) +{ + ListImpl::iterator aIter( std::find( maShapeList.begin(), maShapeList.end(), &rObject ) ); + if( aIter != maShapeList.end() ) + { + bool bIterErased = aIter == maIter; + + (*aIter)->RemoveObjectUser(*this); + aIter = maShapeList.erase( aIter ); + + if( bIterErased ) + maIter = aIter; + } + else + { + OSL_FAIL("sd::ShapeList::removeShape(), given shape not part of list!"); + } +} + +/** removes all shapes from this list + NOTE: iterators will become invalid */ +void ShapeList::clear() +{ + ListImpl aShapeList; + aShapeList.swap( maShapeList ); + + for( auto& rpShape : aShapeList ) + rpShape->RemoveObjectUser(*this); + + maIter = maShapeList.end(); +} + +/** returns true if this list is empty */ +bool ShapeList::isEmpty() const +{ + return maShapeList.empty(); +} + +/** returns true if given shape is part of this list */ +bool ShapeList::hasShape( SdrObject& rObject ) const +{ + return std::find( maShapeList.begin(), maShapeList.end(), &rObject ) != maShapeList.end(); +} + +void ShapeList::ObjectInDestruction(const SdrObject& rObject) +{ + ListImpl::iterator aIter( std::find( maShapeList.begin(), maShapeList.end(), &rObject ) ); + if( aIter != maShapeList.end() ) + { + bool bIterErased = aIter == maIter; + + aIter = maShapeList.erase( aIter ); + + if( bIterErased ) + maIter = aIter; + } + else + { + OSL_FAIL("sd::ShapeList::ObjectInDestruction(), got a call from an unknown friend!"); + } +} + +SdrObject* ShapeList::getNextShape() +{ + if( maIter != maShapeList.end() ) + { + return (*maIter++); + } + else + { + return nullptr; + } +} + +void ShapeList::seekShape( sal_uInt32 nIndex ) +{ + maIter = maShapeList.begin(); + nIndex = std::min(nIndex, static_cast<sal_uInt32>(maShapeList.size())); + std::advance(maIter, nIndex); +} + +bool ShapeList::hasMore() const +{ + return maIter != maShapeList.end(); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/core/stlfamily.cxx b/sd/source/core/stlfamily.cxx new file mode 100644 index 000000000..a396bd170 --- /dev/null +++ b/sd/source/core/stlfamily.cxx @@ -0,0 +1,513 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include <com/sun/star/lang/DisposedException.hpp> +#include <com/sun/star/lang/IllegalAccessException.hpp> +#include <com/sun/star/lang/IndexOutOfBoundsException.hpp> +#include <cppuhelper/supportsservice.hxx> + +#include <vcl/svapp.hxx> + +#include <svl/style.hxx> + +#include <tools/debug.hxx> +#include <unotools/weakref.hxx> + +#include <strings.hrc> +#include <stlfamily.hxx> +#include <stlsheet.hxx> +#include <sdresid.hxx> +#include <sdpage.hxx> +#include <glob.hxx> + +#include <map> +#include <memory> + +using namespace ::com::sun::star::uno; +using namespace ::com::sun::star::lang; +using namespace ::com::sun::star::container; +using namespace ::com::sun::star::style; +using namespace ::com::sun::star::beans; + +typedef std::map< OUString, rtl::Reference< SdStyleSheet > > PresStyleMap; + +struct SdStyleFamilyImpl +{ + unotools::WeakReference<SdPage> mxMasterPage; + OUString maLayoutName; + + PresStyleMap& getStyleSheets(); + rtl::Reference< SfxStyleSheetPool > mxPool; + +private: + PresStyleMap maStyleSheets; +}; + +PresStyleMap& SdStyleFamilyImpl::getStyleSheets() +{ + auto pMasterPage = mxMasterPage.get(); + if (!pMasterPage) + return maStyleSheets; + + if (pMasterPage->GetLayoutName() != maLayoutName ) + { + maLayoutName = pMasterPage->GetLayoutName(); + + OUString aLayoutName( maLayoutName ); + const sal_Int32 nLen = aLayoutName.indexOf(SD_LT_SEPARATOR ) + 4; + aLayoutName = aLayoutName.copy(0, nLen ); + + if( (maStyleSheets.empty()) || !(*maStyleSheets.begin()).second->GetName().startsWith( aLayoutName) ) + { + maStyleSheets.clear(); + + // The iterator will return only style sheets of family master page + std::shared_ptr<SfxStyleSheetIterator> aSSSIterator = std::make_shared<SfxStyleSheetIterator>(mxPool.get(), SfxStyleFamily::Page); + for ( SfxStyleSheetBase* pStyle = aSSSIterator->First(); pStyle; + pStyle = aSSSIterator->Next() ) + { + // we assume that we have only SdStyleSheets + SdStyleSheet* pSdStyle = static_cast< SdStyleSheet* >( pStyle ); + if (pSdStyle->GetName().startsWith(aLayoutName)) + { + maStyleSheets[ pSdStyle->GetApiName() ].set( pSdStyle ); + } + } + } + } + + return maStyleSheets; +} + +SdStyleFamily::SdStyleFamily( const rtl::Reference< SfxStyleSheetPool >& xPool, SfxStyleFamily nFamily ) +: mnFamily( nFamily ) +, mxPool( xPool ) +{ +} + +SdStyleFamily::SdStyleFamily( const rtl::Reference< SfxStyleSheetPool >& xPool, const SdPage* pMasterPage ) +: mnFamily( SfxStyleFamily::Page ) +, mxPool( xPool ) +, mpImpl( new SdStyleFamilyImpl ) +{ + mpImpl->mxMasterPage = const_cast< SdPage* >( pMasterPage ); + mpImpl->mxPool = xPool; +} + +SdStyleFamily::~SdStyleFamily() +{ + DBG_ASSERT( !mxPool.is(), "SdStyleFamily::~SdStyleFamily(), dispose me first!" ); +} + +void SdStyleFamily::throwIfDisposed() const +{ + if( !mxPool.is() ) + throw DisposedException(); +} + +SdStyleSheet* SdStyleFamily::GetValidNewSheet( const Any& rElement ) +{ + Reference< XStyle > xStyle( rElement, UNO_QUERY ); + SdStyleSheet* pStyle = static_cast< SdStyleSheet* >( xStyle.get() ); + + if( pStyle == nullptr || (pStyle->GetFamily() != mnFamily) || (pStyle->GetPool() != mxPool.get()) || (mxPool->Find( pStyle->GetName(), mnFamily) != nullptr) ) + throw IllegalArgumentException(); + + return pStyle; +} + +SdStyleSheet* SdStyleFamily::GetSheetByName( const OUString& rName ) +{ + SdStyleSheet* pRet = nullptr; + if( !rName.isEmpty() ) + { + if( mnFamily == SfxStyleFamily::Page ) + { + PresStyleMap& rStyleMap = mpImpl->getStyleSheets(); + PresStyleMap::iterator iter( rStyleMap.find(rName) ); + if( iter != rStyleMap.end() ) + pRet = (*iter).second.get(); + } + else + { + std::shared_ptr<SfxStyleSheetIterator> aSSSIterator = std::make_shared<SfxStyleSheetIterator>(mxPool.get(), mnFamily); + for ( SfxStyleSheetBase* pStyle = aSSSIterator->First(); pStyle; + pStyle = aSSSIterator->Next() ) + { + // we assume that we have only SdStyleSheets + SdStyleSheet* pSdStyle = static_cast< SdStyleSheet* >( pStyle ); + if (pSdStyle->GetApiName() == rName) + { + pRet = pSdStyle; + break; + } + } + } + } + if( pRet ) + return pRet; + + throw NoSuchElementException(); +} + +// XServiceInfo +OUString SAL_CALL SdStyleFamily::getImplementationName() +{ + return "SdStyleFamily"; +} + +sal_Bool SAL_CALL SdStyleFamily::supportsService( const OUString& ServiceName ) +{ + return cppu::supportsService( this, ServiceName ); +} + +Sequence< OUString > SAL_CALL SdStyleFamily::getSupportedServiceNames() +{ + return { "com.sun.star.style.StyleFamily" }; +} + +// XNamed +OUString SAL_CALL SdStyleFamily::getName() +{ + if( mnFamily == SfxStyleFamily::Page ) + { + rtl::Reference<SdPage> pPage = mpImpl->mxMasterPage.get(); + if( pPage == nullptr ) + throw DisposedException(); + + OUString aLayoutName( pPage->GetLayoutName() ); + sal_Int32 nIndex = aLayoutName.indexOf(SD_LT_SEPARATOR); + if( nIndex != -1 ) + aLayoutName = aLayoutName.copy(0, nIndex); + + return aLayoutName; + } + else + { + return SdStyleSheet::GetFamilyString( mnFamily ); + } +} + +void SAL_CALL SdStyleFamily::setName( const OUString& ) +{ +} + +// XNameAccess + +Any SAL_CALL SdStyleFamily::getByName( const OUString& rName ) +{ + SolarMutexGuard aGuard; + throwIfDisposed(); + return Any( Reference< XStyle >( static_cast<SfxUnoStyleSheet*>(GetSheetByName( rName )) ) ); +} + +Sequence< OUString > SAL_CALL SdStyleFamily::getElementNames() +{ + SolarMutexGuard aGuard; + + throwIfDisposed(); + + if( mnFamily == SfxStyleFamily::Page ) + { + PresStyleMap& rStyleMap = mpImpl->getStyleSheets(); + Sequence< OUString > aNames( rStyleMap.size() ); + + OUString* pNames = aNames.getArray(); + for( const auto& rEntry : rStyleMap ) + { + rtl::Reference< SdStyleSheet > xStyle( rEntry.second ); + if( xStyle.is() ) + { + *pNames++ = xStyle->GetApiName(); + } + } + + return aNames; + } + else + { + std::vector< OUString > aNames; + std::shared_ptr<SfxStyleSheetIterator> aSSSIterator = std::make_shared<SfxStyleSheetIterator>(mxPool.get(), mnFamily); + for ( SfxStyleSheetBase* pStyle = aSSSIterator->First(); pStyle; + pStyle = aSSSIterator->Next() ) + { + // we assume that we have only SdStyleSheets + SdStyleSheet* pSdStyle = static_cast< SdStyleSheet* >( pStyle ); + aNames.push_back(pSdStyle->GetApiName()); + } + return Sequence< OUString >( &(*aNames.begin()), aNames.size() ); + } +} + +sal_Bool SAL_CALL SdStyleFamily::hasByName( const OUString& aName ) +{ + SolarMutexGuard aGuard; + throwIfDisposed(); + + if( !aName.isEmpty() ) + { + if( mnFamily == SfxStyleFamily::Page ) + { + PresStyleMap& rStyleSheets = mpImpl->getStyleSheets(); + PresStyleMap::iterator iter( rStyleSheets.find(aName) ); + return iter != rStyleSheets.end(); + } + else + { + std::shared_ptr<SfxStyleSheetIterator> aSSSIterator = std::make_shared<SfxStyleSheetIterator>(mxPool.get(), mnFamily); + for ( SfxStyleSheetBase* pStyle = aSSSIterator->First(); pStyle; + pStyle = aSSSIterator->Next() ) + { + // we assume that we have only SdStyleSheets + SdStyleSheet* pSdStyle = static_cast< SdStyleSheet* >( pStyle ); + if (pSdStyle->GetApiName() == aName) + { + return true; + } + } + } + } + + return false; +} + +// XElementAccess + +Type SAL_CALL SdStyleFamily::getElementType() +{ + return cppu::UnoType<XStyle>::get(); +} + +sal_Bool SAL_CALL SdStyleFamily::hasElements() +{ + SolarMutexGuard aGuard; + throwIfDisposed(); + + if( mnFamily == SfxStyleFamily::Page ) + { + return true; + } + else + { + std::shared_ptr<SfxStyleSheetIterator> aSSSIterator = std::make_shared<SfxStyleSheetIterator>(mxPool.get(), mnFamily); + if (aSSSIterator->First()) + { + return true; + } + } + + return false; +} + +// XIndexAccess + +sal_Int32 SAL_CALL SdStyleFamily::getCount() +{ + SolarMutexGuard aGuard; + throwIfDisposed(); + + sal_Int32 nCount = 0; + if( mnFamily == SfxStyleFamily::Page ) + { + return mpImpl->getStyleSheets().size(); + } + else + { + std::shared_ptr<SfxStyleSheetIterator> aSSSIterator = std::make_shared<SfxStyleSheetIterator>(mxPool.get(), mnFamily); + for ( SfxStyleSheetBase* pStyle = aSSSIterator->First(); pStyle; + pStyle = aSSSIterator->Next() ) + { + nCount++; + } + } + + return nCount; +} + +Any SAL_CALL SdStyleFamily::getByIndex( sal_Int32 Index ) +{ + SolarMutexGuard aGuard; + throwIfDisposed(); + + if( Index >= 0 ) + { + if( mnFamily == SfxStyleFamily::Page ) + { + PresStyleMap& rStyleSheets = mpImpl->getStyleSheets(); + if( Index < static_cast<sal_Int32>(rStyleSheets.size()) ) + { + PresStyleMap::iterator iter( rStyleSheets.begin() ); + std::advance(iter, Index); + return Any( Reference< XStyle >( (*iter).second ) ); + } + } + else + { + std::shared_ptr<SfxStyleSheetIterator> aSSSIterator = std::make_shared<SfxStyleSheetIterator>(mxPool.get(), mnFamily); + for ( SfxStyleSheetBase* pStyle = aSSSIterator->First(); pStyle; + pStyle = aSSSIterator->Next() ) + { + // we assume that we have only SdStyleSheets + SdStyleSheet* pSdStyle = static_cast< SdStyleSheet* >( pStyle ); + if( Index-- == 0 ) + { + return Any( Reference< XStyle >( pSdStyle ) ); + } + } + } + } + + throw IndexOutOfBoundsException(); +} + +// XNameContainer + +void SAL_CALL SdStyleFamily::insertByName( const OUString& rName, const Any& rElement ) +{ + SolarMutexGuard aGuard; + throwIfDisposed(); + + if(rName.isEmpty()) + throw IllegalArgumentException(); + + SdStyleSheet* pStyle = GetValidNewSheet( rElement ); + if( !pStyle->SetName( rName ) ) + throw ElementExistException(); + + pStyle->SetApiName( rName ); + mxPool->Insert( pStyle ); +} + +void SAL_CALL SdStyleFamily::removeByName( const OUString& rName ) +{ + SolarMutexGuard aGuard; + throwIfDisposed(); + + SdStyleSheet* pStyle = GetSheetByName( rName ); + + if( !pStyle->IsUserDefined() ) + throw WrappedTargetException(); + + mxPool->Remove( pStyle ); +} + +// XNameReplace + +void SAL_CALL SdStyleFamily::replaceByName( const OUString& rName, const Any& aElement ) +{ + SolarMutexGuard aGuard; + throwIfDisposed(); + + SdStyleSheet* pOldStyle = GetSheetByName( rName ); + SdStyleSheet* pNewStyle = GetValidNewSheet( aElement ); + + mxPool->Remove( pOldStyle ); + mxPool->Insert( pNewStyle ); +} + +// XSingleServiceFactory + +Reference< XInterface > SAL_CALL SdStyleFamily::createInstance() +{ + SolarMutexGuard aGuard; + throwIfDisposed(); + + if( mnFamily == SfxStyleFamily::Page ) + { + throw IllegalAccessException(); + } + return Reference<XInterface>( + static_cast<XStyle*>(SdStyleSheet::CreateEmptyUserStyle(*mxPool, mnFamily).get())); +} + +Reference< XInterface > SAL_CALL SdStyleFamily::createInstanceWithArguments( const Sequence< Any >& ) +{ + return createInstance(); +} + +// XComponent + +void SAL_CALL SdStyleFamily::dispose( ) +{ + if( mxPool.is() ) + mxPool.clear(); + + mpImpl.reset(); +} + +void SAL_CALL SdStyleFamily::addEventListener( const Reference< XEventListener >& ) +{ +} + +void SAL_CALL SdStyleFamily::removeEventListener( const Reference< XEventListener >& ) +{ +} + +// XPropertySet + +Reference<XPropertySetInfo> SdStyleFamily::getPropertySetInfo() +{ + OSL_FAIL( "###unexpected!" ); + return Reference<XPropertySetInfo>(); +} + +void SdStyleFamily::setPropertyValue( const OUString& , const Any& ) +{ + OSL_FAIL( "###unexpected!" ); +} + +Any SdStyleFamily::getPropertyValue( const OUString& PropertyName ) +{ + if ( PropertyName != "DisplayName" ) + { + throw UnknownPropertyException( "unknown property: " + PropertyName, static_cast<OWeakObject *>(this) ); + } + + SolarMutexGuard aGuard; + OUString sDisplayName; + switch( mnFamily ) + { + case SfxStyleFamily::Page: sDisplayName = getName(); break; + case SfxStyleFamily::Frame: sDisplayName = SdResId(STR_CELL_STYLE_FAMILY); break; + default: sDisplayName = SdResId(STR_GRAPHICS_STYLE_FAMILY); break; + } + return Any( sDisplayName ); +} + +void SdStyleFamily::addPropertyChangeListener( const OUString& , const Reference<XPropertyChangeListener>& ) +{ + OSL_FAIL( "###unexpected!" ); +} + +void SdStyleFamily::removePropertyChangeListener( const OUString& , const Reference<XPropertyChangeListener>& ) +{ + OSL_FAIL( "###unexpected!" ); +} + +void SdStyleFamily::addVetoableChangeListener( const OUString& , const Reference<XVetoableChangeListener>& ) +{ + OSL_FAIL( "###unexpected!" ); +} + +void SdStyleFamily::removeVetoableChangeListener( const OUString& , const Reference<XVetoableChangeListener>& ) +{ + OSL_FAIL( "###unexpected!" ); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/core/stlpool.cxx b/sd/source/core/stlpool.cxx new file mode 100644 index 000000000..12891698a --- /dev/null +++ b/sd/source/core/stlpool.cxx @@ -0,0 +1,1395 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include <com/sun/star/lang/DisposedException.hpp> +#include <com/sun/star/lang/IndexOutOfBoundsException.hpp> +#include <cppuhelper/supportsservice.hxx> +#include <editeng/eeitem.hxx> +#include <editeng/fhgtitem.hxx> +#include <editeng/colritem.hxx> +#include <editeng/contouritem.hxx> +#include <editeng/shdditem.hxx> +#include <editeng/crossedoutitem.hxx> +#include <editeng/udlnitem.hxx> +#include <editeng/wghtitem.hxx> +#include <editeng/postitem.hxx> +#include <editeng/fontitem.hxx> +#include <svx/xfillit0.hxx> +#include <svx/xlineit0.hxx> +#include <svx/sdtaitm.hxx> +#include <svx/sdtfsitm.hxx> +#include <svx/sdtagitm.hxx> +#include <svx/sdshitm.hxx> +#include <svx/sdshcitm.hxx> +#include <svx/sdsxyitm.hxx> +#include <editeng/ulspitem.hxx> +#include <editeng/numitem.hxx> +#include <editeng/cmapitem.hxx> +#include <svl/hint.hxx> +#include <editeng/charreliefitem.hxx> +#include <editeng/emphasismarkitem.hxx> +#include <svx/sdr/table/tabledesign.hxx> +#include <editeng/autokernitem.hxx> +#include <tools/diagnose_ex.h> +#include <tools/UnitConversion.hxx> + +#include <editeng/lrspitem.hxx> +#include <editeng/adjustitem.hxx> +#include <editeng/numdef.hxx> +#include <svl/itempool.hxx> +#include <svl/IndexedStyleSheets.hxx> + +#include <stlpool.hxx> +#include <sdresid.hxx> +#include <stlsheet.hxx> +#include <strings.hrc> +#include <glob.hxx> +#include <drawdoc.hxx> +#include <svl/itemset.hxx> +#include <app.hrc> +#include <strings.hxx> + +#include <com/sun/star/drawing/LineStyle.hpp> +#include <com/sun/star/drawing/TextFitToSizeType.hpp> + +using namespace ::com::sun::star; +using namespace ::com::sun::star::uno; +using namespace ::com::sun::star::lang; +using namespace ::com::sun::star::style; +using namespace ::com::sun::star::container; + +namespace +{ + +OUString lcl_findRenamedStyleName(std::vector< std::pair< OUString, OUString > > &rRenamedList, OUString const & aOriginalName ) +{ + auto aIter = std::find_if(rRenamedList.begin(), rRenamedList.end(), + [&aOriginalName](const std::pair<OUString, OUString>& rItem) { return rItem.first == aOriginalName; }); + if (aIter != rRenamedList.end()) + return (*aIter).second; + return OUString(); +} + +SfxStyleSheet *lcl_findStyle(StyleSheetCopyResultVector& rStyles, std::u16string_view aStyleName) +{ + if( aStyleName.empty() ) + return nullptr; + for (const auto& a : rStyles) + { + if (a.m_xStyleSheet->GetName() == aStyleName) + return a.m_xStyleSheet.get(); + } + return nullptr; +} + +} + +SdStyleSheetPool::SdStyleSheetPool(SfxItemPool const& _rPool, SdDrawDocument* pDocument) +: SdStyleSheetPoolBase( _rPool ) +, mpActualStyleSheet(nullptr) +, mpDoc(pDocument) +{ + if( !mpDoc ) + return; + + rtl::Reference< SfxStyleSheetPool > xPool( this ); + + // create graphics family + mxGraphicFamily = new SdStyleFamily( xPool, SfxStyleFamily::Para ); + mxCellFamily = new SdStyleFamily( xPool, SfxStyleFamily::Frame ); + + mxTableFamily = sdr::table::CreateTableDesignFamily(); + Reference< XNamed > xNamed( mxTableFamily, UNO_QUERY ); + if( xNamed.is() ) + msTableFamilyName = xNamed->getName(); + + // create presentation families, one for each master page + const sal_uInt16 nCount = mpDoc->GetMasterSdPageCount(PageKind::Standard); + for( sal_uInt16 nPage = 0; nPage < nCount; ++nPage ) + AddStyleFamily( mpDoc->GetMasterSdPage(nPage,PageKind::Standard) ); +} + +SdStyleSheetPool::~SdStyleSheetPool() +{ + DBG_ASSERT( mpDoc == nullptr, "sd::SdStyleSheetPool::~SdStyleSheetPool(), dispose me first!" ); +} + +rtl::Reference<SfxStyleSheetBase> SdStyleSheetPool::Create(const OUString& rName, SfxStyleFamily eFamily, SfxStyleSearchBits _nMask ) +{ + return new SdStyleSheet(rName, *this, eFamily, _nMask); +} + +SfxStyleSheetBase* SdStyleSheetPool::GetTitleSheet(std::u16string_view rLayoutName) +{ + OUString aName = OUString::Concat(rLayoutName) + SD_LT_SEPARATOR + STR_LAYOUT_TITLE; + SfxStyleSheetBase* pResult = Find(aName, SfxStyleFamily::Page); + return pResult; +} + +/************************************************************************* +|* +|* Create a list of outline text templates for a presentation layout. +|* The caller has to delete the list. +|* +\************************************************************************/ + +void SdStyleSheetPool::CreateOutlineSheetList (std::u16string_view rLayoutName, std::vector<SfxStyleSheetBase*> &rOutlineStyles) +{ + OUString aName = OUString::Concat(rLayoutName) + SD_LT_SEPARATOR + STR_LAYOUT_OUTLINE; + + for (sal_Int32 nSheet = 1; nSheet < 10; nSheet++) + { + OUString aFullName(aName + " " + OUString::number( nSheet ) ); + SfxStyleSheetBase* pSheet = Find(aFullName, SfxStyleFamily::Page); + + if (pSheet) + rOutlineStyles.push_back(pSheet); + } +} + +/************************************************************************* +|* +|* Create style sheets with default values for the named presentation layout +|* +\************************************************************************/ + +void SdStyleSheetPool::CreateLayoutStyleSheets(std::u16string_view rLayoutName, bool bCheck /*= sal_False*/ ) +{ + const SfxStyleSearchBits nUsedMask = SfxStyleSearchBits::All & ~SfxStyleSearchBits::UserDefined; + + bool bCreated = false; + + SfxStyleSheetBase* pSheet = nullptr; + + OUString aPrefix(OUString::Concat(rLayoutName) + SD_LT_SEPARATOR); + + vcl::Font aLatinFont, aCJKFont, aCTLFont; + + mpDoc->getDefaultFonts( aLatinFont, aCJKFont, aCTLFont ); + + // Font for title and outline + SvxFontItem aSvxFontItem( aLatinFont.GetFamilyType(), aLatinFont.GetFamilyName(), aLatinFont.GetStyleName(), aLatinFont.GetPitch(), + aLatinFont.GetCharSet(), EE_CHAR_FONTINFO ); + + SvxFontItem aSvxFontItemCJK( aCJKFont.GetFamilyType(), aCJKFont.GetFamilyName(), aCJKFont.GetStyleName(), aCJKFont.GetPitch(), + aCJKFont.GetCharSet(), EE_CHAR_FONTINFO_CJK ); + + SvxFontItem aSvxFontItemCTL( aCTLFont.GetFamilyType(), aCTLFont.GetFamilyName(), aCTLFont.GetStyleName(), aCTLFont.GetPitch(), + aCTLFont.GetCharSet(), EE_CHAR_FONTINFO_CTL ); + + vcl::Font aBulletFont( GetBulletFont() ); + + /************************************************************************** + * outline levels + **************************************************************************/ + OUString aName(STR_LAYOUT_OUTLINE); + const OUString aHelpFile; + + SvxLRSpaceItem aSvxLRSpaceItem( EE_PARA_LRSPACE ); + SvxULSpaceItem aSvxULSpaceItem( EE_PARA_ULSPACE ); + + for( sal_Int32 nLevel = 1; nLevel < 10; nLevel++) + { + OUString aLevelName( aPrefix + aName + " " + OUString::number( nLevel ) ) ; + + if (!Find(aLevelName, SfxStyleFamily::Page)) + { + bCreated = true; + pSheet = &Make(aLevelName, SfxStyleFamily::Page,nUsedMask); + pSheet->SetHelpId( aHelpFile, HID_PSEUDOSHEET_OUTLINE + nLevel ); + + pSheet->SetParent( OUString() ); + + // attributing for level 1, the others levels inherit + if (nLevel == 1) + { + SfxItemSet& rSet = pSheet->GetItemSet(); + + rSet.Put(aSvxFontItem); + rSet.Put(aSvxFontItemCJK); + rSet.Put(aSvxFontItemCTL); + rSet.Put( SvxPostureItem( ITALIC_NONE, EE_CHAR_ITALIC ) ); + rSet.Put( SvxPostureItem( ITALIC_NONE, EE_CHAR_ITALIC_CJK ) ); + rSet.Put( SvxPostureItem( ITALIC_NONE, EE_CHAR_ITALIC_CTL ) ); + rSet.Put( SvxWeightItem( WEIGHT_NORMAL, EE_CHAR_WEIGHT ) ); + rSet.Put( SvxWeightItem( WEIGHT_NORMAL, EE_CHAR_WEIGHT_CJK ) ); + rSet.Put( SvxWeightItem( WEIGHT_NORMAL, EE_CHAR_WEIGHT_CTL ) ); + rSet.Put( SvxUnderlineItem(LINESTYLE_NONE, EE_CHAR_UNDERLINE ) ); + rSet.Put( SvxOverlineItem(LINESTYLE_NONE, EE_CHAR_OVERLINE ) ); + rSet.Put( SvxCrossedOutItem(STRIKEOUT_NONE, EE_CHAR_STRIKEOUT ) ); + rSet.Put( SvxCaseMapItem(SvxCaseMap::NotMapped, EE_CHAR_CASEMAP ) ); + rSet.Put( SvxShadowedItem(false, EE_CHAR_SHADOW ) ); + rSet.Put( SvxContourItem(false, EE_CHAR_OUTLINE ) ); + rSet.Put( SvxEmphasisMarkItem(FontEmphasisMark::NONE, EE_CHAR_EMPHASISMARK ) ); + rSet.Put( SvxCharReliefItem(FontRelief::NONE, EE_CHAR_RELIEF) ); + rSet.Put( SvxColorItem( COL_AUTO, EE_CHAR_COLOR) ); + rSet.Put( SvxColorItem( COL_AUTO, EE_CHAR_BKGCOLOR ) ); + rSet.Put( XLineStyleItem(css::drawing::LineStyle_NONE) ); + rSet.Put( XFillStyleItem(drawing::FillStyle_NONE) ); + rSet.Put( SdrTextFitToSizeTypeItem(drawing::TextFitToSizeType_AUTOFIT) ); + rSet.Put( makeSdrTextAutoGrowHeightItem(false) ); + // #i16874# enable kerning by default but only for new documents + rSet.Put( SvxAutoKernItem( true, EE_CHAR_PAIRKERNING ) ); + + vcl::Font f( GetBulletFont() ); + PutNumBulletItem( pSheet, f ); + } + + sal_uLong nFontSize = 20; + sal_uInt16 nUpper = 100; + + switch (nLevel) + { + case 1: + { + nFontSize = 32; + nUpper = 500; + } + break; + + case 2: + { + nFontSize = 28; + nUpper = 400; + } + break; + + case 3: + { + nFontSize = 24; + nUpper = 300; + } + break; + + case 4: + { + nUpper = 200; + } + break; + } + + // FontSize + nFontSize = static_cast<sal_uInt16>(convertPointToMm100(nFontSize)); + SfxItemSet& rOutlineSet = pSheet->GetItemSet(); + rOutlineSet.Put( SvxFontHeightItem( nFontSize, 100, EE_CHAR_FONTHEIGHT ) ); + rOutlineSet.Put( SvxFontHeightItem( nFontSize, 100, EE_CHAR_FONTHEIGHT_CJK ) ); + rOutlineSet.Put( SvxFontHeightItem( SdDrawDocument::convertFontHeightToCTL( nFontSize ), 100, EE_CHAR_FONTHEIGHT_CTL ) ); + + // Line distance (upwards). Stuff around here cleaned up in i35937 + aSvxULSpaceItem.SetUpper(nUpper); + pSheet->GetItemSet().Put(aSvxULSpaceItem); + } + } + + // if we created outline styles, we need to chain them + if( bCreated ) + { + SfxStyleSheetBase* pParent = nullptr; + for (sal_Int32 nLevel = 1; nLevel < 10; nLevel++) + { + OUString aLevelName( aPrefix + aName + " " + OUString::number( nLevel ) ); + + pSheet = Find(aLevelName, SfxStyleFamily::Page); + + DBG_ASSERT( pSheet, "missing layout style!"); + + if( pSheet ) + { + if (pParent) + pSheet->SetParent(pParent->GetName()); + pParent = pSheet; + } + } + } + + /************************************************************************** + * Title + **************************************************************************/ + aName = aPrefix + STR_LAYOUT_TITLE; + + if (!Find(aName, SfxStyleFamily::Page)) + { + bCreated = true; + + pSheet = &Make(aName, SfxStyleFamily::Page,nUsedMask); + pSheet->SetHelpId( aHelpFile, HID_PSEUDOSHEET_TITLE ); + pSheet->SetParent( OUString() ); + SfxItemSet& rTitleSet = pSheet->GetItemSet(); + rTitleSet.Put(XLineStyleItem(drawing::LineStyle_NONE)); + rTitleSet.Put(XFillStyleItem(drawing::FillStyle_NONE)); + rTitleSet.Put(aSvxFontItem); + rTitleSet.Put(aSvxFontItemCJK); + rTitleSet.Put(aSvxFontItemCTL); + rTitleSet.Put(SvxPostureItem( ITALIC_NONE, EE_CHAR_ITALIC ) ); + rTitleSet.Put(SvxPostureItem( ITALIC_NONE, EE_CHAR_ITALIC_CJK ) ); + rTitleSet.Put(SvxPostureItem( ITALIC_NONE, EE_CHAR_ITALIC_CTL ) ); + rTitleSet.Put(SvxWeightItem( WEIGHT_NORMAL, EE_CHAR_WEIGHT ) ); + rTitleSet.Put(SvxWeightItem( WEIGHT_NORMAL, EE_CHAR_WEIGHT_CJK ) ); + rTitleSet.Put(SvxWeightItem( WEIGHT_NORMAL, EE_CHAR_WEIGHT_CTL ) ); + rTitleSet.Put(SvxFontHeightItem( 1552, 100, EE_CHAR_FONTHEIGHT ) ); // 44 pt + rTitleSet.Put(SvxFontHeightItem( 1552, 100, EE_CHAR_FONTHEIGHT_CJK ) ); // 44 pt + rTitleSet.Put(SvxFontHeightItem( SdDrawDocument::convertFontHeightToCTL( 1552 ), 100, EE_CHAR_FONTHEIGHT_CTL ) ); // 44 pt + rTitleSet.Put(SvxUnderlineItem(LINESTYLE_NONE, EE_CHAR_UNDERLINE )); + rTitleSet.Put(SvxOverlineItem(LINESTYLE_NONE, EE_CHAR_OVERLINE )); + rTitleSet.Put(SvxCrossedOutItem(STRIKEOUT_NONE, EE_CHAR_STRIKEOUT )); + rTitleSet.Put(SvxCaseMapItem(SvxCaseMap::NotMapped, EE_CHAR_CASEMAP )); + rTitleSet.Put(SvxShadowedItem(false, EE_CHAR_SHADOW )); + rTitleSet.Put(SvxContourItem(false, EE_CHAR_OUTLINE )); + rTitleSet.Put( SvxEmphasisMarkItem(FontEmphasisMark::NONE, EE_CHAR_EMPHASISMARK ) ); + rTitleSet.Put( SvxCharReliefItem(FontRelief::NONE, EE_CHAR_RELIEF ) ); + rTitleSet.Put(SvxColorItem( COL_AUTO, EE_CHAR_COLOR )); + rTitleSet.Put(SvxColorItem( COL_AUTO, EE_CHAR_BKGCOLOR )); + rTitleSet.Put(SvxAdjustItem(SvxAdjust::Center, EE_PARA_JUST )); + rTitleSet.Put( SdrTextVertAdjustItem( SDRTEXTVERTADJUST_CENTER ) ); + // #i16874# enable kerning by default but only for new documents + rTitleSet.Put( SvxAutoKernItem( true, EE_CHAR_PAIRKERNING ) ); + + aBulletFont.SetFontSize(Size(0,1552)); // 44 pt + PutNumBulletItem( pSheet, aBulletFont ); + } + + /************************************************************************** + * Subtitle + **************************************************************************/ + aName = aPrefix + STR_LAYOUT_SUBTITLE; + + if (!Find(aName, SfxStyleFamily::Page)) + { + bCreated = true; + + pSheet = &Make(aName, SfxStyleFamily::Page,nUsedMask); + pSheet->SetHelpId( aHelpFile, HID_PSEUDOSHEET_SUBTITLE ); + pSheet->SetParent( OUString() ); + SfxItemSet& rSubtitleSet = pSheet->GetItemSet(); + rSubtitleSet.Put(XLineStyleItem(drawing::LineStyle_NONE)); + rSubtitleSet.Put(XFillStyleItem(drawing::FillStyle_NONE)); + rSubtitleSet.Put(aSvxFontItem); + rSubtitleSet.Put(aSvxFontItemCJK); + rSubtitleSet.Put(aSvxFontItemCTL); + rSubtitleSet.Put(SvxPostureItem( ITALIC_NONE, EE_CHAR_ITALIC ) ); + rSubtitleSet.Put(SvxPostureItem( ITALIC_NONE, EE_CHAR_ITALIC_CJK ) ); + rSubtitleSet.Put(SvxPostureItem( ITALIC_NONE, EE_CHAR_ITALIC_CTL ) ); + rSubtitleSet.Put(SvxWeightItem( WEIGHT_NORMAL, EE_CHAR_WEIGHT ) ); + rSubtitleSet.Put(SvxWeightItem( WEIGHT_NORMAL, EE_CHAR_WEIGHT_CJK ) ); + rSubtitleSet.Put(SvxWeightItem( WEIGHT_NORMAL, EE_CHAR_WEIGHT_CTL ) ); + rSubtitleSet.Put( SvxFontHeightItem( 1129, 100, EE_CHAR_FONTHEIGHT ) ); // 32 pt + rSubtitleSet.Put( SvxFontHeightItem( 1129, 100, EE_CHAR_FONTHEIGHT_CJK ) ); // 32 pt + rSubtitleSet.Put( SvxFontHeightItem( SdDrawDocument::convertFontHeightToCTL( 1129 ), 100, EE_CHAR_FONTHEIGHT_CTL ) ); // 32 pt + rSubtitleSet.Put(SvxUnderlineItem(LINESTYLE_NONE, EE_CHAR_UNDERLINE )); + rSubtitleSet.Put(SvxOverlineItem(LINESTYLE_NONE, EE_CHAR_OVERLINE )); + rSubtitleSet.Put(SvxCrossedOutItem(STRIKEOUT_NONE, EE_CHAR_STRIKEOUT )); + rSubtitleSet.Put(SvxCaseMapItem(SvxCaseMap::NotMapped, EE_CHAR_CASEMAP )); + rSubtitleSet.Put(SvxShadowedItem(false, EE_CHAR_SHADOW )); + rSubtitleSet.Put(SvxContourItem(false, EE_CHAR_OUTLINE )); + rSubtitleSet.Put( SvxEmphasisMarkItem(FontEmphasisMark::NONE, EE_CHAR_EMPHASISMARK ) ); + rSubtitleSet.Put( SvxCharReliefItem(FontRelief::NONE, EE_CHAR_RELIEF ) ); + rSubtitleSet.Put(SvxColorItem( COL_AUTO, EE_CHAR_COLOR )); + rSubtitleSet.Put(SvxColorItem( COL_AUTO, EE_CHAR_BKGCOLOR )); + rSubtitleSet.Put(SvxAdjustItem(SvxAdjust::Center, EE_PARA_JUST )); + rSubtitleSet.Put( SdrTextVertAdjustItem( SDRTEXTVERTADJUST_CENTER ) ); + // #i16874# enable kerning by default but only for new documents + rSubtitleSet.Put( SvxAutoKernItem( true, EE_CHAR_PAIRKERNING ) ); + aSvxLRSpaceItem.SetTextLeft(0); + rSubtitleSet.Put(aSvxLRSpaceItem); + + vcl::Font aTmpFont( GetBulletFont() ); + aTmpFont.SetFontSize(Size(0, 1129)); // 32 pt + PutNumBulletItem( pSheet, aTmpFont ); + } + + /************************************************************************** + * Notes + **************************************************************************/ + aName = aPrefix + STR_LAYOUT_NOTES; + + if (!Find(aName, SfxStyleFamily::Page)) + { + bCreated = true; + + pSheet = &Make(aName, SfxStyleFamily::Page,nUsedMask); + pSheet->SetHelpId( aHelpFile, HID_PSEUDOSHEET_NOTES ); + pSheet->SetParent( OUString() ); + SfxItemSet& rNotesSet = pSheet->GetItemSet(); + rNotesSet.Put(XLineStyleItem(drawing::LineStyle_NONE)); + rNotesSet.Put(XFillStyleItem(drawing::FillStyle_NONE)); + rNotesSet.Put(aSvxFontItem); + rNotesSet.Put(aSvxFontItemCJK); + rNotesSet.Put(aSvxFontItemCTL); + rNotesSet.Put( SvxPostureItem( ITALIC_NONE, EE_CHAR_ITALIC ) ); + rNotesSet.Put( SvxPostureItem( ITALIC_NONE, EE_CHAR_ITALIC_CJK ) ); + rNotesSet.Put( SvxPostureItem( ITALIC_NONE, EE_CHAR_ITALIC_CTL ) ); + rNotesSet.Put( SvxWeightItem( WEIGHT_NORMAL, EE_CHAR_WEIGHT ) ); + rNotesSet.Put( SvxWeightItem( WEIGHT_NORMAL, EE_CHAR_WEIGHT_CJK ) ); + rNotesSet.Put( SvxWeightItem( WEIGHT_NORMAL, EE_CHAR_WEIGHT_CTL ) ); + rNotesSet.Put( SvxFontHeightItem( 705, 100, EE_CHAR_FONTHEIGHT ) ); // 20 pt + rNotesSet.Put( SvxFontHeightItem( 705, 100, EE_CHAR_FONTHEIGHT_CJK ) ); // 20 pt + rNotesSet.Put( SvxFontHeightItem( SdDrawDocument::convertFontHeightToCTL( 705 ), 100, EE_CHAR_FONTHEIGHT_CTL ) ); // 20 pt + rNotesSet.Put( SvxUnderlineItem(LINESTYLE_NONE, EE_CHAR_UNDERLINE ) ); + rNotesSet.Put( SvxOverlineItem(LINESTYLE_NONE, EE_CHAR_OVERLINE ) ); + rNotesSet.Put( SvxCrossedOutItem(STRIKEOUT_NONE, EE_CHAR_STRIKEOUT ) ); + rNotesSet.Put( SvxCaseMapItem(SvxCaseMap::NotMapped, EE_CHAR_CASEMAP ) ); + rNotesSet.Put( SvxShadowedItem(false, EE_CHAR_SHADOW ) ); + rNotesSet.Put( SvxContourItem(false, EE_CHAR_OUTLINE ) ); + rNotesSet.Put( SvxEmphasisMarkItem(FontEmphasisMark::NONE, EE_CHAR_EMPHASISMARK ) ); + rNotesSet.Put( SvxCharReliefItem(FontRelief::NONE, EE_CHAR_RELIEF) ); + rNotesSet.Put( SvxColorItem( COL_AUTO, EE_CHAR_COLOR ) ); + rNotesSet.Put( SvxColorItem( COL_AUTO, EE_CHAR_BKGCOLOR ) ); + rNotesSet.Put( SvxLRSpaceItem( 0, 0, 600, -600, EE_PARA_LRSPACE ) ); + // #i16874# enable kerning by default but only for new documents + rNotesSet.Put( SvxAutoKernItem( true, EE_CHAR_PAIRKERNING ) ); + +/* #i35937# */ + + } + + /************************************************************************** + * Background objects + **************************************************************************/ + aName = aPrefix + STR_LAYOUT_BACKGROUNDOBJECTS; + + if (!Find(aName, SfxStyleFamily::Page)) + { + bCreated = true; + + pSheet = &Make(aName, SfxStyleFamily::Page,nUsedMask); + pSheet->SetHelpId( aHelpFile, HID_PSEUDOSHEET_BACKGROUNDOBJECTS ); + pSheet->SetParent( OUString() ); + SfxItemSet& rBackgroundObjectsSet = pSheet->GetItemSet(); + rBackgroundObjectsSet.Put(makeSdrShadowItem(false)); + rBackgroundObjectsSet.Put(makeSdrShadowColorItem(COL_GRAY)); + rBackgroundObjectsSet.Put(makeSdrShadowXDistItem(200)); // 3 mm shadow distance + rBackgroundObjectsSet.Put(makeSdrShadowYDistItem(200)); + // #i16874# enable kerning by default but only for new documents + rBackgroundObjectsSet.Put( SvxAutoKernItem( true, EE_CHAR_PAIRKERNING ) ); + rBackgroundObjectsSet.Put(SdrTextHorzAdjustItem(SDRTEXTHORZADJUST_BLOCK)); + } + + /************************************************************************** + * Background + **************************************************************************/ + aName = aPrefix + STR_LAYOUT_BACKGROUND; + + if (!Find(aName, SfxStyleFamily::Page)) + { + bCreated = true; + + pSheet = &Make(aName, SfxStyleFamily::Page,nUsedMask); + pSheet->SetHelpId( aHelpFile, HID_PSEUDOSHEET_BACKGROUND ); + pSheet->SetParent( OUString() ); + SfxItemSet& rBackgroundSet = pSheet->GetItemSet(); + rBackgroundSet.Put(XLineStyleItem(drawing::LineStyle_NONE)); + rBackgroundSet.Put(XFillStyleItem(drawing::FillStyle_NONE)); + // #i16874# enable kerning by default but only for new documents + rBackgroundSet.Put( SvxAutoKernItem( true, EE_CHAR_PAIRKERNING ) ); + } + + DBG_ASSERT( !bCheck || !bCreated, "missing layout style sheets detected!" ); +} + +/************************************************************************* +|* +|* Copy graphic style sheets from source pool into this pool +|* +|* (rSourcePool can not be const since SfxStyleSheetPoolBase::Find isn't const) +|* +\************************************************************************/ + +void SdStyleSheetPool::CopyGraphicSheets(SdStyleSheetPool& rSourcePool) +{ + CopySheets( rSourcePool, SfxStyleFamily::Para ); +} + +void SdStyleSheetPool::CopyCellSheets(SdStyleSheetPool& rSourcePool) +{ + CopySheets( rSourcePool, SfxStyleFamily::Frame ); +} + +void SdStyleSheetPool::CopyTableStyles(SdStyleSheetPool const & rSourcePool) +{ + Reference< XIndexAccess > xSource( rSourcePool.mxTableFamily, UNO_QUERY ); + Reference< XNameContainer > xTarget( mxTableFamily, UNO_QUERY ); + Reference< XSingleServiceFactory > xFactory( mxTableFamily, UNO_QUERY ); + + if( !(xSource.is() && xFactory.is() && mxTableFamily.is()) ) + return; + + for( sal_Int32 nIndex = 0; nIndex < xSource->getCount(); nIndex++ ) try + { + Reference< XStyle > xSourceTableStyle( xSource->getByIndex( nIndex ), UNO_QUERY ); + if( xSourceTableStyle.is() ) + { + Reference< XStyle > xNewTableStyle( xFactory->createInstance(), UNO_QUERY ); + if( xNewTableStyle.is() ) + { + Reference< XNameAccess> xSourceNames( xSourceTableStyle, UNO_QUERY_THROW ); + + const Sequence< OUString > aStyleNames( xSourceNames->getElementNames() ); + + Reference< XNameReplace > xTargetNames( xNewTableStyle, UNO_QUERY ); + + for( const OUString& aName : aStyleNames ) + { + Reference< XStyle > xSourceStyle( xSourceNames->getByName( aName ), UNO_QUERY ); + Reference< XStyle > xTargetStyle; + if( xSourceStyle.is() ) try + { + mxCellFamily->getByName( xSourceStyle->getName() ) >>= xTargetStyle; + } + catch( Exception& ) + { + TOOLS_WARN_EXCEPTION( "sd", "sd::SdStyleSheetPool::CopyTableStyles()" ); + } + + if( xTargetStyle.is() ) + xTargetNames->replaceByName( aName, Any( xTargetStyle ) ); + } + } + + OUString sName( xSourceTableStyle->getName() ); + if( xTarget->hasByName( sName ) ) + xTarget->replaceByName( sName, Any( xNewTableStyle ) ); + else + xTarget->insertByName( sName, Any( xNewTableStyle ) ); + } + } + catch( Exception& ) + { + TOOLS_WARN_EXCEPTION( "sd", "sd::SdStyleSheetPool::CopyTableStyles()"); + } +} + +void SdStyleSheetPool::CopyCellSheets(SdStyleSheetPool& rSourcePool, StyleSheetCopyResultVector& rCreatedSheets) +{ + CopySheets( rSourcePool, SfxStyleFamily::Frame, rCreatedSheets ); +} + +void SdStyleSheetPool::RenameAndCopyGraphicSheets(SdStyleSheetPool& rSourcePool, StyleSheetCopyResultVector& rCreatedSheets, std::u16string_view rRenameSuffix) +{ + CopySheets( rSourcePool, SfxStyleFamily::Para, rCreatedSheets, rRenameSuffix ); +} + +void SdStyleSheetPool::CopySheets(SdStyleSheetPool& rSourcePool, SfxStyleFamily eFamily ) +{ + StyleSheetCopyResultVector aTmpSheets; + CopySheets(rSourcePool, eFamily, aTmpSheets); +} + +void SdStyleSheetPool::CopySheets(SdStyleSheetPool& rSourcePool, SfxStyleFamily eFamily, StyleSheetCopyResultVector& rCreatedSheets) +{ + CopySheets(rSourcePool, eFamily, rCreatedSheets, u""); +} + +namespace +{ + +struct HasFamilyPredicate : svl::StyleSheetPredicate +{ + explicit HasFamilyPredicate(SfxStyleFamily eFamily) + : meFamily(eFamily) {} + + bool Check(const SfxStyleSheetBase& sheet) override + { + return sheet.GetFamily() == meFamily; + } + SfxStyleFamily meFamily; +}; + +} + +void SdStyleSheetPool::CopySheets(SdStyleSheetPool& rSourcePool, SfxStyleFamily eFamily, StyleSheetCopyResultVector& rCreatedSheets, std::u16string_view rRenameSuffix) +{ + std::vector< std::pair< rtl::Reference< SfxStyleSheetBase >, OUString > > aNewStyles; + std::vector< std::pair< OUString, OUString > > aRenamedList; + + // find all style sheets of the source pool which have the same family + HasFamilyPredicate aHasFamilyPredicate(eFamily); + std::vector<sal_Int32> aSheetsWithFamily = rSourcePool.GetIndexedStyleSheets().FindPositionsByPredicate(aHasFamilyPredicate); + + for (const auto& rPos : aSheetsWithFamily) + { + SfxStyleSheetBase* pSheet = rSourcePool.GetStyleSheetByPositionInIndex( rPos ); + if( !pSheet ) + continue; + OUString aName( pSheet->GetName() ); + + // now check whether we already have a sheet with the same name + std::vector<sal_Int32> aSheetsWithName = GetIndexedStyleSheets().FindPositionsByName(aName); + bool bAddToList = false; + SfxStyleSheetBase * pExistingSheet = nullptr; + if (!aSheetsWithName.empty()) + { + // if we have a rename suffix, try to find a new name + pExistingSheet = + GetStyleSheetByPositionInIndex(aSheetsWithName.front()); + if (!rRenameSuffix.empty() && + !pExistingSheet->GetItemSet().Equals(pSheet->GetItemSet(), false)) + { + // we have found a sheet with the same name, but different contents. Try to find a new name. + // If we already have a sheet with the new name, and it is equal to the one in the source pool, + // do nothing. + OUString aTmpName = aName + rRenameSuffix; + sal_Int32 nSuffix = 1; + do + { + aTmpName = aName + rRenameSuffix + OUString::number(nSuffix); + pExistingSheet = Find(aTmpName, eFamily); + nSuffix++; + } while (pExistingSheet && + !pExistingSheet->GetItemSet().Equals(pSheet->GetItemSet(), false)); + aName = aTmpName; + bAddToList = true; + } + } + // we do not already have a sheet with the same name and contents. Create a new one. + if (!pExistingSheet) + { + assert(!Find(aName, eFamily)); + rtl::Reference< SfxStyleSheetBase > xNewSheet( &Make( aName, eFamily ) ); + + xNewSheet->SetMask( pSheet->GetMask() ); + + // Also set parent relation for copied style sheets + OUString aParent( pSheet->GetParent() ); + if( !aParent.isEmpty() ) + aNewStyles.emplace_back( xNewSheet, aParent ); + + if( !bAddToList ) + { + OUString aHelpFile; + xNewSheet->SetHelpId( aHelpFile, pSheet->GetHelpId( aHelpFile ) ); + } + xNewSheet->GetItemSet().Put( pSheet->GetItemSet() ); + + rCreatedSheets.emplace_back(static_cast<SdStyleSheet*>(xNewSheet.get()), true); + aRenamedList.emplace_back( pSheet->GetName(), aName ); + } + else if (bAddToList) + { + // Add to list - used for renaming + rCreatedSheets.emplace_back(static_cast<SdStyleSheet*>(pExistingSheet), false); + aRenamedList.emplace_back( pSheet->GetName(), aName ); + } + } + + // set parents on newly added stylesheets + for( auto& rStyle : aNewStyles ) + { + if( !rRenameSuffix.empty() ) + { + SfxStyleSheet *pParent = lcl_findStyle(rCreatedSheets, lcl_findRenamedStyleName(aRenamedList, rStyle.second)); + if( pParent ) + { + rStyle.first->SetParent( pParent->GetName() ); + continue; + } + } + DBG_ASSERT( rSourcePool.Find( rStyle.second, eFamily ), "StyleSheet has invalid parent: Family mismatch" ); + rStyle.first->SetParent( rStyle.second ); + } + // we have changed names of style sheets. Trigger reindexing. + Reindex(); +} + +/************************************************************************* +|* +|* Copy style sheets of the named presentation layout from the source pool into +|* this pool. Copies only the style sheets which aren't yet in this pool. +|* If not NULL, pCreatedSheets is filled with pointers to the created style +|* sheets. +|* +|* (rSourcePool can not be const since SfxStyleSheetPoolBase::Find isn't const) +|* +\************************************************************************/ + +void SdStyleSheetPool::CopyLayoutSheets(std::u16string_view rLayoutName, SdStyleSheetPool& rSourcePool, StyleSheetCopyResultVector& rCreatedSheets) +{ + SfxStyleSheetBase* pSheet = nullptr; + + std::vector<OUString> aNameList; + CreateLayoutSheetNames(rLayoutName,aNameList); + + for (const auto& rName : aNameList) + { + pSheet = Find(rName, SfxStyleFamily::Page); + if (!pSheet) + { + SfxStyleSheetBase* pSourceSheet = rSourcePool.Find(rName, SfxStyleFamily::Page); + DBG_ASSERT(pSourceSheet, "CopyLayoutSheets: Style sheet missing"); + if (pSourceSheet) + { + // In the case one comes with Methusalem-Docs. + SfxStyleSheetBase& rNewSheet = Make(rName, SfxStyleFamily::Page); + OUString file; + rNewSheet.SetHelpId( file, pSourceSheet->GetHelpId( file ) ); + rNewSheet.GetItemSet().Put(pSourceSheet->GetItemSet()); + rCreatedSheets.emplace_back(static_cast<SdStyleSheet*>(&rNewSheet), true); + } + } + } + + // Special treatment for outline templates: create parent relation + std::vector<SfxStyleSheetBase*> aOutlineSheets; + CreateOutlineSheetList(rLayoutName,aOutlineSheets); + + if( aOutlineSheets.empty() ) + return; + + std::vector<SfxStyleSheetBase*>::iterator it = aOutlineSheets.begin(); + SfxStyleSheetBase* pParent = *it; + ++it; + + while (it != aOutlineSheets.end()) + { + pSheet = *it; + + if (!pSheet) + break; + + if (pSheet->GetParent().isEmpty()) + pSheet->SetParent(pParent->GetName()); + + pParent = pSheet; + + ++it; + } +} + +/************************************************************************* +|* +|* Create list with names of the presentation templates of a layout. +|* The list and the containing strings are owned by the caller! +|* +\************************************************************************/ + +void SdStyleSheetPool::CreateLayoutSheetNames(std::u16string_view rLayoutName, std::vector<OUString> &aNameList) +{ + OUString aPrefix(OUString::Concat(rLayoutName) + SD_LT_SEPARATOR); + + for (sal_Int32 nLevel = 1; nLevel < 10; nLevel++) + aNameList.emplace_back(aPrefix + STR_LAYOUT_OUTLINE + " " + OUString::number( nLevel ) ); + + aNameList.emplace_back(aPrefix + STR_LAYOUT_TITLE); + aNameList.emplace_back(aPrefix + STR_LAYOUT_SUBTITLE); + aNameList.emplace_back(aPrefix + STR_LAYOUT_NOTES); + aNameList.emplace_back(aPrefix + STR_LAYOUT_BACKGROUNDOBJECTS); + aNameList.emplace_back(aPrefix + STR_LAYOUT_BACKGROUND); +} + +/************************************************************************* +|* +|* Create a list with pointer to presentation templates of a layout. +|* The list is owned by the caller! +|* +\************************************************************************/ + +void SdStyleSheetPool::CreateLayoutSheetList(std::u16string_view rLayoutName, SdStyleSheetVector& rLayoutSheets ) +{ + OUString aLayoutNameWithSep(OUString::Concat(rLayoutName) + SD_LT_SEPARATOR); + + SfxStyleSheetIterator aIter(this, SfxStyleFamily::Page); + SfxStyleSheetBase* pSheet = aIter.First(); + + while (pSheet) + { + if (pSheet->GetName().startsWith(aLayoutNameWithSep)) + rLayoutSheets.emplace_back( static_cast< SdStyleSheet* >( pSheet ) ); + pSheet = aIter.Next(); + } +} + +/************************************************************************* +|* +|* Create pseudo style sheets if necessary +|* +\************************************************************************/ + +void SdStyleSheetPool::CreatePseudosIfNecessary() +{ + OUString aName; + const OUString aHelpFile; + SfxStyleSheetBase* pSheet = nullptr; + SfxStyleSheetBase* pParent = nullptr; + + SfxStyleSearchBits nUsedMask = SfxStyleSearchBits::Used; + + aName = SdResId(STR_PSEUDOSHEET_TITLE); + if( (pSheet = Find(aName, SfxStyleFamily::Pseudo)) == nullptr ) + { + pSheet = &Make(aName, SfxStyleFamily::Pseudo, nUsedMask); + pSheet->SetParent( OUString() ); + static_cast<SfxStyleSheet*>(pSheet)->StartListening(*this); + } + pSheet->SetHelpId( aHelpFile, HID_PSEUDOSHEET_TITLE ); + + aName = SdResId(STR_PSEUDOSHEET_SUBTITLE); + if( (pSheet = Find(aName, SfxStyleFamily::Pseudo)) == nullptr ) + { + pSheet = &Make(aName, SfxStyleFamily::Pseudo, nUsedMask); + pSheet->SetParent( OUString() ); + static_cast<SfxStyleSheet*>(pSheet)->StartListening(*this); + } + pSheet->SetHelpId( aHelpFile, HID_PSEUDOSHEET_SUBTITLE ); + + aName = SdResId(STR_PSEUDOSHEET_BACKGROUNDOBJECTS); + if( (pSheet = Find(aName, SfxStyleFamily::Pseudo)) == nullptr ) + { + pSheet = &Make(aName, SfxStyleFamily::Pseudo, nUsedMask); + pSheet->SetParent( OUString() ); + static_cast<SfxStyleSheet*>(pSheet)->StartListening(*this); + } + pSheet->SetHelpId( aHelpFile, HID_PSEUDOSHEET_BACKGROUNDOBJECTS ); + + aName = SdResId(STR_PSEUDOSHEET_BACKGROUND); + if( (pSheet = Find(aName, SfxStyleFamily::Pseudo)) == nullptr ) + { + pSheet = &Make(aName, SfxStyleFamily::Pseudo, nUsedMask); + pSheet->SetParent( OUString() ); + static_cast<SfxStyleSheet*>(pSheet)->StartListening(*this); + } + pSheet->SetHelpId( aHelpFile, HID_PSEUDOSHEET_BACKGROUND ); + + aName = SdResId(STR_PSEUDOSHEET_NOTES); + if( (pSheet = Find(aName, SfxStyleFamily::Pseudo)) == nullptr ) + { + pSheet = &Make(aName, SfxStyleFamily::Pseudo, nUsedMask); + pSheet->SetParent( OUString() ); + static_cast<SfxStyleSheet*>(pSheet)->StartListening(*this); + } + pSheet->SetHelpId( aHelpFile, HID_PSEUDOSHEET_NOTES ); + + pParent = nullptr; + aName = SdResId(STR_PSEUDOSHEET_OUTLINE); + for (sal_Int32 nLevel = 1; nLevel < 10; nLevel++) + { + OUString aLevelName( aName + " " + OUString::number( nLevel ) ); + + if( (pSheet = Find(aLevelName, SfxStyleFamily::Pseudo)) == nullptr ) + { + pSheet = &Make(aLevelName, SfxStyleFamily::Pseudo, nUsedMask); + + if (pParent) + pSheet->SetParent(pParent->GetName()); + pParent = pSheet; + static_cast<SfxStyleSheet*>(pSheet)->StartListening(*this); + } + pSheet->SetHelpId( aHelpFile, HID_PSEUDOSHEET_OUTLINE + nLevel ); + } +} + +/************************************************************************* +|* +|* Set the correct name in the program language to the standard styles +|* +\************************************************************************/ + +namespace +{ +struct StyleSheetIsUserDefinedPredicate : svl::StyleSheetPredicate +{ + StyleSheetIsUserDefinedPredicate() + {} + + bool Check(const SfxStyleSheetBase& sheet) override + { + return sheet.IsUserDefined(); + } +}; +} + +void SdStyleSheetPool::UpdateStdNames() +{ + OUString aHelpFile; + StyleSheetIsUserDefinedPredicate aPredicate; + std::vector<SfxStyleSheetBase*> aEraseList; + std::vector<sal_Int32> aUserDefinedStyles = GetIndexedStyleSheets().FindPositionsByPredicate(aPredicate); + for (const auto& rStyle : aUserDefinedStyles) + { + SfxStyleSheetBase* pStyle = GetStyleSheetByPositionInIndex(rStyle); + + if( !pStyle->IsUserDefined() ) + { + OUString aOldName = pStyle->GetName(); + sal_uLong nHelpId = pStyle->GetHelpId( aHelpFile ); + SfxStyleFamily eFam = pStyle->GetFamily(); + + bool bHelpKnown = true; + TranslateId pNameId; + switch( nHelpId ) + { + case HID_STANDARD_STYLESHEET_NAME: pNameId = STR_STANDARD_STYLESHEET_NAME; break; + case HID_POOLSHEET_OBJWITHOUTFILL: pNameId = STR_POOLSHEET_OBJWITHOUTFILL; break; + case HID_POOLSHEET_OBJNOLINENOFILL: pNameId = STR_POOLSHEET_OBJNOLINENOFILL;break; + case HID_POOLSHEET_TEXT: pNameId = STR_POOLSHEET_TEXT; break; + case HID_POOLSHEET_A4: pNameId = STR_POOLSHEET_A4; break; + case HID_POOLSHEET_A4_TITLE: pNameId = STR_POOLSHEET_A4_TITLE; break; + case HID_POOLSHEET_A4_HEADLINE: pNameId = STR_POOLSHEET_A4_HEADLINE; break; + case HID_POOLSHEET_A4_TEXT: pNameId = STR_POOLSHEET_A4_TEXT; break; + case HID_POOLSHEET_A0: pNameId = STR_POOLSHEET_A0; break; + case HID_POOLSHEET_A0_TITLE: pNameId = STR_POOLSHEET_A0_TITLE; break; + case HID_POOLSHEET_A0_HEADLINE: pNameId = STR_POOLSHEET_A0_HEADLINE; break; + case HID_POOLSHEET_A0_TEXT: pNameId = STR_POOLSHEET_A0_TEXT; break; + case HID_POOLSHEET_GRAPHIC: pNameId = STR_POOLSHEET_GRAPHIC; break; + case HID_POOLSHEET_SHAPES: pNameId = STR_POOLSHEET_SHAPES; break; + case HID_POOLSHEET_FILLED: pNameId = STR_POOLSHEET_FILLED; break; + case HID_POOLSHEET_FILLED_BLUE: pNameId = STR_POOLSHEET_FILLED_BLUE; break; + case HID_POOLSHEET_FILLED_GREEN: pNameId = STR_POOLSHEET_FILLED_GREEN; break; + case HID_POOLSHEET_FILLED_RED: pNameId = STR_POOLSHEET_FILLED_RED; break; + case HID_POOLSHEET_FILLED_YELLOW: pNameId = STR_POOLSHEET_FILLED_YELLOW; break; + case HID_POOLSHEET_OUTLINE: pNameId = STR_POOLSHEET_OUTLINE; break; + case HID_POOLSHEET_OUTLINE_BLUE: pNameId = STR_POOLSHEET_OUTLINE_BLUE; break; + case HID_POOLSHEET_OUTLINE_GREEN: pNameId = STR_POOLSHEET_OUTLINE_GREEN; break; + case HID_POOLSHEET_OUTLINE_RED: pNameId = STR_POOLSHEET_OUTLINE_RED; break; + case HID_POOLSHEET_OUTLINE_YELLOW: pNameId = STR_POOLSHEET_OUTLINE_YELLOW; break; + case HID_POOLSHEET_LINES: pNameId = STR_POOLSHEET_LINES; break; + case HID_POOLSHEET_MEASURE: pNameId = STR_POOLSHEET_MEASURE; break; + case HID_POOLSHEET_LINES_DASHED: pNameId = STR_POOLSHEET_LINES_DASHED; break; + + case HID_PSEUDOSHEET_OUTLINE1: + case HID_PSEUDOSHEET_OUTLINE2: + case HID_PSEUDOSHEET_OUTLINE3: + case HID_PSEUDOSHEET_OUTLINE4: + case HID_PSEUDOSHEET_OUTLINE5: + case HID_PSEUDOSHEET_OUTLINE6: + case HID_PSEUDOSHEET_OUTLINE7: + case HID_PSEUDOSHEET_OUTLINE8: + case HID_PSEUDOSHEET_OUTLINE9: pNameId = STR_PSEUDOSHEET_OUTLINE; break; + case HID_PSEUDOSHEET_BACKGROUNDOBJECTS: pNameId = STR_PSEUDOSHEET_BACKGROUNDOBJECTS; break; + case HID_PSEUDOSHEET_BACKGROUND: pNameId = STR_PSEUDOSHEET_BACKGROUND; break; + case HID_PSEUDOSHEET_NOTES: pNameId = STR_PSEUDOSHEET_NOTES; break; + + case HID_SD_CELL_STYLE_DEFAULT: pNameId = STR_STANDARD_STYLESHEET_NAME; break; + case HID_SD_CELL_STYLE_BANDED: pNameId = STR_POOLSHEET_BANDED_CELL; break; + case HID_SD_CELL_STYLE_HEADER: pNameId = STR_POOLSHEET_HEADER; break; + case HID_SD_CELL_STYLE_TOTAL: pNameId = STR_POOLSHEET_TOTAL; break; + case HID_SD_CELL_STYLE_FIRST_COLUMN: pNameId = STR_POOLSHEET_FIRST_COLUMN; break; + case HID_SD_CELL_STYLE_LAST_COLUMN: pNameId = STR_POOLSHEET_LAST_COLUMN; break; + + default: + // 0 or wrong (old) HelpId + bHelpKnown = false; + } + if( bHelpKnown ) + { + OUString aNewName; + if (pNameId) + { + if (pNameId == STR_PSEUDOSHEET_OUTLINE) + { + aNewName += " " + OUString::number( sal_Int32( nHelpId - HID_PSEUDOSHEET_OUTLINE ) ); + } + } + + if( !aNewName.isEmpty() && aNewName != aOldName ) + { + SfxStyleSheetBase* pSheetFound = Find( aNewName, eFam ); + + if ( !pSheetFound ) + { + // Sheet does not yet exist: rename old sheet + pStyle->SetName( aNewName ); // transform also parents + } + else + { + // Sheet does exist: old sheet has to be removed + aEraseList.push_back( pStyle ); + } + } + } + } + } + + if (!aEraseList.empty()) + { + // styles that could not be renamed, must be removed + for (SfxStyleSheetBase* p : aEraseList) + Remove( p ); + Reindex(); + } +} + +void SdStyleSheetPool::setDefaultOutlineNumberFormatBulletAndIndent(sal_uInt16 i, SvxNumberFormat &rNumberFormat) +{ + rNumberFormat.SetBulletChar( 0x25CF ); // StarBats: 0xF000 + 34 + rNumberFormat.SetBulletRelSize(45); + const auto nLSpace = (i + 1) * 1200; + rNumberFormat.SetAbsLSpace(nLSpace); + sal_Int32 nFirstLineOffset = -600; + + switch(i) + { + case 0: + { + nFirstLineOffset = -900; + } + break; + + case 1: + { + rNumberFormat.SetBulletChar( 0x2013 ); // StarBats: 0xF000 + 150 + rNumberFormat.SetBulletRelSize(75); + nFirstLineOffset = -900; + } + break; + + case 2: + { + nFirstLineOffset = -800; + } + break; + + case 3: + { + rNumberFormat.SetBulletChar( 0x2013 ); // StarBats: 0xF000 + 150 + rNumberFormat.SetBulletRelSize(75); + } + break; + } + + rNumberFormat.SetFirstLineOffset(nFirstLineOffset); +} + +// Set new SvxNumBulletItem for the respective style sheet +void SdStyleSheetPool::PutNumBulletItem( SfxStyleSheetBase* pSheet, + vcl::Font& rBulletFont ) +{ + OUString aHelpFile; + sal_uLong nHelpId = pSheet->GetHelpId( aHelpFile ); + SfxItemSet& rSet = pSheet->GetItemSet(); + + switch ( nHelpId ) + { + case HID_STANDARD_STYLESHEET_NAME : + { + // Standard template + SvxNumberFormat aNumberFormat(SVX_NUM_CHAR_SPECIAL); + aNumberFormat.SetBulletFont(&rBulletFont); + aNumberFormat.SetBulletChar( 0x25CF ); // StarBats: 0xF000 + 34 + aNumberFormat.SetBulletRelSize(45); + aNumberFormat.SetBulletColor(COL_AUTO); + aNumberFormat.SetStart(1); + aNumberFormat.SetNumAdjust(SvxAdjust::Left); + + SvxNumRule aNumRule( SvxNumRuleFlags::BULLET_REL_SIZE | SvxNumRuleFlags::BULLET_COLOR, SVX_MAX_NUM, false); + + for( sal_uInt16 i = 0; i < aNumRule.GetLevelCount(); i++ ) + { + const auto nLSpace = (i + 1) * 600; + aNumberFormat.SetAbsLSpace(nLSpace); + aNumberFormat.SetFirstLineOffset(-600); + aNumRule.SetLevel( i, aNumberFormat ); + } + + rSet.Put( SvxNumBulletItem( std::move(aNumRule), EE_PARA_NUMBULLET ) ); + static_cast<SfxStyleSheet*>(pSheet)->Broadcast(SfxHint( SfxHintId::DataChanged ) ); + } + break; + + case HID_PSEUDOSHEET_TITLE: + /* title gets same bullet as subtitle and not that page symbol anymore */ + case HID_PSEUDOSHEET_SUBTITLE : + { + // Subtitle template + SvxNumBulletItem const*const pItem( + rSet.GetPool()->GetSecondaryPool()->GetPoolDefaultItem(EE_PARA_NUMBULLET)); + const SvxNumRule *const pDefaultRule = pItem ? &pItem->GetNumRule() : nullptr; + DBG_ASSERT( pDefaultRule, "Where is my default template? [CL]" ); + + if(pDefaultRule) + { + SvxNumRule aNumRule(pDefaultRule->GetFeatureFlags(), 10, false); + for(sal_uInt16 i=0; i < aNumRule.GetLevelCount(); i++) + { + SvxNumberFormat aFrmt( pDefaultRule->GetLevel(i) ); + aFrmt.SetNumberingType(SVX_NUM_CHAR_SPECIAL); + // #i93908# clear suffix for bullet lists + aFrmt.SetPrefix(OUString()); + aFrmt.SetSuffix(OUString()); + aFrmt.SetStart(1); + aFrmt.SetBulletRelSize(45); + aFrmt.SetBulletChar( 0x25CF ); // StarBats: 0xF000 + 34 + aFrmt.SetBulletFont(&rBulletFont); + aNumRule.SetLevel(i, aFrmt); + } + + rSet.Put( SvxNumBulletItem( std::move(aNumRule), EE_PARA_NUMBULLET ) ); + static_cast<SfxStyleSheet*>(pSheet)->Broadcast(SfxHint( SfxHintId::DataChanged ) ); + } + } + break; + + case HID_PSEUDOSHEET_OUTLINE + 1 : + { + // Outline template + SvxNumberFormat aNumberFormat(SVX_NUM_CHAR_SPECIAL); + aNumberFormat.SetBulletColor(COL_AUTO); + aNumberFormat.SetStart(1); + aNumberFormat.SetNumAdjust(SvxAdjust::Left); + + SvxNumRule aNumRule( SvxNumRuleFlags::BULLET_REL_SIZE | SvxNumRuleFlags::BULLET_COLOR, + SVX_MAX_NUM, false ); + for( sal_uInt16 i = 0; i < aNumRule.GetLevelCount(); i++ ) + { + setDefaultOutlineNumberFormatBulletAndIndent(i, aNumberFormat); + rBulletFont.SetFontSize(Size(0,846)); // 24 pt + aNumberFormat.SetBulletFont(&rBulletFont); + aNumRule.SetLevel( i, aNumberFormat ); + } + + rSet.Put( SvxNumBulletItem( std::move(aNumRule), EE_PARA_NUMBULLET ) ); + static_cast<SfxStyleSheet*>(pSheet)->Broadcast(SfxHint( SfxHintId::DataChanged ) ); + } + break; + } +} + +/************************************************************************* +|* +|* Create standard bullet font (without size) +|* +\************************************************************************/ + +vcl::Font SdStyleSheetPool::GetBulletFont() +{ + vcl::Font aBulletFont( "StarSymbol", Size(0, 1000) ); + aBulletFont.SetCharSet(RTL_TEXTENCODING_UNICODE); + aBulletFont.SetWeight(WEIGHT_NORMAL); + aBulletFont.SetUnderline(LINESTYLE_NONE); + aBulletFont.SetOverline(LINESTYLE_NONE); + aBulletFont.SetStrikeout(STRIKEOUT_NONE); + aBulletFont.SetItalic(ITALIC_NONE); + aBulletFont.SetOutline(false); + aBulletFont.SetShadow(false); + aBulletFont.SetColor(COL_AUTO); + aBulletFont.SetTransparent(true); + + return aBulletFont; +} + +void SdStyleSheetPool::AddStyleFamily( const SdPage* pPage ) +{ + rtl::Reference< SfxStyleSheetPool > xPool( this ); + maStyleFamilyMap[pPage] = new SdStyleFamily( xPool, pPage ); +} + +void SdStyleSheetPool::RemoveStyleFamily( const SdPage* pPage ) +{ + SdStyleFamilyMap::iterator iter( maStyleFamilyMap.find( pPage ) ); + if( iter == maStyleFamilyMap.end() ) + return; + + SdStyleFamilyRef xStyle( (*iter).second ); + maStyleFamilyMap.erase( iter ); + + if( xStyle.is() ) try + { + xStyle->dispose(); + } + catch( Exception& ) + { + } +} + +void SdStyleSheetPool::throwIfDisposed() +{ + if( mpDoc == nullptr ) + throw DisposedException(); +} + +// XServiceInfo +OUString SAL_CALL SdStyleSheetPool::getImplementationName() +{ + return "SdStyleSheetPool"; +} + +sal_Bool SAL_CALL SdStyleSheetPool::supportsService( const OUString& ServiceName ) +{ + return cppu::supportsService(this, ServiceName); +} + +Sequence< OUString > SAL_CALL SdStyleSheetPool::getSupportedServiceNames() +{ + return { "com.sun.star.style.StyleFamilies" }; +} + +// XNameAccess +Any SAL_CALL SdStyleSheetPool::getByName( const OUString& aName ) +{ + throwIfDisposed(); + + if( mxGraphicFamily->getName() == aName ) + return Any( Reference< XNameAccess >( static_cast< XNameAccess* >( mxGraphicFamily.get() ) ) ); + + if( mxCellFamily->getName() == aName ) + return Any( Reference< XNameAccess >( static_cast< XNameAccess* >( mxCellFamily.get() ) ) ); + + if( msTableFamilyName == aName ) + return Any( mxTableFamily ); + + auto iter = std::find_if(maStyleFamilyMap.begin(), maStyleFamilyMap.end(), + [&aName](const SdStyleFamilyMap::value_type& rEntry) { return rEntry.second->getName() == aName; }); + if (iter != maStyleFamilyMap.end()) + return Any( Reference< XNameAccess >( static_cast< XNameAccess* >( (*iter).second.get() ) ) ); + + throw NoSuchElementException(); +} + +Sequence< OUString > SAL_CALL SdStyleSheetPool::getElementNames() +{ + throwIfDisposed(); + + Sequence< OUString > aNames( maStyleFamilyMap.size() + 3 ); + OUString* pNames = aNames.getArray(); + + *pNames++ = mxGraphicFamily->getName(); + *pNames++ = mxCellFamily->getName(); + *pNames++ = msTableFamilyName; + + for( const auto& rEntry : maStyleFamilyMap ) + { + *pNames++ = rEntry.second->getName(); + } + + return aNames; +} + +sal_Bool SAL_CALL SdStyleSheetPool::hasByName( const OUString& aName ) +{ + throwIfDisposed(); + + if( mxGraphicFamily->getName() == aName ) + return true; + + if( mxCellFamily->getName() == aName ) + return true; + + if( msTableFamilyName == aName ) + return true; + + return std::any_of(maStyleFamilyMap.begin(), maStyleFamilyMap.end(), + [&aName](const SdStyleFamilyMap::value_type& rEntry) { return rEntry.second->getName() == aName; }); +} + +// XElementAccess + +Type SAL_CALL SdStyleSheetPool::getElementType() +{ + throwIfDisposed(); + + return cppu::UnoType<XNameAccess>::get(); +} + +sal_Bool SAL_CALL SdStyleSheetPool::hasElements() +{ + return true; +} + +// XIndexAccess + +sal_Int32 SAL_CALL SdStyleSheetPool::getCount() +{ + throwIfDisposed(); + + return maStyleFamilyMap.size() + 3; +} + +Any SAL_CALL SdStyleSheetPool::getByIndex( sal_Int32 Index ) +{ + switch( Index ) + { + case 0: + return Any( Reference< XNameAccess >( static_cast< XNameAccess* >( mxGraphicFamily.get() ) ) ); + + case 1: + return Any( Reference< XNameAccess >( static_cast< XNameAccess* >( mxCellFamily.get() ) ) ); + + case 2: + return Any( mxTableFamily ); + + default: + { + Index -= 3; + if( (Index < 0) || (Index >= sal::static_int_cast<sal_Int32>(maStyleFamilyMap.size())) ) + throw IndexOutOfBoundsException(); + SdStyleFamilyMap::iterator iter( maStyleFamilyMap.begin() ); + std::advance(iter, Index); + + return Any( Reference< XNameAccess >( static_cast< XNameAccess* >( (*iter).second.get() ) ) ); + } + } +} + +// XComponent + +void SAL_CALL SdStyleSheetPool::dispose() +{ + if( !mpDoc ) + return; + + mxGraphicFamily->dispose(); + mxGraphicFamily.clear(); + mxCellFamily->dispose(); + mxCellFamily.clear(); + + Reference< XComponent > xComp( mxTableFamily, UNO_QUERY ); + if( xComp.is() ) + xComp->dispose(); + mxTableFamily = nullptr; + + SdStyleFamilyMap aTempMap; + aTempMap.swap( maStyleFamilyMap ); + + for( auto& rEntry : aTempMap ) try + { + rEntry.second->dispose(); + } + catch( Exception& ) + { + } + + mpDoc = nullptr; + + Clear(); +} + +void SAL_CALL SdStyleSheetPool::addEventListener( const Reference< XEventListener >& /*xListener*/ ) +{ +} + +void SAL_CALL SdStyleSheetPool::removeEventListener( const Reference< XEventListener >& /*aListener*/ ) +{ +} + +SdStyleSheetVector SdStyleSheetPool::CreateChildList( SdStyleSheet const * pSheet ) +{ + SdStyleSheetVector aResult; + + const size_t nListenerCount = pSheet->GetSizeOfVector(); + for (size_t n = 0; n < nListenerCount; ++n) + { + SdStyleSheet* pChild = dynamic_cast< SdStyleSheet* >( pSheet->GetListener(n) ); + if(pChild && pChild->GetParent() == pSheet->GetName()) + { + aResult.emplace_back( pChild ); + } + } + + return aResult; +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/core/stlsheet.cxx b/sd/source/core/stlsheet.cxx new file mode 100644 index 000000000..db061922b --- /dev/null +++ b/sd/source/core/stlsheet.cxx @@ -0,0 +1,1459 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include <com/sun/star/beans/PropertyAttribute.hpp> +#include <com/sun/star/beans/XPropertySet.hpp> +#include <com/sun/star/lang/DisposedException.hpp> +#include <com/sun/star/lang/WrappedTargetRuntimeException.hpp> +#include <com/sun/star/style/XStyle.hpp> +#include <com/sun/star/table/BorderLine.hpp> +#include <com/sun/star/text/XTextColumns.hpp> + +#include <o3tl/string_view.hxx> +#include <osl/mutex.hxx> +#include <vcl/svapp.hxx> +#include <cppuhelper/exc_hlp.hxx> +#include <cppuhelper/supportsservice.hxx> + +#include <editeng/eeitem.hxx> +#include <editeng/fhgtitem.hxx> +#include <editeng/ulspitem.hxx> +#include <svl/hint.hxx> +#include <svl/intitem.hxx> +#include <svl/itemset.hxx> + +#include <svx/xflbmtit.hxx> +#include <svx/xflbstit.hxx> +#include <editeng/bulletitem.hxx> +#include <editeng/lrspitem.hxx> +#include <svx/unoshprp.hxx> +#include <svx/unoshape.hxx> +#include <svx/svdpool.hxx> +#include <svx/sdmetitm.hxx> +#include <svx/sdtaaitm.hxx> +#include <svx/sdtacitm.hxx> +#include <svx/sdtayitm.hxx> +#include <svx/sdtaiitm.hxx> +#include <svx/xit.hxx> +#include <svx/xflclit.hxx> +#include <tools/diagnose_ex.h> +#include <stlsheet.hxx> +#include <sdresid.hxx> +#include <sdpage.hxx> +#include <drawdoc.hxx> +#include <stlpool.hxx> +#include <strings.hrc> +#include <app.hrc> +#include <strings.hxx> +#include <glob.hxx> +#include <DrawViewShell.hxx> +#include <ViewShellBase.hxx> + +#include <cstddef> +#include <memory> +#include <string_view> + +using ::osl::MutexGuard; +using ::osl::ClearableMutexGuard; +using ::com::sun::star::table::BorderLine; +using namespace ::com::sun::star::uno; +using namespace ::com::sun::star::util; +using namespace ::com::sun::star::lang; +using namespace ::com::sun::star::style; +using namespace ::com::sun::star::beans; +using namespace ::com::sun::star::container; +using namespace ::com::sun::star::drawing; + +#define WID_STYLE_HIDDEN 7997 +#define WID_STYLE_DISPNAME 7998 +#define WID_STYLE_FAMILY 7999 + +static SvxItemPropertySet& GetStylePropertySet() +{ + static const SfxItemPropertyMapEntry aFullPropertyMap_Impl[] = + { + { u"Family", WID_STYLE_FAMILY, ::cppu::UnoType<OUString>::get(), PropertyAttribute::READONLY, 0}, + { u"UserDefinedAttributes", SDRATTR_XMLATTRIBUTES, cppu::UnoType<XNameContainer>::get(), 0, 0}, + { u"DisplayName", WID_STYLE_DISPNAME, ::cppu::UnoType<OUString>::get(), PropertyAttribute::READONLY, 0}, + { u"Hidden", WID_STYLE_HIDDEN, cppu::UnoType<bool>::get(), 0, 0}, + + SVX_UNOEDIT_NUMBERING_PROPERTY, + SHADOW_PROPERTIES + LINE_PROPERTIES + LINE_PROPERTIES_START_END + FILL_PROPERTIES + EDGERADIUS_PROPERTIES + TEXT_PROPERTIES_DEFAULTS + CONNECTOR_PROPERTIES + SPECIAL_DIMENSIONING_PROPERTIES_DEFAULTS + { u"TopBorder", SDRATTR_TABLE_BORDER, ::cppu::UnoType<BorderLine>::get(), 0, TOP_BORDER }, + { u"BottomBorder", SDRATTR_TABLE_BORDER, ::cppu::UnoType<BorderLine>::get(), 0, BOTTOM_BORDER }, + { u"LeftBorder", SDRATTR_TABLE_BORDER, ::cppu::UnoType<BorderLine>::get(), 0, LEFT_BORDER }, + { u"RightBorder", SDRATTR_TABLE_BORDER, ::cppu::UnoType<BorderLine>::get(), 0, RIGHT_BORDER }, + { u"", 0, css::uno::Type(), 0, 0 } + }; + + static SvxItemPropertySet aPropSet( aFullPropertyMap_Impl, SdrObject::GetGlobalDrawObjectItemPool() ); + return aPropSet; +} + +class ModifyListenerForwarder : public SfxListener +{ +public: + explicit ModifyListenerForwarder( SdStyleSheet* pStyleSheet ); + + virtual void Notify(SfxBroadcaster& rBC, const SfxHint& rHint) override; + +private: + SdStyleSheet* mpStyleSheet; +}; + +ModifyListenerForwarder::ModifyListenerForwarder( SdStyleSheet* pStyleSheet ) +: mpStyleSheet( pStyleSheet ) +{ + if( pStyleSheet ) + { + SfxBroadcaster& rBC = static_cast< SfxBroadcaster& >( *pStyleSheet ); + StartListening( rBC ); + } +} + +void ModifyListenerForwarder::Notify(SfxBroadcaster& /*rBC*/, const SfxHint& /*rHint*/) +{ + if( mpStyleSheet ) + mpStyleSheet->notifyModifyListener(); +} + +SdStyleSheet::SdStyleSheet(const OUString& rDisplayName, SfxStyleSheetBasePool& _rPool, SfxStyleFamily eFamily, SfxStyleSearchBits _nMask) +: SdStyleSheetBase( rDisplayName, _rPool, eFamily, _nMask) +, ::cppu::BaseMutex() +, msApiName( rDisplayName ) +, mxPool( &_rPool ) +, mrBHelper( m_aMutex ) +{ +} + +SdStyleSheet::~SdStyleSheet() +{ + delete pSet; + pSet = nullptr; // that following destructors also get a change +} + +void SdStyleSheet::SetApiName( const OUString& rApiName ) +{ + msApiName = rApiName; +} + +OUString const & SdStyleSheet::GetApiName() const +{ + if( !msApiName.isEmpty() ) + return msApiName; + else + return GetName(); +} + +bool SdStyleSheet::SetParent(const OUString& rParentName) +{ + bool bResult = false; + + if (SfxStyleSheet::SetParent(rParentName)) + { + // PseudoStyleSheets do not have their own ItemSets + if (nFamily != SfxStyleFamily::Pseudo) + { + if( !rParentName.isEmpty() ) + { + SfxStyleSheetBase* pStyle = m_pPool->Find(rParentName, nFamily); + if (pStyle) + { + bResult = true; + SfxItemSet& rParentSet = pStyle->GetItemSet(); + GetItemSet().SetParent(&rParentSet); + Broadcast( SfxHint( SfxHintId::DataChanged ) ); + } + } + else + { + bResult = true; + GetItemSet().SetParent(nullptr); + Broadcast( SfxHint( SfxHintId::DataChanged ) ); + } + } + else + { + bResult = true; + } + } + return bResult; +} + +/** + * create if necessary and return ItemSets + */ +SfxItemSet& SdStyleSheet::GetItemSet() +{ + if (nFamily == SfxStyleFamily::Para || nFamily == SfxStyleFamily::Page) + { + // we create the ItemSet 'on demand' if necessary + if (!pSet) + { + pSet = new SfxItemSetFixed< + XATTR_LINE_FIRST, XATTR_LINE_LAST, + XATTR_FILL_FIRST, XATTR_FILL_LAST, + SDRATTR_SHADOW_FIRST, SDRATTR_SHADOW_LAST, + SDRATTR_TEXT_MINFRAMEHEIGHT, SDRATTR_TEXT_WORDWRAP, + SDRATTR_EDGE_FIRST, SDRATTR_MEASURE_LAST, + SDRATTR_3D_FIRST, SDRATTR_3D_LAST, + EE_PARA_START, EE_CHAR_END>(GetPool()->GetPool()); + bMySet = true; + } + + return *pSet; + } + + else if( nFamily == SfxStyleFamily::Frame ) + { + if (!pSet) + { + pSet = new SfxItemSetFixed< + XATTR_LINE_FIRST, XATTR_LINE_LAST, + XATTR_FILL_FIRST, XATTR_FILL_LAST, + SDRATTR_SHADOW_FIRST, SDRATTR_SHADOW_LAST, + SDRATTR_TEXT_MINFRAMEHEIGHT, SDRATTR_XMLATTRIBUTES, + SDRATTR_TEXT_WORDWRAP, SDRATTR_TEXT_WORDWRAP, + SDRATTR_TABLE_FIRST, SDRATTR_TABLE_LAST, + EE_PARA_START, EE_CHAR_END>(GetPool()->GetPool()); + + bMySet = true; + } + + return *pSet; + } + + // this is a dummy template for the internal template of the + // current presentation layout; return the ItemSet of that template + else + { + + SdStyleSheet* pSdSheet = GetRealStyleSheet(); + + if (pSdSheet) + { + return pSdSheet->GetItemSet(); + } + else + { + if (!pSet) + { + pSet = new SfxItemSetFixed< + XATTR_LINE_FIRST, XATTR_LINE_LAST, + XATTR_FILL_FIRST, XATTR_FILL_LAST, + SDRATTR_SHADOW_FIRST, SDRATTR_SHADOW_LAST, + SDRATTR_TEXT_MINFRAMEHEIGHT, SDRATTR_TEXT_WORDWRAP, + SDRATTR_EDGE_FIRST, SDRATTR_MEASURE_LAST, + SDRATTR_3D_FIRST, SDRATTR_3D_LAST, + EE_PARA_START, EE_CHAR_END>(GetPool()->GetPool()); + bMySet = true; + } + + return(*pSet); + } + } +} + +/** + * A template is used when it is referenced by inserted object or by a used + * template. + */ +bool SdStyleSheet::IsUsed() const +{ + bool bResult = false; + + const size_t nListenerCount = GetSizeOfVector(); + for (size_t n = 0; n < nListenerCount; ++n) + { + SfxListener* pListener = GetListener(n); + if( pListener == this ) + continue; + + const svl::StyleSheetUser* const pUser(dynamic_cast<svl::StyleSheetUser*>(pListener)); + if (pUser) + bResult = pUser->isUsedByModel(); + if (bResult) + break; + } + + if( !bResult ) + { + MutexGuard aGuard( mrBHelper.rMutex ); + + cppu::OInterfaceContainerHelper * pContainer = mrBHelper.getContainer( cppu::UnoType<XModifyListener>::get() ); + if( pContainer ) + { + const Sequence< Reference< XInterface > > aModifyListeners( pContainer->getElements() ); + bResult = std::any_of(aModifyListeners.begin(), aModifyListeners.end(), + [](const Reference<XInterface>& rListener) { + Reference< XStyle > xStyle( rListener, UNO_QUERY ); + return xStyle.is() && xStyle->isInUse(); + }); + } + } + return bResult; +} + +/** + * Determine the style sheet for which this dummy is for. + */ +SdStyleSheet* SdStyleSheet::GetRealStyleSheet() const +{ + OUString aRealStyle; + OUString aSep( SD_LT_SEPARATOR ); + SdStyleSheet* pRealStyle = nullptr; + SdDrawDocument* pDoc = static_cast<SdStyleSheetPool*>(m_pPool)->GetDoc(); + + ::sd::DrawViewShell* pDrawViewShell = nullptr; + + ::sd::ViewShellBase* pBase = dynamic_cast< ::sd::ViewShellBase* >( SfxViewShell::Current() ); + if( pBase ) + pDrawViewShell = dynamic_cast< ::sd::DrawViewShell* >( pBase->GetMainViewShell().get() ); + + if (pDrawViewShell && pDrawViewShell->GetDoc() == pDoc) + { + SdPage* pPage = pDrawViewShell->getCurrentPage(); + if( pPage ) + { + aRealStyle = pPage->GetLayoutName(); + // cut after separator string + + if( aRealStyle.indexOf(aSep) >= 0) + { + aRealStyle = aRealStyle.copy(0,(aRealStyle.indexOf(aSep) + aSep.getLength())); + } + } + } + if (aRealStyle.isEmpty()) + { + SdPage* pPage = pDoc->GetSdPage(0, PageKind::Standard); + + if (pPage) + { + aRealStyle = pDoc->GetSdPage(0, PageKind::Standard)->GetLayoutName(); + } + else + { + /* no page available yet. This can happen when actualizing the + document templates. */ + SfxStyleSheetIterator aIter(m_pPool, SfxStyleFamily::Page); + SfxStyleSheetBase* pSheet = aIter.First(); + if( pSheet ) + aRealStyle = pSheet->GetName(); + } + + if( aRealStyle.indexOf(aSep) >= 0) + { + aRealStyle = aRealStyle.copy(0,(aRealStyle.indexOf(aSep) + aSep.getLength())); + } + } + + /* now map from the name (specified for country language) to the internal + name (independent of the country language) */ + OUString aInternalName; + OUString aStyleName(aName); + + if (aStyleName == SdResId(STR_PSEUDOSHEET_TITLE)) + { + aInternalName = STR_LAYOUT_TITLE; + } + else if (aStyleName == SdResId(STR_PSEUDOSHEET_SUBTITLE)) + { + aInternalName = STR_LAYOUT_SUBTITLE; + } + else if (aStyleName == SdResId(STR_PSEUDOSHEET_BACKGROUND)) + { + aInternalName = STR_LAYOUT_BACKGROUND; + } + else if (aStyleName == SdResId(STR_PSEUDOSHEET_BACKGROUNDOBJECTS)) + { + aInternalName = STR_LAYOUT_BACKGROUNDOBJECTS; + } + else if (aStyleName == SdResId(STR_PSEUDOSHEET_NOTES)) + { + aInternalName = STR_LAYOUT_NOTES; + } + else + { + OUString aOutlineStr(SdResId(STR_PSEUDOSHEET_OUTLINE)); + sal_Int32 nPos = aStyleName.indexOf(aOutlineStr); + if (nPos >= 0) + { + std::u16string_view aNumStr(aStyleName.subView(aOutlineStr.getLength())); + aInternalName = STR_LAYOUT_OUTLINE + aNumStr; + } + } + + aRealStyle += aInternalName; + pRealStyle = static_cast< SdStyleSheet* >( m_pPool->Find(aRealStyle, SfxStyleFamily::Page) ); + +#ifdef DBG_UTIL + if( !pRealStyle ) + { + SfxStyleSheetIterator aIter(m_pPool, SfxStyleFamily::Page); + if( aIter.Count() > 0 ) + // StyleSheet not found, but pool already loaded + DBG_ASSERT(pRealStyle, "Internal StyleSheet not found"); + } +#endif + + return pRealStyle; +} + +/** + * Determine pseudo style sheet which stands for this style sheet. + */ +SdStyleSheet* SdStyleSheet::GetPseudoStyleSheet() const +{ + SdStyleSheet* pPseudoStyle = nullptr; + OUString aSep( SD_LT_SEPARATOR ); + OUString aStyleName(aName); + // without layout name and separator + + if( aStyleName.indexOf(aSep) >=0 ) + { + aStyleName = aStyleName.copy (aStyleName.indexOf(aSep) + aSep.getLength()); + } + + if (aStyleName == STR_LAYOUT_TITLE) + { + aStyleName = SdResId(STR_PSEUDOSHEET_TITLE); + } + else if (aStyleName == STR_LAYOUT_SUBTITLE) + { + aStyleName = SdResId(STR_PSEUDOSHEET_SUBTITLE); + } + else if (aStyleName == STR_LAYOUT_BACKGROUND) + { + aStyleName = SdResId(STR_PSEUDOSHEET_BACKGROUND); + } + else if (aStyleName == STR_LAYOUT_BACKGROUNDOBJECTS) + { + aStyleName = SdResId(STR_PSEUDOSHEET_BACKGROUNDOBJECTS); + } + else if (aStyleName == STR_LAYOUT_NOTES) + { + aStyleName = SdResId(STR_PSEUDOSHEET_NOTES); + } + else + { + OUString aOutlineStr(STR_LAYOUT_OUTLINE); + sal_Int32 nPos = aStyleName.indexOf(aOutlineStr); + if (nPos != -1) + { + std::u16string_view aNumStr(aStyleName.subView(aOutlineStr.getLength())); + aStyleName = SdResId(STR_PSEUDOSHEET_OUTLINE) + aNumStr; + } + } + + pPseudoStyle = static_cast<SdStyleSheet*>(m_pPool->Find(aStyleName, SfxStyleFamily::Pseudo)); + DBG_ASSERT(pPseudoStyle, "PseudoStyleSheet missing"); + + return pPseudoStyle; +} + +void SdStyleSheet::Notify(SfxBroadcaster& rBC, const SfxHint& rHint) +{ + // first, base class functionality + SfxStyleSheet::Notify(rBC, rHint); + + if (nFamily != SfxStyleFamily::Pseudo) + return; + + /* if the dummy gets a notify about a changed attribute, he takes care that + the actual meant style sheet sends broadcasts. */ + if (rHint.GetId() == SfxHintId::DataChanged) + { + SdStyleSheet* pRealStyle = GetRealStyleSheet(); + if (pRealStyle) + pRealStyle->Broadcast(rHint); + } +} + +/** + * Adjust the bullet width and the left text indent of the provided ItemSets to + * their font height. The new values are calculated that the ratio to the font + * height is as in the style sheet. + * + * @param bOnlyMissingItems If sal_True, only not set items are completed. With + * sal_False, are items are overwritten. + */ +void SdStyleSheet::AdjustToFontHeight(SfxItemSet& rSet, bool bOnlyMissingItems) +{ + /* If not explicit set, adjust bullet width and text indent to new font + height. */ + SfxStyleFamily eFamily = nFamily; + OUString aStyleName(aName); + if (eFamily == SfxStyleFamily::Pseudo) + { + SfxStyleSheet* pRealStyle = GetRealStyleSheet(); + eFamily = pRealStyle->GetFamily(); + aStyleName = pRealStyle->GetName(); + } + + if (!(eFamily == SfxStyleFamily::Page && + aStyleName.indexOf(STR_LAYOUT_OUTLINE) != -1 && + rSet.GetItemState(EE_CHAR_FONTHEIGHT) == SfxItemState::SET)) + return; + + const SfxItemSet* pCurSet = &GetItemSet(); + sal_uInt32 nNewHeight = rSet.Get(EE_CHAR_FONTHEIGHT).GetHeight(); + sal_uInt32 nOldHeight = pCurSet->Get(EE_CHAR_FONTHEIGHT).GetHeight(); + + if (rSet.GetItemState(EE_PARA_BULLET) != SfxItemState::SET || !bOnlyMissingItems) + { + const SvxBulletItem& rBItem = pCurSet->Get(EE_PARA_BULLET); + double fBulletFraction = double(rBItem.GetWidth()) / nOldHeight; + SvxBulletItem aNewBItem(rBItem); + aNewBItem.SetWidth(static_cast<sal_uInt32>(fBulletFraction * nNewHeight)); + rSet.Put(aNewBItem); + } + + if (rSet.GetItemState(EE_PARA_LRSPACE) != SfxItemState::SET || !bOnlyMissingItems) + { + const SvxLRSpaceItem& rLRItem = pCurSet->Get(EE_PARA_LRSPACE); + double fIndentFraction = double(rLRItem.GetTextLeft()) / nOldHeight; + SvxLRSpaceItem aNewLRItem(rLRItem); + aNewLRItem.SetTextLeft(fIndentFraction * nNewHeight); + double fFirstIndentFraction = double(rLRItem.GetTextFirstLineOffset()) / nOldHeight; + aNewLRItem.SetTextFirstLineOffset(static_cast<short>(fFirstIndentFraction * nNewHeight)); + rSet.Put(aNewLRItem); + } + + if (rSet.GetItemState(EE_PARA_ULSPACE) != SfxItemState::SET || !bOnlyMissingItems) + { + const SvxULSpaceItem& rULItem = pCurSet->Get(EE_PARA_ULSPACE); + SvxULSpaceItem aNewULItem(rULItem); + double fLowerFraction = double(rULItem.GetLower()) / nOldHeight; + aNewULItem.SetLower(static_cast<sal_uInt16>(fLowerFraction * nNewHeight)); + double fUpperFraction = double(rULItem.GetUpper()) / nOldHeight; + aNewULItem.SetUpper(static_cast<sal_uInt16>(fUpperFraction * nNewHeight)); + rSet.Put(aNewULItem); + } +} + +bool SdStyleSheet::HasFollowSupport() const +{ + return false; +} + +bool SdStyleSheet::HasParentSupport() const +{ + return true; +} + +bool SdStyleSheet::HasClearParentSupport() const +{ + return true; +} + +namespace +{ +struct ApiNameMap +{ + std::u16string_view mpApiName; + sal_uInt32 mnHelpId; +} const pApiNameMap[] + = { { std::u16string_view(u"title"), HID_PSEUDOSHEET_TITLE }, + { std::u16string_view(u"subtitle"), HID_PSEUDOSHEET_SUBTITLE }, + { std::u16string_view(u"background"), HID_PSEUDOSHEET_BACKGROUND }, + { std::u16string_view(u"backgroundobjects"), HID_PSEUDOSHEET_BACKGROUNDOBJECTS }, + { std::u16string_view(u"notes"), HID_PSEUDOSHEET_NOTES }, + { std::u16string_view(u"standard"), HID_STANDARD_STYLESHEET_NAME }, + { std::u16string_view(u"objectwithoutfill"), HID_POOLSHEET_OBJWITHOUTFILL }, + + { std::u16string_view(u"Text"), HID_POOLSHEET_TEXT }, + { std::u16string_view(u"A4"), HID_POOLSHEET_A4 }, + { std::u16string_view(u"Title A4"), HID_POOLSHEET_A4_TITLE }, + { std::u16string_view(u"Heading A4"), HID_POOLSHEET_A4_HEADLINE }, + { std::u16string_view(u"Text A4"), HID_POOLSHEET_A4_TEXT }, + { std::u16string_view(u"A0"), HID_POOLSHEET_A0 }, + { std::u16string_view(u"Title A0"), HID_POOLSHEET_A0_TITLE }, + { std::u16string_view(u"Heading A0"), HID_POOLSHEET_A0_HEADLINE }, + { std::u16string_view(u"Text A0"), HID_POOLSHEET_A0_TEXT }, + + { std::u16string_view(u"Graphic"), HID_POOLSHEET_GRAPHIC }, + { std::u16string_view(u"Shapes"), HID_POOLSHEET_SHAPES }, + { std::u16string_view(u"Filled"), HID_POOLSHEET_FILLED }, + { std::u16string_view(u"Filled Blue"), HID_POOLSHEET_FILLED_BLUE }, + { std::u16string_view(u"Filled Green"), HID_POOLSHEET_FILLED_GREEN }, + { std::u16string_view(u"Filled Red"), HID_POOLSHEET_FILLED_RED }, + { std::u16string_view(u"Filled Yellow"), HID_POOLSHEET_FILLED_YELLOW }, + { std::u16string_view(u"Outlined"), HID_POOLSHEET_OUTLINE }, + { std::u16string_view(u"Outlined Blue"), HID_POOLSHEET_OUTLINE_BLUE }, + { std::u16string_view(u"Outlined Green"), HID_POOLSHEET_OUTLINE_GREEN }, + { std::u16string_view(u"Outlined Red"), HID_POOLSHEET_OUTLINE_RED }, + { std::u16string_view(u"Outlined Yellow"), HID_POOLSHEET_OUTLINE_YELLOW }, + { std::u16string_view(u"Lines"), HID_POOLSHEET_LINES }, + { std::u16string_view(u"Arrow Line"), HID_POOLSHEET_MEASURE }, + { std::u16string_view(u"Arrow Dashed"), HID_POOLSHEET_LINES_DASHED } + }; + +OUString GetApiNameForHelpId(sal_uLong nId) +{ + if ((nId >= HID_PSEUDOSHEET_OUTLINE1) && (nId <= HID_PSEUDOSHEET_OUTLINE9)) + return "outline" + OUStringChar(sal_Unicode('1' + (nId - HID_PSEUDOSHEET_OUTLINE1))); + + for (const auto& i : pApiNameMap) + if (nId == i.mnHelpId) + return OUString(i.mpApiName); + + return OUString(); +} + +sal_uInt32 GetHelpIdForApiName(std::u16string_view sName) +{ + std::u16string_view sRest; + if (o3tl::starts_with(sName, u"outline", &sRest)) + { + if (sRest.length() == 1) + { + sal_Unicode ch = sRest.front(); + if ('1' <= ch && ch <= '9') + return HID_PSEUDOSHEET_OUTLINE1 + ch - '1'; + } + // No other pre-defined names start with "outline" + return 0; + } + + for (const auto& i : pApiNameMap) + if (sName == i.mpApiName) + return i.mnHelpId; + + return 0; +} +} + +void SdStyleSheet::SetHelpId( const OUString& r, sal_uLong nId ) +{ + SfxStyleSheet::SetHelpId( r, nId ); + + const OUString sNewApiName = GetApiNameForHelpId(nId); + if (!sNewApiName.isEmpty()) + msApiName = sNewApiName; +} + +OUString SdStyleSheet::GetFamilyString( SfxStyleFamily eFamily ) +{ + switch( eFamily ) + { + case SfxStyleFamily::Frame: + return "cell"; + default: + OSL_FAIL( "SdStyleSheet::GetFamilyString(), illegal family!" ); + [[fallthrough]]; + case SfxStyleFamily::Para: + return "graphics"; + } +} + +void SdStyleSheet::throwIfDisposed() +{ + if( !mxPool.is() ) + throw DisposedException(); +} + +rtl::Reference<SdStyleSheet> SdStyleSheet::CreateEmptyUserStyle( SfxStyleSheetBasePool& rPool, SfxStyleFamily eFamily ) +{ + OUString aName; + sal_Int32 nIndex = 1; + do + { + aName = "user" + OUString::number( nIndex++ ); + } + while( rPool.Find( aName, eFamily ) != nullptr ); + + return new SdStyleSheet(aName, rPool, eFamily, SfxStyleSearchBits::UserDefined); +} + +// XInterface + +void SAL_CALL SdStyleSheet::release( ) noexcept +{ + if (osl_atomic_decrement( &m_refCount ) != 0) + return; + + // restore reference count: + osl_atomic_increment( &m_refCount ); + if (! mrBHelper.bDisposed) try + { + dispose(); + } + catch (RuntimeException const&) + { + // don't break throw () + TOOLS_WARN_EXCEPTION( "sd", "" ); + } + OSL_ASSERT( mrBHelper.bDisposed ); + SdStyleSheetBase::release(); +} + +// XComponent + +void SAL_CALL SdStyleSheet::dispose( ) +{ + { + MutexGuard aGuard(mrBHelper.rMutex); + if (mrBHelper.bDisposed || mrBHelper.bInDispose) + return; + + mrBHelper.bInDispose = true; + } + try + { + // side effect: keeping a reference to this + EventObject aEvt( static_cast< OWeakObject * >( this ) ); + try + { + mrBHelper.aLC.disposeAndClear( aEvt ); + disposing(); + } + catch (...) + { + MutexGuard aGuard2( mrBHelper.rMutex ); + // bDisposed and bInDispose must be set in this order: + mrBHelper.bDisposed = true; + mrBHelper.bInDispose = false; + throw; + } + MutexGuard aGuard2( mrBHelper.rMutex ); + // bDisposed and bInDispose must be set in this order: + mrBHelper.bDisposed = true; + mrBHelper.bInDispose = false; + } + catch (RuntimeException &) + { + throw; + } + catch (const Exception & exc) + { + css::uno::Any anyEx = cppu::getCaughtException(); + throw css::lang::WrappedTargetRuntimeException( + "unexpected UNO exception caught: " + exc.Message , + nullptr, anyEx ); + } +} + +void SdStyleSheet::disposing() +{ + SolarMutexGuard aGuard; + if (bMySet) + { + delete pSet; + } + pSet = nullptr; + m_pPool = nullptr; + mxPool.clear(); +} + +void SAL_CALL SdStyleSheet::addEventListener( const Reference< XEventListener >& xListener ) +{ + ClearableMutexGuard aGuard( mrBHelper.rMutex ); + if (mrBHelper.bDisposed || mrBHelper.bInDispose) + { + aGuard.clear(); + EventObject aEvt( static_cast< OWeakObject * >( this ) ); + xListener->disposing( aEvt ); + } + else + { + mrBHelper.addListener( cppu::UnoType<decltype(xListener)>::get(), xListener ); + } +} + +void SAL_CALL SdStyleSheet::removeEventListener( const Reference< XEventListener >& xListener ) +{ + mrBHelper.removeListener( cppu::UnoType<decltype(xListener)>::get(), xListener ); +} + +// XModifyBroadcaster + +void SAL_CALL SdStyleSheet::addModifyListener( const Reference< XModifyListener >& xListener ) +{ + ClearableMutexGuard aGuard( mrBHelper.rMutex ); + if (mrBHelper.bDisposed || mrBHelper.bInDispose) + { + aGuard.clear(); + EventObject aEvt( static_cast< OWeakObject * >( this ) ); + xListener->disposing( aEvt ); + } + else + { + if (!mpModifyListenerForwarder) + mpModifyListenerForwarder.reset( new ModifyListenerForwarder( this ) ); + mrBHelper.addListener( cppu::UnoType<XModifyListener>::get(), xListener ); + } +} + +void SAL_CALL SdStyleSheet::removeModifyListener( const Reference< XModifyListener >& xListener ) +{ + mrBHelper.removeListener( cppu::UnoType<XModifyListener>::get(), xListener ); +} + +void SdStyleSheet::notifyModifyListener() +{ + MutexGuard aGuard( mrBHelper.rMutex ); + + cppu::OInterfaceContainerHelper * pContainer = mrBHelper.getContainer( cppu::UnoType<XModifyListener>::get() ); + if( pContainer ) + { + EventObject aEvt( static_cast< OWeakObject * >( this ) ); + pContainer->forEach<XModifyListener>( + [&] (Reference<XModifyListener> const& xListener) { + return xListener->modified(aEvt); + } ); + } +} + +// XServiceInfo +OUString SAL_CALL SdStyleSheet::getImplementationName() +{ + return "SdStyleSheet"; +} + +sal_Bool SAL_CALL SdStyleSheet::supportsService( const OUString& ServiceName ) +{ + return cppu::supportsService( this, ServiceName ); +} + +Sequence< OUString > SAL_CALL SdStyleSheet::getSupportedServiceNames() +{ + return { "com.sun.star.style.Style", + "com.sun.star.drawing.FillProperties", + "com.sun.star.drawing.LineProperties", + "com.sun.star.drawing.ShadowProperties", + "com.sun.star.drawing.ConnectorProperties", + "com.sun.star.drawing.MeasureProperties", + "com.sun.star.style.ParagraphProperties", + "com.sun.star.style.CharacterProperties", + "com.sun.star.drawing.TextProperties", + "com.sun.star.drawing.Text" }; +} + +bool SdStyleSheet::SetName(const OUString& rNewName, bool bReindexNow) +{ + const bool bResult = SfxUnoStyleSheet::SetName(rNewName, bReindexNow); + // Don't overwrite predefined API names + if (bResult && GetHelpIdForApiName(msApiName) == 0) + { + msApiName = rNewName; + Broadcast(SfxHint(SfxHintId::DataChanged)); + } + return bResult; +} + +// XNamed +OUString SAL_CALL SdStyleSheet::getName() +{ + SolarMutexGuard aGuard; + throwIfDisposed(); + return GetApiName(); +} + +void SAL_CALL SdStyleSheet::setName( const OUString& rName ) +{ + SolarMutexGuard aGuard; + throwIfDisposed(); + SetName(rName); +} + +// XStyle + +sal_Bool SAL_CALL SdStyleSheet::isUserDefined() +{ + SolarMutexGuard aGuard; + throwIfDisposed(); + return IsUserDefined(); +} + +sal_Bool SAL_CALL SdStyleSheet::isInUse() +{ + SolarMutexGuard aGuard; + throwIfDisposed(); + return IsUsed(); +} + +OUString SAL_CALL SdStyleSheet::getParentStyle() +{ + SolarMutexGuard aGuard; + throwIfDisposed(); + + if( !GetParent().isEmpty() ) + { + SdStyleSheet* pParentStyle = static_cast< SdStyleSheet* >( mxPool->Find( GetParent(), nFamily ) ); + if( pParentStyle ) + return pParentStyle->GetApiName(); + } + return OUString(); +} + +void SAL_CALL SdStyleSheet::setParentStyle( const OUString& rParentName ) +{ + SolarMutexGuard aGuard; + throwIfDisposed(); + + if( !rParentName.isEmpty() ) + { + OUString const name(GetName()); + sal_Int32 const sep(name.indexOf(SD_LT_SEPARATOR)); + OUString const master((sep == -1) ? OUString() : name.copy(0, sep)); + std::shared_ptr<SfxStyleSheetIterator> aSSSI = std::make_shared<SfxStyleSheetIterator>(mxPool.get(), nFamily); + for (SfxStyleSheetBase *pStyle = aSSSI->First(); pStyle; pStyle = aSSSI->Next()) + { + // we hope that we have only sd style sheets + SdStyleSheet* pSdStyleSheet = static_cast<SdStyleSheet*>(pStyle); + OUString const curName(pStyle->GetName()); + sal_Int32 const curSep(curName.indexOf(SD_LT_SEPARATOR)); + OUString const curMaster((curSep == -1) + ? OUString() : curName.copy(0, curSep)); + // check that the master matches, as msApiName exists once per + // master page + if (pSdStyleSheet->msApiName == rParentName && master == curMaster) + { + if( pStyle != this ) + { + SetParent(curName); + } + return; + } + } + throw NoSuchElementException(); + } + else + { + SetParent( rParentName ); + } +} + +// XPropertySet/XMultiPropertySet utility functions + +// Does not broadcast +// Must be guarded by solar mutex; must not be disposed +void SdStyleSheet::setPropertyValue_Impl(const OUString& aPropertyName, const css::uno::Any& aValue) +{ + const SfxItemPropertyMapEntry* pEntry = getPropertyMapEntry( aPropertyName ); + if( pEntry == nullptr ) + { + throw UnknownPropertyException( aPropertyName, static_cast<cppu::OWeakObject*>(this)); + } + + if( pEntry->nWID == WID_STYLE_HIDDEN ) + { + bool bValue = false; + if ( aValue >>= bValue ) + SetHidden( bValue ); + return; + } + if( pEntry->nWID == SDRATTR_TEXTDIRECTION ) + return; // not yet implemented for styles + + if( pEntry->nWID == WID_STYLE_FAMILY ) + throw PropertyVetoException(); + + if( (pEntry->nWID == EE_PARA_NUMBULLET) && (GetFamily() == SfxStyleFamily::Page) ) + { + OUString aStr; + const sal_uInt32 nTempHelpId = GetHelpId( aStr ); + + if( (nTempHelpId >= HID_PSEUDOSHEET_OUTLINE2) && (nTempHelpId <= HID_PSEUDOSHEET_OUTLINE9) ) + return; + } + + SfxItemSet &rStyleSet = GetItemSet(); + + if( pEntry->nWID == OWN_ATTR_FILLBMP_MODE ) + { + BitmapMode eMode; + if( aValue >>= eMode ) + { + rStyleSet.Put( XFillBmpStretchItem( eMode == BitmapMode_STRETCH ) ); + rStyleSet.Put( XFillBmpTileItem( eMode == BitmapMode_REPEAT ) ); + return; + } + throw IllegalArgumentException(); + } + + if (pEntry->nWID == OWN_ATTR_TEXTCOLUMNS) + { + if (css::uno::Reference<css::text::XTextColumns> xColumns; aValue >>= xColumns) + { + rStyleSet.Put(SfxInt16Item(SDRATTR_TEXTCOLUMNS_NUMBER, xColumns->getColumnCount())); + if (css::uno::Reference<css::beans::XPropertySet> xPropSet{ xColumns, + css::uno::UNO_QUERY }) + { + auto aVal = xPropSet->getPropertyValue("AutomaticDistance"); + if (sal_Int32 nSpacing; aVal >>= nSpacing) + rStyleSet.Put(SdrMetricItem(SDRATTR_TEXTCOLUMNS_SPACING, nSpacing)); + } + return; + } + throw IllegalArgumentException(); + } + + SfxItemSet aSet( GetPool()->GetPool(), pEntry->nWID, pEntry->nWID); + aSet.Put( rStyleSet ); + + if( !aSet.Count() ) + { + if( EE_PARA_NUMBULLET == pEntry->nWID ) + { + vcl::Font aBulletFont; + SdStyleSheetPool::PutNumBulletItem( this, aBulletFont ); + aSet.Put( rStyleSet ); + } + else + { + aSet.Put( GetPool()->GetPool().GetDefaultItem( pEntry->nWID ) ); + } + } + + if( pEntry->nMemberId == MID_NAME && + ( pEntry->nWID == XATTR_FILLBITMAP || pEntry->nWID == XATTR_FILLGRADIENT || + pEntry->nWID == XATTR_FILLHATCH || pEntry->nWID == XATTR_FILLFLOATTRANSPARENCE || + pEntry->nWID == XATTR_LINESTART || pEntry->nWID == XATTR_LINEEND || pEntry->nWID == XATTR_LINEDASH) ) + { + OUString aTempName; + if(!(aValue >>= aTempName )) + throw IllegalArgumentException(); + + SvxShape::SetFillAttribute( pEntry->nWID, aTempName, aSet ); + } + else if(!SvxUnoTextRangeBase::SetPropertyValueHelper( pEntry, aValue, aSet )) + { + SvxItemPropertySet_setPropertyValue( pEntry, aValue, aSet ); + } + + rStyleSet.Put( aSet ); +} + +// Must be guarded by solar mutex; must not be disposed +css::uno::Any SdStyleSheet::getPropertyValue_Impl(const OUString& PropertyName) +{ + const SfxItemPropertyMapEntry* pEntry = getPropertyMapEntry( PropertyName ); + if( pEntry == nullptr ) + { + throw UnknownPropertyException( PropertyName, static_cast<cppu::OWeakObject*>(this)); + } + + Any aAny; + + if( pEntry->nWID == WID_STYLE_FAMILY ) + { + if( nFamily == SfxStyleFamily::Page ) + { + const OUString aLayoutName( GetName() ); + aAny <<= aLayoutName.copy( 0, aLayoutName.indexOf( SD_LT_SEPARATOR) ); + } + else + { + aAny <<= GetFamilyString(nFamily); + } + } + else if( pEntry->nWID == WID_STYLE_DISPNAME ) + { + OUString aDisplayName; + if ( nFamily == SfxStyleFamily::Page ) + { + const SdStyleSheet* pStyleSheet = GetPseudoStyleSheet(); + if (pStyleSheet != nullptr) + aDisplayName = pStyleSheet->GetName(); + } + + if (aDisplayName.isEmpty()) + aDisplayName = GetName(); + + aAny <<= aDisplayName; + } + else if( pEntry->nWID == SDRATTR_TEXTDIRECTION ) + { + aAny <<= false; + } + else if( pEntry->nWID == OWN_ATTR_FILLBMP_MODE ) + { + SfxItemSet &rStyleSet = GetItemSet(); + + const XFillBmpStretchItem* pStretchItem = rStyleSet.GetItem<XFillBmpStretchItem>(XATTR_FILLBMP_STRETCH); + const XFillBmpTileItem* pTileItem = rStyleSet.GetItem<XFillBmpTileItem>(XATTR_FILLBMP_TILE); + + if( pStretchItem && pTileItem ) + { + if( pTileItem->GetValue() ) + aAny <<= BitmapMode_REPEAT; + else if( pStretchItem->GetValue() ) + aAny <<= BitmapMode_STRETCH; + else + aAny <<= BitmapMode_NO_REPEAT; + } + } + else if( pEntry->nWID == WID_STYLE_HIDDEN ) + { + aAny <<= IsHidden( ); + } + else + { + SfxItemSet aSet( GetPool()->GetPool(), pEntry->nWID, pEntry->nWID); + + const SfxPoolItem* pItem; + SfxItemSet& rStyleSet = GetItemSet(); + + if( rStyleSet.GetItemState( pEntry->nWID, true, &pItem ) == SfxItemState::SET ) + aSet.Put( *pItem ); + + if( !aSet.Count() ) + aSet.Put( GetPool()->GetPool().GetDefaultItem( pEntry->nWID ) ); + + if(SvxUnoTextRangeBase::GetPropertyValueHelper( aSet, pEntry, aAny )) + return aAny; + + // Get value of ItemSet + aAny = SvxItemPropertySet_getPropertyValue( pEntry, aSet ); + } + + if( pEntry->aType != aAny.getValueType() ) + { + // since the sfx uint16 item now exports a sal_Int32, we may have to fix this here + if( ( pEntry->aType == ::cppu::UnoType<sal_Int16>::get()) && aAny.getValueType() == ::cppu::UnoType<sal_Int32>::get() ) + { + sal_Int32 nValue = 0; + aAny >>= nValue; + aAny <<= static_cast<sal_Int16>(nValue); + } + else + { + OSL_FAIL("SvxShape::GetAnyForItem() Returnvalue has wrong Type!" ); + } + } + + return aAny; +} + +// XPropertySet + +Reference< XPropertySetInfo > SdStyleSheet::getPropertySetInfo() +{ + throwIfDisposed(); + static Reference< XPropertySetInfo > xInfo = GetStylePropertySet().getPropertySetInfo(); + return xInfo; +} + +void SAL_CALL SdStyleSheet::setPropertyValue( const OUString& aPropertyName, const Any& aValue ) +{ + SolarMutexGuard aGuard; + throwIfDisposed(); + + setPropertyValue_Impl(aPropertyName, aValue); + Broadcast(SfxHint(SfxHintId::DataChanged)); +} + +Any SAL_CALL SdStyleSheet::getPropertyValue( const OUString& PropertyName ) +{ + SolarMutexGuard aGuard; + throwIfDisposed(); + + return getPropertyValue_Impl(PropertyName); +} + +void SAL_CALL SdStyleSheet::addPropertyChangeListener( const OUString& , const Reference< XPropertyChangeListener >& ) {} +void SAL_CALL SdStyleSheet::removePropertyChangeListener( const OUString& , const Reference< XPropertyChangeListener >& ) {} +void SAL_CALL SdStyleSheet::addVetoableChangeListener( const OUString& , const Reference< XVetoableChangeListener >& ) {} +void SAL_CALL SdStyleSheet::removeVetoableChangeListener( const OUString& , const Reference< XVetoableChangeListener >& ) {} + +// XMultiPropertySet + +void SAL_CALL SdStyleSheet::setPropertyValues(const css::uno::Sequence<OUString>& aPropertyNames, + const css::uno::Sequence<css::uno::Any>& aValues) +{ + const sal_Int32 nCount = aPropertyNames.getLength(); + + if (nCount != aValues.getLength()) + throw css::lang::IllegalArgumentException(); + + if (!nCount) + return; + + SolarMutexGuard aGuard; + throwIfDisposed(); + + for (sal_Int32 i = 0; i < nCount; ++i) + { + try + { + setPropertyValue_Impl(aPropertyNames[i], aValues[i]); + } + catch (const css::beans::UnknownPropertyException&) + { + DBG_UNHANDLED_EXCEPTION("sd"); + } + } + + Broadcast(SfxHint(SfxHintId::DataChanged)); +} + +css::uno::Sequence<css::uno::Any> +SAL_CALL SdStyleSheet::getPropertyValues(const css::uno::Sequence<OUString>& aPropertyNames) +{ + SolarMutexGuard aGuard; + throwIfDisposed(); + + const sal_Int32 nCount = aPropertyNames.getLength(); + css::uno::Sequence<css::uno::Any> aValues(nCount); + Any* pAny = aValues.getArray(); + + for (sal_Int32 i = 0; i < nCount; ++i) + pAny[i] = getPropertyValue_Impl(aPropertyNames[i]); + + return aValues; +} + +void SAL_CALL SdStyleSheet::addPropertiesChangeListener(const css::uno::Sequence<OUString>&, const css::uno::Reference<css::beans::XPropertiesChangeListener>&) {} +void SAL_CALL SdStyleSheet::removePropertiesChangeListener(const css::uno::Reference<css::beans::XPropertiesChangeListener>&) {} +void SAL_CALL SdStyleSheet::firePropertiesChangeEvent(const css::uno::Sequence<OUString>&, const css::uno::Reference<css::beans::XPropertiesChangeListener>&) {} + +// XPropertyState + +PropertyState SAL_CALL SdStyleSheet::getPropertyState( const OUString& PropertyName ) +{ + SolarMutexGuard aGuard; + + throwIfDisposed(); + + const SfxItemPropertyMapEntry* pEntry = getPropertyMapEntry( PropertyName ); + + if( pEntry == nullptr ) + throw UnknownPropertyException( PropertyName, static_cast<cppu::OWeakObject*>(this)); + + if( pEntry->nWID == WID_STYLE_FAMILY ) + { + return PropertyState_DIRECT_VALUE; + } + else if( pEntry->nWID == SDRATTR_TEXTDIRECTION ) + { + return PropertyState_DEFAULT_VALUE; + } + else if( pEntry->nWID == OWN_ATTR_FILLBMP_MODE ) + { + const SfxItemSet& rSet = GetItemSet(); + + if( rSet.GetItemState( XATTR_FILLBMP_STRETCH, false ) == SfxItemState::SET || + rSet.GetItemState( XATTR_FILLBMP_TILE, false ) == SfxItemState::SET ) + { + return PropertyState_DIRECT_VALUE; + } + else + { + return PropertyState_AMBIGUOUS_VALUE; + } + } + else if (pEntry->nWID == OWN_ATTR_TEXTCOLUMNS) + { + const SfxItemSet& rSet = GetItemSet(); + + const auto eState1 = rSet.GetItemState(SDRATTR_TEXTCOLUMNS_NUMBER, false); + const auto eState2 = rSet.GetItemState(SDRATTR_TEXTCOLUMNS_SPACING, false); + if (eState1 == SfxItemState::SET || eState2 == SfxItemState::SET) + return PropertyState_DIRECT_VALUE; + else if (eState1 == SfxItemState::DEFAULT && eState2 == SfxItemState::DEFAULT) + return PropertyState_DEFAULT_VALUE; + else + return PropertyState_AMBIGUOUS_VALUE; + } + else + { + SfxItemSet &rStyleSet = GetItemSet(); + + PropertyState eState; + + switch( rStyleSet.GetItemState( pEntry->nWID, false ) ) + { + case SfxItemState::SET: + eState = PropertyState_DIRECT_VALUE; + break; + case SfxItemState::DEFAULT: + eState = PropertyState_DEFAULT_VALUE; + break; + default: + eState = PropertyState_AMBIGUOUS_VALUE; + break; + } + + // if an item is set, this doesn't mean we want it :) + if( PropertyState_DIRECT_VALUE == eState ) + { + switch( pEntry->nWID ) + { + case XATTR_FILLBITMAP: + case XATTR_FILLGRADIENT: + case XATTR_FILLHATCH: + case XATTR_FILLFLOATTRANSPARENCE: + case XATTR_LINEEND: + case XATTR_LINESTART: + case XATTR_LINEDASH: + { + const NameOrIndex* pItem = rStyleSet.GetItem<NameOrIndex>(pEntry->nWID); + if( ( pItem == nullptr ) || pItem->GetName().isEmpty() ) + eState = PropertyState_DEFAULT_VALUE; + } + break; + case XATTR_FILLCOLOR: + if (pEntry->nMemberId == MID_COLOR_THEME_INDEX) + { + const XFillColorItem* pColor = rStyleSet.GetItem<XFillColorItem>(pEntry->nWID); + if (pColor->GetThemeColor().GetThemeIndex() == -1) + { + eState = PropertyState_DEFAULT_VALUE; + } + } + else if (pEntry->nMemberId == MID_COLOR_LUM_MOD) + { + const XFillColorItem* pColor = rStyleSet.GetItem<XFillColorItem>(pEntry->nWID); + if (pColor->GetThemeColor().GetLumMod() == 10000) + { + eState = PropertyState_DEFAULT_VALUE; + } + } + else if (pEntry->nMemberId == MID_COLOR_LUM_OFF) + { + const XFillColorItem* pColor = rStyleSet.GetItem<XFillColorItem>(pEntry->nWID); + if (pColor->GetThemeColor().GetLumOff() == 0) + { + eState = PropertyState_DEFAULT_VALUE; + } + } + break; + } + } + + return eState; + } +} + +Sequence< PropertyState > SAL_CALL SdStyleSheet::getPropertyStates( const Sequence< OUString >& aPropertyName ) +{ + SolarMutexGuard aGuard; + + throwIfDisposed(); + + sal_Int32 nCount = aPropertyName.getLength(); + + Sequence< PropertyState > aPropertyStateSequence( nCount ); + + std::transform(aPropertyName.begin(), aPropertyName.end(), aPropertyStateSequence.getArray(), + [this](const OUString& rName) -> PropertyState { return getPropertyState(rName); }); + + return aPropertyStateSequence; +} + +void SAL_CALL SdStyleSheet::setPropertyToDefault( const OUString& PropertyName ) +{ + SolarMutexGuard aGuard; + + throwIfDisposed(); + + const SfxItemPropertyMapEntry* pEntry = getPropertyMapEntry( PropertyName ); + if( pEntry == nullptr ) + throw UnknownPropertyException( PropertyName, static_cast<cppu::OWeakObject*>(this)); + + SfxItemSet &rStyleSet = GetItemSet(); + + if( pEntry->nWID == OWN_ATTR_FILLBMP_MODE ) + { + rStyleSet.ClearItem( XATTR_FILLBMP_STRETCH ); + rStyleSet.ClearItem( XATTR_FILLBMP_TILE ); + } + else + { + rStyleSet.ClearItem( pEntry->nWID ); + } + Broadcast(SfxHint(SfxHintId::DataChanged)); +} + +Any SAL_CALL SdStyleSheet::getPropertyDefault( const OUString& aPropertyName ) +{ + SolarMutexGuard aGuard; + + throwIfDisposed(); + + const SfxItemPropertyMapEntry* pEntry = getPropertyMapEntry( aPropertyName ); + if( pEntry == nullptr ) + throw UnknownPropertyException( aPropertyName, static_cast<cppu::OWeakObject*>(this)); + Any aRet; + if( pEntry->nWID == WID_STYLE_FAMILY ) + { + aRet <<= GetFamilyString(nFamily); + } + else if( pEntry->nWID == SDRATTR_TEXTDIRECTION ) + { + aRet <<= false; + } + else if( pEntry->nWID == OWN_ATTR_FILLBMP_MODE ) + { + aRet <<= BitmapMode_REPEAT; + } + else + { + SfxItemPool& rMyPool = GetPool()->GetPool(); + SfxItemSet aSet( rMyPool, pEntry->nWID, pEntry->nWID); + aSet.Put( rMyPool.GetDefaultItem( pEntry->nWID ) ); + aRet = SvxItemPropertySet_getPropertyValue( pEntry, aSet ); + } + return aRet; +} + +/** this is used because our property map is not sorted yet */ +const SfxItemPropertyMapEntry* SdStyleSheet::getPropertyMapEntry( std::u16string_view rPropertyName ) +{ + return GetStylePropertySet().getPropertyMapEntry(rPropertyName); +} + +//Broadcast that a SdStyleSheet has changed, taking into account outline sublevels +//which need to be explicitly broadcast as changing if their parent style was +//the one that changed +void SdStyleSheet::BroadcastSdStyleSheetChange(SfxStyleSheetBase const * pStyleSheet, + PresentationObjects ePO, SfxStyleSheetBasePool* pSSPool) +{ + SdStyleSheet* pRealSheet = static_cast<SdStyleSheet const *>(pStyleSheet)->GetRealStyleSheet(); + pRealSheet->Broadcast(SfxHint(SfxHintId::DataChanged)); + + if( (ePO < PresentationObjects::Outline_1) || (ePO > PresentationObjects::Outline_8) ) + return; + + OUString sStyleName(SdResId(STR_PSEUDOSHEET_OUTLINE) + " "); + + for( sal_uInt16 n = static_cast<sal_uInt16>(ePO) - static_cast<sal_uInt16>(PresentationObjects::Outline_1) + 2; n < 10; n++ ) + { + OUString aName( sStyleName + OUString::number(n) ); + + SfxStyleSheetBase* pSheet = pSSPool->Find( aName, SfxStyleFamily::Pseudo); + + if(pSheet) + { + SdStyleSheet* pRealStyleSheet = static_cast<SdStyleSheet*>(pSheet)->GetRealStyleSheet(); + pRealStyleSheet->Broadcast(SfxHint(SfxHintId::DataChanged)); + } + } +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/core/text/textapi.cxx b/sd/source/core/text/textapi.cxx new file mode 100644 index 000000000..2499588f8 --- /dev/null +++ b/sd/source/core/text/textapi.cxx @@ -0,0 +1,278 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include <com/sun/star/beans/PropertyAttribute.hpp> +#include <com/sun/star/lang/Locale.hpp> +#include <com/sun/star/text/XTextField.hpp> + +#include <textapi.hxx> +#include <drawdoc.hxx> +#include <editeng/eeitem.hxx> +#include <editeng/editeng.hxx> +#include <editeng/outlobj.hxx> +#include <editeng/unoforou.hxx> +#include <editeng/unoprnms.hxx> +#include <editeng/unoipset.hxx> +#include <Outliner.hxx> +#include <svx/svdpool.hxx> +#include <svx/svdundo.hxx> + +namespace com::sun::star::container { class XNameContainer; } + +using namespace ::com::sun::star::uno; +using namespace ::com::sun::star::text; +using namespace ::com::sun::star::beans; +using namespace ::com::sun::star::container; + +namespace sd { + +namespace { + +class UndoTextAPIChanged : public SdrUndoAction +{ +public: + UndoTextAPIChanged( SdrModel& rModel, TextApiObject* pTextObj ); + + virtual void Undo() override; + virtual void Redo() override; + +protected: + std::optional<OutlinerParaObject> mpOldText; + std::optional<OutlinerParaObject> mpNewText; + rtl::Reference< TextApiObject > mxTextObj; +}; + +} + +UndoTextAPIChanged::UndoTextAPIChanged(SdrModel& rModel, TextApiObject* pTextObj ) +: SdrUndoAction( rModel ) +, mpOldText( pTextObj->CreateText() ) +, mxTextObj( pTextObj ) +{ +} + +void UndoTextAPIChanged::Undo() +{ + if( !mpNewText ) + mpNewText = mxTextObj->CreateText(); + + mxTextObj->SetText( *mpOldText ); +} + +void UndoTextAPIChanged::Redo() +{ + if( mpNewText ) + { + mxTextObj->SetText( *mpNewText ); + } +} + +namespace { + +struct TextAPIEditSource_Impl +{ + SdDrawDocument* mpDoc; + Outliner* mpOutliner; + SvxOutlinerForwarder* mpTextForwarder; +}; + +} + +class TextAPIEditSource : public SvxEditSource +{ + // refcounted + std::shared_ptr<TextAPIEditSource_Impl> m_xImpl; + + virtual std::unique_ptr<SvxEditSource> Clone() const override; + virtual SvxTextForwarder* GetTextForwarder() override; + virtual void UpdateData() override; + explicit TextAPIEditSource( const TextAPIEditSource& rSource ); + +public: + explicit TextAPIEditSource(SdDrawDocument* pDoc); + + void Dispose(); + void SetText( OutlinerParaObject const & rText ); + std::optional<OutlinerParaObject> CreateText(); + OUString GetText() const; + SdDrawDocument* GetDoc() { return m_xImpl->mpDoc; } +}; + +static const SvxItemPropertySet* ImplGetSdTextPortionPropertyMap() +{ + static const SfxItemPropertyMapEntry aSdTextPortionPropertyEntries[] = + { + SVX_UNOEDIT_CHAR_PROPERTIES, + SVX_UNOEDIT_FONT_PROPERTIES, + SVX_UNOEDIT_OUTLINER_PROPERTIES, + SVX_UNOEDIT_PARA_PROPERTIES, + {u"TextField", EE_FEATURE_FIELD, cppu::UnoType<XTextField>::get(), PropertyAttribute::READONLY, 0 }, + {u"TextPortionType", WID_PORTIONTYPE, ::cppu::UnoType<OUString>::get(), PropertyAttribute::READONLY, 0 }, + {u"TextUserDefinedAttributes", EE_CHAR_XMLATTRIBS, cppu::UnoType<XNameContainer>::get(), 0, 0}, + {u"ParaUserDefinedAttributes", EE_PARA_XMLATTRIBS, cppu::UnoType<XNameContainer>::get(), 0, 0}, + { u"", 0, css::uno::Type(), 0, 0 } + }; + static SvxItemPropertySet aSdTextPortionPropertyMap( aSdTextPortionPropertyEntries, SdrObject::GetGlobalDrawObjectItemPool() ); + + return &aSdTextPortionPropertyMap; +} + +TextApiObject::TextApiObject( std::unique_ptr<TextAPIEditSource> pEditSource ) +: SvxUnoText( pEditSource.get(), ImplGetSdTextPortionPropertyMap(), Reference < XText >() ) +, mpSource(std::move(pEditSource)) +{ +} + +TextApiObject::~TextApiObject() noexcept +{ + dispose(); +} + +rtl::Reference< TextApiObject > TextApiObject::create( SdDrawDocument* pDoc ) +{ + rtl::Reference< TextApiObject > xRet( new TextApiObject( std::make_unique<TextAPIEditSource>( pDoc ) ) ); + return xRet; +} + +void TextApiObject::dispose() +{ + if( mpSource ) + { + mpSource->Dispose(); + mpSource.reset(); + } + +} + +std::optional<OutlinerParaObject> TextApiObject::CreateText() +{ + return mpSource->CreateText(); +} + +void TextApiObject::SetText( OutlinerParaObject const & rText ) +{ + SdrModel* pModel = mpSource->GetDoc(); + if( pModel && pModel->IsUndoEnabled() ) + pModel->AddUndo( std::make_unique<UndoTextAPIChanged>( *pModel, this ) ); + + mpSource->SetText( rText ); + maSelection.nStartPara = EE_PARA_MAX_COUNT; +} + +OUString TextApiObject::GetText() const +{ + return mpSource->GetText(); +} + +TextApiObject* TextApiObject::getImplementation( const css::uno::Reference< css::text::XText >& xText ) +{ + TextApiObject* pImpl = dynamic_cast< TextApiObject* >( xText.get() ); + + if( !pImpl ) + pImpl = dynamic_cast< TextApiObject* >( comphelper::getFromUnoTunnel<SvxUnoTextBase>( xText ) ); + + return pImpl; +} + +TextAPIEditSource::TextAPIEditSource(const TextAPIEditSource& rSource) + : SvxEditSource(*this) + , m_xImpl(rSource.m_xImpl) // shallow copy; uses internal refcounting +{ +} + +std::unique_ptr<SvxEditSource> TextAPIEditSource::Clone() const +{ + return std::unique_ptr<SvxEditSource>(new TextAPIEditSource( *this )); +} + +void TextAPIEditSource::UpdateData() +{ + // data is kept in outliner all the time +} + +TextAPIEditSource::TextAPIEditSource(SdDrawDocument* pDoc) +: m_xImpl(std::make_shared<TextAPIEditSource_Impl>()) +{ + m_xImpl->mpDoc = pDoc; + m_xImpl->mpOutliner = nullptr; + m_xImpl->mpTextForwarder = nullptr; +} + +void TextAPIEditSource::Dispose() +{ + m_xImpl->mpDoc=nullptr; + delete m_xImpl->mpTextForwarder; + m_xImpl->mpTextForwarder = nullptr; + + delete m_xImpl->mpOutliner; + m_xImpl->mpOutliner = nullptr; +} + +SvxTextForwarder* TextAPIEditSource::GetTextForwarder() +{ + if(!m_xImpl->mpDoc) + return nullptr; // mpDoc == 0 can be used to flag this as disposed + + if (!m_xImpl->mpOutliner) + { + //init draw model first + m_xImpl->mpOutliner = new SdOutliner(m_xImpl->mpDoc, OutlinerMode::TextObject); + SdDrawDocument::SetCalcFieldValueHdl(m_xImpl->mpOutliner); + } + + if (!m_xImpl->mpTextForwarder) + m_xImpl->mpTextForwarder = new SvxOutlinerForwarder(*m_xImpl->mpOutliner, false); + + return m_xImpl->mpTextForwarder; +} + +void TextAPIEditSource::SetText( OutlinerParaObject const & rText ) +{ + if (m_xImpl->mpDoc) + { + if (!m_xImpl->mpOutliner) + { + //init draw model first + m_xImpl->mpOutliner = new SdOutliner(m_xImpl->mpDoc, OutlinerMode::TextObject); + SdDrawDocument::SetCalcFieldValueHdl(m_xImpl->mpOutliner); + } + + m_xImpl->mpOutliner->SetText( rText ); + } +} + +std::optional<OutlinerParaObject> TextAPIEditSource::CreateText() +{ + if (m_xImpl->mpDoc && m_xImpl->mpOutliner) + return m_xImpl->mpOutliner->CreateParaObject(); + else + return std::nullopt; +} + +OUString TextAPIEditSource::GetText() const +{ + if (m_xImpl->mpDoc && m_xImpl->mpOutliner) + return m_xImpl->mpOutliner->GetEditEngine().GetText(); + else + return OUString(); +} + +} // namespace sd + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/core/typemap.cxx b/sd/source/core/typemap.cxx new file mode 100644 index 000000000..4378ad2d2 --- /dev/null +++ b/sd/source/core/typemap.cxx @@ -0,0 +1,143 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include <config_options.h> + +#include <editeng/cmapitem.hxx> +#include <editeng/langitem.hxx> +#include <editeng/fhgtitem.hxx> +#include <editeng/colritem.hxx> +#include <editeng/contouritem.hxx> +#include <editeng/shdditem.hxx> +#include <editeng/crossedoutitem.hxx> +#include <editeng/udlnitem.hxx> +#include <editeng/wghtitem.hxx> +#include <editeng/postitem.hxx> +#include <editeng/fontitem.hxx> +#include <editeng/tstpitem.hxx> +#include <editeng/kernitem.hxx> +#include <editeng/lspcitem.hxx> +#include <editeng/ulspitem.hxx> +#include <editeng/lrspitem.hxx> +#include <editeng/protitem.hxx> +#include <svx/chrtitem.hxx> +#include <sfx2/msg.hxx> +#include <svl/globalnameitem.hxx> +#include <svx/hlnkitem.hxx> +#include <svx/postattr.hxx> +#include <svx/rulritem.hxx> +#include <svx/clipfmtitem.hxx> +#include <svl/srchitem.hxx> +#include <editeng/sizeitem.hxx> +#include <sfx2/zoomitem.hxx> +#include <svx/pageitem.hxx> +#include <svx/sdooitm.hxx> +#include <svx/sdtfsitm.hxx> +#include <svx/sdprcitm.hxx> +#include <svx/sdmetitm.hxx> +#include <editeng/boxitem.hxx> +#include <editeng/lineitem.hxx> +#include <svx/zoomslideritem.hxx> +#include <editeng/memberids.h> +#include <svx/unomid.hxx> +#include <svx/xftstit.hxx> +#include <svx/xlnwtit.hxx> +#include <svx/xlineit0.hxx> +#include <svx/xlnclit.hxx> +#include <svx/xlndsit.hxx> +#include <svx/xflclit.hxx> +#include <svx/xfillit0.hxx> +#include <svx/xlnedit.hxx> +#include <svx/xlnstit.hxx> +#include <svx/xflhtit.hxx> +#include <svx/xbtmpit.hxx> +#include <svx/xtextit0.hxx> +#include <svx/xftadit.hxx> +#include <svx/xftdiit.hxx> +#include <svx/xftmrit.hxx> +#include <svx/xftouit.hxx> +#include <svx/xftshit.hxx> +#include <svx/xftshcit.hxx> +#include <svx/xftshxy.hxx> +#include <avmedia/mediaitem.hxx> +#include <svx/drawitem.hxx> + +// #UndoRedo# +#include <svl/slstitm.hxx> + +#include <svl/lckbitem.hxx> + +#define avmedia_MediaItem ::avmedia::MediaItem +#include <sfx2/tplpitem.hxx> +#include <svl/ptitem.hxx> +#include <svl/rectitem.hxx> + +#include <sfx2/frame.hxx> +#include <svx/xlncapit.hxx> +#include <svx/xflftrit.hxx> +#include <svx/xlinjoit.hxx> +#include <svx/galleryitem.hxx> +#include <svx/sdangitm.hxx> + +#ifdef DISABLE_DYNLOADING +/* Avoid clash with the ones from svx/source/form/typemap.cxx */ +#define aSfxBoolItem_Impl sd_source_core_typemap_aSfxBoolItem_Impl +#define aSfxInt32Item_Impl sd_source_core_typemap_aSfxInt32Item_Impl +#define aSfxStringItem_Impl sd_source_core_typemap_aSfxStringItem_Impl +#define aSfxUInt16Item_Impl sd_source_core_typemap_aSfxUInt16Item_Impl +#define aSfxUInt32Item_Impl sd_source_core_typemap_aSfxUInt32Item_Impl +#define aSfxVoidItem_Impl sd_source_core_typemap_aSfxVoidItem_Impl +#define aSvxClipboardFormatItem_Impl sd_source_core_typemap_aSvxClipboardFormatItem_Impl +#define aSvxColorItem_Impl sd_source_core_typemap_aSvxColorItem_Impl +#define aSvxContourItem_Impl sd_source_core_typemap_aSvxContourItem_Impl +#define aSvxCrossedOutItem_Impl sd_source_core_typemap_aSvxCrossedOutItem_Impl +#define aSvxFontHeightItem_Impl sd_source_core_typemap_aSvxFontHeightItem_Impl +#define aSvxFontItem_Impl sd_source_core_typemap_aSvxFontItem_Impl +#define aSvxLanguageItem_Impl sd_source_core_typemap_aSvxLanguageItem_Impl +#define aSvxPostureItem_Impl sd_source_core_typemap_aSvxPostureItem_Impl +#define aSvxShadowedItem_Impl sd_source_core_typemap_aSvxShadowedItem_Impl +#define aSvxUnderlineItem_Impl sd_source_core_typemap_aSvxUnderlineItem_Impl +#define aSvxOverlineItem_Impl sd_source_core_typemap_aSvxOverlineItem_Impl +#define aSvxWeightItem_Impl sd_source_core_typemap_aSvxWeightItem_Impl +#endif + +#define SFX_TYPEMAP +#include <sdslots.hxx> + +#ifdef DISABLE_DYNLOADING +#undef aSfxBoolItem_Impl +#undef aSfxInt32Item_Impl +#undef aSfxStringItem_Impl +#undef aSfxUInt16Item_Impl +#undef aSfxUInt32Item_Impl +#undef aSfxVoidItem_Impl +#undef aSvxClipboardFormatItem_Impl +#undef aSvxColorItem_Impl +#undef aSvxContourItem_Impl +#undef aSvxCrossedOutItem_Impl +#undef aSvxFontHeightItem_Impl +#undef aSvxFontItem_Impl +#undef aSvxLanguageItem_Impl +#undef aSvxPostureItem_Impl +#undef aSvxShadowedItem_Impl +#undef aSvxTextLineItem_Impl +#undef aSvxWeightItem_Impl +#endif + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/core/undo/undofactory.cxx b/sd/source/core/undo/undofactory.cxx new file mode 100644 index 000000000..c87433b81 --- /dev/null +++ b/sd/source/core/undo/undofactory.cxx @@ -0,0 +1,55 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include <undo/undofactory.hxx> +#include <undo/undoobjects.hxx> + +using namespace sd; + +std::unique_ptr<SdrUndoAction> UndoFactory::CreateUndoRemoveObject(SdrObject& rObject) +{ + return std::make_unique<UndoRemoveObject>(rObject); +} + +std::unique_ptr<SdrUndoAction> UndoFactory::CreateUndoDeleteObject( SdrObject& rObject, bool bOrdNumDirect ) +{ + return std::make_unique<UndoDeleteObject>( rObject, bOrdNumDirect ); +} + +std::unique_ptr<SdrUndoAction> UndoFactory::CreateUndoObjectSetText( SdrObject& rNewObj, sal_Int32 nText ) +{ + return std::make_unique<UndoObjectSetText>( rNewObj, nText ); +} + +std::unique_ptr<SdrUndoAction> UndoFactory::CreateUndoReplaceObject( SdrObject& rOldObject, SdrObject& rNewObject ) +{ + return std::make_unique<UndoReplaceObject>( rOldObject, rNewObject ); +} + +std::unique_ptr<SdrUndoAction> UndoFactory::CreateUndoGeoObject( SdrObject& rObject ) +{ + return std::make_unique<UndoGeoObject>( rObject ); +} + +std::unique_ptr<SdrUndoAction> UndoFactory::CreateUndoAttrObject( SdrObject& rObject, bool bStyleSheet1, bool bSaveText ) +{ + return std::make_unique<UndoAttrObject>( rObject, bStyleSheet1, bSaveText ); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/core/undo/undomanager.cxx b/sd/source/core/undo/undomanager.cxx new file mode 100644 index 000000000..672fe00e1 --- /dev/null +++ b/sd/source/core/undo/undomanager.cxx @@ -0,0 +1,58 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include <undo/undomanager.hxx> + +using namespace sd; + +UndoManager::UndoManager() + : mpLinkedUndoManager(nullptr) +{ +} + +void UndoManager::EnterListAction(const OUString &rComment, const OUString& rRepeatComment, sal_uInt16 nId, ViewShellId nViewShellId) +{ + if( !IsDoing() ) + { + ClearLinkedRedoActions(); + SdrUndoManager::EnterListAction( rComment, rRepeatComment, nId, nViewShellId ); + } +} + +void UndoManager::AddUndoAction( std::unique_ptr<SfxUndoAction> pAction, bool bTryMerg /* = sal_False */ ) +{ + if( !IsDoing() ) + { + ClearLinkedRedoActions(); + SdrUndoManager::AddUndoAction( std::move(pAction), bTryMerg ); + } +} + +void UndoManager::SetLinkedUndoManager (SfxUndoManager* pLinkedUndoManager) +{ + mpLinkedUndoManager = pLinkedUndoManager; +} + +void UndoManager::ClearLinkedRedoActions() +{ + if (mpLinkedUndoManager != nullptr) + mpLinkedUndoManager->ClearRedo(); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/core/undo/undoobjects.cxx b/sd/source/core/undo/undoobjects.cxx new file mode 100644 index 000000000..f586dac21 --- /dev/null +++ b/sd/source/core/undo/undoobjects.cxx @@ -0,0 +1,392 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include <tools/debug.hxx> +#include <undo/undoobjects.hxx> +#include <sdpage.hxx> +#include <CustomAnimationEffect.hxx> +#include <drawdoc.hxx> +#include <undoanim.hxx> +#include <ViewShell.hxx> +#include <ViewShellBase.hxx> +#include <DrawDocShell.hxx> + +using namespace sd; + +SdUndoAction::SdUndoAction(SdDrawDocument* pSdDrawDocument) + : mpDoc(pSdDrawDocument), + mnViewShellId(-1) +{ + sd::DrawDocShell* pDocShell = pSdDrawDocument ? pSdDrawDocument->GetDocSh() : nullptr; + sd::ViewShell* pViewShell = pDocShell ? pDocShell->GetViewShell() : nullptr; + if (pViewShell) + mnViewShellId = pViewShell->GetViewShellBase().GetViewShellId(); +} + +ViewShellId SdUndoAction::GetViewShellId() const +{ + return mnViewShellId; +} + +UndoRemovePresObjectImpl::UndoRemovePresObjectImpl( SdrObject& rObject ) +{ + SdPage* pPage = dynamic_cast< SdPage* >( rObject.getSdrPageFromSdrObject() ); + if( !pPage ) + return; + + if( pPage->IsPresObj(&rObject) ) + mpUndoPresObj.reset( new UndoObjectPresentationKind( rObject ) ); + if( rObject.GetUserCall() ) + mpUndoUsercall.reset( new UndoObjectUserCall(rObject) ); + + if( pPage->hasAnimationNode() ) + { + css::uno::Reference< css::drawing::XShape > xShape( rObject.getUnoShape(), css::uno::UNO_QUERY ); + if( pPage->getMainSequence()->hasEffect( xShape ) ) + { + mpUndoAnimation.reset( + new UndoAnimation( // TTTT may use ref? Or just *SdrPage? + static_cast< SdDrawDocument* >(&pPage->getSdrModelFromSdrPage()), + pPage)); + } + } +} + +UndoRemovePresObjectImpl::~UndoRemovePresObjectImpl() +{ +} + +void UndoRemovePresObjectImpl::Undo() +{ + if( mpUndoUsercall ) + mpUndoUsercall->Undo(); + if( mpUndoPresObj ) + mpUndoPresObj->Undo(); + if( mpUndoAnimation ) + mpUndoAnimation->Undo(); +} + +void UndoRemovePresObjectImpl::Redo() +{ + if( mpUndoAnimation ) + mpUndoAnimation->Redo(); + if( mpUndoPresObj ) + mpUndoPresObj->Redo(); + if( mpUndoUsercall ) + mpUndoUsercall->Redo(); +} + +UndoRemoveObject::UndoRemoveObject( SdrObject& rObject ) +: SdrUndoRemoveObj( rObject ), UndoRemovePresObjectImpl( rObject ) +, mxSdrObject(&rObject) +{ +} + +void UndoRemoveObject::Undo() +{ + DBG_ASSERT( mxSdrObject.is(), "sd::UndoRemoveObject::Undo(), object already dead!" ); + if( mxSdrObject.is() ) + { + SdrUndoRemoveObj::Undo(); + UndoRemovePresObjectImpl::Undo(); + } +} + +void UndoRemoveObject::Redo() +{ + DBG_ASSERT( mxSdrObject.is(), "sd::UndoRemoveObject::Redo(), object already dead!" ); + if( mxSdrObject.is() ) + { + UndoRemovePresObjectImpl::Redo(); + SdrUndoRemoveObj::Redo(); + } +} + +UndoDeleteObject::UndoDeleteObject( SdrObject& rObject, bool bOrdNumDirect ) +: SdrUndoDelObj( rObject, bOrdNumDirect ) +, UndoRemovePresObjectImpl( rObject ) +, mxSdrObject(&rObject) +{ +} + +void UndoDeleteObject::Undo() +{ + DBG_ASSERT( mxSdrObject.is(), "sd::UndoDeleteObject::Undo(), object already dead!" ); + if( mxSdrObject.is() ) + { + SdrUndoDelObj::Undo(); + UndoRemovePresObjectImpl::Undo(); + } +} + +void UndoDeleteObject::Redo() +{ + DBG_ASSERT( mxSdrObject.is(), "sd::UndoDeleteObject::Redo(), object already dead!" ); + if( mxSdrObject.is() ) + { + UndoRemovePresObjectImpl::Redo(); + SdrUndoDelObj::Redo(); + } +} + +UndoReplaceObject::UndoReplaceObject( SdrObject& rOldObject, SdrObject& rNewObject ) +: SdrUndoReplaceObj( rOldObject, rNewObject ) +, UndoRemovePresObjectImpl( rOldObject ) +, mxSdrObject( &rOldObject ) +{ +} + +void UndoReplaceObject::Undo() +{ + DBG_ASSERT( mxSdrObject.is(), "sd::UndoReplaceObject::Undo(), object already dead!" ); + if( mxSdrObject.is() ) + { + SdrUndoReplaceObj::Undo(); + UndoRemovePresObjectImpl::Undo(); + } +} + +void UndoReplaceObject::Redo() +{ + DBG_ASSERT( mxSdrObject.is(), "sd::UndoReplaceObject::Redo(), object already dead!" ); + if( mxSdrObject.is() ) + { + UndoRemovePresObjectImpl::Redo(); + SdrUndoReplaceObj::Redo(); + } +} + +UndoObjectSetText::UndoObjectSetText( SdrObject& rObject, sal_Int32 nText ) +: SdrUndoObjSetText( rObject, nText ) +, mbNewEmptyPresObj(false) +, mxSdrObject( &rObject ) +{ + SdPage* pPage = dynamic_cast< SdPage* >( rObject.getSdrPageFromSdrObject() ); + if( pPage && pPage->hasAnimationNode() ) + { + css::uno::Reference< css::drawing::XShape > xShape( rObject.getUnoShape(), css::uno::UNO_QUERY ); + if( pPage->getMainSequence()->hasEffect( xShape ) ) + { + mpUndoAnimation.reset( + new UndoAnimation( + static_cast< SdDrawDocument* >(&pPage->getSdrModelFromSdrPage()), + pPage)); + } + } +} + +UndoObjectSetText::~UndoObjectSetText() +{ +} + +void UndoObjectSetText::Undo() +{ + DBG_ASSERT( mxSdrObject.is(), "sd::UndoObjectSetText::Undo(), object already dead!" ); + if( mxSdrObject.is() ) + { + mbNewEmptyPresObj = mxSdrObject->IsEmptyPresObj(); + SdrUndoObjSetText::Undo(); + if( mpUndoAnimation ) + mpUndoAnimation->Undo(); + } +} + +void UndoObjectSetText::Redo() +{ + DBG_ASSERT( mxSdrObject.is(), "sd::UndoObjectSetText::Redo(), object already dead!" ); + if( mxSdrObject.is() ) + { + if( mpUndoAnimation ) + mpUndoAnimation->Redo(); + SdrUndoObjSetText::Redo(); + mxSdrObject->SetEmptyPresObj(mbNewEmptyPresObj); + } +} + +// Undo for SdrObject::SetUserCall() + +UndoObjectUserCall::UndoObjectUserCall(SdrObject& rObject) +: SdrUndoObj(rObject) +, mpOldUserCall(static_cast<SdPage*>(rObject.GetUserCall())) +, mpNewUserCall(nullptr) +, mxSdrObject( &rObject ) +{ +} + +void UndoObjectUserCall::Undo() +{ + DBG_ASSERT( mxSdrObject.is(), "sd::UndoObjectUserCall::Undo(), object already dead!" ); + if( mxSdrObject.is() ) + { + mpNewUserCall = mxSdrObject->GetUserCall(); + mxSdrObject->SetUserCall(mpOldUserCall); + } +} + +void UndoObjectUserCall::Redo() +{ + DBG_ASSERT( mxSdrObject.is(), "sd::UndoObjectUserCall::Redo(), object already dead!" ); + if( mxSdrObject.is() ) + { + mxSdrObject->SetUserCall(mpNewUserCall); + } +} + +// Undo for SdPage::InsertPresObj() and SdPage::RemovePresObj() + +UndoObjectPresentationKind::UndoObjectPresentationKind(SdrObject& rObject) +: SdrUndoObj(rObject) +, meOldKind(PresObjKind::NONE) +, meNewKind(PresObjKind::NONE) +, mxPage( static_cast<SdPage*>(rObject.getSdrPageFromSdrObject()) ) +, mxSdrObject( &rObject ) +{ + DBG_ASSERT( mxPage.get(), "sd::UndoObjectPresentationKind::UndoObjectPresentationKind(), does not work for shapes without a slide!" ); + + if( auto pPage = mxPage.get() ) + meOldKind = pPage->GetPresObjKind( &rObject ); +} + +void UndoObjectPresentationKind::Undo() +{ + if( !mxSdrObject.is() ) + return; + if( rtl::Reference<SdPage> pPage = mxPage.get() ) + { + meNewKind = pPage->GetPresObjKind( mxSdrObject.get() ); + if( meNewKind != PresObjKind::NONE ) + pPage->RemovePresObj( mxSdrObject.get() ); + if( meOldKind != PresObjKind::NONE ) + pPage->InsertPresObj( mxSdrObject.get(), meOldKind ); + } +} + +void UndoObjectPresentationKind::Redo() +{ + if( !mxSdrObject.is() ) + return; + if( rtl::Reference<SdPage> pPage = mxPage.get() ) + { + if( meOldKind != PresObjKind::NONE ) + pPage->RemovePresObj( mxSdrObject.get() ); + if( meNewKind != PresObjKind::NONE ) + pPage->InsertPresObj( mxSdrObject.get(), meNewKind ); + } +} + +UndoAutoLayoutPosAndSize::UndoAutoLayoutPosAndSize( SdPage& rPage ) +: mxPage( &rPage ) +{ +} + +void UndoAutoLayoutPosAndSize::Undo() +{ + // do nothing +} + +void UndoAutoLayoutPosAndSize::Redo() +{ + rtl::Reference<SdPage> pPage = mxPage.get(); + if( pPage ) + pPage->SetAutoLayout( pPage->GetAutoLayout() ); +} + +UndoGeoObject::UndoGeoObject( SdrObject& rNewObj ) +: SdrUndoGeoObj( rNewObj ) +, mxPage( static_cast<SdPage*>(rNewObj.getSdrPageFromSdrObject()) ) +, mxSdrObject( &rNewObj ) +{ +} + +void UndoGeoObject::Undo() +{ + DBG_ASSERT( mxSdrObject.is(), "sd::UndoGeoObject::Undo(), object already dead!" ); + if( mxSdrObject.is() ) + { + if( auto pPage = mxPage.get() ) + { + ScopeLockGuard aGuard( pPage->maLockAutoLayoutArrangement ); + SdrUndoGeoObj::Undo(); + } + else + { + SdrUndoGeoObj::Undo(); + } + } +} + +void UndoGeoObject::Redo() +{ + DBG_ASSERT( mxSdrObject.is(), "sd::UndoGeoObject::Redo(), object already dead!" ); + if( mxSdrObject.is() ) + { + if( auto pPage = mxPage.get() ) + { + ScopeLockGuard aGuard( pPage->maLockAutoLayoutArrangement ); + SdrUndoGeoObj::Redo(); + } + else + { + SdrUndoGeoObj::Redo(); + } + } +} + +UndoAttrObject::UndoAttrObject( SdrObject& rObject, bool bStyleSheet1, bool bSaveText ) +: SdrUndoAttrObj( rObject, bStyleSheet1, bSaveText ) +, mxPage( static_cast<SdPage*>(rObject.getSdrPageFromSdrObject()) ) +, mxSdrObject( &rObject ) +{ +} + +void UndoAttrObject::Undo() +{ + DBG_ASSERT( mxSdrObject.is(), "sd::UndoAttrObject::Undo(), object already dead!" ); + if( mxSdrObject.is() ) + { + if( auto pPage = mxPage.get() ) + { + ScopeLockGuard aGuard( pPage->maLockAutoLayoutArrangement ); + SdrUndoAttrObj::Undo(); + } + else + { + SdrUndoAttrObj::Undo(); + } + } +} + +void UndoAttrObject::Redo() +{ + DBG_ASSERT( mxSdrObject.is(), "sd::UndoAttrObject::Redo(), object already dead!" ); + if( mxSdrObject.is() ) + { + if( auto pPage = mxPage.get() ) + { + ScopeLockGuard aGuard( pPage->maLockAutoLayoutArrangement ); + SdrUndoAttrObj::Redo(); + } + else + { + SdrUndoAttrObj::Redo(); + } + } +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/core/undoanim.cxx b/sd/source/core/undoanim.cxx new file mode 100644 index 000000000..64b233322 --- /dev/null +++ b/sd/source/core/undoanim.cxx @@ -0,0 +1,280 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include <sal/config.h> + +#include <CustomAnimationCloner.hxx> + +#include <undoanim.hxx> +#include <strings.hrc> +#include <sdpage.hxx> +#include <sdresid.hxx> +#include <CustomAnimationEffect.hxx> +#include <drawdoc.hxx> +#include <tools/diagnose_ex.h> + +namespace com::sun::star::animations { class XAnimationNode; } + +using ::com::sun::star::uno::Reference; +using ::com::sun::star::uno::Exception; +using namespace ::com::sun::star::animations; + +namespace sd +{ + +struct UndoAnimationImpl +{ + SdPage* mpPage; + Reference< XAnimationNode > mxOldNode; + Reference< XAnimationNode > mxNewNode; + bool mbNewNodeSet; +}; + +UndoAnimation::UndoAnimation( SdDrawDocument* pDoc, SdPage* pThePage ) +: SdrUndoAction( *pDoc ), mpImpl( new UndoAnimationImpl ) +{ + mpImpl->mpPage = pThePage; + mpImpl->mbNewNodeSet = false; + + try + { + if( pThePage->mxAnimationNode.is() ) + mpImpl->mxOldNode = ::sd::Clone( pThePage->getAnimationNode() ); + } + catch( Exception& ) + { + TOOLS_WARN_EXCEPTION( "sd", "sd::UndoAnimation::UndoAnimation()"); + } +} + +UndoAnimation::~UndoAnimation() +{ +} + +void UndoAnimation::Undo() +{ + try + { + if( !mpImpl->mbNewNodeSet ) + { + if( mpImpl->mpPage->mxAnimationNode.is() ) + mpImpl->mxNewNode.set( ::sd::Clone( mpImpl->mpPage->mxAnimationNode ) ); + mpImpl->mbNewNodeSet = true; + } + + Reference< XAnimationNode > xOldNode; + if( mpImpl->mxOldNode.is() ) + xOldNode = ::sd::Clone( mpImpl->mxOldNode ); + + mpImpl->mpPage->setAnimationNode( xOldNode ); + } + catch( Exception& ) + { + TOOLS_WARN_EXCEPTION( "sd", "sd::UndoAnimation::Undo()"); + } +} + +void UndoAnimation::Redo() +{ + try + { + Reference< XAnimationNode > xNewNode; + if( mpImpl->mxNewNode.is() ) + xNewNode = ::sd::Clone( mpImpl->mxNewNode ); + mpImpl->mpPage->setAnimationNode( xNewNode ); + } + catch( Exception& ) + { + TOOLS_WARN_EXCEPTION( "sd", "sd::UndoAnimation::Redo()"); + } +} + +OUString UndoAnimation::GetComment() const +{ + return SdResId(STR_UNDO_ANIMATION); +} + +struct UndoAnimationPathImpl +{ + SdPage* mpPage; + sal_Int32 mnEffectOffset; + OUString msUndoPath; + OUString msRedoPath; + + UndoAnimationPathImpl( SdPage* pThePage, const css::uno::Reference< css::animations::XAnimationNode >& xNode ) + : mpPage( pThePage ) + , mnEffectOffset( -1 ) + { + if( !(mpPage && xNode.is()) ) + return; + + std::shared_ptr< sd::MainSequence > pMainSequence( mpPage->getMainSequence() ); + if( pMainSequence ) + { + CustomAnimationEffectPtr pEffect( pMainSequence->findEffect( xNode ) ); + if( pEffect ) + { + mnEffectOffset = pMainSequence->getOffsetFromEffect( pEffect ); + msUndoPath = pEffect->getPath(); + } + } + } + UndoAnimationPathImpl(const UndoAnimationPathImpl&) = delete; + UndoAnimationPathImpl& operator=(const UndoAnimationPathImpl&) = delete; + + CustomAnimationEffectPtr getEffect() const + { + CustomAnimationEffectPtr pEffect; + if( mpPage && (mnEffectOffset >= 0) ) + { + std::shared_ptr< sd::MainSequence > pMainSequence( mpPage->getMainSequence() ); + if( pMainSequence ) + pEffect = pMainSequence->getEffectFromOffset( mnEffectOffset ); + } + return pEffect; + } +}; + +UndoAnimationPath::UndoAnimationPath( SdDrawDocument* pDoc, SdPage* pThePage, const css::uno::Reference< css::animations::XAnimationNode >& xNode ) +: SdrUndoAction( *pDoc ) +, mpImpl( new UndoAnimationPathImpl( pThePage, xNode ) ) +{ +} + +UndoAnimationPath::~UndoAnimationPath() +{ +} + +void UndoAnimationPath::Undo() +{ + CustomAnimationEffectPtr pEffect = mpImpl->getEffect(); + if( pEffect ) + { + mpImpl->msRedoPath = pEffect->getPath(); + pEffect->setPath( mpImpl->msUndoPath ); + } +} + +void UndoAnimationPath::Redo() +{ + CustomAnimationEffectPtr pEffect = mpImpl->getEffect(); + if( pEffect ) + { + pEffect->setPath( mpImpl->msRedoPath ); + } +} + +OUString UndoAnimationPath::GetComment() const +{ + return SdResId(STR_UNDO_ANIMATION); +} + +struct UndoTransitionImpl +{ + SdPage* mpPage; + + sal_Int16 mnNewTransitionType; + sal_Int16 mnNewTransitionSubtype; + bool mbNewTransitionDirection; + sal_Int32 mnNewTransitionFadeColor; + double mfNewTransitionDuration; + OUString maNewSoundFile; + bool mbNewSoundOn; + bool mbNewLoopSound; + bool mbNewStopSound; + + sal_Int16 mnOldTransitionType; + sal_Int16 mnOldTransitionSubtype; + bool mbOldTransitionDirection; + sal_Int32 mnOldTransitionFadeColor; + double mfOldTransitionDuration; + OUString maOldSoundFile; + bool mbOldSoundOn; + bool mbOldLoopSound; + bool mbOldStopSound; +}; + +UndoTransition::UndoTransition( SdDrawDocument* _pDoc, SdPage* pThePage ) +: SdUndoAction( _pDoc ), mpImpl( new UndoTransitionImpl ) +{ + mpImpl->mpPage = pThePage; + + mpImpl->mnNewTransitionType = -1; + mpImpl->mnOldTransitionType = pThePage->mnTransitionType; + mpImpl->mnOldTransitionSubtype = pThePage->mnTransitionSubtype; + mpImpl->mbOldTransitionDirection = pThePage->mbTransitionDirection; + mpImpl->mnOldTransitionFadeColor = pThePage->mnTransitionFadeColor; + mpImpl->mfOldTransitionDuration = pThePage->mfTransitionDuration; + mpImpl->maOldSoundFile = pThePage->maSoundFile; + mpImpl->mbOldSoundOn = pThePage->mbSoundOn; + mpImpl->mbOldLoopSound = pThePage->mbLoopSound; + mpImpl->mbOldStopSound = pThePage->mbStopSound; +} + +UndoTransition::~UndoTransition() +{ +} + +void UndoTransition::Undo() +{ + if( mpImpl->mnNewTransitionType == -1 ) + { + mpImpl->mnNewTransitionType = mpImpl->mpPage->mnTransitionType; + mpImpl->mnNewTransitionSubtype = mpImpl->mpPage->mnTransitionSubtype; + mpImpl->mbNewTransitionDirection = mpImpl->mpPage->mbTransitionDirection; + mpImpl->mnNewTransitionFadeColor = mpImpl->mpPage->mnTransitionFadeColor; + mpImpl->mfNewTransitionDuration = mpImpl->mpPage->mfTransitionDuration; + mpImpl->maNewSoundFile = mpImpl->mpPage->maSoundFile; + mpImpl->mbNewSoundOn = mpImpl->mpPage->mbSoundOn; + mpImpl->mbNewLoopSound = mpImpl->mpPage->mbLoopSound; + mpImpl->mbNewStopSound = mpImpl->mpPage->mbStopSound; + } + + mpImpl->mpPage->mnTransitionType = mpImpl->mnOldTransitionType; + mpImpl->mpPage->mnTransitionSubtype = mpImpl->mnOldTransitionSubtype; + mpImpl->mpPage->mbTransitionDirection = mpImpl->mbOldTransitionDirection; + mpImpl->mpPage->mnTransitionFadeColor = mpImpl->mnOldTransitionFadeColor; + mpImpl->mpPage->mfTransitionDuration = mpImpl->mfOldTransitionDuration; + mpImpl->mpPage->maSoundFile = mpImpl->maOldSoundFile; + mpImpl->mpPage->mbSoundOn = mpImpl->mbOldSoundOn; + mpImpl->mpPage->mbLoopSound = mpImpl->mbOldLoopSound; + mpImpl->mpPage->mbStopSound = mpImpl->mbOldStopSound; +} + +void UndoTransition::Redo() +{ + mpImpl->mpPage->mnTransitionType = mpImpl->mnNewTransitionType; + mpImpl->mpPage->mnTransitionSubtype = mpImpl->mnNewTransitionSubtype; + mpImpl->mpPage->mbTransitionDirection = mpImpl->mbNewTransitionDirection; + mpImpl->mpPage->mnTransitionFadeColor = mpImpl->mnNewTransitionFadeColor; + mpImpl->mpPage->mfTransitionDuration = mpImpl->mfNewTransitionDuration; + mpImpl->mpPage->maSoundFile = mpImpl->maNewSoundFile; + mpImpl->mpPage->mbSoundOn = mpImpl->mbNewSoundOn; + mpImpl->mpPage->mbLoopSound = mpImpl->mbNewLoopSound; + mpImpl->mpPage->mbStopSound = mpImpl->mbNewStopSound; +} + +OUString UndoTransition::GetComment() const +{ + return SdResId(STR_UNDO_SLIDE_PARAMS); +} + +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ |